Skip to content

Commit

Permalink
Merge pull request #103 from ssciwr/cmake-dependency
Browse files Browse the repository at this point in the history
Optionally add external dependencies
  • Loading branch information
dokempf authored Mar 23, 2023
2 parents fa5f592 + a208588 commit e3d3e4e
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 0 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ This cookiecutter accepts the following configuration options:
* `header_only`: Whether the C++ project is header-only. If `No` is selected, a library will
be added to the project. In both cases, a target is exported that dependent projects can
link against.
* `external_dependency`: The name of an external dependency of your library. Defaults to `None`.
This will generate the required CMake code for handling of external dependencies. Note that
this support will typically not work straight away, as it highly depends on the dependency.
See the generated TODO list for hints at what to do. If you have multiple external dependencies
add one of them here and then manually add the other ones analoguously.
* `github_actions_ci`: Whether to add a CI workflow for Github Actions
* `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
Expand Down
1 change: 1 addition & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"license": ["MIT", "BSD-2", "GPL-3.0", "LGPL-3.0", "None"],
"use_submodules": ["Yes", "No"],
"header_only": ["Yes", "No"],
"external_dependency": "None",
"github_actions_ci": ["Yes", "No"],
"gitlab_ci": ["Yes", "No"],
"readthedocs": ["Yes", "No"],
Expand Down
1 change: 1 addition & 0 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def conditional_remove(condition, path):
conditional_remove("{{ cookiecutter.sonarcloud }}" == "No", "sonar-project.properties")
conditional_remove("{{ cookiecutter.sonarcloud }}" == "No", ".github/workflows/sonarcloud.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")

Expand Down
45 changes: 45 additions & 0 deletions {{cookiecutter.project_slug}}/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ cmake_minimum_required(VERSION 3.9)
# 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 <Package>_ROOT (env) variables for locating dependencies
cmake_policy(SET CMP0074 NEW)
{%- endif %}

# Initialize some default paths
include(GNUInstallDirs)

Expand All @@ -22,11 +30,18 @@ option(BUILD_PYTHON "Enable building of Python bindings" ON)
option(BUILD_DOCS "Enable building of documentation" ON)
{%- endif %}

{%- if cookiecutter.external_dependency != "None" %}

# Find external dependencies
find_package({{ cookiecutter.external_dependency }})
{%- endif %}

{%- if cookiecutter.header_only == "No" %}

# compile the library
add_subdirectory(src)
{% else %}

# Add an interface target for our header-only library
add_library({{ cookiecutter.project_slug }} INTERFACE)
target_include_directories({{ cookiecutter.project_slug }} INTERFACE
Expand Down Expand Up @@ -78,6 +93,7 @@ endif()
add_library({{ cookiecutter.project_slug }}::{{ cookiecutter.project_slug }} ALIAS {{ cookiecutter.project_slug }})

# Install targets and configuration
{%- if cookiecutter.external_dependency == "None" %}
install(
TARGETS {{ cookiecutter.project_slug }}
EXPORT {{ cookiecutter.project_slug }}-config
Expand All @@ -91,6 +107,35 @@ install(
NAMESPACE {{ cookiecutter.project_slug }}::
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(CMakePackageConfigHelpers)
configure_package_config_file(
${CMAKE_CURRENT_LIST_DIR}/{{ 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(
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 %}

install(
DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/
Expand Down
7 changes: 7 additions & 0 deletions {{cookiecutter.project_slug}}/FILESTRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ generated for you:
the `CMakeLists.txt` file from the directory `<dir>` is immediately executed. A comprehensive
reference of CMake's capabilities can be found in the [official CMake docs](https://cmake.org/documentation/).
A well-written, opinionated book for beginners and experts is [Modern CMake](https://cliutils.gitlab.io/modern-cmake/).
{%- if cookiecutter.external_dependency != "None" %}
* `{{ cookiecutter.project_slug }}Config.cmake.in` provides a template for the configuration
installed alongside your project. This is required to implement the transitivity of your dependency
on `{{ cookiecutter.external_dependency }}`: If downstream projects use your library, they should
automatically search for `{{ cookiecutter.external_dependency }}`. The config file template implements
exactly this logic.
{%- endif %}
{%- if cookiecutter.use_submodules == "Yes" %}
* The `ext` directory contains any submodules that were added by the cookiecutter.
{%- endif %}
Expand Down
3 changes: 3 additions & 0 deletions {{cookiecutter.project_slug}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ Building {{ cookiecutter.project_name }} requires the following software install

* A C++{{ cookiecutter.cxx_minimum_standard }}-compliant compiler
* CMake `>= 3.9`
{%- if cookiecutter.external_dependency != "None" %}
* {{ cookiecutter.external_dependency }}
{%- endif %}
{%- if cookiecutter.doxygen == "Yes" or cookiecutter.readthedocs == "Yes" %}
* Doxygen (optional, documentation building is skipped if missing)
{%- endif %}
Expand Down
11 changes: 11 additions & 0 deletions {{cookiecutter.project_slug}}/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ The following tasks need to be done to get a fully working project:
* The [PyBind11](https://github.com/pybind/pybind11) library
{%- endif %}
{%- endif %}
{%- if cookiecutter.external_dependency != "None" %}
* Adapt your list of external dependencies in `CMakeLists.txt` and `{{ 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
individual, this cookiecutter could not do this for you.
* 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`
{%- endif %}
{%- if cookiecutter.gitlab_ci == "Yes" %}
* Make sure that CI/CD pipelines are enabled in your Gitlab project settings and that
there is a suitable Runner available. If you are using the cloud-hosted gitlab.com,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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()

0 comments on commit e3d3e4e

Please sign in to comment.