1 Step 4: Adding Generator Expressions
2 =====================================
4 :manual:`Generator expressions <cmake-generator-expressions(7)>` are evaluated
5 during build system generation to produce information specific to each build
8 :manual:`Generator expressions <cmake-generator-expressions(7)>` are allowed in
9 the context of many target properties, such as :prop_tgt:`LINK_LIBRARIES`,
10 :prop_tgt:`INCLUDE_DIRECTORIES`, :prop_tgt:`COMPILE_DEFINITIONS` and others.
11 They may also be used when using commands to populate those properties, such as
12 :command:`target_link_libraries`, :command:`target_include_directories`,
13 :command:`target_compile_definitions` and others.
15 :manual:`Generator expressions <cmake-generator-expressions(7)>` may be used
16 to enable conditional linking, conditional definitions used when compiling,
17 conditional include directories and more. The conditions may be based on the
18 build configuration, target properties, platform information or any other
19 queryable information.
21 There are different types of
22 :manual:`generator expressions <cmake-generator-expressions(7)>` including
23 Logical, Informational, and Output expressions.
25 Logical expressions are used to create conditional output. The basic
26 expressions are the ``0`` and ``1`` expressions. A ``$<0:...>`` results in the
27 empty string, and ``<1:...>`` results in the content of ``...``. They can also
30 Exercise 1 - Setting the C++ Standard with Interface Libraries
31 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33 Before we use :manual:`generator expressions <cmake-generator-expressions(7)>`
34 let's refactor our existing code to use an ``INTERFACE`` library. We will
35 use that library in the next step to demonstrate a common use for
36 :manual:`generator expressions <cmake-generator-expressions(7)>`.
41 Add an ``INTERFACE`` library target to specify the required C++ standard.
46 * :command:`add_library`
47 * :command:`target_compile_features`
48 * :command:`target_link_libraries`
54 * ``MathFunctions/CMakeLists.txt``
59 In this exercise, we will refactor our code to use an ``INTERFACE`` library to
60 specify the C++ standard.
62 The starting source code is provided in the ``Step4`` directory. In this
63 exercise, complete ``TODO 1`` through ``TODO 3``.
65 Start by editing the top level ``CMakeLists.txt`` file. Construct an
66 ``INTERFACE`` library target called ``tutorial_compiler_flags`` and
67 specify ``cxx_std_11`` as a target compiler feature.
69 Modify ``CMakeLists.txt`` and ``MathFunctions/CMakeLists.txt`` so that all
70 targets have a :command:`target_link_libraries` call to
71 ``tutorial_compiler_flags``.
76 Make a new directory called ``Step4_build``, run the :manual:`cmake <cmake(1)>`
77 executable or the :manual:`cmake-gui <cmake-gui(1)>` to configure the project
78 and then build it with your chosen build tool or by using ``cmake --build .``
79 from the build directory.
81 Here's a refresher of what that looks like from the command line:
83 .. code-block:: console
90 Next, use the newly built ``Tutorial`` and verify that it is working as
96 Let's update our code from the previous step to use interface libraries
97 to set our C++ requirements.
99 To start, we need to remove the two :command:`set` calls on the variables
100 :variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`.
101 The specific lines to remove are as follows:
103 .. literalinclude:: Step4/CMakeLists.txt
104 :caption: CMakeLists.txt
105 :name: CMakeLists.txt-CXX_STANDARD-variable-remove
107 :start-after: # specify the C++ standard
108 :end-before: # TODO 5: Create helper variables
110 Next, we need to create an interface library, ``tutorial_compiler_flags``. And
111 then use :command:`target_compile_features` to add the compiler feature
117 <details><summary>TODO 1: Click to show/hide answer</summary>
119 .. literalinclude:: Step5/CMakeLists.txt
120 :caption: TODO 1: CMakeLists.txt
121 :name: CMakeLists.txt-cxx_std-feature
123 :start-after: # specify the C++ standard
124 :end-before: # add compiler warning flags just
130 Finally, with our interface library set up, we need to link our
131 executable ``Target`` and our ``MathFunctions`` library to our new
132 ``tutorial_compiler_flags`` library. Respectively, the code will look like
137 <details><summary>TODO 2: Click to show/hide answer</summary>
139 .. literalinclude:: Step5/CMakeLists.txt
140 :caption: TODO 2: CMakeLists.txt
141 :name: CMakeLists.txt-target_link_libraries-step4
143 :start-after: add_executable(Tutorial tutorial.cxx)
144 :end-before: # add the binary tree to the search path for include file
154 <details><summary>TODO 3: Click to show/hide answer</summary>
156 .. literalinclude:: Step5/MathFunctions/CMakeLists.txt
157 :caption: TODO 3: MathFunctions/CMakeLists.txt
158 :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
160 :start-after: # link our compiler flags interface library
161 :end-before: # TODO 1
167 With this, all of our code still requires C++ 11 to build. Notice
168 though that with this method, it gives us the ability to be specific about
169 which targets get specific requirements. In addition, we create a single
170 source of truth in our interface library.
172 Exercise 2 - Adding Compiler Warning Flags with Generator Expressions
173 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
176 :manual:`generator expressions <cmake-generator-expressions(7)>` is to
177 conditionally add compiler flags, such as those for language levels or
178 warnings. A nice pattern is to associate this information to an ``INTERFACE``
179 target allowing this information to propagate.
184 Add compiler warning flags when building but not for installed versions.
189 * :manual:`cmake-generator-expressions(7)`
190 * :command:`cmake_minimum_required`
192 * :command:`target_compile_options`
202 Start with the resulting files from Exercise 1. Complete ``TODO 4`` through
205 First, in the top level ``CMakeLists.txt`` file, we need to set the
206 :command:`cmake_minimum_required` to ``3.15``. In this exercise we are going
207 to use a generator expression which was introduced in CMake 3.15.
209 Next we add the desired compiler warning flags that we want for our project.
210 As warning flags vary based on the compiler, we use the
211 ``COMPILE_LANG_AND_ID`` generator expression to control which flags to apply
212 given a language and a set of compiler ids.
217 Since we have our build directory already configured from Exercise 1, simply
218 rebuild our code by calling the following:
220 .. code-block:: console
228 Update the :command:`cmake_minimum_required` to require at least CMake
233 <details><summary>TODO 4: Click to show/hide answer</summary>
235 .. literalinclude:: Step5/CMakeLists.txt
236 :caption: TODO 4: CMakeLists.txt
237 :name: MathFunctions-CMakeLists.txt-minimum-required-step4
239 :end-before: # set the project name and version
245 Next we determine which compiler our system is currently using to build
246 since warning flags vary based on the compiler we use. This is done with
247 the ``COMPILE_LANG_AND_ID`` generator expression. We set the result in the
248 variables ``gcc_like_cxx`` and ``msvc_cxx`` as follows:
252 <details><summary>TODO 5: Click to show/hide answer</summary>
254 .. literalinclude:: Step5/CMakeLists.txt
255 :caption: TODO 5: CMakeLists.txt
256 :name: CMakeLists.txt-compile_lang_and_id
258 :start-after: # the BUILD_INTERFACE genex
259 :end-before: target_compile_options(tutorial_compiler_flags INTERFACE
265 Next we add the desired compiler warning flags that we want for our project.
266 Using our variables ``gcc_like_cxx`` and ``msvc_cxx``, we can use another
267 generator expression to apply the respective flags only when the variables are
268 true. We use :command:`target_compile_options` to apply these flags to our
273 <details><summary>TODO 6: Click to show/hide answer</summary>
275 .. code-block:: cmake
276 :caption: TODO 6: CMakeLists.txt
277 :name: CMakeLists.txt-compile_flags
279 target_compile_options(tutorial_compiler_flags INTERFACE
280 "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
288 Lastly, we only want these warning flags to be used during builds. Consumers
289 of our installed project should not inherit our warning flags. To specify
290 this, we wrap our flags in a generator expression using the ``BUILD_INTERFACE``
291 condition. The resulting full code looks like the following:
295 <details><summary>TODO 7: Click to show/hide answer</summary>
297 .. literalinclude:: Step5/CMakeLists.txt
298 :caption: TODO 7: CMakeLists.txt
299 :name: CMakeLists.txt-target_compile_options-genex
301 :start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
302 :end-before: # should we use our own math functions