├── .clang-format ├── .dev └── Dockerfile ├── .dockerignore ├── .github ├── FUNDING.yml └── workflows │ ├── build-shared.yml │ └── build-static.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── clang_format.cmake ├── compiler_options.cmake ├── cpack.cmake ├── cppcheck.cmake └── dependencies.cmake ├── conanfile.txt ├── cppcheck_suppressions.txt └── project ├── CMakeLists.txt ├── helloapp ├── CMakeLists.txt └── src │ └── helloapp.cpp └── hellolib ├── CMakeLists.txt ├── include └── hellolib.h ├── src └── hellolib.cpp └── test └── hellolib_test.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | ColumnLimit: 80 5 | IndentWidth: 2 6 | Standard: c++17 -------------------------------------------------------------------------------- /.dev/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | # Define Conan and CMake versions 4 | ARG CONAN_VERSION=2.16.1 5 | ARG CMAKE_VERSION=4.0.2 6 | 7 | # Define User ID and Group ID 8 | ARG USER_ID=1000 9 | ARG GROUP_ID=1000 10 | 11 | WORKDIR /project 12 | 13 | # Define Labels 14 | LABEL maintainer="Michele Adduci " \ 15 | description="Docker image for C++ development with Conan and CMake" \ 16 | cmake.version="${CMAKE_VERSION}" \ 17 | conan.version="${CONAN_VERSION}" 18 | 19 | # Install basic tooling 20 | RUN apt-get update && \ 21 | apt-get install -y \ 22 | build-essential \ 23 | curl \ 24 | gcc \ 25 | cppcheck \ 26 | clang-format && \ 27 | apt-get clean && \ 28 | rm -rf /var/lib/apt/lists/* 29 | 30 | # Install Conan and CMake from GitHub Releases 31 | RUN curl -fsSL https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/conan-${CONAN_VERSION}-amd64.deb -o /tmp/conan.deb && \ 32 | dpkg -i /tmp/conan.deb && \ 33 | curl -fsSL https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.tar.gz -o /tmp/cmake.tar.gz && \ 34 | mkdir -p /opt/cmake && \ 35 | tar -xzf /tmp/cmake.tar.gz -C /opt/cmake/ --strip-components=1 && \ 36 | chmod +x /opt/cmake/bin/* && \ 37 | ln -sf /opt/cmake/bin/cmake /usr/bin/cmake && \ 38 | ln -sf /opt/cmake/bin/ctest /usr/bin/ctest && \ 39 | ln -sf /opt/cmake/bin/cpack /usr/bin/cpack && \ 40 | ln -sf /opt/cmake/bin/ccmake /usr/bin/ccmake && \ 41 | rm -rf /tmp/* 42 | 43 | # Create non-root user to run container with less privileges 44 | USER ${USER_ID} 45 | 46 | # Run Conan profile detection as non-root user 47 | RUN conan profile detect 48 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | *.yml 2 | .travis/ 3 | README.md 4 | .git 5 | .gitignore -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: madduci 4 | -------------------------------------------------------------------------------- /.github/workflows/build-shared.yml: -------------------------------------------------------------------------------- 1 | name: Build-Shared 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [ubuntu-latest, macos-latest, windows-latest] 13 | 14 | runs-on: ${{ matrix.os }} 15 | 16 | env: 17 | CONAN_USER_HOME: "${{ github.workspace }}/release" 18 | CONAN_USER_HOME_SHORT: "${{ github.workspace }}/release/short" 19 | INSTALL_DIR: ${{ github.workspace }}/install/ 20 | 21 | steps: 22 | - name: Perform checkout 23 | uses: actions/checkout@v4 24 | 25 | - name: Install Python environment 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: '3.12' 29 | 30 | - name: Install conan 31 | run: | 32 | pip install conan --upgrade 33 | conan profile detect --force 34 | 35 | - name: Linux build 36 | if: matrix.os == 'ubuntu-latest' 37 | run: | 38 | mkdir -p build && cd build 39 | conan install .. --output-folder=. --build=missing 40 | cmake .. -DBUILD_TESTING=TRUE -DBUILD_SHARED_LIBS=TRUE -DCMAKE_BUILD_TYPE=Release 41 | cmake --build . --config Release --target install 42 | 43 | - name: Mac build 44 | if: matrix.os == 'macos-latest' 45 | run: | 46 | mkdir -p build && cd build 47 | conan install .. --output-folder=. --build=missing 48 | cmake .. -DBUILD_TESTING=TRUE -DBUILD_SHARED_LIBS=TRUE -DCMAKE_BUILD_TYPE=Release 49 | cmake --build . --config Release --target install 50 | 51 | - name: Windows build 52 | if: matrix.os == 'windows-latest' 53 | run: | 54 | md build && cd build 55 | conan install .. --output-folder=. --build=missing 56 | cmake .. -DBUILD_TESTING=TRUE -DBUILD_SHARED_LIBS=TRUE -DCMAKE_BUILD_TYPE=Release 57 | cmake --build . --config Release --target install 58 | -------------------------------------------------------------------------------- /.github/workflows/build-static.yml: -------------------------------------------------------------------------------- 1 | name: Build-Static 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [ubuntu-latest, macos-latest, windows-latest] 13 | 14 | runs-on: ${{ matrix.os }} 15 | 16 | env: 17 | CONAN_USER_HOME: "${{ github.workspace }}/release" 18 | CONAN_USER_HOME_SHORT: "${{ github.workspace }}/release/short" 19 | INSTALL_DIR: ${{ github.workspace }}/install/ 20 | 21 | steps: 22 | - name: Perform checkout 23 | uses: actions/checkout@v4 24 | 25 | - name: Install Python environment 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: '3.12' 29 | 30 | - name: Install conan 31 | run: | 32 | pip install conan --upgrade 33 | conan profile detect --force 34 | 35 | - name: Linux build 36 | if: matrix.os == 'ubuntu-latest' 37 | run: | 38 | mkdir -p build && cd build 39 | conan install .. --output-folder=. --build=missing 40 | cmake .. -DBUILD_TESTING=TRUE -DBUILD_SHARED_LIBS=FALSE -DCMAKE_BUILD_TYPE=Release 41 | cmake --build . --config Release --target install 42 | 43 | - name: Mac build 44 | if: matrix.os == 'macos-latest' 45 | run: | 46 | mkdir -p build && cd build 47 | conan install .. --output-folder=. --build=missing 48 | cmake .. -DBUILD_TESTING=TRUE -DBUILD_SHARED_LIBS=FALSE -DCMAKE_BUILD_TYPE=Release 49 | cmake --build . --config Release --target install 50 | 51 | - name: Windows build 52 | if: matrix.os == 'windows-latest' 53 | run: | 54 | md build && cd build 55 | conan install .. --output-folder=. --build=missing 56 | cmake .. -DBUILD_TESTING=TRUE -DBUILD_SHARED_LIBS=FALSE -DCMAKE_BUILD_TYPE=Release 57 | cmake --build . --config Release --target install 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.pyc 3 | .vscode 4 | CMakeUserPresets.json -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # Main CMake Project file 5 | 6 | ##################################### 7 | # Define Project-Wide Settings 8 | ##################################### 9 | cmake_minimum_required(VERSION 3.15.0 FATAL_ERROR) 10 | 11 | # Enables the Visibility Property on all target types 12 | cmake_policy(SET CMP0063 NEW) 13 | # Enables the MSVC_RUNTIME_LIBRARY property on targets 14 | cmake_policy(SET CMP0091 NEW) 15 | # Define the Project Name and Description 16 | project(moderncpp-project LANGUAGES CXX DESCRIPTION "moderncpp-project is a starter project for Modern C++") 17 | 18 | # Set Project version (used for library versioning and for CPack) 19 | set(CMAKE_PROJECT_VERSION_MAJOR 1) 20 | set(CMAKE_PROJECT_VERSION_MINOR 2) 21 | set(CMAKE_PROJECT_VERSION_PATCH 0) 22 | set(CMAKE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) 23 | 24 | # Set required C++ Standard 25 | set(CMAKE_CXX_STANDARD 20) 26 | set(CMAKE_CXX_STANDARD_REQUIRED TRUE) 27 | # Generate the compile_commands.json file 28 | set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) 29 | # Set the Relative Path Configurations 30 | set(CMAKE_SKIP_BUILD_RPATH FALSE) 31 | set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 32 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 33 | # Configure the visibility of symbols in targets 34 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 35 | set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) 36 | # Define installation folder and the module path (this one required by conan) 37 | set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install) 38 | set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR} ${CMAKE_MODULE_PATH}) 39 | set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR} ${CMAKE_PREFIX_PATH}) 40 | 41 | ##################################### 42 | # Define Options 43 | ##################################### 44 | option(BUILD_TESTING "Build tests" FALSE) 45 | option(BUILD_SHARED_LIBS "Build shared libraries" FALSE) 46 | option(BUILD_WITH_MT "Build libraries as MultiThreaded DLL (Windows Only)" FALSE) 47 | 48 | ##################################### 49 | # Define CMake Module Imports 50 | ##################################### 51 | include(GNUInstallDirs) 52 | include(CMakePackageConfigHelpers) 53 | include(InstallRequiredSystemLibraries) 54 | include(GenerateExportHeader) 55 | include(${CMAKE_SOURCE_DIR}/cmake/clang_format.cmake) 56 | include(${CMAKE_SOURCE_DIR}/cmake/compiler_options.cmake) 57 | include(${CMAKE_SOURCE_DIR}/cmake/cpack.cmake) 58 | include(${CMAKE_SOURCE_DIR}/cmake/cppcheck.cmake) 59 | include(${CMAKE_SOURCE_DIR}/cmake/dependencies.cmake) 60 | 61 | ##################################### 62 | # Define Targets 63 | ##################################### 64 | add_subdirectory(project) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-Today Michele Adduci 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # moderncpp-project-template 2 | 3 | ![Static Build](https://github.com/madduci/moderncpp-project-template/workflows/Build-Static/badge.svg) 4 | 5 | ![Shared Build](https://github.com/madduci/moderncpp-project-template/workflows/Build-Shared/badge.svg) 6 | 7 | This repository aims to represent a template for Modern C++ projects, including static analysis checks, autoformatting, a BDD/TDD capable test-suite and packaging 8 | 9 | ## Requirements 10 | 11 | * a modern C++17 compiler (`gcc-8`, `clang-6.0`, `MSVC 2017` or above) 12 | * [`cmake`](https://cmake.org) 3.15+ 13 | * [`conan`](https://conan.io) 2.0+ (optional) 14 | * `cppcheck` (optional) 15 | * `clang-format` (optional) 16 | 17 | ## Features 18 | 19 | * CMake-based project management, including dependencies 20 | * Conan support for dependency management in CMake, completely optional 21 | * Additional tools such as `clang-format` and `cppcheck` 22 | * Support for shared/static libraries, including generation of export information 23 | * Basic CPack configuration for redistributables 24 | * GitHub Actions 25 | 26 | ## Repository layout 27 | 28 | The repository layout is pretty straightforward, including the CMake files to build the project, a `conanfile` where are declared examples of dependencies, a suppression list for cppcheck and the C++ source code: 29 | 30 | ```plain 31 | -- conanfile.txt - the main `conan` configuration file listing dependencies 32 | -- cppcheck_suppressions.txt - optional list of suppressions for cppcheck 33 | -- CMakeLists.txt - the main `CMake` Project configuration file 34 | -- .dockerignore - files to be excluded by Docker 35 | -- .gitignore - files to be excluded by git 36 | +- `cmake/` - CMake modules 37 | | -- clang-format.cmake - CMake target definitions for clang-format 38 | | -- compiler-options.cmake - Common compiler options for major platforms/compilers 39 | | -- cpack.cmake - Packaging configuration with CPack 40 | | -- dependencies.cmake - Project dependencies, CMake-Style 41 | +- `project/` - the whole C++ project 42 | | -- .clang-format - the formatter rules for the C++ project 43 | | -- CMakeLists.txt 44 | | +- `helloapp/` - your application files (including CMakeLists.txt, sources) 45 | | +- `hellolib/` - your library files (including CMakeLists.txt, sources 46 | +- `build/` - working directory for the build 47 | ``` 48 | 49 | ## Available CMake Options 50 | 51 | * BUILD_TESTING - builds the tests (requires `doctest`) 52 | * BUILD_SHARED_LIBS - enables or disables the generation of shared libraries 53 | * BUILD_WITH_MT - valid only for MSVC, builds libraries as MultiThreaded DLL 54 | 55 | If you activate the `BUILD_TESTING` flag, you need to perform in advance a `conan install` step, just to fetch the `doctest` dependency. Another dependency (OpenSSL) is used in this project as a demonstration of including a third-party library in the process, but it is totally optional and you can activate it only if you run conan in advance. 56 | 57 | ## How to build from command line 58 | 59 | The project can be built using the following commands: 60 | 61 | ```shell 62 | cd /path/to/this/project 63 | mkdir -p build # md build (on Windows) 64 | cd build 65 | conan install .. --output-folder=. --build=missing 66 | cmake -DBUILD_TESTING=TRUE -DBUILD_SHARED_LIBS=TRUE -DCMAKE_BUILD_TYPE=Release .. 67 | cmake --build . 68 | cmake --build . --target format 69 | cmake --build . --target package 70 | ``` 71 | 72 | ## How to build the project using a Docker Environment 73 | 74 | ### Linux/gcc 75 | 76 | `docker run --rm -it -v $(pwd):/project madduci/docker-cpp-env:latest "mkdir -p build && cd build && conan install .. && cmake -DBUILD_TESTING=TRUE -DBUILD_SHARED_LIBS=TRUE .. && cmake --build ."` 77 | 78 | ### Windows/msvc 79 | 80 | `docker run --rm -it -v $(pwd):/home/wine/.wine/drive_c/project madduci/docker-wine-msvc:17.8-2022` 81 | 82 | and then: 83 | 84 | ``` 85 | md project/build 86 | cd project/build 87 | conan install --output-folder . .. 88 | cmake -G "Ninja" -DBUILD_TESTING=TRUE .. 89 | cmake --build . 90 | ``` 91 | -------------------------------------------------------------------------------- /cmake/clang_format.cmake: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # Clang-Format instructions 5 | 6 | find_program(CLANG_FORMAT_BIN NAMES clang-format) 7 | 8 | if(CLANG_FORMAT_BIN) 9 | message(STATUS "Found: clang-format") 10 | file(GLOB_RECURSE CPP_SOURCE_FILES *.cpp) 11 | file(GLOB_RECURSE CPP_HEADER_FILES *.h) 12 | 13 | add_custom_target( 14 | format-sources 15 | COMMAND clang-format --style=file -i ${CPP_SOURCE_FILES} 16 | COMMAND_EXPAND_LISTS VERBATIM) 17 | 18 | add_custom_target( 19 | format-headers 20 | COMMAND clang-format --style=file -i ${CPP_HEADER_FILES} 21 | COMMAND_EXPAND_LISTS VERBATIM) 22 | 23 | add_custom_target( 24 | format 25 | COMMENT "Running clang-format..." 26 | DEPENDS format-sources format-headers) 27 | endif() 28 | -------------------------------------------------------------------------------- /cmake/compiler_options.cmake: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # Compiler options with hardening flags 5 | 6 | if(MSVC) 7 | 8 | list(APPEND compiler_options 9 | /W4 10 | /permissive- 11 | $<$:/O2 /Ob2 > 12 | $<$:/O1 /Ob1> 13 | $<$:/Zi /O2 /Ob1> 14 | $<$:/Zi /Ob0 /Od /RTC1>) 15 | 16 | list(APPEND compiler_definitions 17 | _UNICODE 18 | WINDOWS 19 | $<$,$,$>:NDEBUG> 20 | $<$:_DEBUG>) 21 | 22 | list(APPEND linker_flags 23 | $<$:/LTCG> 24 | ) 25 | 26 | set(MSVC_RUNTIME_TYPE $,MultiThreaded$<$:Debug>,MultiThreaded$<$:Debug>>DLL) 27 | 28 | else(MSVC) 29 | 30 | list(APPEND compiler_options 31 | -Wall 32 | -Wextra 33 | -Wpedantic 34 | $<$:-O2> 35 | $<$:-O0 -g -p -pg>) 36 | 37 | list(APPEND compiler_definitions 38 | $<$,$>:_FORTIFY_SOURCE=2> 39 | ) 40 | 41 | list(APPEND linker_flags 42 | $<$>:-Wl,-z,defs> 43 | $<$>:-Wl,-z,now> 44 | $<$>:-Wl,-z,relro> 45 | # Clang doesn't support these hardening flags 46 | $<$>,$>,$>>:-Wl,-pie> 47 | $<$>,$>,$>>:-fpie> 48 | $<$>,$>,$>>:-pipe> 49 | $<$>,$>,$>>:-static-libstdc++> 50 | $<$:-fno-omit-frame-pointer> 51 | $<$:-fsanitize=address> 52 | $<$:-fsanitize=leak> 53 | $<$:-fsanitize=undefined> 54 | $<$>,$>>:-fstack-clash-protection> 55 | $<$>,$>>:-fbounds-check> 56 | -fstack-protector 57 | -fPIC) 58 | 59 | endif() 60 | -------------------------------------------------------------------------------- /cmake/cpack.cmake: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # Packaging instructios 5 | 6 | set(CPACK_PACKAGE_VENDOR "Michele Adduci ") 7 | set(CPACK_PACKAGE_VERSION_MAJOR "${CMAKE_PROJECT_VERSION_MAJOR}") 8 | set(CPACK_PACKAGE_VERSION_MINOR "${CMAKE_PROJECT_VERSION_MINOR}") 9 | set(CPACK_PACKAGE_VERSION_PATCH "${CMAKE_PROJECT_VERSION_PATCH}") 10 | set(CPACK_GENERATOR "ZIP;TGZ") 11 | set(CPACK_STRIP_FILES "TRUE") 12 | 13 | include(CPack) -------------------------------------------------------------------------------- /cmake/cppcheck.cmake: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # cppcheck instructions 5 | 6 | find_program(CPPCHECK_BIN NAMES cppcheck) 7 | 8 | if(CPPCHECK_BIN) 9 | message(STATUS "Found: cppcheck") 10 | list( 11 | APPEND CMAKE_CXX_CPPCHECK 12 | "${CPPCHECK_BIN}" 13 | "--enable=all" 14 | "--enable=warning,performance,portability,information" 15 | "--inconclusive" 16 | "--check-config" 17 | "--force" 18 | "--inline-suppr" 19 | "--suppressions-list=${CMAKE_SOURCE_DIR}/cppcheck_suppressions.txt" 20 | "--xml" 21 | "--output-file=${CMAKE_BINARY_DIR}/cppcheck.xml" 22 | ) 23 | endif() 24 | -------------------------------------------------------------------------------- /cmake/dependencies.cmake: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # Dependencies 5 | 6 | # Required for Testing 7 | if(BUILD_TESTING) 8 | find_package(doctest REQUIRED) 9 | endif() 10 | 11 | # Optional Dependency, doesn't trigger error if missing 12 | find_package(OpenSSL) -------------------------------------------------------------------------------- /conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | openssl/3.4.1 3 | doctest/2.4.11 4 | 5 | [generators] 6 | CMakeDeps 7 | 8 | [options] 9 | pkg/openssl:shared=False 10 | -------------------------------------------------------------------------------- /cppcheck_suppressions.txt: -------------------------------------------------------------------------------- 1 | missingIncludeSystem -------------------------------------------------------------------------------- /project/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # Project-related instructions 5 | 6 | # Activate Testing, if given 7 | if(BUILD_TESTING) 8 | enable_testing() 9 | endif() 10 | 11 | ##################################### 12 | # Define Targets 13 | ##################################### 14 | add_subdirectory(hellolib) 15 | add_subdirectory(helloapp) 16 | 17 | ##################################### 18 | # Define Install Targets 19 | ##################################### 20 | install(TARGETS 21 | hellolib 22 | helloapp 23 | RUNTIME DESTINATION bin 24 | LIBRARY DESTINATION lib 25 | ARCHIVE DESTINATION lib 26 | PUBLIC_HEADER DESTINATION include 27 | ) 28 | 29 | install( 30 | FILES 31 | ${CMAKE_SOURCE_DIR}/project/hellolib/include/hellolib.h 32 | ${CMAKE_BINARY_DIR}/exports/hellolib_export.h 33 | DESTINATION 34 | include/hellolib) 35 | 36 | install(FILES 37 | ${CMAKE_SOURCE_DIR}/LICENSE 38 | DESTINATION .) -------------------------------------------------------------------------------- /project/helloapp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # helloapp target instructions 5 | 6 | ##################################### 7 | # Define Target Information 8 | ##################################### 9 | set(TARGET_NAME helloapp) 10 | set(TARGET_INCLUDE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/include) 11 | set(TARGET_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/helloapp.cpp) 12 | 13 | ##################################### 14 | # Support IDE Visualization 15 | ##################################### 16 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${TARGET_SOURCES}) 17 | 18 | ##################################### 19 | # Define Target according to options 20 | ##################################### 21 | add_executable(${TARGET_NAME} ${TARGET_SOURCES}) 22 | 23 | ##################################### 24 | # Define Target Properties and Optional OpenSSL Dependency 25 | ##################################### 26 | 27 | # add target properties such as debug postfix and MSVC runtime 28 | set_target_properties(${TARGET_NAME} PROPERTIES DEBUG_POSTFIX "-d" MSVC_RUNTIME_LIBRARY "${MSVC_RUNTIME_TYPE}") 29 | 30 | # set target compile options as defined in the cmake/compiler_options.cmake Module 31 | target_compile_options(${TARGET_NAME} PRIVATE ${compiler_options}) 32 | 33 | # add compiler definition WITH_OPENSSL, if found 34 | target_compile_definitions(${TARGET_NAME} PRIVATE ${compiler_definitions} $<$:WITH_OPENSSL>) 35 | 36 | # set target link options as defined in the cmake/compiler_options.cmake Module 37 | target_link_options(${TARGET_NAME} PRIVATE ${linker_flags}) 38 | 39 | # include all the necessary directories 40 | target_include_directories(${TARGET_NAME} 41 | PUBLIC 42 | $ 43 | PRIVATE 44 | ${TARGET_INCLUDE_FOLDER} 45 | ${CMAKE_CURRENT_SOURCE_DIR}/../hellolib/include 46 | # Any other extra include required 47 | ) 48 | 49 | # link against hellolib 50 | target_link_libraries(${TARGET_NAME} PUBLIC hellolib) 51 | -------------------------------------------------------------------------------- /project/helloapp/src/helloapp.cpp: -------------------------------------------------------------------------------- 1 | #include "hellolib.h" 2 | 3 | using namespace hello; 4 | 5 | int main() { 6 | hellolib hello{}; 7 | int32_t error_code = hello.saySomething("Hello Modern C++ Development"); 8 | if (error_code > 0) { 9 | return error_code; 10 | } 11 | #ifdef WITH_OPENSSL 12 | error_code = hello.saySomethingHashed("Hello Modern C++ Development"); 13 | if (error_code > 0) { 14 | return error_code; 15 | } 16 | #endif 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /project/hellolib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (c) 2018-Today Michele Adduci 3 | # 4 | # hellolib target instructions 5 | 6 | ##################################### 7 | # Define Target Information 8 | ##################################### 9 | set(TARGET_NAME hellolib) 10 | set(TARGET_INCLUDE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/include) 11 | set(TARGET_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/hellolib.cpp) 12 | 13 | ##################################### 14 | # Support IDE Visualization 15 | ##################################### 16 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${TARGET_SOURCES}) 17 | 18 | ##################################### 19 | # Define Target according to options 20 | ##################################### 21 | if(BUILD_SHARED_LIBS) 22 | add_library(${TARGET_NAME} SHARED ${TARGET_SOURCES}) 23 | else() 24 | add_library(${TARGET_NAME} STATIC ${TARGET_SOURCES}) 25 | endif() 26 | 27 | ##################################### 28 | # Define Target Properties and Optional OpenSSL Dependency 29 | ##################################### 30 | 31 | # generate an export header to expose symbols when using the library 32 | generate_export_header(${TARGET_NAME} EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/${TARGET_NAME}_export.h) 33 | 34 | # add target properties such as debug postfix, MSVC Runtime and library version 35 | set_target_properties(${TARGET_NAME} PROPERTIES 36 | DEBUG_POSTFIX "-d" 37 | MSVC_RUNTIME_LIBRARY "${MSVC_RUNTIME_TYPE}" 38 | VERSION ${CMAKE_PROJECT_VERSION} 39 | SOVERSION ${CMAKE_PROJECT_VERSION}) 40 | 41 | # set target compile options as defined in the cmake/compiler_options.cmake Module 42 | target_compile_options(${TARGET_NAME} PRIVATE ${compiler_options}) 43 | 44 | # add compiler definition WITH_OPENSSL, if found 45 | target_compile_definitions(${TARGET_NAME} PRIVATE ${compiler_definitions} $<$:WITH_OPENSSL>) 46 | 47 | # set target link options as defined in the cmake/compiler_options.cmake Module 48 | target_link_options(${TARGET_NAME} PRIVATE ${linker_flags}) 49 | 50 | # include all the necessary directories 51 | target_include_directories(${TARGET_NAME} 52 | PUBLIC 53 | $ 54 | PRIVATE 55 | ${TARGET_INCLUDE_FOLDER} 56 | # include OpenSSL directories if present 57 | $<$:${OPENSSL_INCLUDE_DIR}> 58 | # Any other extra include required 59 | ) 60 | 61 | # link OpenSSL libraries if present 62 | if(OPENSSL_FOUND) 63 | target_link_libraries(${TARGET_NAME} PUBLIC OpenSSL::SSL OpenSSL::Crypto) 64 | endif() 65 | 66 | if(BUILD_TESTING) 67 | list(APPEND TEST_CASES 68 | hellolib_test 69 | ) 70 | foreach(TEST_CASE ${TEST_CASES}) 71 | add_executable(${TEST_CASE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${TEST_CASE}.cpp) 72 | target_link_libraries(${TEST_CASE} PRIVATE ${TARGET_NAME} doctest::doctest) 73 | target_compile_options(${TEST_CASE} PRIVATE ${compiler_options}) 74 | target_compile_definitions(${TEST_CASE} PRIVATE ${compiler_definitions} $<$:WITH_OPENSSL>) 75 | target_link_options(${TEST_CASE} PRIVATE ${linker_flags}) 76 | 77 | target_include_directories(${TEST_CASE} 78 | PUBLIC 79 | $ 80 | PRIVATE 81 | ${TARGET_INCLUDE_FOLDER} 82 | # Any other extra include required 83 | ) 84 | 85 | add_test(NAME ${TEST_CASE} COMMAND ${TEST_CASE}) 86 | endforeach(TEST_CASE ${TEST_CASES}) 87 | endif() -------------------------------------------------------------------------------- /project/hellolib/include/hellolib.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "hellolib_export.h" 4 | 5 | namespace hello { 6 | class HELLOLIB_EXPORT hellolib { 7 | public: 8 | [[nodiscard]] int32_t saySomething( 9 | const std::string &something) const noexcept; 10 | #ifdef WITH_OPENSSL 11 | [[nodiscard]] int32_t saySomethingHashed( 12 | const std::string &something) const noexcept; 13 | #endif 14 | }; 15 | } // namespace hello -------------------------------------------------------------------------------- /project/hellolib/src/hellolib.cpp: -------------------------------------------------------------------------------- 1 | #include "hellolib.h" 2 | 3 | #include 4 | 5 | #ifdef WITH_OPENSSL 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #endif 12 | 13 | using namespace hello; 14 | 15 | int32_t hellolib::saySomething(const std::string &something) const noexcept { 16 | if (something.empty()) { 17 | std::cerr << "No value passed\n"; 18 | return 1; 19 | } 20 | 21 | std::cout << something << '\n'; 22 | return 0; 23 | } 24 | 25 | #ifdef WITH_OPENSSL 26 | int32_t hellolib::saySomethingHashed( 27 | const std::string &something) const noexcept { 28 | if (something.empty()) { 29 | std::cerr << "No value passed\n"; 30 | return 1; 31 | } 32 | 33 | EVP_MD_CTX *context = EVP_MD_CTX_new(); 34 | if (context == NULL) { 35 | std::cerr << "Failed to create context\n"; 36 | return 2; 37 | } 38 | 39 | if (1 != EVP_DigestInit_ex(context, EVP_sha256(), NULL)) { 40 | std::cerr << "Failed to initialize context\n"; 41 | return 3; 42 | } 43 | 44 | if (1 != EVP_DigestUpdate(context, (unsigned char *)something.c_str(), 45 | something.size())) { 46 | std::cerr << "Failed to create hash value\n"; 47 | return 4; 48 | } 49 | 50 | std::array buffer{}; 51 | if (1 != EVP_DigestFinal_ex(context, buffer.data(), NULL)) { 52 | std::cerr << "Failed to finalize hash result\n"; 53 | return 5; 54 | } 55 | 56 | // Transform byte-array to string 57 | std::stringstream shastr; 58 | shastr << std::hex << std::setfill('0'); 59 | for (const auto &byte : buffer) { 60 | shastr << std::setw(2) << (int)byte; 61 | } 62 | 63 | std::cout << shastr.str() << '\n'; 64 | return 0; 65 | } 66 | #endif -------------------------------------------------------------------------------- /project/hellolib/test/hellolib_test.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include "hellolib.h" 3 | 4 | #include 5 | 6 | using namespace hello; 7 | 8 | SCENARIO("Implementation shoud proceed successfully") { 9 | GIVEN("An implementation") { 10 | // WRITE here your implemented object 11 | hellolib hello{}; 12 | WHEN("it is called correctly") { 13 | // WRITE here something 14 | auto return_code = hello.saySomething("I love BDD"); 15 | THEN("The return code should indicate success") { 16 | REQUIRE(return_code == 0); 17 | } 18 | } 19 | AND_WHEN("its is called badly") { 20 | auto return_code = hello.saySomething(""); 21 | THEN("The return code should indicate failure") { 22 | REQUIRE(return_code == 1); 23 | } 24 | } 25 | } 26 | } 27 | 28 | #ifdef WITH_OPENSSL 29 | SCENARIO("Implementation with OpenSSL shoud proceed successfully") { 30 | GIVEN("An implementation") { 31 | // WRITE here your implemented object 32 | hellolib hello{}; 33 | WHEN("it is called correctly") { 34 | // WRITE here something 35 | auto return_code = hello.saySomethingHashed("I love BDD"); 36 | THEN("The return code should indicate success") { 37 | REQUIRE(return_code == 0); 38 | } 39 | } 40 | AND_WHEN("its is called badly") { 41 | auto return_code = hello.saySomethingHashed(""); 42 | THEN("The return code should indicate failure") { 43 | REQUIRE(return_code > 0); 44 | } 45 | } 46 | } 47 | } 48 | #endif --------------------------------------------------------------------------------