diff --git a/.clang-format b/.clang-format index 1fdaf20..78f8042 100644 --- a/.clang-format +++ b/.clang-format @@ -1,4 +1,11 @@ --- +BasedOnStyle: Google +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +IndentWidth: 2 +TabWidth: 2 +UseTab: Never +--- Language: Cpp Standard: c++17 UseTab: Never diff --git a/.cmake-format.yaml b/.cmake-format.yaml new file mode 100644 index 0000000..988038f --- /dev/null +++ b/.cmake-format.yaml @@ -0,0 +1,16 @@ +format: + line_width: 120 + tab_size: 4 + max_subgroups_hwrap: 4 + max_pargs_hwrap: 4 + max_rows_cmdline: 8 + separate_ctrl_name_with_space: false + separate_fn_name_with_space: false + dangle_parens: true + min_prefix_chars: 8 + max_lines_hwrap: 3 + line_ending: unix +markup: + bullet_char: '*' + enum_char: . + enable_markup: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e33a40e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,74 @@ +--- +name: Continuous Integration + +on: + push: + branches: + - master + - develop + + pull_request: + branches: + - develop + +jobs: + test: + strategy: + fail-fast: false + + matrix: + os: [macos-15, ubuntu-24.04, windows-2022] + + type: [Debug, Release] + + # link: [shared, static] + # include: + # - {link: shared, shared: YES} + # - {link: static, shared: NO} + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Cpp + if: startsWith(matrix.os, 'macos') + uses: aminya/setup-cpp@v1 + with: + # compiler: llvm-18 + clangtidy: true + cmake: true + ninja: true + + - name: Install static analyzers + if: matrix.os == 'ubuntu-24.04' + run: >- + sudo apt-get install clang-tidy-18 ninja-build -y -q + + sudo update-alternatives --install + /usr/bin/clang-tidy clang-tidy + /usr/bin/clang-tidy-18 140 + + - name: Setup MultiToolTask + if: matrix.os == 'windows-2022' + run: | + Add-Content "$env:GITHUB_ENV" 'UseMultiToolTask=true' + Add-Content "$env:GITHUB_ENV" 'EnforceProcessCountAcrossBuilds=true' + + - name: Configure + shell: pwsh + run: cmake --preset=${{ matrix.type }} + + - name: Setup PATH + if: matrix.os == 'windows-2022' && matrix.type == 'shared' + run: Add-Content "$env:GITHUB_PATH" "$(Get-Location)\build\${{ matrix.type }}" + + - name: Build + run: cmake --build build --config ${{ matrix.type }} -j 2 + + - name: Test + working-directory: build + run: ctest --output-on-failure --no-tests=error -C ${{ matrix.type }} + + - name: Install + run: cmake --install build --prefix prefix --config ${{ matrix.type }} diff --git a/.gitignore b/.gitignore index d484323..a0fd4da 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build*/ _build*/ cmake-build*/ +/stagedir/ # CMake CMakeUserPresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e7f909..2ef93a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,5 @@ -cmake_minimum_required(VERSION 3.14) -project(mylib - VERSION 1.0.0 - DESCRIPTION "Template for C++ library built with CMake" - LANGUAGES CXX) +cmake_minimum_required(VERSION 3.25...3.30) +project(mylib VERSION 1.0.0 DESCRIPTION "Template for C++ library built with CMake" LANGUAGES CXX) #---------------------------------------------------------------------------------------------------------------------- # general settings and options @@ -11,15 +8,18 @@ project(mylib include(cmake/utils.cmake) include(GNUInstallDirs) -string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" is_top_level) - # MYLIB_SHARED_LIBS option (undefined by default) can be used to force shared/static build -option(MYLIB_BUILD_TESTS "Build mylib tests" OFF) -option(MYLIB_BUILD_EXAMPLES "Build mylib examples" OFF) +option(MYLIB_BUILD_TESTS "Build mylib tests" ${PROJECT_IS_TOP_LEVEL}) +option(MYLIB_BUILD_EXAMPLES "Build mylib examples" ${PROJECT_IS_TOP_LEVEL}) option(MYLIB_BUILD_DOCS "Build mylib documentation" OFF) -option(MYLIB_INSTALL "Generate target for installing mylib" ${is_top_level}) -set_if_undefined(MYLIB_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/mylib" CACHE STRING - "Install path for mylib package-related CMake files") +option(MYLIB_INSTALL "Generate target for installing mylib" ${PROJECT_IS_TOP_LEVEL}) +set_if_undefined( + MYLIB_INSTALL_CMAKEDIR + "${CMAKE_INSTALL_LIBDIR}/cmake/mylib" + CACHE + STRING + "Install path for mylib package-related CMake files" +) if(DEFINED MYLIB_SHARED_LIBS) set(BUILD_SHARED_LIBS ${MYLIB_SHARED_LIBS}) @@ -27,9 +27,20 @@ endif() if(NOT DEFINED CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") + set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS + "Debug" + "Release" + "MinSizeRel" + "RelWithDebInfo" + ) endif() +# Neither of these two are technically needed, but they make the expectation clear +set_if_undefined(CMAKE_CXX_STANDARD 17) +set_if_undefined(CMAKE_CXX_EXTENSIONS FALSE) + set_if_undefined(CMAKE_CXX_VISIBILITY_PRESET hidden) set_if_undefined(CMAKE_VISIBILITY_INLINES_HIDDEN ON) @@ -55,10 +66,7 @@ endif() generate_export_header(mylib EXPORT_FILE_NAME include/mylib/${export_file_name}) -set(sources - include/mylib/export.h - include/mylib/mylib.h - src/mylib.cpp) +set(sources include/mylib/export.h include/mylib/mylib.h src/mylib.cpp) source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources}) #---------------------------------------------------------------------------------------------------------------------- @@ -67,36 +75,42 @@ source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources}) include(CMakePackageConfigHelpers) -target_sources(mylib PRIVATE ${sources}) -target_compile_definitions(mylib PUBLIC "$<$>:MYLIB_STATIC_DEFINE>") +target_sources(mylib PRIVATE src/mylib.cpp) -target_include_directories(mylib +# cmake-format: off +target_sources(mylib PUBLIC - "$" - "$") + FILE_SET HEADERS + BASE_DIRS + include + ${CMAKE_CURRENT_BINARY_DIR}/include + FILES + include/mylib/mylib.h + include/mylib/export.h + ${CMAKE_CURRENT_BINARY_DIR}/include/mylib/${export_file_name} +) +# cmake-format: on + +target_compile_definitions(mylib PUBLIC "$<$>:MYLIB_STATIC_DEFINE>") -set_target_properties(mylib PROPERTIES - SOVERSION ${PROJECT_VERSION_MAJOR} - VERSION ${PROJECT_VERSION}) +set_target_properties(mylib PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR} VERSION ${PROJECT_VERSION}) if(MYLIB_INSTALL AND NOT CMAKE_SKIP_INSTALL_RULES) - configure_package_config_file(cmake/mylib-config.cmake.in mylib-config.cmake - INSTALL_DESTINATION "${MYLIB_INSTALL_CMAKEDIR}") - - write_basic_package_version_file(mylib-config-version.cmake - COMPATIBILITY SameMajorVersion) - - install(TARGETS mylib EXPORT mylib_export - RUNTIME COMPONENT mylib - LIBRARY COMPONENT mylib NAMELINK_COMPONENT mylib-dev - ARCHIVE COMPONENT mylib-dev - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - install(DIRECTORY include/ - TYPE INCLUDE - COMPONENT mylib-dev) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/mylib/${export_file_name}" - COMPONENT mylib-dev - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/mylib") + configure_package_config_file( + cmake/mylib-config.cmake.in mylib-config.cmake INSTALL_DESTINATION "${MYLIB_INSTALL_CMAKEDIR}" + ) + + write_basic_package_version_file(mylib-config-version.cmake COMPATIBILITY SameMajorVersion) + + # cmake-format: off + install(TARGETS mylib + EXPORT mylib_export + RUNTIME COMPONENT mylib + LIBRARY COMPONENT mylib NAMELINK_COMPONENT mylib-dev + ARCHIVE COMPONENT mylib-dev + FILE_SET HEADERS COMPONENT mylib-dev + ) + # cmake-format: on set(targets_file "mylib-shared-targets.cmake") @@ -105,16 +119,16 @@ if(MYLIB_INSTALL AND NOT CMAKE_SKIP_INSTALL_RULES) endif() install(EXPORT mylib_export - COMPONENT mylib-dev - FILE "${targets_file}" - DESTINATION "${MYLIB_INSTALL_CMAKEDIR}" - NAMESPACE mylib::) + COMPONENT mylib-dev + FILE "${targets_file}" + DESTINATION "${MYLIB_INSTALL_CMAKEDIR}" + NAMESPACE mylib:: + ) - install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mylib-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/mylib-config-version.cmake" - COMPONENT mylib-dev - DESTINATION "${MYLIB_INSTALL_CMAKEDIR}") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mylib-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/mylib-config-version.cmake" COMPONENT mylib-dev + DESTINATION "${MYLIB_INSTALL_CMAKEDIR}" + ) if(MSVC) set(pdb_file "") @@ -130,11 +144,14 @@ if(MYLIB_INSTALL AND NOT CMAKE_SKIP_INSTALL_RULES) endif() install(FILES "${pdb_file}" - COMPONENT mylib-dev - CONFIGURATIONS Debug RelWithDebInfo - DESTINATION "${pdb_file_destination}" - OPTIONAL) + COMPONENT mylib-dev + CONFIGURATIONS Debug RelWithDebInfo + DESTINATION "${pdb_file_destination}" + OPTIONAL + ) endif() + + include(CPack) endif() #---------------------------------------------------------------------------------------------------------------------- diff --git a/CMakePresets.json b/CMakePresets.json index a8adc4e..1eac1e0 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,38 +1,123 @@ { - "version": 3, + "version": 9, "cmakeMinimumRequired": { "major": 3, - "minor": 14, + "minor": 30, "patch": 0 }, - "configurePresets": [ - { - "name": "dev", - "description": "Base preset for library developers", - "binaryDir": "${sourceDir}/build", - "hidden": true, - "cacheVariables": { - "MYLIB_BUILD_TESTS": "ON", - "MYLIB_BUILD_EXAMPLES": "ON" - } + "include": [ + "cmake/presets/CMake${hostSystemName}Presets.json" + ], + "buildPresets": [ + { + "name": "Debug", + "configurePreset": "Debug" + }, + { + "name": "Release", + "configurePreset": "Release" }, { - "name": "dev-win", - "description": "Windows preset for library developers", - "hidden": false, - "inherits": ["dev"], - "cacheVariables": { - "CMAKE_CXX_FLAGS": "/W4 /EHsc /w14242 /w14254 /w14263 /w14265 /w14287 /w14289 /w14296 /w14311 /w14545 /w14546 /w14547 /w14549 /w14555 /w14640 /w14826 /w14928 /WX" + "name": "install-Debug", + "configurePreset": "Debug", + "targets": [ + "install" + ] + }, + { + "name": "verify_interface_header-Debug", + "configurePreset": "Debug", + "targets": [ + "all_verify_interface_header_sets" + ] + } + ], + "testPresets": [ + { + "name": "Debug", + "configurePreset": "Debug", + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "ignore", + "stopOnFailure": true } }, { - "name": "dev-linux", - "description": "Linux preset for library developers", - "hidden": false, - "inherits": ["dev"], - "cacheVariables": { - "CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wsign-conversion -Wcast-align -Wcast-qual -Wnull-dereference -Woverloaded-virtual -Wformat=2 -Werror" + "name": "Release", + "configurePreset": "Release", + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error", + "stopOnFailure": false } } + ], + "packagePresets": [ + { + "name": "Debug", + "configurePreset": "Debug", + "generators": [ + "TGZ" + ] + }, + { + "name": "Release", + "configurePreset": "Release", + "generators": [ + "TGZ" + ] + } + ], + "workflowPresets": [ + { + "name": "Debug", + "steps": [ + { + "type": "configure", + "name": "Debug" + }, + { + "type": "build", + "name": "Debug" + }, + { + "type": "test", + "name": "Debug" + }, + { + "type": "build", + "name": "verify_interface_header-Debug" + }, + { + "type": "build", + "name": "install-Debug" + } + ] + }, + { + "name": "Release", + "steps": [ + { + "type": "configure", + "name": "Release" + }, + { + "type": "build", + "name": "Release" + }, + { + "type": "test", + "name": "Release" + }, + { + "type": "package", + "name": "Release" + } + ] + } ] } diff --git a/README.md b/README.md index 8987554..7463450 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,42 @@ # Overview -Template repository to be used as a base for creating C++ libraries built with CMake. This repository provides: -* Commonly accepted directory layout: `cmake` for CMake utilities and package config, `include/` for public headers, `src` for library sources and private headers, `examples` and `tests` for library examples and tests correspondingly. +Template repository to be used as a base for creating C++ libraries built with CMake. + +## This repository provides: + +* Commonly accepted directory layout: + + - `cmake` for CMake utilities and package config + - `include/` for public headers + - `src` for library sources and private headers + - `examples` + - `tests` for library examples and tests correspondingly + * `CMakeLists.txt` files thoroughly implemented with the following ideas in mind: - * user should be able to build library both as subproject (probably fetched with CMake's [FetchContent_MakeAvailable](https://cmake.org/cmake/help/latest/module/FetchContent.html)) and as a stand-alone project - * user should be able to build examples/tests as part of the library build or as a stand-alone projects - * multi-configuration CMake generators should be supported - * package systems (conan, vcpkg and others) should be respected, which means that library `CMakeLists.txt` file should contain only **build requirements**, i.e. should not hardcode any compiler/linker flags unless they are absolutely required to build the library -* Basic [preset](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) file `CMakePresets.json` which is a modern way (since CMake 3.19) for specifying build options (instead of hardcoding them in the `CMakeLists.txt` or setting on the command line) + + - user should be able to build library both as subproject (probably fetched with CMake's + [FetchContent_MakeAvailable](https://cmake.org/cmake/help/latest/module/FetchContent.html)) and as a stand-alone project + - user should be able to build examples/tests as part of the library build or as a stand-alone projects + - multi-configuration CMake generators should be supported + - package systems (conan, vcpkg and others) should be respected, which means that library `CMakeLists.txt` file should contain + only **build requirements**, i.e. should not hardcode any compiler/linker flags unless they are absolutely required to build + the library + +* [CMake Workflow preset](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) file `CMakePresets.json` which is a + modern way (since CMake 3.25) for specifying build options (instead of hardcoding them in the `CMakeLists.txt` or setting + on the command line) + * My personal `.clang-format` for code-style worth trying -Note, that I prefer [googletest](https://github.com/google/googletest) as a testing framework so it's the one used in this repository. Still it's not a big deal to replace it with any other of your choice. +* And too `.cmake-format.yml` to format the CMake files + +Note, that I prefer [googletest](https://github.com/google/googletest) as a testing framework so it's the one used in this +repository. Still it's not a big deal to replace it with any other of your choice. + + +## Links + +* Anton Pantyukhin - [Modern CMake tutorial for C++ library developers](https://medium.com/@pananton/modern-cmake-tutorial-for-c-library-developers-12f3bf57dc87) +* Dominik Berner - [Organizing CMake presets](https://dominikberner.ch/cmake-presets-best-practices/) +* Craig Scott -[C++20 Modules, CMake, And Shared Libraries](https://crascit.com/category/cmake/) +* [cmake-init - CMake project generator](https://github.com/friendlyanon/cmake-init) that creates good `CMakeLists.tx` files diff --git a/cmake/presets/CMakeDarwinPresets.json b/cmake/presets/CMakeDarwinPresets.json new file mode 100644 index 0000000..7efefcd --- /dev/null +++ b/cmake/presets/CMakeDarwinPresets.json @@ -0,0 +1,11 @@ +{ + "version": 4, + "cmakeMinimumRequired": { + "major": 3, + "minor": 23, + "patch": 0 + }, + "include": [ + "CMakeLinuxPresets.json" + ] +} diff --git a/cmake/presets/CMakeGenericPresets.json b/cmake/presets/CMakeGenericPresets.json new file mode 100644 index 0000000..1f8725a --- /dev/null +++ b/cmake/presets/CMakeGenericPresets.json @@ -0,0 +1,89 @@ +{ + "version": 9, + "cmakeMinimumRequired": { + "major": 3, + "minor": 30, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "description": "Base preset for library developers", + "binaryDir": "${sourceDir}/build", + "generator": "Ninja", + "hidden": true, + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": true, + "CMAKE_MESSAGE_LOG_LEVEL": "WARNING", + "CMAKE_VERIFY_INTERFACE_HEADER_SETS": true + } + }, + { + "name": "cmake-pedantic", + "hidden": true, + "inherits": "default", + "warnings": { + "dev": true, + "deprecated": true, + "uninitialized": false, + "unusedCli": false, + "systemVars": false + }, + "errors": { + "dev": true, + "deprecated": true + } + }, + { + "name": "dev-mode", + "hidden": true, + "inherits": "cmake-pedantic", + "installDir": "${sourceDir}/stagedir", + "cacheVariables": { + "CMAKE_PREFIX_PATH": { + "type": "path", + "value": "${sourceDir}/stagedir" + }, + "BUILD_SHARED_LIBS": false, + "BUILD_TESTING": true, + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_MESSAGE_LOG_LEVEL": "STATUS" + } + }, + { + "name": "cppcheck", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_CPPCHECK": "cppcheck;--inline-suppr" + } + }, + { + "name": "clang-tidy", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_CLANG_TIDY": "clang-tidy;--header-filter=^${sourceDir}/include" + } + }, + { + "name": "ci-std", + "description": "This preset makes sure the project actually builds with at least the specified standard", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_EXTENSIONS": false, + "CMAKE_CXX_STANDARD": "20", + "CMAKE_CXX_STANDARD_REQUIRED": true + } + }, + { + "name": "ci-build", + "hidden": true, + "binaryDir": "${sourceDir}/build", + "generator": "Ninja", + "inherits": "default", + "cacheVariables": { + "BUILD_SHARED_LIBS": true, + "CMAKE_BUILD_TYPE": "Release" + } + } + ] +} diff --git a/cmake/presets/CMakeLinuxPresets.json b/cmake/presets/CMakeLinuxPresets.json new file mode 100644 index 0000000..82e4f3f --- /dev/null +++ b/cmake/presets/CMakeLinuxPresets.json @@ -0,0 +1,38 @@ +{ + "version": 9, + "cmakeMinimumRequired": { + "major": 3, + "minor": 30, + "patch": 0 + }, + "include": [ + "CMakeGenericPresets.json" + ], + "configurePresets": [ + { + "name": "Release", + "description": "Possix preset for library developers", + "inherits": [ + "ci-build", + "ci-std" + ], + "cacheVariables": { + "CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wsign-conversion -Wcast-align -Wcast-qual -Wno-null-dereference -Woverloaded-virtual -Wformat=2 -Werror" + }, + "condition": { + "type": "notEquals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "Debug", + "description": "Possix preset for library developers", + "inherits": [ + "dev-mode", + "Release", + "clang-tidy" + ] + } + ] +} diff --git a/cmake/presets/CMakeWindowsPresets.json b/cmake/presets/CMakeWindowsPresets.json new file mode 100644 index 0000000..9996eec --- /dev/null +++ b/cmake/presets/CMakeWindowsPresets.json @@ -0,0 +1,39 @@ +{ + "version": 9, + "cmakeMinimumRequired": { + "major": 3, + "minor": 30, + "patch": 0 + }, + "include": [ + "CMakeGenericPresets.json" + ], + "configurePresets": [ + { + "name": "Release", + "description": "Windows preset for library developers", + "inherits": [ + "ci-build", + "ci-std" + ], + "generator": "Visual Studio 17 2022", + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/W4 /EHsc /w14242 /w14254 /w14263 /w14265 /w14287 /w14289 /w14296 /w14311 /w14545 /w14546 /w14547 /w14549 /w14555 /w14640 /w14826 /w14928 /WX" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "Debug", + "description": "Windows preset for library developers", + "inherits": [ + "dev-mode", + "Release" + ], + "generator": "Visual Studio 17 2022" + } + ] +} diff --git a/cmake/silent_copy.cmake b/cmake/silent_copy.cmake index 1e3b52b..e296d31 100644 --- a/cmake/silent_copy.cmake +++ b/cmake/silent_copy.cmake @@ -6,7 +6,7 @@ math(EXPR dir_idx "${CMAKE_ARGC} - 1") set(first_file_idx ${dir_idx}) foreach(i RANGE ${dir_idx}) - if ("${CMAKE_ARGV${i}}" STREQUAL "-P") + if("${CMAKE_ARGV${i}}" STREQUAL "-P") math(EXPR first_file_idx "${i} + 2") break() endif() diff --git a/cmake/utils.cmake b/cmake/utils.cmake index b3d36d4..5988aa5 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -23,10 +23,11 @@ function(win_copy_deps_to_target_dir target) if(CMAKE_MAJOR_VERSION GREATER 3 OR CMAKE_MINOR_VERSION GREATER_EQUAL 21) set(has_runtime_dll_genex YES) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -P "${mylib_SOURCE_DIR}/cmake/silent_copy.cmake" - "$" "$" - COMMAND_EXPAND_LISTS) + add_custom_command( + TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -P "${mylib_SOURCE_DIR}/cmake/silent_copy.cmake" + "$" "$" + COMMAND_EXPAND_LISTS + ) endif() foreach(dep ${ARGN}) @@ -34,15 +35,17 @@ function(win_copy_deps_to_target_dir target) if(dep_type STREQUAL "SHARED_LIBRARY") if(NOT has_runtime_dll_genex) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -P "${mylib_SOURCE_DIR}/cmake/silent_copy.cmake" - "$" "$" "$" - COMMAND_EXPAND_LISTS) + add_custom_command( + TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -P "${mylib_SOURCE_DIR}/cmake/silent_copy.cmake" "$" + "$" "$" COMMAND_EXPAND_LISTS + ) else() - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -P "${mylib_SOURCE_DIR}/cmake/silent_copy.cmake" - "$" "$" - COMMAND_EXPAND_LISTS) + add_custom_command( + TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -P "${mylib_SOURCE_DIR}/cmake/silent_copy.cmake" + "$" "$" + COMMAND_EXPAND_LISTS + ) endif() endif() endforeach() diff --git a/examples/add/CMakeLists.txt b/examples/add/CMakeLists.txt index 66dde79..2cb4c55 100644 --- a/examples/add/CMakeLists.txt +++ b/examples/add/CMakeLists.txt @@ -1,11 +1,10 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.25...3.30) project(mylib-add LANGUAGES CXX) include("../../cmake/utils.cmake") -string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" is_top_level) -if(is_top_level) - find_package(mylib REQUIRED) +if(PROJECT_IS_TOP_LEVEL) + find_package(mylib 1.0.0 REQUIRED) endif() set(sources main.cpp) @@ -15,6 +14,6 @@ add_executable(mylib-add) target_sources(mylib-add PRIVATE ${sources}) target_link_libraries(mylib-add PRIVATE mylib::mylib) -if(NOT is_top_level) +if(NOT PROJECT_IS_TOP_LEVEL) win_copy_deps_to_target_dir(mylib-add mylib::mylib) endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1492052..a363f26 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,15 +1,18 @@ -cmake_minimum_required(VERSION 3.14) -project(mylib-tests) +cmake_minimum_required(VERSION 3.25...3.30) +project(mylib-tests LANGUAGES CXX) #---------------------------------------------------------------------------------------------------------------------- # general settings and options #---------------------------------------------------------------------------------------------------------------------- include("../cmake/utils.cmake") -string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" is_top_level) -if(is_top_level) +if(PROJECT_IS_TOP_LEVEL) enable_testing() + + # Neither of these two are technically needed, but they make the expectation clear + set_if_undefined(CMAKE_CXX_STANDARD 17) + set_if_undefined(CMAKE_CXX_EXTENSIONS FALSE) endif() #---------------------------------------------------------------------------------------------------------------------- @@ -17,14 +20,14 @@ endif() #---------------------------------------------------------------------------------------------------------------------- include(FetchContent) -FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.tar.gz) +FetchContent_Declare( + googletest URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.tar.gz DOWNLOAD_EXTRACT_TIMESTAMP ON + DOWNLOAD_NO_PROGRESS ON FIND_PACKAGE_ARGS NAMES GTest +) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # do not override parent project's runtime settings on Windows set(INSTALL_GTEST OFF) - -# For simplicity, always build googletest as static library. This prevents mylib-tests executable from -# complaining about missing googletest DLLs on Windows. -set(BUILD_SHARED_LIBS OFF) +set(BUILD_GMOCK OFF) FetchContent_MakeAvailable(googletest) @@ -32,16 +35,20 @@ FetchContent_MakeAvailable(googletest) # tests dependencies #---------------------------------------------------------------------------------------------------------------------- -if(is_top_level) - find_package(mylib REQUIRED) +if(PROJECT_IS_TOP_LEVEL) + find_package(mylib 1.0.0 QUIET) + if(NOT mylib_FOUND) + message(STATUS "find_package(mylib) was NOT found, use as subproject ...") + # test if the targets are usable if used as subproject + add_subdirectory(.. mylib EXCLUDE_FROM_ALL) + endif() endif() #---------------------------------------------------------------------------------------------------------------------- # tests sources #---------------------------------------------------------------------------------------------------------------------- -set(sources - add_test.cpp) +set(sources add_test.cpp) source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources}) #---------------------------------------------------------------------------------------------------------------------- @@ -51,12 +58,26 @@ source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources}) add_executable(mylib-tests) target_sources(mylib-tests PRIVATE ${sources}) -target_link_libraries(mylib-tests - PRIVATE - mylib::mylib - gtest_main) +target_link_libraries(mylib-tests PRIVATE mylib::mylib GTest::gtest_main) + +if(NOT PROJECT_IS_TOP_LEVEL) + # test if the targets are findable from the build directory + # cmake-format: off + add_test(find-package-test + ${CMAKE_CTEST_COMMAND} + -C ${CMAKE_BUILD_TYPE} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}/find-package-test" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" + ) + # cmake-format: on -if(NOT is_top_level) win_copy_deps_to_target_dir(mylib-tests mylib::mylib) endif()