mirror of
https://github.com/Kitware/CMake.git
synced 2026-03-12 12:30:08 -05:00
During the training, we talk about INTERFACE libraries just before Step3 so it makes more sense to do the exercise related to them in Step3
324 lines
9.1 KiB
ReStructuredText
324 lines
9.1 KiB
ReStructuredText
Step 3: Adding Usage Requirements for a Library
|
|
===============================================
|
|
|
|
Exercise 1 - Adding Usage Requirements for a Library
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
:ref:`Usage requirements <Target Usage Requirements>` of a target parameters
|
|
allow for far better control over a library or executable's link and include
|
|
line while also giving more control over the transitive property of targets
|
|
inside CMake. The primary commands that
|
|
leverage usage requirements are:
|
|
|
|
* :command:`target_compile_definitions`
|
|
* :command:`target_compile_options`
|
|
* :command:`target_include_directories`
|
|
* :command:`target_link_directories`
|
|
* :command:`target_link_options`
|
|
* :command:`target_precompile_headers`
|
|
* :command:`target_sources`
|
|
|
|
|
|
Goal
|
|
----
|
|
|
|
Add usage requirements for a library.
|
|
|
|
Helpful Materials
|
|
-----------------
|
|
|
|
* :variable:`CMAKE_CURRENT_SOURCE_DIR`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``MathFunctions/CMakeLists.txt``
|
|
* ``CMakeLists.txt``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
In this exercise, we will refactor our code from
|
|
:guide:`tutorial/Adding a Library` to use the modern CMake approach. We will
|
|
let our library define its own usage requirements so they are passed
|
|
transitively to other targets as necessary. In this case, ``MathFunctions``
|
|
will specify any needed include directories itself. Then, the consuming target
|
|
``Tutorial`` simply needs to link to ``MathFunctions`` and not worry about
|
|
any additional include directories.
|
|
|
|
The starting source code is provided in the ``Step3`` directory. In this
|
|
exercise, complete ``TODO 1`` through ``TODO 3``.
|
|
|
|
First, add a call to :command:`target_include_directories` in
|
|
``MathFunctions/CMakeLists``. Remember that
|
|
:variable:`CMAKE_CURRENT_SOURCE_DIR` is the path to the source directory
|
|
currently being processed.
|
|
|
|
Then, update (and simplify!) the call to
|
|
:command:`target_include_directories` in the top-level ``CMakeLists.txt``.
|
|
|
|
Build and Run
|
|
-------------
|
|
|
|
Make a new directory called ``Step3_build``, run the :manual:`cmake
|
|
<cmake(1)>` executable or the :manual:`cmake-gui <cmake-gui(1)>` to
|
|
configure the project and then build it with your chosen build tool or by
|
|
using :option:`cmake --build . <cmake --build>` from the build directory.
|
|
Here's a refresher of what that looks like from the command line:
|
|
|
|
.. code-block:: console
|
|
|
|
mkdir Step3_build
|
|
cd Step3_build
|
|
cmake ../Step3
|
|
cmake --build .
|
|
|
|
Next, use the newly built ``Tutorial`` and verify that it is working as
|
|
expected.
|
|
|
|
Solution
|
|
--------
|
|
|
|
Let's update the code from the previous step to use the modern CMake
|
|
approach of usage requirements.
|
|
|
|
We want to state that anybody linking to ``MathFunctions`` needs to include
|
|
the current source directory, while ``MathFunctions`` itself doesn't. This
|
|
can be expressed with an ``INTERFACE`` usage requirement. Remember
|
|
``INTERFACE`` means things that consumers require but the producer doesn't.
|
|
|
|
At the end of ``MathFunctions/CMakeLists.txt``, use
|
|
:command:`target_include_directories` with the ``INTERFACE`` keyword, as
|
|
follows:
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 1: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step4/MathFunctions/CMakeLists.txt
|
|
:caption: TODO 1: MathFunctions/CMakeLists.txt
|
|
:name: MathFunctions/CMakeLists.txt-target_include_directories-INTERFACE
|
|
:language: cmake
|
|
:start-after: # to find MathFunctions.h
|
|
:end-before: # should we use our own
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Now that we've specified usage requirements for ``MathFunctions`` we can
|
|
safely remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level
|
|
``CMakeLists.txt``.
|
|
|
|
Remove this line:
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 2: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/CMakeLists.txt
|
|
:caption: TODO 2: CMakeLists.txt
|
|
:name: CMakeLists.txt-remove-EXTRA_INCLUDES
|
|
:language: cmake
|
|
:start-after: add_subdirectory(MathFunctions)
|
|
:end-before: # add the executable
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
And the lines:
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 3: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step4/CMakeLists.txt
|
|
:caption: TODO 3: CMakeLists.txt
|
|
:name: CMakeLists.txt-target_include_directories-remove-EXTRA_INCLUDES
|
|
:language: cmake
|
|
:start-after: # so that we will find TutorialConfig.h
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
The remaining code looks like:
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>Click to show/hide the resulting code</summary>
|
|
|
|
.. literalinclude:: Step4/CMakeLists.txt
|
|
:caption: Remaining code after removing EXTRA_INCLUDES
|
|
:name: CMakeLists.txt-after-removing-EXTRA_INCLUDES
|
|
:language: cmake
|
|
:start-after: add_subdirectory(MathFunctions)
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
|
|
Notice that with this technique, the only thing our executable target does to
|
|
use our library is call :command:`target_link_libraries` with the name
|
|
of the library target. In larger projects, the classic method of specifying
|
|
library dependencies manually becomes very complicated very quickly.
|
|
|
|
Exercise 2 - Setting the C++ Standard with Interface Libraries
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Now that we have switched our code to a more modern approach, let's demonstrate
|
|
a modern technique to set properties to multiple targets.
|
|
|
|
Let's refactor our existing code to use an ``INTERFACE`` library. We will
|
|
use that library in the next step to demonstrate a common use for
|
|
:manual:`generator expressions <cmake-generator-expressions(7)>`.
|
|
|
|
Goal
|
|
----
|
|
|
|
Add an ``INTERFACE`` library target to specify the required C++ standard.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`add_library`
|
|
* :command:`target_compile_features`
|
|
* :command:`target_link_libraries`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``CMakeLists.txt``
|
|
* ``MathFunctions/CMakeLists.txt``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
In this exercise, we will refactor our code to use an ``INTERFACE`` library to
|
|
specify the C++ standard.
|
|
|
|
Start this exercise from what we left at the end of Step3 exercise 1. You will
|
|
have to complete ``TODO 4`` through ``TODO 7``.
|
|
|
|
Start by editing the top level ``CMakeLists.txt`` file. Construct an
|
|
``INTERFACE`` library target called ``tutorial_compiler_flags`` and
|
|
specify ``cxx_std_11`` as a target compiler feature.
|
|
|
|
Modify ``CMakeLists.txt`` and ``MathFunctions/CMakeLists.txt`` so that all
|
|
targets have a :command:`target_link_libraries` call to
|
|
``tutorial_compiler_flags``.
|
|
|
|
Build and Run
|
|
-------------
|
|
|
|
Since we have our build directory already configured from Exercise 1, simply
|
|
rebuild our code by calling the following:
|
|
|
|
.. code-block:: console
|
|
|
|
cd Step3_build
|
|
cmake --build .
|
|
|
|
Next, use the newly built ``Tutorial`` and verify that it is working as
|
|
expected.
|
|
|
|
Solution
|
|
--------
|
|
|
|
Let's update our code from the previous step to use interface libraries
|
|
to set our C++ requirements.
|
|
|
|
To start, we need to remove the two :command:`set` calls on the variables
|
|
:variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`.
|
|
The specific lines to remove are as follows:
|
|
|
|
.. literalinclude:: Step3/CMakeLists.txt
|
|
:caption: CMakeLists.txt
|
|
:name: CMakeLists.txt-CXX_STANDARD-variable-remove
|
|
:language: cmake
|
|
:start-after: # specify the C++ standard
|
|
:end-before: # configure a header file
|
|
|
|
Next, we need to create an interface library, ``tutorial_compiler_flags``. And
|
|
then use :command:`target_compile_features` to add the compiler feature
|
|
``cxx_std_11``.
|
|
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 4: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step4/CMakeLists.txt
|
|
:caption: TODO 4: CMakeLists.txt
|
|
:name: CMakeLists.txt-cxx_std-feature
|
|
:language: cmake
|
|
:start-after: # specify the C++ standard
|
|
:end-before: # TODO 2: Create helper
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Finally, with our interface library set up, we need to link our
|
|
executable ``Target``, our ``MathFunctions`` library, and our ``SqrtLibrary``
|
|
library to our new
|
|
``tutorial_compiler_flags`` library. Respectively, the code will look like
|
|
this:
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 5: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step4/CMakeLists.txt
|
|
:caption: TODO 5: CMakeLists.txt
|
|
:name: CMakeLists.txt-target_link_libraries-step4
|
|
:language: cmake
|
|
:start-after: add_executable(Tutorial tutorial.cxx)
|
|
:end-before: # add the binary tree to the search path for include file
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
this:
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 6: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step4/MathFunctions/CMakeLists.txt
|
|
:caption: TODO 6: MathFunctions/CMakeLists.txt
|
|
:name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
|
|
:language: cmake
|
|
:start-after: # link our compiler flags interface library
|
|
:end-before: target_link_libraries(MathFunctions
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
and this:
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 7: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step4/MathFunctions/CMakeLists.txt
|
|
:caption: TODO 7: MathFunctions/CMakeLists.txt
|
|
:name: MathFunctions-SqrtLibrary-target_link_libraries-step4
|
|
:language: cmake
|
|
:start-after: target_link_libraries(SqrtLibrary
|
|
:end-before: endif()
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
|
|
With this, all of our code still requires C++ 11 to build. Notice
|
|
though that with this method, it gives us the ability to be specific about
|
|
which targets get specific requirements. In addition, we create a single
|
|
source of truth in our interface library.
|