1
0

Adding Generator Expressions.rst 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 6: 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``, our ``MathFunctions`` library, and our ``SqrtLibrary``
  96. library to our new
  97. ``tutorial_compiler_flags`` library. Respectively, the code will look like
  98. this:
  99. .. raw:: html
  100. <details><summary>TODO 2: Click to show/hide answer</summary>
  101. .. literalinclude:: Step5/CMakeLists.txt
  102. :caption: TODO 2: CMakeLists.txt
  103. :name: CMakeLists.txt-target_link_libraries-step4
  104. :language: cmake
  105. :start-after: add_executable(Tutorial tutorial.cxx)
  106. :end-before: # add the binary tree to the search path for include file
  107. .. raw:: html
  108. </details>
  109. this:
  110. .. raw:: html
  111. <details><summary>TODO 3: Click to show/hide answer</summary>
  112. .. literalinclude:: Step5/MathFunctions/CMakeLists.txt
  113. :caption: TODO 3: MathFunctions/CMakeLists.txt
  114. :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
  115. :language: cmake
  116. :start-after: # link our compiler flags interface library
  117. :end-before: target_link_libraries(MathFunctions
  118. .. raw:: html
  119. </details>
  120. and this:
  121. .. raw:: html
  122. <details><summary>TODO 4: Click to show/hide answer</summary>
  123. .. literalinclude:: Step5/MathFunctions/CMakeLists.txt
  124. :caption: TODO 4: MathFunctions/CMakeLists.txt
  125. :name: MathFunctions-SqrtLibrary-target_link_libraries-step4
  126. :language: cmake
  127. :start-after: target_link_libraries(SqrtLibrary
  128. :end-before: endif()
  129. .. raw:: html
  130. </details>
  131. With this, all of our code still requires C++ 11 to build. Notice
  132. though that with this method, it gives us the ability to be specific about
  133. which targets get specific requirements. In addition, we create a single
  134. source of truth in our interface library.
  135. Exercise 2 - Adding Compiler Warning Flags with Generator Expressions
  136. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  137. A common usage of
  138. :manual:`generator expressions <cmake-generator-expressions(7)>` is to
  139. conditionally add compiler flags, such as those for language levels or
  140. warnings. A nice pattern is to associate this information to an ``INTERFACE``
  141. target allowing this information to propagate.
  142. Goal
  143. ----
  144. Add compiler warning flags when building but not for installed versions.
  145. Helpful Resources
  146. -----------------
  147. * :manual:`cmake-generator-expressions(7)`
  148. * :command:`cmake_minimum_required`
  149. * :command:`set`
  150. * :command:`target_compile_options`
  151. Files to Edit
  152. -------------
  153. * ``CMakeLists.txt``
  154. Getting Started
  155. ---------------
  156. Start with the resulting files from Exercise 1. Complete ``TODO 5`` through
  157. ``TODO 8``.
  158. First, in the top level ``CMakeLists.txt`` file, we need to set the
  159. :command:`cmake_minimum_required` to ``3.15``. In this exercise we are going
  160. to use a generator expression which was introduced in CMake 3.15.
  161. Next we add the desired compiler warning flags that we want for our project.
  162. As warning flags vary based on the compiler, we use the
  163. ``COMPILE_LANG_AND_ID`` generator expression to control which flags to apply
  164. given a language and a set of compiler ids.
  165. Build and Run
  166. -------------
  167. Since we have our build directory already configured from Exercise 1, simply
  168. rebuild our code by calling the following:
  169. .. code-block:: console
  170. cd Step4_build
  171. cmake --build .
  172. Solution
  173. --------
  174. Update the :command:`cmake_minimum_required` to require at least CMake
  175. version ``3.15``:
  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: MathFunctions-CMakeLists.txt-minimum-required-step4
  181. :language: cmake
  182. :end-before: # set the project name and version
  183. .. raw:: html
  184. </details>
  185. Next we determine which compiler our system is currently using to build
  186. since warning flags vary based on the compiler we use. This is done with
  187. the ``COMPILE_LANG_AND_ID`` generator expression. We set the result in the
  188. variables ``gcc_like_cxx`` and ``msvc_cxx`` as follows:
  189. .. raw:: html
  190. <details><summary>TODO 6: Click to show/hide answer</summary>
  191. .. literalinclude:: Step5/CMakeLists.txt
  192. :caption: TODO 6: CMakeLists.txt
  193. :name: CMakeLists.txt-compile_lang_and_id
  194. :language: cmake
  195. :start-after: # the BUILD_INTERFACE genex
  196. :end-before: target_compile_options(tutorial_compiler_flags INTERFACE
  197. .. raw:: html
  198. </details>
  199. Next we add the desired compiler warning flags that we want for our project.
  200. Using our variables ``gcc_like_cxx`` and ``msvc_cxx``, we can use another
  201. generator expression to apply the respective flags only when the variables are
  202. true. We use :command:`target_compile_options` to apply these flags to our
  203. interface library.
  204. .. raw:: html
  205. <details><summary>TODO 7: Click to show/hide answer</summary>
  206. .. code-block:: cmake
  207. :caption: TODO 7: CMakeLists.txt
  208. :name: CMakeLists.txt-compile_flags
  209. target_compile_options(tutorial_compiler_flags INTERFACE
  210. "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
  211. "$<${msvc_cxx}:-W3>"
  212. )
  213. .. raw:: html
  214. </details>
  215. Lastly, we only want these warning flags to be used during builds. Consumers
  216. of our installed project should not inherit our warning flags. To specify
  217. this, we wrap our flags in a generator expression using the ``BUILD_INTERFACE``
  218. condition. The resulting full code looks like the following:
  219. .. raw:: html
  220. <details><summary>TODO 8: Click to show/hide answer</summary>
  221. .. literalinclude:: Step5/CMakeLists.txt
  222. :caption: TODO 8: CMakeLists.txt
  223. :name: CMakeLists.txt-target_compile_options-genex
  224. :language: cmake
  225. :start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
  226. :end-before: # configure a header file to pass some of the CMake settings
  227. .. raw:: html
  228. </details>