| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- 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 - Setting the C++ Standard with Interface Libraries
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Before we use :manual:`generator expressions <cmake-generator-expressions(7)>`
- 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.
- The starting source code is provided in the ``Step4`` directory. In this
- exercise, complete ``TODO 1`` through ``TODO 3``.
- 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
- -------------
- 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.
- Here's a refresher of what that looks like from the command line:
- .. code-block:: console
- mkdir Step4_build
- cd Step4_build
- cmake ../Step4
- 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:: Step4/CMakeLists.txt
- :caption: CMakeLists.txt
- :name: CMakeLists.txt-CXX_STANDARD-variable-remove
- :language: cmake
- :start-after: # specify the C++ standard
- :end-before: # TODO 5: Create helper variables
- 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 1: Click to show/hide answer</summary>
- .. literalinclude:: Step5/CMakeLists.txt
- :caption: TODO 1: CMakeLists.txt
- :name: CMakeLists.txt-cxx_std-feature
- :language: cmake
- :start-after: # specify the C++ standard
- :end-before: # add compiler warning flags just
- .. raw:: html
- </details>
- Finally, with our interface library set up, we need to link our
- executable ``Target`` and our ``MathFunctions`` library to our new
- ``tutorial_compiler_flags`` library. Respectively, the code will look like
- this:
- .. raw:: html
- <details><summary>TODO 2: Click to show/hide answer</summary>
- .. literalinclude:: Step5/CMakeLists.txt
- :caption: TODO 2: 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>
- and this:
- .. raw:: html
- <details><summary>TODO 3: Click to show/hide answer</summary>
- .. literalinclude:: Step5/MathFunctions/CMakeLists.txt
- :caption: TODO 3: MathFunctions/CMakeLists.txt
- :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
- :language: cmake
- :start-after: # link our compiler flags interface library
- .. 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.
- Exercise 2 - 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
- ---------------
- Start with the resulting files from Exercise 1. Complete ``TODO 4`` through
- ``TODO 7``.
- 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
- -------------
- Since we have our build directory already configured from Exercise 1, simply
- rebuild our code by calling the following:
- .. code-block:: console
- cd Step4_build
- cmake --build .
- Solution
- --------
- Update the :command:`cmake_minimum_required` to require at least CMake
- version ``3.15``:
- .. raw:: html
- <details><summary>TODO 4: Click to show/hide answer</summary>
- .. literalinclude:: Step5/CMakeLists.txt
- :caption: TODO 4: 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 5: Click to show/hide answer</summary>
- .. literalinclude:: Step5/CMakeLists.txt
- :caption: TODO 5: 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 6: Click to show/hide answer</summary>
- .. code-block:: cmake
- :caption: TODO 6: 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 in a generator expression using the ``BUILD_INTERFACE``
- condition. The resulting full code looks like the following:
- .. raw:: html
- <details><summary>TODO 7: Click to show/hide answer</summary>
- .. literalinclude:: Step5/CMakeLists.txt
- :caption: TODO 7: CMakeLists.txt
- :name: CMakeLists.txt-target_compile_options-genex
- :language: cmake
- :start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
- :end-before: # should we use our own math functions
- .. raw:: html
- </details>
|