Adding Usage Requirements for a Library.rst 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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 ``Tutorial``, our ``SqrtLibrary`` library and our ``MathFunctions``
  175. library to our new ``tutorial_compiler_flags`` library. Respectively, the code
  176. will look like this:
  177. .. raw:: html
  178. <details><summary>TODO 5: Click to show/hide answer</summary>
  179. .. literalinclude:: Step4/CMakeLists.txt
  180. :caption: TODO 5: CMakeLists.txt
  181. :name: CMakeLists.txt-target_link_libraries-step4
  182. :language: cmake
  183. :start-after: add_executable(Tutorial tutorial.cxx)
  184. :end-before: # add the binary tree to the search path for include file
  185. .. raw:: html
  186. </details>
  187. this:
  188. .. raw:: html
  189. <details><summary>TODO 6: Click to show/hide answer</summary>
  190. .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
  191. :caption: TODO 6: MathFunctions/CMakeLists.txt
  192. :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
  193. :language: cmake
  194. :start-after: # link SqrtLibrary to tutorial_compiler_flags
  195. :end-before: target_link_libraries(MathFunctions
  196. .. raw:: html
  197. </details>
  198. and this:
  199. .. raw:: html
  200. <details><summary>TODO 7: Click to show/hide answer</summary>
  201. .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
  202. :caption: TODO 7: MathFunctions/CMakeLists.txt
  203. :name: MathFunctions-SqrtLibrary-target_link_libraries-step4
  204. :language: cmake
  205. :start-after: # link MathFunctions to tutorial_compiler_flags
  206. .. raw:: html
  207. </details>
  208. With this, all of our code still requires C++ 11 to build. Notice
  209. though that with this method, it gives us the ability to be specific about
  210. which targets get specific requirements. In addition, we create a single
  211. source of truth in our interface library.