summaryrefslogtreecommitdiffstats
path: root/Help/guide/tutorial/Adding Generator Expressions.rst
diff options
context:
space:
mode:
Diffstat (limited to 'Help/guide/tutorial/Adding Generator Expressions.rst')
-rw-r--r--Help/guide/tutorial/Adding Generator Expressions.rst306
1 files changed, 306 insertions, 0 deletions
diff --git a/Help/guide/tutorial/Adding Generator Expressions.rst b/Help/guide/tutorial/Adding Generator Expressions.rst
new file mode 100644
index 0000000..08f3eea
--- /dev/null
+++ b/Help/guide/tutorial/Adding Generator Expressions.rst
@@ -0,0 +1,306 @@
+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
+ :end-before: # TODO 1
+
+.. 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: # configure a header file to pass some of the CMake settings
+
+.. raw:: html
+
+ </details>