mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-06 13:51:33 -06:00
This is a full re-write of the CMake Tutorial for CMake 3.23, both the functionality it provides, as well as the modern workflows that developers use when interfacing with CMake. Issue: #22663, #23086, #23799, #26053, #26105, #26153, #26914
533 lines
17 KiB
ReStructuredText
533 lines
17 KiB
ReStructuredText
Step 4: In-Depth CMake Target Commands
|
|
======================================
|
|
|
|
There are several target commands within CMake we can use to describe
|
|
requirements. As a reminder, a target command is one which modifies the
|
|
properties of the target it is applied to. These properties describe
|
|
requirements needed to build the software, such as sources, compile flags,
|
|
and output names; or properties necessary to consume the target, such as header
|
|
includes, library directories, and linkage rules.
|
|
|
|
.. note::
|
|
As discussed in ``Step1``, properties required to build a target should be
|
|
described with the ``PRIVATE`` :ref:`scope keyword <Target Command Scope>`,
|
|
those required to consume the target with ``INTERFACE``, and properties needed
|
|
for both are described with ``PUBLIC``.
|
|
|
|
In this step we will go over all the available target commands in CMake. Not all
|
|
target commands are created equal. We have already discussed the two most
|
|
important target commands, :command:`target_sources` and
|
|
:command:`target_link_libraries`. Of the remaining commands, some are almost
|
|
as common as these two, others have more advanced applications, and a couple
|
|
should only be used as a last resort when other options are not available.
|
|
|
|
Background
|
|
^^^^^^^^^^
|
|
|
|
Before going any further, let's name all of the CMake target commands. We'll
|
|
split these into three groups: the recommended and generally useful commands,
|
|
the advanced and cautionary commands, and the "footgun" commands which should
|
|
be avoided unless necessary.
|
|
|
|
+-----------------------------------------+--------------------------------------+---------------------------------------+
|
|
| Common/Recommended | Advanced/Caution | Esoteric/Footguns |
|
|
+=========================================+======================================+=======================================+
|
|
| :command:`target_compile_definitions` | :command:`get_target_property` | :command:`target_include_directories` |
|
|
| :command:`target_compile_features` | :command:`set_target_properties` | :command:`target_link_directories` |
|
|
| :command:`target_link_libraries` | :command:`target_compile_options` | |
|
|
| :command:`target_sources` | :command:`target_link_options` | |
|
|
| | :command:`target_precompile_headers` | |
|
|
+-----------------------------------------+--------------------------------------+---------------------------------------+
|
|
|
|
.. note::
|
|
There's no such thing as a "bad" CMake target command. They all have valid
|
|
use cases. This categorization is provided to give newcomers a simple
|
|
intuition about which commands they should consider first when tackling
|
|
a problem.
|
|
|
|
We'll demonstrate most of these in the following exercises. The three we won't
|
|
be using are :command:`get_target_property`, :command:`set_target_properties`
|
|
and :command:`target_precompile_headers`, so we will briefly discuss their
|
|
purpose here.
|
|
|
|
The :command:`get_target_property` and :command:`set_target_properties` commands
|
|
give direct access to a target's properties by name. They can even be used
|
|
to attach arbitrary property names to a target.
|
|
|
|
.. code-block:: cmake
|
|
|
|
add_library(Example)
|
|
set_target_properties(Example
|
|
PROPERTIES
|
|
Key Value
|
|
Hello World
|
|
)
|
|
|
|
get_target_property(KeyVar Example Key)
|
|
get_target_property(HelloVar Example Hello)
|
|
|
|
message("Key: ${KeyVar}")
|
|
message("Hello: ${HelloVar}")
|
|
|
|
.. code-block:: console
|
|
|
|
$ cmake -B build
|
|
...
|
|
Key: Value
|
|
Hello: World
|
|
|
|
The full list of target properties which are semantically meaningful to CMake
|
|
are documented at :manual:`cmake-properties(7)`, however most of these should
|
|
be modified with their dedicated commands. For example, it is unnecessary to
|
|
directly manipulate ``LINK_LIBRARIES`` and ``INTERFACE_LINK_LIBRARIES``, as
|
|
these are handled by :command:`target_link_libraries`.
|
|
|
|
Conversely, some lesser-used properties are only accessible via these commands.
|
|
The :prop_tgt:`DEPRECATION` property, used to attach deprecation notices to
|
|
targets, can only be set via :command:`set_target_properties`; as can the
|
|
:prop_tgt:`ADDITIONAL_CLEAN_FILES`, for describing additional files to be
|
|
removed by CMake's ``clean`` target; and other properties of this sort.
|
|
|
|
The :command:`target_precompile_headers` command takes a list of header files,
|
|
similar to :command:`target_sources`, and creates a precompiled header from
|
|
them. This precompiled header is then force included into all translation
|
|
units in the target. This can be useful for build performance.
|
|
|
|
Exercise 1 - Features and Definitions
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
In earlier steps we cautioned against globally setting
|
|
:variable:`CMAKE_<LANG>_STANDARD` and overriding packagers' decision concerning
|
|
which language standard to use. On the other hand, many libraries have a
|
|
minimum required feature set they need in order to build, and for these it
|
|
is appropriate to use the :command:`target_compile_features` command to
|
|
communicate those requirements.
|
|
|
|
.. code-block:: cmake
|
|
|
|
target_compile_features(MyApp PRIVATE cxx_std_20)
|
|
|
|
The :command:`target_compile_features` command describes a minimum language
|
|
standard as a target property. If the :variable:`CMAKE_<LANG>_STANDARD` is above
|
|
this version, or the compiler default already provides this language standard,
|
|
no action is taken. If additional flags are necessary to enable the standard,
|
|
these will be added by CMake.
|
|
|
|
.. note::
|
|
:command:`target_compile_features` manipulates the same style of interface and
|
|
non-interface properties as the other target commands. This means it is
|
|
possible to *inherit* a language standard requirement specified with
|
|
``INTERFACE`` or ``PUBLIC`` scope keywords.
|
|
|
|
If language features are used only in implementation files, then the
|
|
respective compile features should be ``PRIVATE``. If the target's headers
|
|
use the features, then ``PUBLIC`` or ``INTERFACE`` should be used.
|
|
|
|
For C++, the compile features are of the form ``cxx_std_YY`` where ``YY`` is
|
|
the standardization year, e.g. ``14``, ``17``, ``20``, etc.
|
|
|
|
The :command:`target_compile_definitions` command describes compile definitions
|
|
as target properties. It is the most common mechanism for communicating build
|
|
configuration information to the source code itself. As with all properties,
|
|
the scope keywords apply as we have discussed.
|
|
|
|
.. code-block:: cmake
|
|
|
|
target_compile_definitions(MyLibrary
|
|
PRIVATE
|
|
MYLIBRARY_USE_EXPERIMENTAL_IMPLEMENTATION
|
|
|
|
PUBLIC
|
|
MYLIBRARY_EXCLUDE_DEPRECATED_FUNCTIONS
|
|
)
|
|
|
|
It is neither required nor desired that we attach ``-D`` prefixes to compile
|
|
definitions described with :command:`target_compile_definitions`. CMake will
|
|
determine the correct flag for the current compiler.
|
|
|
|
Goal
|
|
----
|
|
|
|
Use :command:`target_compile_features` and :command:`target_compile_definitions`
|
|
to communicate language standard and compile definition requirements.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`target_compile_features`
|
|
* :command:`target_compile_definitions`
|
|
* :command:`option`
|
|
* :command:`if`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``Tutorial/CMakeLists.txt``
|
|
* ``MathFunctions/CMakeLists.txt``
|
|
* ``MathFunctions/MathFunctions.cxx``
|
|
* ``CMakePresets.json``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
The ``Help/guide/tutorial/Step4`` directory contains the complete, recommended
|
|
solution to ``Step3`` and relevant ``TODOs`` for this step. Complete ``TODO 1``
|
|
through ``TODO 8``.
|
|
|
|
Build and Run
|
|
-------------
|
|
|
|
We can run CMake using our ``tutorial`` preset, and then build as usual.
|
|
|
|
.. code-block:: console
|
|
|
|
cmake --preset tutorial
|
|
cmake --build build
|
|
|
|
Verify that the output of ``Tutorial`` is what we would expect for ``std::sqrt``.
|
|
|
|
Solution
|
|
--------
|
|
|
|
First we add a new option to the top-level CML.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 1: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step5/CMakeLists.txt
|
|
:caption: TODO 1: CMakeLists.txt
|
|
:name: CMakeLists.txt-TUTORIAL_USE_STD_SQRT
|
|
:language: cmake
|
|
:start-at: option(TUTORIAL_BUILD_UTILITIES
|
|
:end-at: option(TUTORIAL_USE_STD_SQRT
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Then we add the compile feature and definitions to ``MathFunctions``.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 2-3: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step5/MathFunctions/CMakeLists.txt
|
|
:caption: TODO 2-3: MathFunctions/CMakeLists.txt
|
|
:name: MathFunctions/CMakeLists.txt-target_compile_features
|
|
:language: cmake
|
|
:start-at: target_compile_features
|
|
:end-at: endif()
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
And the compile feature for ``Tutorial``.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 4: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step5/Tutorial/CMakeLists.txt
|
|
:caption: TODO 4: Tutorial/CMakeLists.txt
|
|
:name: Tutorial/CMakeLists.txt-target_compile_features
|
|
:language: cmake
|
|
:start-at: target_compile_features
|
|
:end-at: target_compile_features
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Now we can modify ``MathFunctions`` to take advantage of the new definition.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 5-6: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step5/MathFunctions/MathFunctions.cxx
|
|
:caption: TODO 5: MathFunctions/MathFunctions.cxx
|
|
:name: MathFunctions/MathFunctions.cxx-cmath
|
|
:language: c++
|
|
:start-at: cmath
|
|
:end-at: format
|
|
:append: #include <iostream>
|
|
|
|
.. literalinclude:: Step5/MathFunctions/MathFunctions.cxx
|
|
:caption: TODO 6: MathFunctions/MathFunctions.cxx
|
|
:name: MathFunctions/MathFunctions.cxx-std-sqrt
|
|
:language: c++
|
|
:start-at: double sqrt(double x)
|
|
:end-at: }
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Finally we can update our ``CMakePresets.json``. We don't need to set
|
|
``CMAKE_CXX_STANDARD`` anymore, but we do want to try out our new
|
|
compile definition.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 7-8: Click to show/hide answer</summary>
|
|
|
|
.. code-block:: json
|
|
:caption: TODO 7-8: CMakePresets.json
|
|
:name: CMakePresets.json-std-sqrt
|
|
|
|
"cacheVariables": {
|
|
"TUTORIAL_USE_STD_SQRT": "ON"
|
|
}
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Exercise 2 - Compile and Link Options
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Sometimes, we need to exercise specific control over the exact options being
|
|
passed on the compile and link line. These situations are addressed by
|
|
:command:`target_compile_options` and :command:`target_link_options`.
|
|
|
|
.. code:: c++
|
|
|
|
target_compile_options(MyApp PRIVATE -Wall -Werror)
|
|
target_link_options(MyApp PRIVATE -T LinksScript.ld)
|
|
|
|
There are several problems with unconditionally calling
|
|
:command:`target_compile_options` or :command:`target_link_options`. The primary
|
|
problem is compiler flags are specific to the compiler frontend being used. In
|
|
order to ensure that our project supports multiple compiler frontends, we must
|
|
only pass compatible flags to the compiler.
|
|
|
|
We can achieve this by checking the :variable:`CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT`
|
|
variable which tells us the style of flags supported by the compiler frontend.
|
|
|
|
.. note::
|
|
Prior to CMake 3.26, :variable:`CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT` was
|
|
only set for compilers with multiple frontend variants. In versions after
|
|
CMake 3.26 checking this variable alone is sufficient.
|
|
|
|
However this tutorial targets CMake 3.23. As such, the logic is more
|
|
complicated than we have time for here. This tutorial step already includes
|
|
correct logic for checking the compiler variant for MSVC, GCC, Clang, and
|
|
AppleClang on CMake 3.23.
|
|
|
|
Even if a compiler accepts the flags we pass, the semantics of compiler flags
|
|
change over time. This is especially true with regards to warnings. Projects
|
|
should not turn warnings-as-error flags by default, as this can break their
|
|
build on otherwise innocuous compiler warnings included in later releases.
|
|
|
|
.. note::
|
|
For errors and warnings, consider placing flags in :variable:`CMAKE_<LANG>_FLAGS`
|
|
for local development builds and during CI runs (via preset or
|
|
:option:`-D <cmake -D>` flags). We know exactly which compiler and
|
|
toolchain are being used in these contexts, so we can customize the behavior
|
|
precisely without risking build breakages on other platforms.
|
|
|
|
Goal
|
|
----
|
|
|
|
Add appropriate warning flags to the ``Tutorial`` executable for MSVC-style and
|
|
GNU-style compiler frontends.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`target_compile_options`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``Tutorial/CMakeLists.txt``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
Continue editing files in the ``Step4`` directory. The conditional for checking
|
|
the frontend variant has already been written. Complete ``TODO 9`` and
|
|
``TODO 10`` to add warning flags to ``Tutorial``.
|
|
|
|
Build and Run
|
|
-------------
|
|
|
|
Since we have already configured for this step, we can build with the usual
|
|
command.
|
|
|
|
.. code-block:: cmake
|
|
|
|
cmake --build build
|
|
|
|
This should reveal a simple warning in the build. You can go ahead and fix it.
|
|
|
|
Solution
|
|
--------
|
|
|
|
We need to add two compile options to ``Tutorial``, one MSVC-style flag and
|
|
one GNU-style flag.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 9-10: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step5/Tutorial/CMakeLists.txt
|
|
:caption: TODO 9-10: Tutorial/CMakeLists.txt
|
|
:name: Tutorial/CMakeLists.txt-target_compile_options
|
|
:language: cmake
|
|
:start-at: if(
|
|
:end-at: endif()
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Exercise 3 - Include and Link Directories
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
.. note::
|
|
This exercise requires building an archive using a compiler directly on the
|
|
command line. It is not used in later steps. It is included only to
|
|
demonstrate a use case for :command:`target_include_directories` and
|
|
:command:`target_link_directories`.
|
|
|
|
If you cannot complete this exercise for whatever reason feel free to treat
|
|
it as informational-only, or skip it entirely.
|
|
|
|
It is generally unnecessary to directly describe include and link directories,
|
|
as these requirements are inherited when linking together targets generated
|
|
within CMake, or from external dependencies imported into CMake with commands
|
|
we will cover in later steps.
|
|
|
|
If we happen to have some libraries or header files which are not described
|
|
by a CMake target which we need to bring into the build, perhaps pre-compiled
|
|
binaries provided by a vendor, we can incorporate with the
|
|
:command:`target_link_directories` and :command:`target_include_directories`
|
|
commands.
|
|
|
|
.. code-block:: cmake
|
|
|
|
target_link_directories(MyApp PRIVATE Vendor/lib)
|
|
target_include_directories(MyApp PRIVATE Vendor/include)
|
|
|
|
|
|
These commands use properties which map to the ``-L`` and ``-I`` compiler flags
|
|
(or whatever flags the compiler uses for link and include directories).
|
|
|
|
Of course, passing a link directory doesn't tell the compiler to link anything
|
|
into the build. For that we need :command:`target_link_libraries`. When
|
|
:command:`target_link_libraries` is given an argument which does not map to
|
|
a target name, it will add the string directly to the link line as a library
|
|
to be linked into the build (prepending any appropriate flags, such a ``-l``).
|
|
|
|
Goal
|
|
----
|
|
|
|
Describe a pre-compiled, vendored, static library and its headers inside a
|
|
project using :command:`target_link_directories` and
|
|
:command:`target_include_directories`.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`target_link_directories`
|
|
* :command:`target_include_directories`
|
|
* :command:`target_link_libraries`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``Vendor/CMakeLists.txt``
|
|
* ``Tutorial/CMakeLists.txt``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
You will need to build the vendor library into a static archive to complete this
|
|
exercise. Navigate to the ``Help/guide/tutorial/Step4/Vendor/lib`` directory
|
|
and build the code as appropriate for your platform. On Unix-like operating
|
|
systems the appropriate commands are usually:
|
|
|
|
.. code-block:: console
|
|
|
|
g++ -c Vendors.cxx
|
|
ar rvs libVendor.a Vendor.o
|
|
|
|
Then complete ``TODO 11`` through ``TODO 14``.
|
|
|
|
.. note::
|
|
``VendorLib`` is an ``INTERFACE`` library, meaning it has no build requirements
|
|
(because it has already been built). All of its properties should also be
|
|
interface properties.
|
|
|
|
We'll discuss ``INTERFACE`` libraries in greater depth during the next step.
|
|
|
|
|
|
Build and Run
|
|
-------------
|
|
|
|
If you have successfully built ``libVendor``, you can rebuild ``Tutorial``
|
|
using the normal command.
|
|
|
|
.. code-block:: console
|
|
|
|
cmake --build build
|
|
|
|
Running ``Tutorial`` should now output a message about the acceptability of the
|
|
result to the vendor.
|
|
|
|
Solution
|
|
--------
|
|
|
|
We need to use the target link and include commands to describe the archive
|
|
and its headers as ``INTERFACE`` requirements of ``VendorLib``.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 11-13: Click to show/hide answer</summary>
|
|
|
|
.. code-block:: cmake
|
|
:caption: TODO 11-13: Vendor/CMakeLists.txt
|
|
:name: Vendor/CMakeLists.txt
|
|
|
|
target_include_directories(VendorLib
|
|
INTERFACE
|
|
include
|
|
)
|
|
|
|
target_link_directories(VendorLib
|
|
INTERFACE
|
|
lib
|
|
)
|
|
|
|
target_link_libraries(VendorLib
|
|
INTERFACE
|
|
Vendor
|
|
)
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Then we can add ``VendorLib`` to ``Tutorial``'s linked libraries.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 14: Click to show/hide answer</summary>
|
|
|
|
.. code-block:: cmake
|
|
:caption: TODO 14: Tutorial/CMakeLists.txt
|
|
:name: Tutorial/CMakeLists.txt-VendorLib
|
|
|
|
target_link_libraries(Tutorial
|
|
PRIVATE
|
|
MathFunctions
|
|
VendorLib
|
|
)
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|