Adding Usage Requirements for a Library.rst 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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 the lines:
  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. The remaining code looks like:
  104. .. raw:: html
  105. <details><summary>Click to show/hide the resulting code</summary>
  106. .. literalinclude:: Step4/CMakeLists.txt
  107. :caption: Remaining code after removing EXTRA_INCLUDES
  108. :name: CMakeLists.txt-after-removing-EXTRA_INCLUDES
  109. :language: cmake
  110. :start-after: add_subdirectory(MathFunctions)
  111. .. raw:: html
  112. </details>
  113. Notice that with this technique, the only thing our executable target does to
  114. use our library is call :command:`target_link_libraries` with the name
  115. of the library target. In larger projects, the classic method of specifying
  116. library dependencies manually becomes very complicated very quickly.
  117. Exercise 2 - Setting the C++ Standard with Interface Libraries
  118. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  119. Now that we have switched our code to a more modern approach, let's demonstrate
  120. a modern technique to set properties to multiple targets.
  121. Let's refactor our existing code to use an ``INTERFACE`` library. We will
  122. use that library in the next step to demonstrate a common use for
  123. :manual:`generator expressions <cmake-generator-expressions(7)>`.
  124. Goal
  125. ----
  126. Add an ``INTERFACE`` library target to specify the required C++ standard.
  127. Helpful Resources
  128. -----------------
  129. * :command:`add_library`
  130. * :command:`target_compile_features`
  131. * :command:`target_link_libraries`
  132. Files to Edit
  133. -------------
  134. * ``CMakeLists.txt``
  135. * ``MathFunctions/CMakeLists.txt``
  136. Getting Started
  137. ---------------
  138. In this exercise, we will refactor our code to use an ``INTERFACE`` library to
  139. specify the C++ standard.
  140. Start this exercise from what we left at the end of Step3 exercise 1. You will
  141. have to complete ``TODO 4`` through ``TODO 7``.
  142. Start by editing the top level ``CMakeLists.txt`` file. Construct an
  143. ``INTERFACE`` library target called ``tutorial_compiler_flags`` and
  144. specify ``cxx_std_11`` as a target compiler feature.
  145. Modify ``CMakeLists.txt`` and ``MathFunctions/CMakeLists.txt`` so that all
  146. targets have a :command:`target_link_libraries` call to
  147. ``tutorial_compiler_flags``.
  148. Build and Run
  149. -------------
  150. Since we have our build directory already configured from Exercise 1, simply
  151. rebuild our code by calling the following:
  152. .. code-block:: console
  153. cd Step3_build
  154. cmake --build .
  155. Next, use the newly built ``Tutorial`` and verify that it is working as
  156. expected.
  157. Solution
  158. --------
  159. Let's update our code from the previous step to use interface libraries
  160. to set our C++ requirements.
  161. To start, we need to remove the two :command:`set` calls on the variables
  162. :variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`.
  163. The specific lines to remove are as follows:
  164. .. literalinclude:: Step3/CMakeLists.txt
  165. :caption: CMakeLists.txt
  166. :name: CMakeLists.txt-CXX_STANDARD-variable-remove
  167. :language: cmake
  168. :start-after: # specify the C++ standard
  169. :end-before: # configure a header file
  170. Next, we need to create an interface library, ``tutorial_compiler_flags``. And
  171. then use :command:`target_compile_features` to add the compiler feature
  172. ``cxx_std_11``.
  173. .. raw:: html
  174. <details><summary>TODO 4: Click to show/hide answer</summary>
  175. .. literalinclude:: Step4/CMakeLists.txt
  176. :caption: TODO 4: CMakeLists.txt
  177. :name: CMakeLists.txt-cxx_std-feature
  178. :language: cmake
  179. :start-after: # specify the C++ standard
  180. :end-before: # TODO 2: Create helper
  181. .. raw:: html
  182. </details>
  183. Finally, with our interface library set up, we need to link our
  184. executable ``Target``, our ``MathFunctions`` library, and our ``SqrtLibrary``
  185. library to our new
  186. ``tutorial_compiler_flags`` library. Respectively, the code will look like
  187. this:
  188. .. raw:: html
  189. <details><summary>TODO 5: Click to show/hide answer</summary>
  190. .. literalinclude:: Step4/CMakeLists.txt
  191. :caption: TODO 5: CMakeLists.txt
  192. :name: CMakeLists.txt-target_link_libraries-step4
  193. :language: cmake
  194. :start-after: add_executable(Tutorial tutorial.cxx)
  195. :end-before: # add the binary tree to the search path for include file
  196. .. raw:: html
  197. </details>
  198. this:
  199. .. raw:: html
  200. <details><summary>TODO 6: Click to show/hide answer</summary>
  201. .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
  202. :caption: TODO 6: MathFunctions/CMakeLists.txt
  203. :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
  204. :language: cmake
  205. :start-after: # link our compiler flags interface library
  206. :end-before: target_link_libraries(MathFunctions
  207. .. raw:: html
  208. </details>
  209. and this:
  210. .. raw:: html
  211. <details><summary>TODO 7: Click to show/hide answer</summary>
  212. .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
  213. :caption: TODO 7: MathFunctions/CMakeLists.txt
  214. :name: MathFunctions-SqrtLibrary-target_link_libraries-step4
  215. :language: cmake
  216. :start-after: target_link_libraries(SqrtLibrary
  217. :end-before: endif()
  218. .. raw:: html
  219. </details>
  220. With this, all of our code still requires C++ 11 to build. Notice
  221. though that with this method, it gives us the ability to be specific about
  222. which targets get specific requirements. In addition, we create a single
  223. source of truth in our interface library.