Adding Generator Expressions.rst 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. Step 4: Adding Generator Expressions
  2. =====================================
  3. :manual:`Generator expressions <cmake-generator-expressions(7)>` are evaluated
  4. during build system generation to produce information specific to each build
  5. configuration.
  6. :manual:`Generator expressions <cmake-generator-expressions(7)>` are allowed in
  7. the context of many target properties, such as :prop_tgt:`LINK_LIBRARIES`,
  8. :prop_tgt:`INCLUDE_DIRECTORIES`, :prop_tgt:`COMPILE_DEFINITIONS` and others.
  9. They may also be used when using commands to populate those properties, such as
  10. :command:`target_link_libraries`, :command:`target_include_directories`,
  11. :command:`target_compile_definitions` and others.
  12. :manual:`Generator expressions <cmake-generator-expressions(7)>` may be used
  13. to enable conditional linking, conditional definitions used when compiling,
  14. conditional include directories and more. The conditions may be based on the
  15. build configuration, target properties, platform information or any other
  16. queryable information.
  17. There are different types of
  18. :manual:`generator expressions <cmake-generator-expressions(7)>` including
  19. Logical, Informational, and Output expressions.
  20. Logical expressions are used to create conditional output. The basic
  21. expressions are the ``0`` and ``1`` expressions. A ``$<0:...>`` results in the
  22. empty string, and ``<1:...>`` results in the content of ``...``. They can also
  23. be nested.
  24. Exercise 1 - Setting the C++ Standard with Interface Libraries
  25. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  26. Before we use :manual:`generator expressions <cmake-generator-expressions(7)>`
  27. let's refactor our existing code to use an ``INTERFACE`` library. We will
  28. use that library in the next step to demonstrate a common use for
  29. :manual:`generator expressions <cmake-generator-expressions(7)>`.
  30. Goal
  31. ----
  32. Add an ``INTERFACE`` library target to specify the required C++ standard.
  33. Helpful Resources
  34. -----------------
  35. * :command:`add_library`
  36. * :command:`target_compile_features`
  37. * :command:`target_link_libraries`
  38. Files to Edit
  39. -------------
  40. * ``CMakeLists.txt``
  41. * ``MathFunctions/CMakeLists.txt``
  42. Getting Started
  43. ---------------
  44. In this exercise, we will refactor our code to use an ``INTERFACE`` library to
  45. specify the C++ standard.
  46. The starting source code is provided in the ``Step4`` directory. In this
  47. exercise, complete ``TODO 1`` through ``TODO 3``.
  48. Start by editing the top level ``CMakeLists.txt`` file. Construct an
  49. ``INTERFACE`` library target called ``tutorial_compiler_flags`` and
  50. specify ``cxx_std_11`` as a target compiler feature.
  51. Modify ``CMakeLists.txt`` and ``MathFunctions/CMakeLists.txt`` so that all
  52. targets have a :command:`target_link_libraries` call to
  53. ``tutorial_compiler_flags``.
  54. Build and Run
  55. -------------
  56. Make a new directory called ``Step4_build``, run the :manual:`cmake <cmake(1)>`
  57. executable or the :manual:`cmake-gui <cmake-gui(1)>` to configure the project
  58. and then build it with your chosen build tool or by using ``cmake --build .``
  59. from the build directory.
  60. Here's a refresher of what that looks like from the command line:
  61. .. code-block:: console
  62. mkdir Step4_build
  63. cd Step4_build
  64. cmake ../Step4
  65. cmake --build .
  66. Next, use the newly built ``Tutorial`` and verify that it is working as
  67. expected.
  68. Solution
  69. --------
  70. Let's update our code from the previous step to use interface libraries
  71. to set our C++ requirements.
  72. To start, we need to remove the two :command:`set` calls on the variables
  73. :variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`.
  74. The specific lines to remove are as follows:
  75. .. literalinclude:: Step4/CMakeLists.txt
  76. :caption: CMakeLists.txt
  77. :name: CMakeLists.txt-CXX_STANDARD-variable-remove
  78. :language: cmake
  79. :start-after: # specify the C++ standard
  80. :end-before: # TODO 5: Create helper variables
  81. Next, we need to create an interface library, ``tutorial_compiler_flags``. And
  82. then use :command:`target_compile_features` to add the compiler feature
  83. ``cxx_std_11``.
  84. .. raw:: html
  85. <details><summary>TODO 1: Click to show/hide answer</summary>
  86. .. literalinclude:: Step5/CMakeLists.txt
  87. :caption: TODO 1: CMakeLists.txt
  88. :name: CMakeLists.txt-cxx_std-feature
  89. :language: cmake
  90. :start-after: # specify the C++ standard
  91. :end-before: # add compiler warning flags just
  92. .. raw:: html
  93. </details>
  94. Finally, with our interface library set up, we need to link our
  95. executable ``Target`` and our ``MathFunctions`` library to our new
  96. ``tutorial_compiler_flags`` library. Respectively, the code will look like
  97. this:
  98. .. raw:: html
  99. <details><summary>TODO 2: Click to show/hide answer</summary>
  100. .. literalinclude:: Step5/CMakeLists.txt
  101. :caption: TODO 2: CMakeLists.txt
  102. :name: CMakeLists.txt-target_link_libraries-step4
  103. :language: cmake
  104. :start-after: add_executable(Tutorial tutorial.cxx)
  105. :end-before: # add the binary tree to the search path for include file
  106. .. raw:: html
  107. </details>
  108. and this:
  109. .. raw:: html
  110. <details><summary>TODO 3: Click to show/hide answer</summary>
  111. .. literalinclude:: Step5/MathFunctions/CMakeLists.txt
  112. :caption: TODO 3: MathFunctions/CMakeLists.txt
  113. :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
  114. :language: cmake
  115. :start-after: # link our compiler flags interface library
  116. .. raw:: html
  117. </details>
  118. With this, all of our code still requires C++ 11 to build. Notice
  119. though that with this method, it gives us the ability to be specific about
  120. which targets get specific requirements. In addition, we create a single
  121. source of truth in our interface library.
  122. Exercise 2 - Adding Compiler Warning Flags with Generator Expressions
  123. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  124. A common usage of
  125. :manual:`generator expressions <cmake-generator-expressions(7)>` is to
  126. conditionally add compiler flags, such as those for language levels or
  127. warnings. A nice pattern is to associate this information to an ``INTERFACE``
  128. target allowing this information to propagate.
  129. Goal
  130. ----
  131. Add compiler warning flags when building but not for installed versions.
  132. Helpful Resources
  133. -----------------
  134. * :manual:`cmake-generator-expressions(7)`
  135. * :command:`cmake_minimum_required`
  136. * :command:`set`
  137. * :command:`target_compile_options`
  138. Files to Edit
  139. -------------
  140. * ``CMakeLists.txt``
  141. Getting Started
  142. ---------------
  143. Start with the resulting files from Exercise 1. Complete ``TODO 4`` through
  144. ``TODO 7``.
  145. First, in the top level ``CMakeLists.txt`` file, we need to set the
  146. :command:`cmake_minimum_required` to ``3.15``. In this exercise we are going
  147. to use a generator expression which was introduced in CMake 3.15.
  148. Next we add the desired compiler warning flags that we want for our project.
  149. As warning flags vary based on the compiler, we use the
  150. ``COMPILE_LANG_AND_ID`` generator expression to control which flags to apply
  151. given a language and a set of compiler ids.
  152. Build and Run
  153. -------------
  154. Since we have our build directory already configured from Exercise 1, simply
  155. rebuild our code by calling the following:
  156. .. code-block:: console
  157. cd Step4_build
  158. cmake --build .
  159. Solution
  160. --------
  161. Update the :command:`cmake_minimum_required` to require at least CMake
  162. version ``3.15``:
  163. .. raw:: html
  164. <details><summary>TODO 4: Click to show/hide answer</summary>
  165. .. literalinclude:: Step5/CMakeLists.txt
  166. :caption: TODO 4: CMakeLists.txt
  167. :name: MathFunctions-CMakeLists.txt-minimum-required-step4
  168. :language: cmake
  169. :end-before: # set the project name and version
  170. .. raw:: html
  171. </details>
  172. Next we determine which compiler our system is currently using to build
  173. since warning flags vary based on the compiler we use. This is done with
  174. the ``COMPILE_LANG_AND_ID`` generator expression. We set the result in the
  175. variables ``gcc_like_cxx`` and ``msvc_cxx`` as follows:
  176. .. raw:: html
  177. <details><summary>TODO 5: Click to show/hide answer</summary>
  178. .. literalinclude:: Step5/CMakeLists.txt
  179. :caption: TODO 5: CMakeLists.txt
  180. :name: CMakeLists.txt-compile_lang_and_id
  181. :language: cmake
  182. :start-after: # the BUILD_INTERFACE genex
  183. :end-before: target_compile_options(tutorial_compiler_flags INTERFACE
  184. .. raw:: html
  185. </details>
  186. Next we add the desired compiler warning flags that we want for our project.
  187. Using our variables ``gcc_like_cxx`` and ``msvc_cxx``, we can use another
  188. generator expression to apply the respective flags only when the variables are
  189. true. We use :command:`target_compile_options` to apply these flags to our
  190. interface library.
  191. .. raw:: html
  192. <details><summary>TODO 6: Click to show/hide answer</summary>
  193. .. code-block:: cmake
  194. :caption: TODO 6: CMakeLists.txt
  195. :name: CMakeLists.txt-compile_flags
  196. target_compile_options(tutorial_compiler_flags INTERFACE
  197. "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
  198. "$<${msvc_cxx}:-W3>"
  199. )
  200. .. raw:: html
  201. </details>
  202. Lastly, we only want these warning flags to be used during builds. Consumers
  203. of our installed project should not inherit our warning flags. To specify
  204. this, we wrap our flags in a generator expression using the ``BUILD_INTERFACE``
  205. condition. The resulting full code looks like the following:
  206. .. raw:: html
  207. <details><summary>TODO 7: Click to show/hide answer</summary>
  208. .. literalinclude:: Step5/CMakeLists.txt
  209. :caption: TODO 7: CMakeLists.txt
  210. :name: CMakeLists.txt-target_compile_options-genex
  211. :language: cmake
  212. :start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
  213. :end-before: # should we use our own math functions
  214. .. raw:: html
  215. </details>