Adding Usage Requirements for a Library.rst 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. Step 3: Adding Usage Requirements for a Library
  2. ===============================================
  3. Exercise 1 - Adding Usage Requirements for a Library
  4. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  5. :ref:`Usage requirements <Target Usage Requirements>` of a target parameters
  6. allow for far better control over a library or executable's link and include
  7. line while also giving more control over the transitive property of targets
  8. inside CMake. The primary commands that
  9. leverage usage requirements are:
  10. * :command:`target_compile_definitions`
  11. * :command:`target_compile_options`
  12. * :command:`target_include_directories`
  13. * :command:`target_link_directories`
  14. * :command:`target_link_options`
  15. * :command:`target_precompile_headers`
  16. * :command:`target_sources`
  17. Goal
  18. ----
  19. Add usage requirements for a library.
  20. Helpful Materials
  21. -----------------
  22. * :variable:`CMAKE_CURRENT_SOURCE_DIR`
  23. Files to Edit
  24. -------------
  25. * ``MathFunctions/CMakeLists.txt``
  26. * ``CMakeLists.txt``
  27. Getting Started
  28. ---------------
  29. In this exercise, we will refactor our code from
  30. :guide:`tutorial/Adding a Library` to use the modern CMake approach. We will
  31. let our library define its own usage requirements so they are passed
  32. transitively to other targets as necessary. In this case, ``MathFunctions``
  33. will specify any needed include directories itself. Then, the consuming target
  34. ``Tutorial`` simply needs to link to ``MathFunctions`` and not worry about
  35. any additional include directories.
  36. The starting source code is provided in the ``Step3`` directory. In this
  37. exercise, complete ``TODO 1`` through ``TODO 3``.
  38. First, add a call to :command:`target_include_directories` in
  39. ``MathFunctions/CMakeLists``. Remember that
  40. :variable:`CMAKE_CURRENT_SOURCE_DIR` is the path to the source directory
  41. currently being processed.
  42. Then, update (and simplify!) the call to
  43. :command:`target_include_directories` in the top-level ``CMakeLists.txt``.
  44. Build and Run
  45. -------------
  46. Make a new directory called ``Step3_build``, run the :manual:`cmake
  47. <cmake(1)>` executable or the :manual:`cmake-gui <cmake-gui(1)>` to
  48. configure the project and then build it with your chosen build tool or by
  49. using :option:`cmake --build . <cmake --build>` from the build directory.
  50. Here's a refresher of what that looks like from the command line:
  51. .. code-block:: console
  52. mkdir Step3_build
  53. cd Step3_build
  54. cmake ../Step3
  55. cmake --build .
  56. Next, use the newly built ``Tutorial`` and verify that it is working as
  57. expected.
  58. Solution
  59. --------
  60. Let's update the code from the previous step to use the modern CMake
  61. approach of usage requirements.
  62. We want to state that anybody linking to ``MathFunctions`` needs to include
  63. the current source directory, while ``MathFunctions`` itself doesn't. This
  64. can be expressed with an ``INTERFACE`` usage requirement. Remember
  65. ``INTERFACE`` means things that consumers require but the producer doesn't.
  66. At the end of ``MathFunctions/CMakeLists.txt``, use
  67. :command:`target_include_directories` with the ``INTERFACE`` keyword, as
  68. follows:
  69. .. raw:: html
  70. <details><summary>TODO 1: Click to show/hide answer</summary>
  71. .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
  72. :caption: TODO 1: MathFunctions/CMakeLists.txt
  73. :name: MathFunctions/CMakeLists.txt-target_include_directories-INTERFACE
  74. :language: cmake
  75. :start-after: # to find MathFunctions.h
  76. :end-before: # should we use our own
  77. .. raw:: html
  78. </details>
  79. Now that we've specified usage requirements for ``MathFunctions`` we can
  80. safely remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level
  81. ``CMakeLists.txt``.
  82. Remove this line:
  83. .. raw:: html
  84. <details><summary>TODO 2: Click to show/hide answer</summary>
  85. .. literalinclude:: Step3/CMakeLists.txt
  86. :caption: TODO 2: CMakeLists.txt
  87. :name: CMakeLists.txt-remove-EXTRA_INCLUDES
  88. :language: cmake
  89. :start-after: add_subdirectory(MathFunctions)
  90. :end-before: # add the executable
  91. .. raw:: html
  92. </details>
  93. And remove ``EXTRA_INCLUDES`` from ``target_include_directories``:
  94. .. raw:: html
  95. <details><summary>TODO 3: Click to show/hide answer</summary>
  96. .. literalinclude:: Step4/CMakeLists.txt
  97. :caption: TODO 3: CMakeLists.txt
  98. :name: CMakeLists.txt-target_include_directories-remove-EXTRA_INCLUDES
  99. :language: cmake
  100. :start-after: # so that we will find TutorialConfig.h
  101. .. raw:: html
  102. </details>
  103. Notice that with this technique, the only thing our executable target does to
  104. use our library is call :command:`target_link_libraries` with the name
  105. of the library target. In larger projects, the classic method of specifying
  106. library dependencies manually becomes very complicated very quickly.
  107. Exercise 2 - Setting the C++ Standard with Interface Libraries
  108. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  109. Now that we have switched our code to a more modern approach, let's demonstrate
  110. a modern technique to set properties to multiple targets.
  111. Let's refactor our existing code to use an ``INTERFACE`` library. We will
  112. use that library in the next step to demonstrate a common use for
  113. :manual:`generator expressions <cmake-generator-expressions(7)>`.
  114. Goal
  115. ----
  116. Add an ``INTERFACE`` library target to specify the required C++ standard.
  117. Helpful Resources
  118. -----------------
  119. * :command:`add_library`
  120. * :command:`target_compile_features`
  121. * :command:`target_link_libraries`
  122. Files to Edit
  123. -------------
  124. * ``CMakeLists.txt``
  125. * ``MathFunctions/CMakeLists.txt``
  126. Getting Started
  127. ---------------
  128. In this exercise, we will refactor our code to use an ``INTERFACE`` library to
  129. specify the C++ standard.
  130. Start this exercise from what we left at the end of Step3 exercise 1. You will
  131. have to complete ``TODO 4`` through ``TODO 7``.
  132. Start by editing the top level ``CMakeLists.txt`` file. Construct an
  133. ``INTERFACE`` library target called ``tutorial_compiler_flags`` and
  134. specify ``cxx_std_11`` as a target compiler feature.
  135. Modify ``CMakeLists.txt`` and ``MathFunctions/CMakeLists.txt`` so that all
  136. targets have a :command:`target_link_libraries` call to
  137. ``tutorial_compiler_flags``.
  138. Build and Run
  139. -------------
  140. Since we have our build directory already configured from Exercise 1, simply
  141. rebuild our code by calling the following:
  142. .. code-block:: console
  143. cd Step3_build
  144. cmake --build .
  145. Next, use the newly built ``Tutorial`` and verify that it is working as
  146. expected.
  147. Solution
  148. --------
  149. Let's update our code from the previous step to use interface libraries
  150. to set our C++ requirements.
  151. To start, we need to remove the two :command:`set` calls on the variables
  152. :variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`.
  153. The specific lines to remove are as follows:
  154. .. literalinclude:: Step3/CMakeLists.txt
  155. :caption: CMakeLists.txt
  156. :name: CMakeLists.txt-CXX_STANDARD-variable-remove
  157. :language: cmake
  158. :start-after: # specify the C++ standard
  159. :end-before: # configure a header file
  160. Next, we need to create an interface library, ``tutorial_compiler_flags``. And
  161. then use :command:`target_compile_features` to add the compiler feature
  162. ``cxx_std_11``.
  163. .. raw:: html
  164. <details><summary>TODO 4: Click to show/hide answer</summary>
  165. .. literalinclude:: Step4/CMakeLists.txt
  166. :caption: TODO 4: CMakeLists.txt
  167. :name: CMakeLists.txt-cxx_std-feature
  168. :language: cmake
  169. :start-after: # specify the C++ standard
  170. :end-before: # TODO 2: Create helper
  171. .. raw:: html
  172. </details>
  173. Finally, with our interface library set up, we need to link our
  174. executable ``Target``, our ``MathFunctions`` library, and our ``SqrtLibrary``
  175. library to our new
  176. ``tutorial_compiler_flags`` library. Respectively, the code will look like
  177. this:
  178. .. raw:: html
  179. <details><summary>TODO 5: Click to show/hide answer</summary>
  180. .. literalinclude:: Step4/CMakeLists.txt
  181. :caption: TODO 5: CMakeLists.txt
  182. :name: CMakeLists.txt-target_link_libraries-step4
  183. :language: cmake
  184. :start-after: add_executable(Tutorial tutorial.cxx)
  185. :end-before: # add the binary tree to the search path for include file
  186. .. raw:: html
  187. </details>
  188. this:
  189. .. raw:: html
  190. <details><summary>TODO 6: Click to show/hide answer</summary>
  191. .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
  192. :caption: TODO 6: MathFunctions/CMakeLists.txt
  193. :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
  194. :language: cmake
  195. :start-after: # link our compiler flags interface library
  196. :end-before: target_link_libraries(MathFunctions
  197. .. raw:: html
  198. </details>
  199. and this:
  200. .. raw:: html
  201. <details><summary>TODO 7: Click to show/hide answer</summary>
  202. .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
  203. :caption: TODO 7: MathFunctions/CMakeLists.txt
  204. :name: MathFunctions-SqrtLibrary-target_link_libraries-step4
  205. :language: cmake
  206. :start-after: # link our compiler flags interface library
  207. :end-before: target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
  208. .. raw:: html
  209. </details>
  210. With this, all of our code still requires C++ 11 to build. Notice
  211. though that with this method, it gives us the ability to be specific about
  212. which targets get specific requirements. In addition, we create a single
  213. source of truth in our interface library.