Adding Usage Requirements for a Library.rst 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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: # TODO 3: Link to
  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``, here:
  82. .. raw:: html
  83. <details><summary>TODO 2: Click to show/hide answer</summary>
  84. .. literalinclude:: Step4/CMakeLists.txt
  85. :caption: TODO 2: CMakeLists.txt
  86. :name: CMakeLists.txt-remove-EXTRA_INCLUDES
  87. :language: cmake
  88. :start-after: # add the MathFunctions library
  89. :end-before: # add the executable
  90. .. raw:: html
  91. </details>
  92. And here:
  93. .. raw:: html
  94. <details><summary>TODO 3: Click to show/hide answer</summary>
  95. .. literalinclude:: Step4/CMakeLists.txt
  96. :caption: TODO 3: CMakeLists.txt
  97. :name: CMakeLists.txt-target_include_directories-remove-EXTRA_INCLUDES
  98. :language: cmake
  99. :start-after: # so that we will find TutorialConfig.h
  100. .. raw:: html
  101. </details>
  102. Notice that with this technique, the only thing our executable target does to
  103. use our library is call :command:`target_link_libraries` with the name
  104. of the library target. In larger projects, the classic method of specifying
  105. library dependencies manually becomes very complicated very quickly.