|
|
|
Step 4: Adding Generator Expressions
|
|
|
|
=====================================
|
|
|
|
|
|
|
|
:manual:`Generator expressions <cmake-generator-expressions(7)>` are evaluated
|
|
|
|
during build system generation to produce information specific to each build
|
|
|
|
configuration.
|
|
|
|
|
|
|
|
:manual:`Generator expressions <cmake-generator-expressions(7)>` are allowed in
|
|
|
|
the context of many target properties, such as :prop_tgt:`LINK_LIBRARIES`,
|
|
|
|
:prop_tgt:`INCLUDE_DIRECTORIES`, :prop_tgt:`COMPILE_DEFINITIONS` and others.
|
|
|
|
They may also be used when using commands to populate those properties, such as
|
|
|
|
:command:`target_link_libraries`, :command:`target_include_directories`,
|
|
|
|
:command:`target_compile_definitions` and others.
|
|
|
|
|
|
|
|
:manual:`Generator expressions <cmake-generator-expressions(7)>` may be used
|
|
|
|
to enable conditional linking, conditional definitions used when compiling,
|
|
|
|
conditional include directories and more. The conditions may be based on the
|
|
|
|
build configuration, target properties, platform information or any other
|
|
|
|
queryable information.
|
|
|
|
|
|
|
|
There are different types of
|
|
|
|
:manual:`generator expressions <cmake-generator-expressions(7)>` including
|
|
|
|
Logical, Informational, and Output expressions.
|
|
|
|
|
|
|
|
Logical expressions are used to create conditional output. The basic
|
|
|
|
expressions are the ``0`` and ``1`` expressions. A ``$<0:...>`` results in the
|
|
|
|
empty string, and ``$<1:...>`` results in the content of ``...``. They can also
|
|
|
|
be nested.
|
|
|
|
|
|
|
|
Exercise 1 - Adding Compiler Warning Flags with Generator Expressions
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
A common usage of
|
|
|
|
:manual:`generator expressions <cmake-generator-expressions(7)>` is to
|
|
|
|
conditionally add compiler flags, such as those for language levels or
|
|
|
|
warnings. A nice pattern is to associate this information to an ``INTERFACE``
|
|
|
|
target allowing this information to propagate.
|
|
|
|
|
|
|
|
Goal
|
|
|
|
----
|
|
|
|
|
|
|
|
Add compiler warning flags when building but not for installed versions.
|
|
|
|
|
|
|
|
Helpful Resources
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
* :manual:`cmake-generator-expressions(7)`
|
|
|
|
* :command:`cmake_minimum_required`
|
|
|
|
* :command:`set`
|
|
|
|
* :command:`target_compile_options`
|
|
|
|
|
|
|
|
Files to Edit
|
|
|
|
-------------
|
|
|
|
|
|
|
|
* ``CMakeLists.txt``
|
|
|
|
|
|
|
|
Getting Started
|
|
|
|
---------------
|
|
|
|
|
|
|
|
Open the file ``Step4/CMakeLists.txt`` and complete ``TODO 1`` through
|
|
|
|
``TODO 4``.
|
|
|
|
|
|
|
|
First, in the top level ``CMakeLists.txt`` file, we need to set the
|
|
|
|
:command:`cmake_minimum_required` to ``3.15``. In this exercise we are going
|
|
|
|
to use a generator expression which was introduced in CMake 3.15.
|
|
|
|
|
|
|
|
Next we add the desired compiler warning flags that we want for our project.
|
|
|
|
As warning flags vary based on the compiler, we use the
|
|
|
|
``COMPILE_LANG_AND_ID`` generator expression to control which flags to apply
|
|
|
|
given a language and a set of compiler ids.
|
|
|
|
|
|
|
|
Build and Run
|
|
|
|
-------------
|
|
|
|
|
|
|
|
Make a new directory called ``Step4_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 ``cmake --build .``
|
|
|
|
from the build directory.
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
mkdir Step4_build
|
|
|
|
cd Step4_build
|
|
|
|
cmake ../Step4
|
|
|
|
cmake --build .
|
|
|
|
|
|
|
|
Solution
|
|
|
|
--------
|
|
|
|
|
|
|
|
Update the :command:`cmake_minimum_required` to require at least CMake
|
|
|
|
version ``3.15``:
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
<details><summary>TODO 1: Click to show/hide answer</summary>
|
|
|
|
|
|
|
|
.. literalinclude:: Step5/CMakeLists.txt
|
|
|
|
:caption: TODO 1: CMakeLists.txt
|
|
|
|
:name: MathFunctions-CMakeLists.txt-minimum-required-step4
|
|
|
|
:language: cmake
|
|
|
|
:end-before: # set the project name and version
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
Next we determine which compiler our system is currently using to build
|
|
|
|
since warning flags vary based on the compiler we use. This is done with
|
|
|
|
the ``COMPILE_LANG_AND_ID`` generator expression. We set the result in the
|
|
|
|
variables ``gcc_like_cxx`` and ``msvc_cxx`` as follows:
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
<details><summary>TODO 2: Click to show/hide answer</summary>
|
|
|
|
|
|
|
|
.. literalinclude:: Step5/CMakeLists.txt
|
|
|
|
:caption: TODO 2: CMakeLists.txt
|
|
|
|
:name: CMakeLists.txt-compile_lang_and_id
|
|
|
|
:language: cmake
|
|
|
|
:start-after: # the BUILD_INTERFACE genex
|
|
|
|
:end-before: target_compile_options(tutorial_compiler_flags INTERFACE
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
Next we add the desired compiler warning flags that we want for our project.
|
|
|
|
Using our variables ``gcc_like_cxx`` and ``msvc_cxx``, we can use another
|
|
|
|
generator expression to apply the respective flags only when the variables are
|
|
|
|
true. We use :command:`target_compile_options` to apply these flags to our
|
|
|
|
interface library.
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
<details><summary>TODO 3: Click to show/hide answer</summary>
|
|
|
|
|
|
|
|
.. code-block:: cmake
|
|
|
|
:caption: TODO 3: CMakeLists.txt
|
|
|
|
:name: CMakeLists.txt-compile_flags
|
|
|
|
|
|
|
|
target_compile_options(tutorial_compiler_flags INTERFACE
|
|
|
|
"$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
|
|
|
|
"$<${msvc_cxx}:-W3>"
|
|
|
|
)
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
Lastly, we only want these warning flags to be used during builds. Consumers
|
|
|
|
of our installed project should not inherit our warning flags. To specify
|
|
|
|
this, we wrap our flags from TODO 3 in a generator expression using the
|
|
|
|
``BUILD_INTERFACE`` condition. The resulting full code looks like the following:
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
<details><summary>TODO 4: Click to show/hide answer</summary>
|
|
|
|
|
|
|
|
.. literalinclude:: Step5/CMakeLists.txt
|
|
|
|
:caption: TODO 4: CMakeLists.txt
|
|
|
|
:name: CMakeLists.txt-target_compile_options-genex
|
|
|
|
:language: cmake
|
|
|
|
:start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
|
|
|
|
:end-before: # configure a header file to pass some of the CMake settings
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
</details>
|