Tutorial: Update step 4 style

This commit is contained in:
Markus Ferrell 2022-08-26 13:42:16 -04:00
parent 9b1b9b9aae
commit 37fceb6fd3
10 changed files with 289 additions and 33 deletions

View File

@ -27,58 +27,279 @@ 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.
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. Let's start by constructing an
``INTERFACE`` target and specifying the required C++ standard level of ``11``
instead of using :variable:`CMAKE_CXX_STANDARD`.
Exercise 1 - Setting the C++ Standard with Interface Libraries
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So the following code:
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: project(Tutorial VERSION 1.0)
:end-before: # should we use our own math functions
:start-after: # specify the C++ standard
:end-before: # TODO 5: Create helper variables
Would be replaced with:
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: CMakeLists.txt
:caption: TODO 1: CMakeLists.txt
:name: CMakeLists.txt-cxx_std-feature
:language: cmake
:start-after: project(Tutorial VERSION 1.0)
:end-before: # add compiler warning flags just when building this project via
:start-after: # specify the C++ standard
:end-before: # add compiler warning flags just
**Note**: This upcoming section will require a change to the
:command:`cmake_minimum_required` usage in the code. The Generator Expression
that is about to be used was introduced in `3.15`. Update the call to require
that more recent version:
.. raw:: html
.. code-block:: cmake
:caption: CMakeLists.txt
:name: CMakeLists.txt-version-update
</details>
cmake_minimum_required(VERSION 3.15)
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:
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 as seen below:
.. raw:: html
<details><summary>TODO 2: Click to show/hide answer</summary>
.. literalinclude:: Step5/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-target_compile_options-genex
: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
Looking at this we see that the warning flags are encapsulated inside a
``BUILD_INTERFACE`` condition. This is done so that consumers of our installed
project will not inherit our warning flags.
.. raw:: html
**Exercise**: Modify ``MathFunctions/CMakeLists.txt`` so that all targets have
a :command:`target_link_libraries` call to ``tutorial_compiler_flags``.
</details>

View File

@ -96,6 +96,7 @@ follows:
:name: MathFunctions/CMakeLists.txt-target_include_directories-INTERFACE
:language: cmake
:start-after: # to find MathFunctions.h
:end-before: # TODO 3: Link to
.. raw:: html

View File

@ -22,6 +22,7 @@ target_include_directories(MathFunctions
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
# link our compiler flags interface library
target_link_libraries(MathFunctions tutorial_compiler_flags)
# install rules

View File

@ -1,12 +1,36 @@
# TODO 4: Update the minimum required version to 3.15
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
# TODO 1: Replace the following code by:
# * Creating an interface library called tutorial_compiler_flags
# Hint: use add_library() with the INTERFACE signature
# * Add compiler feature cxx_std_11 to tutorial_compiler_flags
# Hint: Use target_compile_features()
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# TODO 5: Create helper variables to determine which compiler we are using:
# * Create a new variable gcc_like_cxx that is true if we are using CXX and
# any of the following compilers: ARMClang, AppleClang, Clang, GNU, LCC
# * Create a new variable msvc_cxx that is true if we are using CXX and MSVC
# Hint: Use set() and COMPILE_LANG_AND_ID
# TODO 6: Add warning flag compile options to the interface library
# tutorial_compiler_flags.
# * For gcc_like_cxx, add flags -Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused
# * For msvc_cxx, add flags -W3
# Hint: Use target_compile_options()
# TODO 7: With nested generator expressions, only use the flags for the
# build-tree
# Hint: Use BUILD_INTERFACE
# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
@ -23,6 +47,8 @@ endif()
# add the executable
add_executable(Tutorial tutorial.cxx)
# TODO 2: Link to tutorial_compiler_flags
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
# add the binary tree to the search path for include files

View File

@ -5,3 +5,5 @@ add_library(MathFunctions mysqrt.cxx)
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# TODO 3: Link to tutorial_compiler_flags

View File

@ -6,4 +6,5 @@ target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# link our compiler flags interface library
target_link_libraries(MathFunctions tutorial_compiler_flags)

View File

@ -6,6 +6,7 @@ target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# link our compiler flags interface library
target_link_libraries(MathFunctions tutorial_compiler_flags)
# install rules

View File

@ -6,6 +6,7 @@ target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# link our compiler flags interface library
target_link_libraries(MathFunctions tutorial_compiler_flags)
# install rules

View File

@ -6,6 +6,7 @@ target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# link our compiler flags interface library
target_link_libraries(MathFunctions tutorial_compiler_flags)
# does this system provide the log and exp functions?

View File

@ -24,6 +24,7 @@ target_include_directories(MathFunctions
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
# link our compiler flags interface library
target_link_libraries(MathFunctions tutorial_compiler_flags)
# install rules