diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f11500..16b68ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,9 +15,6 @@ on: schedule: - cron: "0 5 * * 1" -env: - CATCH2_VERSION: 3.11.0 - jobs: test: name: Testing on ${{ matrix.os }} with Python ${{ matrix.python-version }} @@ -25,15 +22,11 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - # We test on the oldest and newest supported Python versions - # Unfortenately, we can't test on the latest Python version, as the - # pytest-virtualenv plugin does not support it yet, see - # https://github.com/man-group/pytest-plugins/issues/220 - python-version: ["3.9", "3.13"] + python-version: ["3.10", "3.14"] steps: - name: Checking out the cookie cutter repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 @@ -46,26 +39,6 @@ jobs: - uses: ssciwr/doxygen-install@v1 - - name: Install Catch2 (Linux + MacOS) - if: runner.os != 'Windows' - run: | - git clone -b v$CATCH2_VERSION https://github.com/catchorg/Catch2.git - cd Catch2 - mkdir build - cd build - cmake -DBUILD_TESTING=OFF .. - sudo cmake --build . --target install - - - name: Install Catch2 (Windows) - if: runner.os == 'Windows' - run: | - git clone -b v$Env:CATCH2_VERSION https://github.com/catchorg/Catch2.git - cd Catch2 - mkdir build - cd build - cmake -DBUILD_TESTING=OFF .. - cmake --build . --target install - - name: Set up git identity run: | git config --global user.email "ssc@citestuser.com" @@ -86,11 +59,11 @@ jobs: steps: - name: Checking out the cookie cutter repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14" - name: Install test requirements run: | diff --git a/README.md b/README.md index 6379085..382046e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ In order to use this C++ Project Cookiecutter you need the following software in In addition, the project that is generated from this cookiecutter will require the following software: * A C++ compiler, e.g. `g++` or `clang++` -* CMake `>= 3.9` +* CMake `>= 3.23` * Doxygen (optional, but recommended) # Using C++ Project Cookiecutter @@ -73,8 +73,8 @@ This cookiecutter accepts the following configuration options: * `gitlab_ci`: Whether to add a CI workflow for GitLab CI * `readthedocs`: Whether to create a Sphinx-documentation that can automatically be deployed to readthedocs.org * `doxygen`: Whether a Doxygen documentation should be extracted from the project -* `cxx_minimum_standard`: The minimum C++ standard required for this project. It can be chosen from `11` (default), `14`, `17` and `20`. - `C++03` and earlier are not supported, because the generated project will depend on libraries that require `C++11` ([Catch2](https://github.com/catchorg/Catch2) +* `cxx_minimum_standard`: The minimum C++ standard required for this project. It can be chosen from `14` (default), `17`, `20` and `23`. + `C++11` and earlier are not supported, because the generated project will depend on libraries that require `C++14` ([Catch2](https://github.com/catchorg/Catch2) for testing and [pybind11](https://github.com/pybind/pybind11) for potential Python bindings). * `python_bindings`: Whether to automatically add a PyBind11-based Python binding package. * `pypi_release`: Whether to add an automatic PyPI deploy workflow to the CI system. diff --git a/cookiecutter.json b/cookiecutter.json index 5fe092a..47eddbf 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -21,6 +21,6 @@ "local_extensions.CurrentDateExtension" ], "__python_module": "{{ cookiecutter|modname }}", - "_catch_version": "3.11.0", - "_cibuildwheel_version": "3.3.0" + "_catch_version": "3.12.0", + "_cibuildwheel_version": "3.3.1" } diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 3bba2bc..01ad4f0 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -79,7 +79,6 @@ def conditional_remove(condition, path): conditional_remove("{{ cookiecutter.pypi_release }}" != "Yes", ".github/workflows/pypi.yml") conditional_remove("{{ cookiecutter.codecovio }}" == "No", "codecov.yml") conditional_remove("{{ cookiecutter.github_actions_ci }}" == "No", ".github") -conditional_remove("{{ cookiecutter.external_dependency }}" == "None", "{{ cookiecutter.project_slug }}Config.cmake.in") conditional_remove(not {{ have_precommit }}, ".pre-commit-config.yaml") conditional_remove(os.stat("TODO.md").st_size == 0, "TODO.md") diff --git a/tests/test_bake_project.py b/tests/test_bake_project.py index 31d9195..ad060b9 100644 --- a/tests/test_bake_project.py +++ b/tests/test_bake_project.py @@ -123,7 +123,8 @@ def test_readthedocs(cookies): ) check_bake(bake) with inside_bake(bake): - build_cmake(target='sphinx-doc') + project = bake.context['project_slug'] + build_cmake(target=f'{project}-sphinx-doc') assert os.path.exists(os.path.join(os.getcwd(), "doc", "sphinx", "index.html")) @@ -136,7 +137,8 @@ def test_doxygen(cookies): ) check_bake(bake) with inside_bake(bake): - build_cmake(target='doxygen') + project = bake.context['project_slug'] + build_cmake(target=f'{project}-doxygen') assert os.path.exists(os.path.join(os.getcwd(), "doc", "html", "index.html")) diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml index ad6d673..8aca8b5 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml @@ -12,11 +12,6 @@ on: # as well as upon manual triggers through the 'Actions' tab of the Github UI workflow_dispatch: -{%- if cookiecutter.use_submodules == "No" %} -env: - CATCH2_VERSION: {{ cookiecutter._catch_version }} -{%- endif %} - jobs: build-and-test: name: Testing on ${{ "{{matrix.os}}" }} @@ -31,43 +26,24 @@ jobs: with: submodules: 'recursive' {%- endif %} - -{% if cookiecutter.use_submodules == "No" %} - - name: Install Catch2 (Linux + MacOS) - if: runner.os != 'Windows' - run: | - git clone -b v$CATCH2_VERSION https://github.com/catchorg/Catch2.git - cd Catch2 - mkdir build - cd build - cmake -DBUILD_TESTING=OFF .. - sudo cmake --build . --target install - - - name: Install Catch2 (Windows) - if: runner.os == 'Windows' - run: | - git clone -b v$Env:CATCH2_VERSION https://github.com/catchorg/Catch2.git - cd Catch2 - mkdir build - cd build - cmake -DBUILD_TESTING=OFF .. - cmake --build . --target install -{%- endif %} - - - name: make build directory +{% if cookiecutter.doxygen == "Yes" %} + - name: Install Doxygen + uses: ssciwr/doxygen-install@v1 +{% endif %} + - name: Make build directory run: cmake -E make_directory ${{ "{{ github.workspace }}" }}/build - - name: configure cmake + - name: Configure cmake shell: bash working-directory: ${{ "{{ github.workspace }}" }}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=Debug -DBUILD_DOCS=OFF + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=Debug -D{{ cookiecutter.project_slug }}_BUILD_DOCS=OFF -D{{ cookiecutter.project_slug }}_BUILD_PYTHON=OFF - - name: build + - name: Build shell: bash working-directory: ${{ "{{ github.workspace }}" }}/build run: cmake --build . - - name: run tests + - name: Run tests shell: bash working-directory: ${{ "{{ github.workspace }}" }}/build run: ctest @@ -89,8 +65,11 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.13" - + python-version: "3.14" +{% if cookiecutter.doxygen == "Yes" %} + - name: Install Doxygen + uses: ssciwr/doxygen-install@v1 +{% endif %} - name: Run Python tests shell: bash run: | @@ -114,18 +93,10 @@ jobs: - name: Install LCov run: | sudo apt-get install -y lcov - -{% if cookiecutter.use_submodules == "No" %} - - name: Install Catch2 - run: | - git clone -b v$CATCH2_VERSION https://github.com/catchorg/Catch2.git - cd Catch2 - mkdir build - cd build - cmake -DBUILD_TESTING=OFF .. - sudo cmake --build . --target install -{%- endif %} - +{% if cookiecutter.doxygen == "Yes" %} + - name: Install Doxygen + uses: ssciwr/doxygen-install@v1 +{% endif %} {% if cookiecutter.python_bindings == "Yes" %} - name: Install Python package editable run: | @@ -137,31 +108,31 @@ jobs: run: | python -m pytest --cov --cov-report=xml {% else %} - - name: Creat cmake build directory + - name: Create cmake build directory run: cmake -E make_directory ${{ "{{ github.workspace }}" }}/build - - name: configure cmake + - name: Configure cmake shell: bash working-directory: ${{ "{{ github.workspace }}" }}/build run: | - cmake $GITHUB_WORKSPACE -DCMAKE_CXX_FLAGS="--coverage" -DBUILD_DOCS=OFF + cmake $GITHUB_WORKSPACE -DCMAKE_CXX_FLAGS="--coverage" -D{{ cookiecutter.project_slug }}_BUILD_DOCS=OFF - - name: build + - name: Build shell: bash working-directory: ${{ "{{ github.workspace }}" }}/build run: cmake --build . {% endif %} - - name: run tests + - name: Run tests shell: bash working-directory: ${{ "{{ github.workspace }}" }}/build run: ctest - - name: collect coverage report + - name: Collect coverage report shell: bash working-directory: ${{ "{{ github.workspace }}" }} run: | - lcov --directory ./build{% if cookiecutter.header_only == "No" %}/src{% endif %} --capture --output-file coverage.info --ignore-errors mismatch --exclude '*/catch2/*' + lcov --directory ./build{% if cookiecutter.header_only == "No" %}/src{% endif %} --capture --output-file coverage.info --ignore-errors mismatch,unused --exclude '*/catch2/*' - name: Upload C++ coverage to Codecov uses: codecov/codecov-action@v5 diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index 1172b1f..f0dcc75 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -1,8 +1,6 @@ variables: {%- if cookiecutter.use_submodules == "Yes" %} GIT_SUBMODULE_STRATEGY: recursive -{%- else %} - CATCH2_VERSION: {{ cookiecutter._catch_version }} {%- endif %} .template: &template @@ -14,22 +12,13 @@ variables: {% if cookiecutter.python_bindings == "Yes" %} - apt install -y python3-dev {%- endif %} -{% if cookiecutter.use_submodules == "No" %} - - git clone -b v$CATCH2_VERSION https://github.com/catchorg/Catch2.git - - cd Catch2 - - mkdir build - - cd build - - cmake -DBUILD_TESTING=OFF .. - - make install - - cd ../.. -{%- endif %} +{% if cookiecutter.doxygen == "Yes" %} + - apt install -y doxygen +{% endif %} script: - - cmake -E make_directory build - - cd build - - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_DOCS=OFF .. - - cmake --build . - - ctest - + - cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -D{{ cookiecutter.project_slug }}_BUILD_DOCS=OFF -D{{ cookiecutter.project_slug }}_BUILD_PYTHON=OFF + - cmake --build build + - ctest --test-dir build # Note: You can use your own Docker images here that e.g. include relevant # dependencies and development tools. We choose well-maintained images diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml index eca991b..f2c2945 100644 --- a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -33,12 +33,6 @@ repos: # Unify file endings - id: end-of-file-fixer - # CMake Formatting/Linting Utility - - repo: https://github.com/cheshirekow/cmake-format-precommit - rev: v0.6.13 - hooks: - - id: cmake-format - - id: cmake-lint {%- if cookiecutter.github_actions_ci == "Yes" %} # GitHub Actions Workflow linter diff --git a/{{cookiecutter.project_slug}}/CMakeLists.txt b/{{cookiecutter.project_slug}}/CMakeLists.txt index a53d6c9..b354d10 100644 --- a/{{cookiecutter.project_slug}}/CMakeLists.txt +++ b/{{cookiecutter.project_slug}}/CMakeLists.txt @@ -1,138 +1,152 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.23) # Set a name and a version number for your project: -project({{ cookiecutter.project_slug }} VERSION 0.0.1 LANGUAGES CXX) - -{%- if cookiecutter.external_dependency != "None" %} - -# Set CMake policies for this project - -# We allow _ROOT (env) variables for locating dependencies -cmake_policy(SET CMP0074 NEW) -{%- endif %} +project({{ cookiecutter.project_slug }} + VERSION 0.0.1 + LANGUAGES CXX +) # Initialize some default paths include(GNUInstallDirs) -# Define the minimum C++ standard that is required -set(CMAKE_CXX_STANDARD {{ cookiecutter.cxx_minimum_standard }}) -set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Define build options +option({{ cookiecutter.project_slug }}_BUILD_TESTING "Enable building of tests" OFF) +{%- if cookiecutter.python_bindings == "Yes" %} -{% if cookiecutter.python_bindings == "Yes" -%} -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -{%- endif %} +if(PROJECT_IS_TOP_LEVEL) + option({{ cookiecutter.project_slug }}_BUILD_PYTHON "Enable building of Python bindings" ON) +else() + option({{ cookiecutter.project_slug }}_BUILD_PYTHON "Enable building of Python bindings" OFF) +endif() +{%- endif -%} +{% if cookiecutter.doxygen == "Yes" or cookiecutter.readthedocs == "Yes" %} -# Compilation options -{%- if cookiecutter.python_bindings == "Yes" %} -option(BUILD_PYTHON "Enable building of Python bindings" OFF) -{%- endif %} -{%- if cookiecutter.doxygen == "Yes" or cookiecutter.readthedocs == "Yes" %} -option(BUILD_DOCS "Enable building of documentation" ON) +if(PROJECT_IS_TOP_LEVEL) + option({{ cookiecutter.project_slug }}_BUILD_DOCS "Enable building of documentation" ON) +else() + option({{ cookiecutter.project_slug }}_BUILD_DOCS "Enable building of documentation" OFF) +endif() {%- endif %} - {%- if cookiecutter.external_dependency != "None" %} # Find external dependencies find_package({{ cookiecutter.external_dependency }}) {%- endif %} - {%- if cookiecutter.header_only == "No" %} -# compile the library +# Compile the library add_subdirectory(src) {% else %} -# Add an interface target for our header-only library +# Add an interface target for the header-only library add_library({{ cookiecutter.project_slug }} INTERFACE) -target_include_directories({{ cookiecutter.project_slug }} INTERFACE - $ - $ + +# Add the public headers that belong to this library +target_sources({{ cookiecutter.project_slug }} + INTERFACE + FILE_SET HEADERS + BASE_DIRS include + FILES + include/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}.hpp +) + +# Request a minimum C++ standard for this target +target_compile_features({{ cookiecutter.project_slug }} + INTERFACE + cxx_std_{{ cookiecutter.cxx_minimum_standard }} ) {%- endif %} -# compile the application -add_subdirectory(app) +if(PROJECT_IS_TOP_LEVEL) + # Compile the application executable + add_subdirectory(app) +endif() -# compile the tests -include(CTest) -if(BUILD_TESTING) +if(PROJECT_IS_TOP_LEVEL) + # Compile the tests + include(CTest) + if({{ cookiecutter.project_slug }}_BUILD_TESTING) {%- if cookiecutter.use_submodules == "Yes" %} - add_subdirectory(ext/Catch2) - include(./ext/Catch2/extras/Catch.cmake) + add_subdirectory(ext/Catch2) {%- endif %} - add_subdirectory(tests) + add_subdirectory(tests) + endif() endif() +{%- if cookiecutter.doxygen == "Yes" %} -{% if cookiecutter.doxygen == "Yes" -%} -if(BUILD_DOCS) - # Add the documentation +# Add the documentation +if(PROJECT_IS_TOP_LEVEL AND {{ cookiecutter.project_slug }}_BUILD_DOCS) add_subdirectory(doc) endif() {%- endif %} -{%- if cookiecutter.python_bindings == "Yes" %} -if(BUILD_PYTHON) - # Add Python bindings +{% if cookiecutter.python_bindings == "Yes" %} +# Add Python bindings +if(PROJECT_IS_TOP_LEVEL AND {{ cookiecutter.project_slug }}_BUILD_PYTHON) + # Enable PIC for Python bindings + set_target_properties({{ cookiecutter.project_slug }} + PROPERTIES + POSITION_INDEPENDENT_CODE ON + ) + + set(PYBIND11_FINDPYTHON ON) find_package(pybind11 REQUIRED) # Compile the Pybind11 module pybind11_add_module(_{{ cookiecutter|modname }} python/{{ cookiecutter|modname }}/_{{ cookiecutter.project_slug }}.cpp) - target_link_libraries(_{{ cookiecutter|modname }} PUBLIC {{ cookiecutter.project_slug }}) + target_link_libraries(_{{ cookiecutter|modname }} + PUBLIC + {{ cookiecutter.project_slug }} + ) # Install the Python module shared library install(TARGETS _{{ cookiecutter|modname }} DESTINATION .) endif() -{%- endif %} - +{% endif %} # Add an alias target for use if this project is included as a subproject in another project add_library({{ cookiecutter.project_slug }}::{{ cookiecutter.project_slug }} ALIAS {{ cookiecutter.project_slug }}) -# Install targets and configuration -{%- if cookiecutter.external_dependency == "None" %} +# Install the target and headers install( TARGETS {{ cookiecutter.project_slug }} - EXPORT {{ cookiecutter.project_slug }}-config - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + EXPORT {{ cookiecutter.project_slug }}Targets + FILE_SET HEADERS ) +# Install the exported targets (this creates {{ cookiecutter.project_slug }}Targets.cmake) install( - EXPORT {{ cookiecutter.project_slug }}-config - NAMESPACE {{ cookiecutter.project_slug }}:: + EXPORT {{ cookiecutter.project_slug }}Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/{{ cookiecutter.project_slug }} -) -{%- else %} -install( - TARGETS {{ cookiecutter.project_slug }} - EXPORT {{ cookiecutter.project_slug }}-targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -install( - EXPORT {{ cookiecutter.project_slug }}-targets - FILE {{ cookiecutter.project_slug }}Targets.cmake NAMESPACE {{ cookiecutter.project_slug }}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/{{ cookiecutter.project_slug }}) +) +# Include helpers for calling configure_package_config_file include(CMakePackageConfigHelpers) + +# Generate the {{ cookiecutter.project_slug }}Config.cmake file configure_package_config_file( - ${CMAKE_CURRENT_LIST_DIR}/{{ cookiecutter.project_slug }}Config.cmake.in + ${CMAKE_CURRENT_LIST_DIR}/cmake/{{ cookiecutter.project_slug }}Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/{{ cookiecutter.project_slug }}Config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/{{ cookiecutter.project_slug }}) + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/{{ cookiecutter.project_slug }} +) +# Install the generated Config file install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/{{ cookiecutter.project_slug }}Config.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/{{ cookiecutter.project_slug }}) + FILES + ${CMAKE_CURRENT_BINARY_DIR}/{{ cookiecutter.project_slug }}Config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/{{ cookiecutter.project_slug }} +) -export( - EXPORT {{ cookiecutter.project_slug }}-targets - FILE ${CMAKE_CURRENT_BINARY_DIR}/{{ cookiecutter.project_slug }}Targets.cmake - NAMESPACE {{ cookiecutter.project_slug }}::) -{%- endif %} +# Generate a version file +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/{{ cookiecutter.project_slug }}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) +# Install the version file install( - DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES + ${CMAKE_CURRENT_BINARY_DIR}/{{ cookiecutter.project_slug }}ConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/{{ cookiecutter.project_slug }} ) # This prints a summary of found dependencies diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md index 17912f6..0c36fd0 100644 --- a/{{cookiecutter.project_slug}}/README.md +++ b/{{cookiecutter.project_slug}}/README.md @@ -40,7 +40,7 @@ Building {{ cookiecutter.project_name }} requires the following software installed: * A C++{{ cookiecutter.cxx_minimum_standard }}-compliant compiler -* CMake `>= 3.9` +* CMake `>= 3.23` {%- if cookiecutter.external_dependency != "None" %} * {{ cookiecutter.external_dependency }} {%- endif %} @@ -50,8 +50,8 @@ Building {{ cookiecutter.project_name }} requires the following software install {%- if cookiecutter.use_submodules == "No" %} * The testing framework [Catch2](https://github.com/catchorg/Catch2) for building the test suite {%- endif %} -{%- if cookiecutter.python_bindings == "Yes" -%} -* Python `>= 3.8` for building Python bindings +{%- if cookiecutter.python_bindings == "Yes" %} +* Python `>= 3.10` for building Python bindings {%- endif %} # Building {{ cookiecutter.project_name }} @@ -61,21 +61,19 @@ It assumes that your current working directory is the top-level directory of the freshly cloned repository: ``` -mkdir build -cd build -cmake -DCMAKE_BUILD_TYPE=Release .. -cmake --build . +cmake -B build +cmake --build build ``` The build process can be customized with the following CMake variables, which can be set by adding `-D={ON, OFF}` to the `cmake` call: -* `BUILD_TESTING`: Enable building of the test suite (default: `ON`) +* `{{ cookiecutter.project_slug }}_BUILD_TESTING`: Enable building of the test suite (default: `ON`) {%- if cookiecutter.doxygen == "Yes" or cookiecutter.readthedocs == "Yes" %} -* `BUILD_DOCS`: Enable building the documentation (default: `ON`) +* `{{ cookiecutter.project_slug }}_BUILD_DOCS`: Enable building the documentation (default: `ON`) {%- endif %} {%- if cookiecutter.python_bindings == "Yes" %} -* `BUILD_PYTHON`: Enable building the Python bindings (default: `ON`) +* `{{ cookiecutter.project_slug }}_BUILD_PYTHON`: Enable building the Python bindings (default: `ON`) {%- endif %} {% if cookiecutter.python_bindings == "Yes" %} @@ -90,7 +88,7 @@ python -m pip install . # Testing {{ cookiecutter.project_name }} -When built according to the above explanation (with `-DBUILD_TESTING=ON`), +When built according to the above explanation (with `-D{{ cookiecutter.project_slug }}_BUILD_TESTING=ON`), the C++ test suite of `{{ cookiecutter.project_name }}` can be run using `ctest` from the build directory: @@ -118,13 +116,13 @@ To build it locally, first ensure the requirements are installed by running this pip install -r doc/requirements.txt ``` -Then build the sphinx documentation from the top-level build directory: +Then build the sphinx documentation from the top-level directory: ``` -cmake --build . --target sphinx-doc +cmake --build build --target sphinx-doc ``` -The web documentation can then be browsed by opening `doc/sphinx/index.html` in your browser. +The web documentation can then be browsed by opening `build/doc/sphinx/index.html` in your browser. {% elif cookiecutter.doxygen == "Yes" %} {{ cookiecutter.project_name }} provides a Doxygen documentation. You can build the documentation locally by making sure that `Doxygen` is installed on your system @@ -134,7 +132,7 @@ and running this command from the top-level build directory: cmake --build . --target doxygen ``` -The web documentation can then be browsed by opening `doc/html/index.html` in your browser. +The web documentation can then be browsed by opening `build/doc/html/index.html` in your browser. {% else %} {{ cookiecutter.project_name }} *should* provide a documentation. {% endif -%} diff --git a/{{cookiecutter.project_slug}}/TODO.md b/{{cookiecutter.project_slug}}/TODO.md index 8192e76..45833ad 100644 --- a/{{cookiecutter.project_slug}}/TODO.md +++ b/{{cookiecutter.project_slug}}/TODO.md @@ -10,12 +10,12 @@ The following tasks need to be done to get a fully working project: {%- endif %} * Make sure that the following software is installed on your computer: * A C++-{{ cookiecutter.cxx_minimum_standard}}-compliant C++ compiler - * CMake `>= 3.9` + * CMake `>= 3.23` {%- if cookiecutter.use_submodules == "No" %} * The testing framework [Catch2](https://github.com/catchorg/Catch2) {%- endif %} {%- if cookiecutter.external_dependency != "None" %} - * Adapt your list of external dependencies in `CMakeLists.txt` and `{{ cookiecutter.project_slug }}Config.cmake.in`. + * Adapt your list of external dependencies in `CMakeLists.txt` and `cmake/{{ cookiecutter.project_slug }}Config.cmake.in`. You can e.g. * Link your library or applications to your dependency. For this to work, you need to see if your dependency exports targets and what their name is. As this is highly @@ -23,7 +23,7 @@ The following tasks need to be done to get a fully working project: * Add more dependencies in analogy to `{{ cookiecutter.external_dependency }}` * Make dependencies requirements by adding `REQUIRED` to `find_package()` * Add version constraints to dependencies by adding `VERSION` to `find_package()` - * Make a dependency a pure build time dependency by removing it from `{{ cookiecutter.project_slug }}Config.cmake.in` + * Make a dependency a pure build time dependency by removing it from `cmake/{{ cookiecutter.project_slug }}Config.cmake.in` {%- endif %} {%- if cookiecutter.gitlab_ci == "Yes" %} * Make sure that CI/CD pipelines are enabled in your Gitlab project settings and that diff --git a/{{cookiecutter.project_slug}}/app/CMakeLists.txt b/{{cookiecutter.project_slug}}/app/CMakeLists.txt index 6ad2c76..7cf57e7 100644 --- a/{{cookiecutter.project_slug}}/app/CMakeLists.txt +++ b/{{cookiecutter.project_slug}}/app/CMakeLists.txt @@ -1,2 +1,14 @@ -add_executable({{ cookiecutter.project_slug }}_app {{ cookiecutter.project_slug }}_app.cpp) -target_link_libraries({{ cookiecutter.project_slug }}_app PRIVATE {{ cookiecutter.project_slug }}) +# Declare an executable target +add_executable({{ cookiecutter.project_slug }}_app) + +# Add source files to the executable target +target_sources({{ cookiecutter.project_slug }}_app + PRIVATE + {{ cookiecutter.project_slug }}_app.cpp +) + +# Link the executable against the library +target_link_libraries({{ cookiecutter.project_slug }}_app + PRIVATE + {{ cookiecutter.project_slug }} +) diff --git a/{{cookiecutter.project_slug}}/cmake/{{cookiecutter.project_slug}}Config.cmake.in b/{{cookiecutter.project_slug}}/cmake/{{cookiecutter.project_slug}}Config.cmake.in new file mode 100644 index 0000000..83c1e48 --- /dev/null +++ b/{{cookiecutter.project_slug}}/cmake/{{cookiecutter.project_slug}}Config.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +{% if cookiecutter.external_dependency != "None" %} +include(CMakeFindDependencyMacro) + +find_dependency({{ cookiecutter.external_dependency }}) +{% endif %} +include("${CMAKE_CURRENT_LIST_DIR}/{{ cookiecutter.project_slug }}Targets.cmake") diff --git a/{{cookiecutter.project_slug}}/doc/CMakeLists.txt b/{{cookiecutter.project_slug}}/doc/CMakeLists.txt index 7793e77..a28041e 100644 --- a/{{cookiecutter.project_slug}}/doc/CMakeLists.txt +++ b/{{cookiecutter.project_slug}}/doc/CMakeLists.txt @@ -5,22 +5,27 @@ set(DOXYGEN_SHORT_NAMES YES) {% if cookiecutter.readthedocs == "Yes" -%} set(DOXYGEN_GENERATE_XML YES) {%- endif %} -doxygen_add_docs(doxygen - ${CMAKE_SOURCE_DIR} + +doxygen_add_docs({{ cookiecutter.project_slug }}-doxygen + ${CMAKE_SOURCE_DIR}/include +{%- if cookiecutter.header_only == "No" %} + ${CMAKE_SOURCE_DIR}/src +{%- endif %} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT Building doxygen documentation... ) -{% if cookiecutter.readthedocs == "Yes" -%} -add_custom_target(sphinx-doc - COMMAND - sphinx-build -b html - -Dbreathe_projects.{{ cookiecutter.project_slug }}="${CMAKE_CURRENT_BINARY_DIR}/xml" - -c ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/sphinx - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating documentation with Sphinx..." - ) -add_dependencies(sphinx-doc doxygen) +{%- if cookiecutter.readthedocs == "Yes" %} + +add_custom_target({{ cookiecutter.project_slug }}-sphinx-doc + COMMAND + sphinx-build -b html + -Dbreathe_projects.{{ cookiecutter.project_slug }}="${CMAKE_CURRENT_BINARY_DIR}/xml" + -c ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/sphinx + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating documentation with Sphinx..." +) +add_dependencies({{ cookiecutter.project_slug }}-sphinx-doc {{ cookiecutter.project_slug }}-doxygen) {%- endif %} {%- endif -%} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/doc/conf.py b/{{cookiecutter.project_slug}}/doc/conf.py index da38342..3a71811 100644 --- a/{{cookiecutter.project_slug}}/doc/conf.py +++ b/{{cookiecutter.project_slug}}/doc/conf.py @@ -67,6 +67,6 @@ cwd = os.getcwd() os.makedirs("build-cmake", exist_ok=True) builddir = os.path.join(cwd, "build-cmake") - subprocess.check_call("cmake -DBUILD_DOCS=ON -DBUILD_TESTING=OFF {% if cookiecutter.python_bindings == 'Yes' %}-DBUILD_PYTHON=OFF{% endif %} ../..".split(), cwd=builddir) - subprocess.check_call("cmake --build . --target doxygen".split(), cwd=builddir) + subprocess.check_call("cmake -D{{ cookiecutter.project_slug }}_BUILD_DOCS=ON -D{{ cookiecutter.project_slug }}_BUILD_TESTING=OFF {% if cookiecutter.python_bindings == 'Yes' %}-D{{ cookiecutter.project_slug }}_BUILD_PYTHON=OFF{% endif %} ../..".split(), cwd=builddir) + subprocess.check_call("cmake --build . --target {{ cookiecutter.project_slug }}-doxygen".split(), cwd=builddir) breathe_projects["{{ cookiecutter.project_slug }}"] = os.path.join(builddir, "doc", "xml") diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index 4dda9b1..b0017d5 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -19,7 +19,7 @@ readme = "README.md" maintainers = [ { name = "{{ cookiecutter.full_name}}", email = "your@email.com" }, ] -requires-python = ">=3.9" +requires-python = ">=3.10" {%- if cookiecutter.license != "None" %} license-files = ["LICEN[CS]E*"] {%- if cookiecutter.license == "MIT" %} @@ -42,13 +42,13 @@ dependencies = [] # The configuration for the scikit-build-core build process [tool.scikit-build] -cmake.version = ">=3.9" +cmake.version = ">=3.23" build.verbose = true [tool.scikit-build.cmake.define] -BUILD_PYTHON = "ON" -BUILD_TESTING = "OFF" -BUILD_DOCS = "OFF" +{{ cookiecutter.project_slug }}_BUILD_PYTHON = "ON" +{{ cookiecutter.project_slug }}_BUILD_TESTING = "OFF" +{{ cookiecutter.project_slug }}_BUILD_DOCS = "OFF" # The following is the configuration for the pytest test suite [tool.pytest.ini_options] @@ -67,7 +67,7 @@ testpaths = ["tests/python"] build-verbosity = 3 # We restrict ourselves to recent Python versions -skip = "pp* *p27-* cp35-* cp36-* cp37-* cp38-* *musllinux*" +skip = "pp* *p27-* cp35-* cp36-* cp37-* cp38-* cp39-* *musllinux*" # Testing commands for our wheels test-command = "pytest {package}/tests/python" diff --git a/{{cookiecutter.project_slug}}/src/CMakeLists.txt b/{{cookiecutter.project_slug}}/src/CMakeLists.txt index afe63b1..8c53dac 100644 --- a/{{cookiecutter.project_slug}}/src/CMakeLists.txt +++ b/{{cookiecutter.project_slug}}/src/CMakeLists.txt @@ -1,5 +1,20 @@ -add_library({{ cookiecutter.project_slug }} {{ cookiecutter.project_slug }}.cpp) -target_include_directories({{ cookiecutter.project_slug }} PUBLIC - $ - $ +# Declare a new library target +add_library({{ cookiecutter.project_slug }}) + +# Add the implementation file and mark the public headers that belong to this library +target_sources({{ cookiecutter.project_slug }} + PRIVATE + {{ cookiecutter.project_slug }}.cpp + + PUBLIC + FILE_SET HEADERS + BASE_DIRS ../include + FILES + ../include/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}.hpp +) + +# Request a minimum C++ standard for this target +target_compile_features({{ cookiecutter.project_slug }} + PUBLIC + cxx_std_{{ cookiecutter.cxx_minimum_standard }} ) diff --git a/{{cookiecutter.project_slug}}/tests/CMakeLists.txt b/{{cookiecutter.project_slug}}/tests/CMakeLists.txt index 3b7ca76..6f02f52 100644 --- a/{{cookiecutter.project_slug}}/tests/CMakeLists.txt +++ b/{{cookiecutter.project_slug}}/tests/CMakeLists.txt @@ -1,39 +1,41 @@ -{%- if cookiecutter.use_submodules == "No" %} -# ------------------------------------------------------------------------------ -# Enable CMake's FetchContent module, which allows downloading dependencies like -# Catch2 automatically at configure time. -# ------------------------------------------------------------------------------ +{%- if cookiecutter.use_submodules == "No" -%} +# Enable CMake's FetchContent module, which allows downloading dependencies like Catch2 automatically at configure time. include(FetchContent) -# ------------------------------------------------------------------------------ -# Declare a dependency on Catch2 via FetchContent. This will clone the Catch2 -# GitHub repository during configuration. -# ------------------------------------------------------------------------------ +# Declare a dependency on Catch2 via FetchContent. This will clone the Catch2 GitHub repository during configuration. FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_TAG v{{ cookiecutter._catch_version }}) -# ------------------------------------------------------------------------------ -# Make Catch2 available in this project. This defines the CMake targets like -# Catch2::Catch2 and Catch2::Catch2WithMain. -# ------------------------------------------------------------------------------ +# Make Catch2 available in this project. This defines the imported targets Catch2::Catch2 and Catch2::Catch2WithMain. FetchContent_MakeAvailable(Catch2) -# ------------------------------------------------------------------------------ -# Make Catch2 available in this project. This defines the CMake targets like -# Catch2::Catch2 and Catch2::Catch2WithMain. -# ------------------------------------------------------------------------------ -include(Catch) +{%- else -%} +# Find the Catch2 package +find_package(Catch2 3 REQUIRED) {%- endif %} -# ------------------------------------------------------------------------------ +# Include Catch2's CMake helper functions (e.g., catch_discover_tests). +include(Catch) + # Create a single test binary that compiles all test files. -# ------------------------------------------------------------------------------ -add_executable(tests {{ cookiecutter.project_slug }}_t.cpp) +add_executable({{ cookiecutter.project_slug }}_tests) + +# Add the implementation file +target_sources({{ cookiecutter.project_slug }}_tests + PRIVATE + {{ cookiecutter.project_slug }}_t.cpp +) # Link the test binary -target_link_libraries(tests PUBLIC {{ cookiecutter.project_slug }} Catch2::Catch2WithMain) +target_link_libraries({{ cookiecutter.project_slug }}_tests + PUBLIC + {{ cookiecutter.project_slug }} + Catch2::Catch2WithMain +) # allow user to run tests with `make test` or `ctest` -catch_discover_tests(tests) +catch_discover_tests({{ cookiecutter.project_slug }}_tests + TEST_PREFIX "{{ cookiecutter.project_slug }}." +) diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}Config.cmake.in b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}Config.cmake.in deleted file mode 100644 index 0a57aac..0000000 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}Config.cmake.in +++ /dev/null @@ -1,15 +0,0 @@ -get_filename_component( - {{ cookiecutter.project_slug|upper }}_CMAKE_DIR - ${CMAKE_CURRENT_LIST_FILE} - PATH -) -set(CMAKE_MODULE_PATH ${{ "{" }}{{ cookiecutter.project_slug|upper }}_CMAKE_DIR} ${CMAKE_MODULE_PATH}) - -include(CMakeFindDependencyMacro) -if(@{{ cookiecutter.external_dependency|upper }}_FOUND@) -find_dependency({{ cookiecutter.external_dependency }}) -endif() - -if(NOT TARGET {{ cookiecutter.project_slug }}::{{ cookiecutter.project_slug }}) - include("${{ "{" }}{{ cookiecutter.project_slug|upper }}_CMAKE_DIR}/{{ cookiecutter.project_slug }}Targets.cmake") -endif()