diff --git a/docs/conf.py b/docs/conf.py index 04a8d80..6842482 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,6 +11,7 @@ "breathe", "sphinx.ext.intersphinx", "sphinx_tippy", + "sphinx.ext.todo", ] templates_path = [] @@ -29,20 +30,35 @@ "tasklist", "colon_fence", ] +myst_heading_anchors = 3 intersphinx_mapping = { "cmake": ("https://cmake.org/cmake/help/latest", None), "sphinx": ("https://www.sphinx-doc.org/en/master", None), "rtd": ("https://docs.readthedocs.io/en/stable", None), "tmt": ("https://tmt.readthedocs.io/en/stable", None), + "sphinx-tippy": ("https://sphinx-tippy.readthedocs.io/en/latest", None), + "sphinx-hoverxref": ("https://sphinx-hoverxref.readthedocs.io/en/latest", None), } tippy_rtd_urls = [ - ## Only works with RTD hosted intersphinx + # Only works with RTD hosted intersphinx # "https://cmake.org/cmake/help/latest", - # "https://www.sphinx-doc.org/en/master", + "https://www.sphinx-doc.org/en/master", "https://docs.readthedocs.io/en/stable", "https://tmt.readthedocs.io/en/stable", + "https://sphinx-tippy.readthedocs.io/en/latest", + "https://sphinx-hoverxref.readthedocs.io/en/latest", ] copybutton_exclude = ".linenos, .gp, .go" + +linkcheck_ignore = [ + # pkgs.org gives 402 Client Error: Payment Required + r"https://pkgs.org", +] +linkcheck_anchors_ignore_for_url = [ + r"https://gitlab.kitware.com/.*", +] + +todo_include_todos = True diff --git a/docs/guides/developer.md b/docs/guides/developer/index.md similarity index 57% rename from docs/guides/developer.md rename to docs/guides/developer/index.md index 2346b1d..dc2f0f3 100644 --- a/docs/guides/developer.md +++ b/docs/guides/developer/index.md @@ -1,5 +1,14 @@ # Developer guide +```{toctree} +: maxdepth: 2 +: titlesonly: true +: hidden: true +: glob: true + +who-is-the-developer +``` + This is a developer's guide intended to explain many of the paradigms used in this template project diff --git a/docs/guides/developer/who-is-the-developer.md b/docs/guides/developer/who-is-the-developer.md new file mode 100644 index 0000000..3877f23 --- /dev/null +++ b/docs/guides/developer/who-is-the-developer.md @@ -0,0 +1,11 @@ +# Who is the developer? + +In the context of this guide, the developer is someone who: +- is looking to expand the build system to add more options, expand the project + dependencies +- a project developer who is looking at implementing this template project +- a CMake enthusiast looking for a common standard for their design. (Feel free + to interact in the template [issue page] if you have any comments) +- a non-CMake build-system enthusiast. There is no discrimination here :) + +[issue page]: https://github.com/LecrisUT/CMake-Template/issues diff --git a/docs/guides/index.md b/docs/guides/index.md index c5683ba..b98a853 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -1,11 +1,85 @@ # Guides ```{toctree} ---- -maxdepth: 2 -titlesonly: true -caption: Guides -glob: true ---- -* +: maxdepth: 2 +: titlesonly: true + +user/index +developer/index +``` + +## What are these guides? + +In this template project, you will find a [user guide] and a [developer guide] +intended to answer common questions that either a [user] or a [developer] would +have when encountering a project that uses this template. + +Feel free to reference and/or [include] these guides or sections of it in your +documentation. + +[user]: user/who-is-the-user.md +[developer]: developer/who-is-the-developer.md +[user guide]: user/index.md +[developer guide]: developer/index.md +[include]: #including-these-guides-in-your-downstream-project + +## Including these guides in your downstream project + +The best way to include these guides is if your project uses a +[sphinx backend][rtd-sphinx] for your documentation. There you can simply link +to it using [inter-sphinx]. For example, include the following configurations +to your `docs/conf.py` file: + +```{code-block} python +: caption: docs/conf.py +: emphasize-lines: 5 + +extensions = [ + "sphinx.ext.intersphinx", +] +intersphinx_mapping = { + "template-guide": ("https://lecrisut-cmake-template.readthedocs.io/en/latest/", None), +} ``` + +:::{note} +If you want to have the links pop-up like in this documentation, use either +[`sphinx-tippy`] or [`sphinx-hoverxref`]. The latter produces better results, +however it does [not yet support Markdown documents][sphinx-hoverxref-issue]. +::: + +Then, you can use it in your documentation files as follows: + +::::{tab-set} +:::{tab-item} Markdown +```{code-block} markdown +: caption: docs/example.md +: emphasize-lines: 4 + +Refer to the [following guide][template-user-guide] for a basic how-to interact +with a CMake project... + +[template-user-guide]: inv:template-user-guide:std:doc#guides/user/index +``` +::: +:::{tab-item} reStructuredText +```{code-block} rst +: caption: docs/example.rst +: emphasize-lines: 1 + +Refer to the :external+template-user-guide:std:doc:`following guide` +for a basic how-to interact with a CMake project... +``` +::: +:::{tab-item} (Result) +Refer to the [following guide][user guide] for a basic how-to interact with a +CMake project... +::: +:::: + +[rtd-sphinx]: inv:rtd:std:doc#intro/getting-started-with-sphinx +[//]: # (TODO: Fix the intersphinx link) +[inter-sphinx]: https://www.sphinx-doc.org/en/master/usage/quickstart.html#intersphinx +[`sphinx-tippy`]: inv:sphinx-tippy#index +[`sphinx-hoverxref`]: inv:sphinx-hoverxref#index +[sphinx-hoverxref-issue]: https://github.com/readthedocs/sphinx-hoverxref/issues/250 diff --git a/docs/guides/user.md b/docs/guides/user.md deleted file mode 100644 index 8826d75..0000000 --- a/docs/guides/user.md +++ /dev/null @@ -1,6 +0,0 @@ -# User guide - -This is a general guide for how a user can interact with modern CMake project -that follows such a template - -**Coming soon** diff --git a/docs/guides/user/build.md b/docs/guides/user/build.md new file mode 100644 index 0000000..f55031e --- /dev/null +++ b/docs/guides/user/build.md @@ -0,0 +1,39 @@ +# Building the project + +:::{admonition} Tl;dr +```console +$ cmake --build ./build-dir +``` +::: + +At this point the build process is more-or-less up to the actual project, and +you simply run the build as shown above. Here are also a few tips: + +## Using presets + +If you have used a [`configurePreset`] in the previous section, you may not be +aware of the build directory. Often times, the project also provides a +`buildPreset` with the same name, so you can simply run: +```console +$ cmake --build --preset default +``` + +You can find a list of them using: +```console +$ cmake --list-preset=build +``` + +## Running in parallel + +Use the [`-j`] option, either by itself (the build system decides) or pass +`$(nproc)` as its value. + +```console +$ cmake --build ./build-dir -j +``` + +Alternatively, set the [`CMAKE_BUILD_PARALLEL_LEVEL`] environment variable. + +[`-j`]: inv:cmake:std:cmdoption#cmake--build.--parallel +[`configurePreset`]: configure/index.md#cmake-presets +[`CMAKE_BUILD_PARALLEL_LEVEL`]: inv:cmake:cmake:envvar#envvar:CMAKE_BUILD_PARALLEL_LEVEL diff --git a/docs/guides/user/configure/import.md b/docs/guides/user/configure/import.md new file mode 100644 index 0000000..70c1027 --- /dev/null +++ b/docs/guides/user/configure/import.md @@ -0,0 +1,129 @@ +# Importing dependencies + +This section covers both how to import a project in another CMake project +([importing]), and how a user can control where a project is found ([finding]). + +## Importing dependencies in a CMake project + +:::{admonition} Tl;dr +```cmake +include(FetchContent) +FetchContent_Declare(awesome-project + GIT_REPOSITORY https://github.com/user/awesome-project + GIT_TAG main + FIND_PACKAGE_ARGS CONFIG +) +FetchContent_MakeAvailable(awesome-project) +``` +::: + +The best way to consume a project is within CMake, using the +[`FetchContent`/`find_package` integration]. This will run [`find_package`] +first to try and import the system installed [packaged] version, and if none is +available it will git clone the [source] project and include it as +[`add_subdirectory`]. See the [configuration] section on how to configure the +included project. + +From there, navigate the dependency's top-level `CMakeLists.txt` to find the +available targets you can use. + +If you need to overwrite the upstream project's options, simply add the +[`option`] before the [`FetchContent_MakeAvailable`] call, e.g.: +```cmake +option(PROJECT2_TESTS "Project1: Override" OFF) + +FetchContent_MakeAvailable(Project2) +``` + +### Making a dependency optional + +Currently, the only clear design for supporting optional dependencies is by +avoiding [`FetchContent`] and using [`find_package`] only: + +```cmake +find_package(awesome-project) +``` + +See the [controlling] section on how to disable or make it a required +dependency. + +:::{todo} +Make the optional dependency work with [`FetchContent`]. Currently, there is no +clear way how to combine [`find_package`] and [`FetchContent`] fallthrough. +Key part is the interaction with [`CMAKE_REQUIRE_FIND_PACKAGE_`] +and [`CMAKE_DISABLE_FIND_PACKAGE_`]. +::: + +## How dependencies are found + +Here we assume that all projects in the chain use the modern [importing] +approach to define their dependencies. + +### Default behavior + +By default, the project will first try to find the [`Config.cmake`] +file from the system installed [packaged], and if that fails, it would either +use [`FetchContent`] to download the [source] project if it is a required +dependency, or simply disable the feature if it is an [optional dependency]. + +### Controlling how dependencies are imported + +| Option | Effect | Notes | +|:--------------------------------------------:|:-------------------------------------------------------|:-------------------------------------------------------------------------------------------| +| [`CMAKE_PREFIX_PATH`] | Where to look for system installed package | | +| [`_ROOT`] | Where to look for system installed package | It can happen that this is not enforced | +| [`FETCHCONTENT_TRY_FIND_PACKAGE_MODE`] | If `NEVER`, will use downloaded version | | +| [`FETCHCONTENT_SOURCE_DIR_`] | Path to package source to use (instead of downloading) | Takes precedence of any other option | +| [`CMAKE_REQUIRE_FIND_PACKAGE_`] | Make optional dependency required | If dependency is already required via `FetchContent`, it ensure the system package is used | +| [`CMAKE_DISABLE_FIND_PACKAGE_`] | Disable finding system installed package | | + +Here `` is the case-sensitive name of the dependency, and +`` is the equivalent uppercase name. + +:::{caution} +[`CMAKE_REQUIRE_FIND_PACKAGE_`] and +[`CMAKE_DISABLE_FIND_PACKAGE_`] options propagate to other +dependencies in the chain. Normally this would not be an issue, unless a project +uses [`Find.cmake`] and do not take this into account [^1]. +::: + +### Troubleshooting the dependency search + +:::{admonition} Tl;dr +```console +$ cmake -b ./build --debug-find-pkg= +``` +::: + +The most comprehensive way of finding out how a dependency has been searched is +to use the [`--debug-find-pkg`] option. This does not cover the +[`Find.cmake`], where the find process is non-standard. + +[importing]: #importing-dependencies-in-a-cmake-project +[finding]: #how-dependencies-are-found +[controlling]: #controlling-how-dependencies-are-imported +[optional dependency]: #making-a-dependency-optional + +[packaged]: ../download.md#packaged-version +[source]: ../download.md#source-project +[configuration]: options.md#upstream-project-options + +[`FetchContent`]: inv:cmake:cmake:module#module:FetchContent +[`find_package`]: inv:cmake:cmake:command#command:find_package +[`FetchContent`/`find_package` integration]: inv:cmake:std:label#fetchcontent-find_package-integration-examples +[`add_subdirectory`]: inv:cmake:cmake:command#command:add_subdirectory + +[`CMAKE_PREFIX_PATH`]: inv:cmake:cmake:variable#variable:CMAKE_PREFIX_PATH +[`_ROOT`]: inv:cmake:cmake:variable#variable:_ROOT +[`FETCHCONTENT_TRY_FIND_PACKAGE_MODE`]: inv:cmake:cmake:variable#variable:FETCHCONTENT_TRY_FIND_PACKAGE_MODE +[`FETCHCONTENT_SOURCE_DIR_`]: inv:cmake:cmake:variable#variable:FETCHCONTENT_SOURCE_DIR_ +[`CMAKE_REQUIRE_FIND_PACKAGE_`]: inv:cmake:cmake:variable#variable:CMAKE_REQUIRE_FIND_PACKAGE_ +[`CMAKE_DISABLE_FIND_PACKAGE_`]: inv:cmake:cmake:variable#variable:CMAKE_DISABLE_FIND_PACKAGE_ + +[`FetchContent_MakeAvailable`]: inv:cmake:cmake:command#command:fetchcontent_makeavailable +[`option`]: inv:cmake:cmake:command#command:option +[`Config.cmake`]: +[`Find.cmake`]: +[`--debug-find-pkg`]: inv:cmake:std:cmdoption#cmake.--debug-find-pkg + +[^1]: diff --git a/docs/guides/user/configure/index.md b/docs/guides/user/configure/index.md new file mode 100644 index 0000000..8ccff7f --- /dev/null +++ b/docs/guides/user/configure/index.md @@ -0,0 +1,236 @@ +# Configuring the project + +```{toctree} +: maxdepth: 1 +: titlesonly: true +: hidden: true + +options +import +``` + +The heart of a CMake project is the configure stage, where most of the user +experience diverge between each user and their needs. + +We will first focus on the situation where you have downloaded the project from +[source]. The case where we are [using `FetchContent`] is more-or-less the same, +and will be explored in more details in the [importing projects] section. + +:::{note} +This section only covers how you interact with CMake in order to pass +configuration options. The details of the configuration options standard +themselves are covered in [configuration options] section. +::: + +[source]: ../download.md#source-project +[using `FetchContent`]: ../download.md#using-fetchcontent +[importing projects]: import.md +[configuration options]: options.md + +## How do I configure the project? + +::::{tab-set} +:::{tab-item} As an end-user +Use the [pre-defined presets][presets] that are defined by the project +maintainers. This can be complemented with [manual options] if needed. +::: +:::{tab-item} As an upstream developer +You should setup your [`CMakeUserPresets.json`] file. See the [presets] section +on how to interact with presets. +::: +:::{tab-item} As a downstream packager +Often times the packaging system will define the default configuration options +and pass them as [manual options], but sometimes they can also be introduced via +[environment variables]. +::: +:::: + +:::{attention} +Do not create any `build` folder manually. Stay in the project's root path and +run your commands from there. +::: + +## CMake presets + +:::{admonition} Tl;dr +```console +$ cmake --preset default +``` +::: + +The project should be designed to have already provided all the common +configurations that the user would need. These already take care of: +- using the appropriate compiler +- setting up any compiler options needed +- detecting and importing the dependencies + +Pay attention to the output of this configure step to check if there have been +any errors encountered. Some common issues are collected in the +[troubleshooting] section. If the configuration was successful, you will see +an output summary at the end listing the dependencies imported and the features +used. + +[//]: # (TODO: Include an output example) + +Note that this typically does not display all the configuration setup, e.g. the +compiler used. Pay attention to the full log for such information. + +To get the list of all presets that are exported, use: +```console +$ cmake --list-presets +``` + +:::{caution} +The installation path cannot be set within the preset itself as that varies for +each person. By default, this is set to a system dependent path like +`/usr/local`, which you should not be installing to unless you are aware of the +consequences. + +The installation path can be overwritten using when calling `cmake --install`: +```console +$ cmake --install ./build --prefix /path/to/install/prefix +``` +But beware that this might affect projects that hard-code the installation path +at compile/build time. +::: + +## `CMakeUserPresets.json` + +:::{admonition} Tl;dr +```{code-block} json +: emphasize-lines: 9-11 +: caption: CMakeUserPresets.json + +{ + "version": 6, + "configurePresets": [ + { + "name": "debug", + "displayName": "Debug", + "binaryDir": "cmake-build-debug", + "inherits": ["default"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + } + ] +} +``` +::: + +As the developer you would have specific configuration option that you always +want to use, such as setting the `BUILD_TYPE` to `Debug`. In order to save such +options, you can create your personal `CMakeUserPresets.json` outside of the git +version control. This is a simple json file, for which you are primarily +concerned about the `configurePresets` which is a list of preset objects. There +the crucial variables are: +- `name`: The name you interface with through `cmake --preset` +- `binaryDir`: The build directory +- `inherits`: A list of other presets that the current preset will add on top +- `cacheVariables`: A dictionary of configuration options to be setup + +All the `cacheVariables` can be taken from the [manual options], but often times +the project would also export component-like presets so that you only need to +interface via the `inherits` field. + +### Component presets + +These component presets are just shortcuts for having to manually define +`cacheVariables`, and are a preferred way of designing a preset, mainly due to +the more natural naming it can use. They are typically found in a file like +`cmake/CMakePresets-components.json`. These component presets are typically +hidden, and, currently, there is no interface to print hidden interfaces. + +:::{warning} +If there is a conflict in the `cacheVariables` defined in the cache presets used +by `inherits`, precedence goes to the first preset that defined in, and **NOT** +to the last preset to define it. + +The `cacheVariables` defined in the current preset always takes precedence over +those defined from `inherits`. +::: + +## Passing manual options + +:::{admonition} Tl;dr +```console +$ cmake -b ./build -DAWESOME_PROJECT_COOL_OPTION=On +``` +::: + +Manually passing options is done by prepending `-D` to the option name and +assigning its value, i.e. `-D=`. For the list of +available options, it is best to simply navigate the top-level `CMakeLists.txt` +and find the [`option`] and [`cmake_dependent_option`] in the relevant section, +e.g.: +```cmake +#[==============================================================================================[ +# Options # +]==============================================================================================] +option(TEMPLATE_TESTS "Template: Build test-suite" ${PROJECT_IS_TOP_LEVEL}) +option(TEMPLATE_SHARED_LIBS "Template: Build as a shared library" ${PROJECT_IS_TOP_LEVEL}) +option(TEMPLATE_INSTALL "Template: Install project" ${PROJECT_IS_TOP_LEVEL}) +``` + +:::{note} +Option names are case-sensitive, make sure you pass it correctly to `-D` +argument. + +These options should always be prefixed by the project's name (in this case +`TEMPLATE_*`), including the CMake native options (`CMAKE_*`). +::: + +which you can interpret as: +```cmake +option( ) +cmake_dependent_option( ) +``` + +Because these options can be dynamic, this is the only reliable way of +identifying the project defined options available. You can sometimes use +[`ccmake`], to display the options, but this is not recommended because it will +not show the options that have been dynamically skipped. + +## Beware of environment variables + +Finally, be careful that environment variables can inject or alter the CMake +options. For the most part, you can ignore the [cmake specific][cmake-env], but +beware of the following environment variables: + +| Variable | Preferred alternative | Effect | +|:--------------:|:------------------------------------------:|:------------------------------------------------| +| [`CC`] | [`CMAKE_C_COMPILER`][CMAKE_COMPILER] | Selects C compiler used | +| [`CXX`] | [`CMAKE_CXX_COMPILER`][CMAKE_COMPILER] | Selects C++ compiler used | +| [`FC`] | [`CMAKE_Fortran_COMPILER`][CMAKE_COMPILER] | Selects Fortran compiler used | +| [`CFLAGS`] | Project specified options | Appends to [`CMAKE_C_FLAGS`][CMAKE_FLAGS] | +| [`CXXFLAGS`] | Project specified options | Appends to [`CMAKE_CXX_FLAGS`][CMAKE_FLAGS] | +| [`FFLAGS`] | Project specified options | Appends to [`CMAKE_Fortran_FLAGS`][CMAKE_FLAGS] | +| `CPATH` | Unset the variable | Uncontrollable consequences [^1] | +| `LIBRARY_PATH` | Unset the variable | Uncontrollable consequences [^1] | + +It is recommended to just clear out these environment variables, if you are not +aware of how these may affect the build. + +[presets]: #cmake-presets +[`CMakeUserPresets.json`]: #cmakeuserpresetsjson +[component presets]: #component-presets +[manual options]: #passing-manual-options +[environment variables]: #beware-of-environment-variables + +[`option`]: inv:cmake:cmake:command#command:option +[`cmake_dependent_option`]: inv:cmake:cmake:command#command:cmake_dependent_option +[`ccmake`]: inv:cmake:cmake:manual#manual:ccmake(1) +[cmake-env]: inv:cmake:cmake:manual#manual:cmake-env-variables(7) + +[`CC`]: inv:cmake:cmake:envvar#envvar:CC +[`CXX`]: inv:cmake:cmake:envvar#envvar:CXX +[`FC`]: inv:cmake:cmake:envvar#envvar:FC +[`CFLAGS`]: inv:cmake:cmake:envvar#envvar:CFLAGS +[`CXXFLAGS`]: inv:cmake:cmake:envvar#envvar:CXXFLAGS +[`FFLAGS`]: inv:cmake:cmake:envvar#envvar:FFLAGS +[CMAKE_COMPILER]: inv:cmake:cmake:variable#variable:CMAKE__COMPILER +[CMAKE_FLAGS]: inv:cmake:cmake:variable#variable:CMAKE__FLAGS + +[^1]: + +[troubleshooting]: ../troubleshooting.md diff --git a/docs/guides/user/configure/options.md b/docs/guides/user/configure/options.md new file mode 100644 index 0000000..3249446 --- /dev/null +++ b/docs/guides/user/configure/options.md @@ -0,0 +1,67 @@ +# Configuration options + +The configuration options that you will encounter would be one of the + +[CMake native]: #cmake-native-options +[project specific]: #project-specific-options +[upstream project]: #upstream-project-options + +## CMake native options + +The full list of [cmake variables] can be overwhelming for newcomers, however +this is a great resource to find all that CMake can do. Here I will give just +list the very most basic options that you typically need to get started. + +| Option | Effect | Notes | +|:-------------------------:|:----------------------------------------|---------------------------------------------------| +| [`CMAKE__COMPILER`] | Specify `` compiler | | +| [`CMAKE__FLAGS`] | Append `` compiler flags | Try to avoid [^1] | +| [`CMAKE_INSTALL_PREFIX`] | Specify where to install the project | | +| [`CMAKE_BUILD_TYPE`] | Specify what build variant to configure | Can be set to `""` to disable CMake default flags | + +Here `` is typically one of `C`, `CXX`, `Fortran`, and +[many others][`enable_language`]. + +:::{important} +There are more options related to finding/importing dependencies explained in +the [import] section. Please read that section carefully. +::: + +[^1]: Use project native options. Otherwise, these flags will propagate to all + dependencies (consider the effect of `-Wall` flag) + +## Project specific options + +Consult with the actual project's documentation for the options available. Here +is just the list of options that are encouraged to be implemented by this +template. + +| Option | Effect | Notes | +|:-------------------------:|:--------------------------------------------|:---------------------| +| `_TESTS` | Build project's test-suite | Dynamic default [^2] | +| `_SHARED_LIBS` | Build project's libraries as shared library | Dynamic default [^2] | +| `_INSTALL` | Enable installing the project | Dynamic default [^2] | + +Here `` is the project's option namespace, which is typically the +uppercase name of the project. + +[^2]: If the project is the main CMake project, i.e. it is not being imported, + the default is `ON`, otherwise it is `OFF` + +## Upstream project options + +CMake options are propagated to upstream dependencies that are imported if the +project is imported via `FetchContent` (see [import] section for more details). +You can thus control the options of these dependencies as well, e.g. you could +set `_TESTS=ON` to test the imported `` as well. + +This, however, does not work with installed projects. + +[import]: import.md#controlling-how-dependencies-are-imported + +[cmake variables]: inv:cmake:cmake:manual#manual:cmake-variables(7) +[`enable_language`]: inv:cmake:cmake:command#command:enable_language +[`CMAKE__COMPILER`]: inv:cmake:cmake:variable#variable:CMAKE__COMPILER +[`CMAKE__FLAGS`]: inv:cmake:cmake:variable#variable:CMAKE__FLAGS +[`CMAKE_INSTALL_PREFIX`]: inv:cmake:cmake:variable#variable:CMAKE_INSTALL_PREFIX +[`CMAKE_BUILD_TYPE`]: inv:cmake:cmake:variable#variable:CMAKE_BUILD_TYPE diff --git a/docs/guides/user/download.md b/docs/guides/user/download.md new file mode 100644 index 0000000..3d29190 --- /dev/null +++ b/docs/guides/user/download.md @@ -0,0 +1,156 @@ +# Downloading the project + +First step is where do we even get the files we need to start using the project. +As you're probably aware by now, C/C++ projects do not have a concise packaging +system [^1][^2] like [PyPI] is for python, so the starting point here is not that +straight-forward. The actual project that implemented this template should +document where they are officially packaging the project, usually on the +front-page of their git repository or documentation. + +Regardless, here I will try to cover some of the common sources and conventions +where you can find the project. + +[PyPI]: https://pypi.org/ + +--- + +## Where do I download the project from? +::::{tab-set} +:::{tab-item} As an end-user +For the most part you should look for a [packaged] source, even the project you +want to use is a library that you would be using in your own C/C++ project. + +When importing the project within another CMake project, the recommended way is +[using `FetchContent`], which will internally use either the [packaged] or +[source] version. +::: +:::{tab-item} As an upstream developer +You are primarily interested in the [source] version. When working on multiple +projects, it is especially useful to know how to setup your [CMakeUserPreset] so +that you can easily switch between one project and another. +::: +:::{tab-item} As a downstream packager +You should be aware on how get the project from [source]. The original git +source is preferred since CMake does not bundle any additional metadata in its +archive. +::: +:::: + +## Packaged version + +:::{admonition} Tl;dr +One of the: +```console +$ dnf install awesome-project-devel +$ apt install awesome-project-dev +$ brew install awesome-project +$ conda install awesome-project +$ pip install awesome-project +... +``` +::: + +From the OS distribution, you will find the project separated into runtime +packages that have the plain name of the project e.g. `awesome-project` and +development packages like `awesome-project-devel`, which you only need if the +project is library that you need to import. If you are not sure if the project +is packaged on your distribution, you can check [pkgs.org]. + +On other packaging systems like [conda] or [homebrew], you will get both +development and runtime packages at the same time. Just be careful with such +systems that the appropriate packages are being picked up instead of the system +installed ones. + +For native Windows systems, I do not have any experience there, but I am open +to any contributions for this section. + +These variants would typically have a specific configuration and feature subset +of the project. If you find it lacking, you can either raise an issue downstream +at the packagers (there are relevant links in [pkgs.org]), or consider building +from source. + +## Source project + +:::{admonition} Tl;dr +```console +$ git clone https://github.com/user/awesome-project +``` +::: + +:::{attention} +If you intend to import the project to be used inside another project, consider +[using `FetchContent`] instead. This part is specifically for using the built +binary in a production setup and for upstream development. +::: + +You might already be aware of the homepage and/or git repository where the +project is located, at which point git cloning or downloading the relative +archive is straightforward. Unlike autotools, there is no distinction between +the source from git and archive, so you can start building right away. + +If you are still uncertain where the project source is located, take a look at +[pkgs.org], or any other packaging system that you know that has it, like +[PyPI]. Even when you are certain about the project source, it can still be +helpful to check what sources these packaging systems use, as often times they +will show which source/variant are deprecated, e.g. [`pkgconf`] vs +[`pkg-config`], and various patches that the packagers applied to fix upstream +issues. + +## Using `FetchContent` + +[//]: # (TODO: Synchronize with configure/import.md https://github.com/sphinx-doc/sphinx/issues/11807) + +:::{admonition} Tl;dr +```cmake +include(FetchContent) +FetchContent_Declare(awesome-project + GIT_REPOSITORY https://github.com/user/awesome-project + GIT_TAG main + FIND_PACKAGE_ARGS CONFIG +) +FetchContent_MakeAvailable(awesome-project) +``` +::: + +:::{attention} +If you primarily want the compiled binary, then use the [source] version. This +part is specifically if you want to import the project to be used in another +one downstream, either as a library, precompiler or any other utility used for +building a compiled project. +::: + +See the [importing dependencies] section. + +## Other build-system + +Good luck, you have reached the wild-west. You are mostly on your own now. + +Some projects can have other ways of importing the project using [`pkgconf`], +however, for consuming CMake projects, it is greatly discouraged to rely on +these because it cannot contain the same amount of metadata, e.g. for +[c++ modules], it does not contain the compiler used for packaged compiled +library, that would tell the consumer if they need to re-compile the project or +not, but on the other-hand, such information is used when consuming CMake's +[`find_package`]. + +[packaged]: #packaged-version +[source]: #source-project +[using `FetchContent`]: #using-fetchcontent +[`pkgconf`]: https://github.com/pkgconf/pkgconf +[`pkg-config`]: https://gitlab.freedesktop.org/pkg-config/pkg-config +[c++ modules]: https://en.cppreference.com/w/cpp/language/modules +[conda]: https://conda.io/projects/conda/en/latest/index.html +[homebrew]: https://brew.sh +[pkgs.org]: https://pkgs.org + +[CMakeUserPreset]: configure/index.md#cmakeuserpresetsjson +[configuration]: configure/index.md +[importing dependencies]: configure/import.md#importing-dependencies-in-a-cmake-project + +[`find_package`]: inv:cmake:cmake:command#command:find_package + +[^1]: There is a proposal within CMake to at least unify the dependency backend + [^2] which would be a pre-requisite for having a unified packaging system + using CMake. But still it is a long way until there can be a `pip`-like + interface to install projects. +[^2]: diff --git a/docs/guides/user/index.md b/docs/guides/user/index.md new file mode 100644 index 0000000..630f3d4 --- /dev/null +++ b/docs/guides/user/index.md @@ -0,0 +1,27 @@ +# User guide + +```{toctree} +: maxdepth: 2 +: titlesonly: true +: hidden: true + +who-is-the-user +download +configure/index +build +test +install +troubleshooting +``` + +This is a general guide for how a user can interact with modern CMake project +that follows such a template. + +This guide will walk you through all the steps that the user would typically +follow from how to get the project they are interested in, to how to install it +for day-to-day usage. + +For quick links and tl;dr questions, see the following section, otherwise +consider navigating the guide step-by-step. + +## tl;dr diff --git a/docs/guides/user/install.md b/docs/guides/user/install.md new file mode 100644 index 0000000..cd64b59 --- /dev/null +++ b/docs/guides/user/install.md @@ -0,0 +1,31 @@ +# Installing the project + +:::{admonition} Tl;dr +```console +$ cmake --install ./build-dir --prefix /path/to/install/prefix +``` +::: + +Finally, for the installation step, simply run the step mentioned above. There +is not much to mention regarding this step. In this case we do not have a preset +interface to run the install step. + +The prefix specified expands where you will find your installed project: + +| Type | Path | +|:-------------------|:-------------------------------------------| +| Executables | `${prefix}/bin` | +| Libraries | `${prefix}/lib64` | +| Headers | `${prefix}/include` | +| Data | `${prefix}/share` | +| CMake export files | `${prefix}/[lib64/share]/cmake/${project}` | + +:::{warning} +In some project you would not be able to overwrite the installation path with +[`--prefix`]. In such cases, make sure you have passed the appropriate +[`CMAKE_INSTALL_PREFIX`] at the [configure] step. +::: + +[configure]: configure/index.md +[`--prefix`]: inv:cmake:std:cmdoption#cmake--install.--prefix +[`CMAKE_INSTALL_PREFIX`]: inv:cmake:cmake:variable#variable:CMAKE_INSTALL_PREFIX diff --git a/docs/guides/user/test.md b/docs/guides/user/test.md new file mode 100644 index 0000000..cc8547c --- /dev/null +++ b/docs/guides/user/test.md @@ -0,0 +1,84 @@ +# Testing the project + +:::{admonition} Tl;dr +```console +$ ctest --test-dir ./build-dir +``` +::: + +Similar to the build step, this step is straightforward as the option above +shows. Here are also a few tips: + +## Using presets + +Similar to the `buildPreset` you often have `testPreset` that you can execute +as: +```console +$ ctest --preset default +``` + +You can find a list of them using: +```console +$ cmake --list-preset=test +``` + +## Listing the tests + +You can list tests instead of running them by passing the [`-N`] flag + +```console +$ ctest --test-dir ./build-dir -N +``` + +The tests are often grouped by labels, which you can list using +[`--print-labels`] +```console +$ ctest --test-dir ./build-dir --print-labels +``` + +These can be combined with [filter tests], to show what tests would be executed. + +:::{note} +Some testing backends like [catch] might not have the list of tests available +straightaway, but they should be available after the build stage. +::: + +## Filter tests + +You have two main methods of filtering tests, by the name using [`-R`]/[`-E`], +or by the label [`-L`]/[`-LE`]. These are parsed as regex that include/exclude +the tests with name or label that match the regex. So for example, if you want +to run only unit tests, this might look like: +```console +$ ctest --test-dir ./build-dir -L "^unit$" +``` + +## Running in parallel + +Use the [`-j`] option with the number of tests to be run in parallel, often +times just `$(nproc)`. + +```console +$ ctest --test-dir ./build-dir -j $(nproc) +``` + +:::{note} +The parallelization of the tests themselves, e.g. due to MPI or OpenMP should +be already handled by ctest when passing [`-j`]. +::: + +Alternatively, set the [`CTEST_PARALLEL_LEVEL`] environment variable. + +[filter tests]: #filter-tests + +[`-R`]: inv:cmake:std:cmdoption#ctest.--tests-regex +[`-E`]: inv:cmake:std:cmdoption#ctest.--exclude-regex +[`-L`]: inv:cmake:std:cmdoption#ctest.--label-regex +[`-LE`]: inv:cmake:std:cmdoption#ctest.--label-exclude +[`-j`]: inv:cmake:std:cmdoption#ctest.--parallel +[`-N`]: inv:cmake:std:cmdoption#ctest.--show-only +[`--print-labels`]: inv:cmake:std:cmdoption#ctest.--print-labels +[`configurePreset`]: configure.md#cmake-presets +[`CTEST_PARALLEL_LEVEL`]: inv:cmake:cmake:envvar#envvar:CTEST_PARALLEL_LEVEL + +[catch]: https://github.com/catchorg/Catch2 diff --git a/docs/guides/user/troubleshooting.md b/docs/guides/user/troubleshooting.md new file mode 100644 index 0000000..de4c6a7 --- /dev/null +++ b/docs/guides/user/troubleshooting.md @@ -0,0 +1,3 @@ +# Troubleshooting + +**Coming soon** diff --git a/docs/guides/user/who-is-the-user.md b/docs/guides/user/who-is-the-user.md new file mode 100644 index 0000000..4f21bb9 --- /dev/null +++ b/docs/guides/user/who-is-the-user.md @@ -0,0 +1,69 @@ +# Who is the user? + +## Not the end-user + +The end-user of the project should not have to build the project directly using +CMake, not even if the project is a library that is meant to be used by other +downstream projects. For these situations the user should have access to +packaged versions of the project (Fedora, PyPI, homebrew package etc.) See +the [developer guide] on how to set it up. + +[developer guide]: ../developer/index.md + +## User interacting with CMake + +For the context of this guide, we are focusing on the user who would actually +have to interact with CMake. Each kind of users will have different level of +familiarity with CMake and needs for using it. Throughout this guide there will +be relevant notes that are particularly important to a subset of people, e.g.: + +[//]: # (TODO: Is it possible to link the tab-item back to this page?) + +> #### How do I configure the project? +> ::::{tab-set} +> :::{tab-item} As an end-user +> Here is the most basic instructions +> ::: +> :::{tab-item} As an upstream developer +> Here is how you can make your life easier +> ::: +> :::{tab-item} As a downstream packager +> Beware of the packaging guidelines +> ::: +> :::: + +### End-user + +Ok, I've lied, there are situations when the end-user would need to build the +project from source, e.g. when the packaged project does not include the +features you need. Nevertheless, even here the user will have a bare minimum +interaction with CMake, and it is assumed they will not have to interact with it +(at least in the context of building your project) after this. + +### Upstream developer + +Here the upstream developer refers to the day-to-day developer of the current +project that uses this guide's template. Other developers that would be +importing the current project are considered to be an [end user]. + +This type of developer would not be handling the build-system, the project CI, +or any other bells-and-whistles that are implemented with this template. +Instead, their main goal is to maintain the main C/C++ (or other language) code. + +### Packager + +The final type of user of this guide is the rarest, of them all, and they should +be having the most experience with CMake itself. Nevertheless, there are a few +nuances that they should be aware of regarding the modern CMake design +implemented here. + +Even so, in this guide, I will not be assuming any prior CMake expertise, even +when the sections are aimed primarily for this group of people, and hopefully at +the end of this guide, you should be able to interact with any CMake project +old-or-new, in any packaging environment. I will not be explaining much of the +CMake inner-workings more than it is relevant, and if you are interested in that +check the [developer guide] as well. + +[end user]: #end-user +[developer]: #upstream-developer +[packager]: #packager diff --git a/docs/index.md b/docs/index.md index 3dbf231..cdec9d4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,22 +1,19 @@ # CMake Template ```{toctree} ---- -maxdepth: 2 -titlesonly: true -caption: Contents -hidden: true -glob: true ---- +: maxdepth: 2 +: titlesonly: true +: caption: Contents +: hidden: true +: glob: true + self guides/index.md ``` ```{include} ../README.md ---- -start-after: -end-before: ---- +: start-after: +: end-before: ``` [cmake-presets]: inv:cmake:std:doc#manual/cmake-presets.7