1
0

In-Depth CMake Library Concepts.rst 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. Step 5: In-Depth CMake Library Concepts
  2. =======================================
  3. While executables are mostly one-size-fits-all, libraries come in many
  4. different forms. There are static archives, shared objects, modules,
  5. object libraries, header-only libraries, and libraries which describe advanced
  6. CMake properties to be inherited by other targets, just to name a few.
  7. In this step you will learn about some of the most common kinds of libraries
  8. that CMake can describe. This will cover most of the in-project uses of
  9. :command:`add_library`. Libraries which are imported from dependencies (or
  10. exported by the project to be consumed as a dependency) will be covered in
  11. later steps.
  12. Background
  13. ^^^^^^^^^^
  14. As we learned in ``Step1``, the :command:`add_library` command accepts the name
  15. of the library target to be created as its first argument. The second
  16. argument is an optional ``<type>`` for which the following values are valid:
  17. ``STATIC``
  18. A :ref:`Static Library <Static Libraries>`:
  19. an archive of object files for use when linking other targets.
  20. ``SHARED``
  21. A :ref:`Shared Library <Shared Libraries>`:
  22. a dynamic library that may be linked by other targets and loaded
  23. at runtime.
  24. ``MODULE``
  25. A :ref:`Module Library <Module Libraries>`:
  26. a plugin that may not be linked by other targets, but may be
  27. dynamically loaded at runtime using dlopen-like functionality.
  28. ``OBJECT``
  29. An :ref:`Object Library <Object Libraries>`:
  30. a collection of object files which have not been archived or linked
  31. into a library.
  32. ``INTERFACE``
  33. An :ref:`Interface Library <Interface Libraries>`:
  34. a library target which specifies usage requirements for dependents but
  35. does not compile sources and does not produce a library artifact on disk.
  36. In addition, there are ``IMPORTED`` libraries which describe library targets
  37. from foreign projects or modules, imported into the current project. We will
  38. cover these briefly in later steps.
  39. ``MODULE`` libraries are most commonly found in plugin systems, or as extensions
  40. to runtime-loading languages like Python or Javascript. They act very similar to
  41. normal shared libraries, except they cannot be directly linked by other targets.
  42. They are sufficiently similar that we won't cover them in further depth here.
  43. Exercise 1 - Static and Shared
  44. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  45. While the :command:`add_library` command supports explicitly setting ``STATIC``
  46. or ``SHARED``, and this is sometimes necessary, it is best to leave the second
  47. argument empty for most "normal" libraries which can operate as either.
  48. When not given a type, :command:`add_library` will create either a ``STATIC``
  49. or ``SHARED`` library depending on the value of :variable:`BUILD_SHARED_LIBS`.
  50. If :variable:`BUILD_SHARED_LIBS` is true, a ``SHARED`` library will be created,
  51. otherwise it will be ``STATIC``.
  52. .. code-block:: cmake
  53. add_library(MyLib-static STATIC)
  54. add_library(MyLib-shared SHARED)
  55. # Depends on BUILD_SHARED_LIBS
  56. add_library(MyLib)
  57. This is desirable behavior, as it allows packagers to determine what kind of
  58. library will be produced, and ensure dependents link to that version of the
  59. library without needing to modify their source code. In some contexts, fully
  60. static builds are appropriate, and in others shared libraries are desirable.
  61. .. note::
  62. CMake does not define the :variable:`BUILD_SHARED_LIBS` variable by default,
  63. meaning without project or user intervention :command:`add_library` will
  64. produce ``STATIC`` libraries.
  65. By leaving the second argument to :command:`add_library()` blank, projects
  66. provide additional flexibility to their packagers and downstream dependents.
  67. Goal
  68. ----
  69. Build ``MathFunctions`` as a shared library.
  70. .. note::
  71. On Windows, you might see warnings about an empty DLL, as ``MathFunctions``
  72. doesn't export any symbols.
  73. Helpful Resources
  74. -----------------
  75. * :variable:`BUILD_SHARED_LIBS`
  76. Files to Edit
  77. -------------
  78. There are no files to edit.
  79. Getting Started
  80. ---------------
  81. The ``Help/guide/tutorial/Step5`` directory contains the complete, recommended
  82. solution to ``Step4``. This step is about building the ``MathFunctions``
  83. library, there are no ``TODOs`` necessary. You can proceed directly to the
  84. build step.
  85. Build and Run
  86. -------------
  87. We can configure using our preset, turning on :variable:`BUILD_SHARED_LIBS` with
  88. a :option:`-D <cmake -D>` flag.
  89. .. code-block:: console
  90. cmake --preset tutorial -DBUILD_SHARED_LIBS=ON
  91. Then we can build only the ``MathFunctions`` library with
  92. :option:`-t <cmake--build -t>`.
  93. .. code-block:: console
  94. cmake --build build -t MathFunctions
  95. Verify a shared library is produced for ``MathFunctions`` then reset
  96. :variable:`BUILD_SHARED_LIBS`, either by reconfiguring with
  97. ``-DBUILD_SHARED_LIBS=OFF`` or deleting the ``CMakeCache.txt``.
  98. Solution
  99. --------
  100. There are no changes to the project for this exercise.
  101. Exercise 2 - Interface Libraries
  102. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  103. Interface libraries are those which only communicate usage requirements for
  104. other targets, they do not build or produce any artifacts of their own. As such
  105. all the properties of an interface library must themselves be interface
  106. properties, specified with the ``INTERFACE`` :ref:`scope keywords <Target Command Scope>`.
  107. .. code-block:: cmake
  108. add_library(MyInterface INTERFACE)
  109. target_compile_definitions(MyInterface INTERFACE MYINTERFACE_COMPILE_DEF)
  110. The most common kind of interface library in C++ development is a header-only
  111. library. Such libraries do not build anything, only providing the flags
  112. necessary to discover their headers.
  113. Goal
  114. ----
  115. Add a header-only library to the tutorial project, and use it inside the
  116. ``Tutorial`` executable.
  117. Helpful Resources
  118. -----------------
  119. * :command:`add_library`
  120. * :command:`target_sources`
  121. Files to Edit
  122. -------------
  123. * ``MathFunctions/MathLogger/CMakeLists.txt``
  124. * ``MathFunctions/CMakeLists.txt``
  125. * ``MathFunctions/MathFunctions.cxx``
  126. Getting Started
  127. ---------------
  128. In our previous discussions of :command:`target_sources(FILE_SET)`, we noted
  129. we can omit the ``TYPE`` parameter if the file set's name is the same as the
  130. file set's type. We also said we can omit the ``BASE_DIRS`` parameter if
  131. we want to use the current source directory as the only base directory.
  132. We're ready to introduce a third shortcut, we only need to include the ``FILES``
  133. parameter if the headers are intended to be installed, such as public headers
  134. of a library.
  135. The ``MathLogger`` headers in this exercise are only used internally by the
  136. ``MathFunctions`` implementation. They will not be installed. This should
  137. make for a very abbreviated call to :command:`target_sources(FILE_SET)`.
  138. .. note::
  139. The headers will be discovered by the compiler's dependency scanner to ensure
  140. correct incremental builds. It can be useful to list header files in these
  141. contexts anyway, as the list can be used to generate metadata some IDEs
  142. rely on.
  143. You can begin editing the ``Step5`` directory. Complete ``TODO 1`` through
  144. ``TODO 7``.
  145. Build and Run
  146. -------------
  147. The preset has already been updated to use ``mathfunctions::sqrt`` instead of
  148. ``std::sqrt``. We can build and configure as usual.
  149. .. code-block:: console
  150. cmake --preset tutorial
  151. cmake --build build
  152. Verify that the ``Tutorial`` output now uses the logging framework.
  153. Solution
  154. --------
  155. First we add a new ``INTERFACE`` library named ``MathLogger``.
  156. .. raw:: html
  157. <details><summary>TODO 1: Click to show/hide answer</summary>
  158. .. literalinclude:: Step6/MathFunctions/MathLogger/CMakeLists.txt
  159. :caption: TODO 1: MathFunctions/MathLogger/CMakeLists.txt
  160. :name: MathFunctions/MathLogger/CMakeLists.txt-add_library
  161. :language: cmake
  162. :start-at: add_library
  163. :end-at: add_library
  164. .. raw:: html
  165. </details>
  166. Then we add the appropriate :command:`target_sources` call to capture the
  167. header information. We give this file set the name ``HEADERS`` so we can
  168. omit the ``TYPE``, we don't need ``BASE_DIRS`` as we will use the default
  169. of the current source directory, and we can exclude the ``FILES`` list because
  170. we don't intend to install the library.
  171. .. raw:: html
  172. <details><summary>TODO 2: Click to show/hide answer</summary>
  173. .. literalinclude:: Step6/MathFunctions/MathLogger/CMakeLists.txt
  174. :caption: TODO 2: MathFunctions/MathLogger/CMakeLists.txt
  175. :name: MathFunctions/MathLogger/CMakeLists.txt-target_sources
  176. :language: cmake
  177. :start-at: target_sources(
  178. :end-at: )
  179. .. raw:: html
  180. </details>
  181. Now we can add the ``MathLogger`` library to the ``MathFunctions`` linked
  182. libraries, and at the ``MathLogger`` folder to the project.
  183. .. raw:: html
  184. <details><summary>TODO 3-4: Click to show/hide answer</summary>
  185. .. literalinclude:: Step6/MathFunctions/CMakeLists.txt
  186. :caption: TODO 3: MathFunctions/CMakeLists.txt
  187. :name: MathFunctions/CMakeLists.txt-link-mathlogger
  188. :language: cmake
  189. :start-at: target_link_libraries(
  190. :end-at: MathLogger
  191. :append: )
  192. .. literalinclude:: Step6/MathFunctions/CMakeLists.txt
  193. :caption: TODO 4: MathFunctions/CMakeLists.txt
  194. :name: MathFunctions/CMakeLists.txt-add-mathlogger
  195. :language: cmake
  196. :start-at: add_subdirectory(MathLogger
  197. :end-at: add_subdirectory(MathLogger
  198. .. raw:: html
  199. </details>
  200. Finally we can update ``MathFunctions.cxx`` to take advantage of the new logger.
  201. .. raw:: html
  202. <details><summary>TODO 5-7: Click to show/hide answer</summary>
  203. .. literalinclude:: Step6/MathFunctions/MathFunctions.cxx
  204. :caption: TODO 5: MathFunctions/MathFunctions.cxx
  205. :name: MathFunctions/MathFunctions.cxx-mathlogger-header
  206. :language: c++
  207. :start-at: cmath
  208. :end-at: MathLogger
  209. .. literalinclude:: Step6/MathFunctions/MathFunctions.cxx
  210. :caption: TODO 6: MathFunctions/MathFunctions.cxx
  211. :name: MathFunctions/MathFunctions.cxx-mathlogger-logger
  212. :language: c++
  213. :start-at: mathlogger::Logger Logger
  214. :end-at: mathlogger::Logger Logger
  215. .. literalinclude:: Step6/MathFunctions/MathFunctions.cxx
  216. :caption: TODO 7: MathFunctions/MathFunctions.cxx
  217. :name: MathFunctions/MathFunctions.cxx-mathlogger-code
  218. :language: c++
  219. :start-at: Logger.Log(std::format("Computing sqrt of {} to be {}\n"
  220. :end-at: std::format
  221. :dedent: 4
  222. .. raw:: html
  223. </details>
  224. Exercise 3 - Object Libraries
  225. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  226. Object libraries have several advanced uses, but also tricky nuances which
  227. are difficult to fully enumerate in the scope of this tutorial.
  228. .. code-block:: cmake
  229. add_library(MyObjects OBJECT)
  230. The most obvious drawback to object libraries is the objects themselves cannot
  231. be transitively linked. If an object library appears in the
  232. :prop_tgt:`INTERFACE_LINK_LIBRARIES` of a target, the dependents which link that
  233. target will not "see" the objects. The object library will act like an
  234. ``INTERFACE`` library in such contexts. In the general case, object libraries
  235. are only suitable for ``PRIVATE`` or ``PUBLIC`` consumption via
  236. :command:`target_link_libraries`.
  237. A common use case for object libraries is coalescing several library targets
  238. into a single archive or shared library object. Even within a single project
  239. libraries may be maintained as different targets for a variety of reasons, such
  240. as belonging to different teams within an organization. However, it may be
  241. desirable to distribute these as a single consumer-facing binary. Object
  242. libraries make this possible.
  243. Goal
  244. ----
  245. Add several object libraries to the ``MathFunctions`` library.
  246. Helpful Resources
  247. -----------------
  248. * :command:`target_link_libraries`
  249. * :command:`add_subdirectory`
  250. Files to Edit
  251. -------------
  252. * ``MathFunctions/CMakeLists.txt``
  253. * ``MathFunctions/MathFunctions.h``
  254. * ``Tutorial/Tutorial.cxx``
  255. Getting Started
  256. ---------------
  257. Several extensions for our ``MathFunctions`` library have been made available
  258. (we can imagine these coming from other teams in our organization). Take
  259. a minute to look at the targets made available in ``MathFunctions/MathExtensions``.
  260. Then complete ``TODO 8`` through ``TODO 11``.
  261. Build and Run
  262. -------------
  263. There's no reconfiguration needed, we can build as usual.
  264. .. code-block:: console
  265. cmake --build build
  266. Verify the output of ``Tutorial`` now includes the verification message. Also
  267. take a minute to inspect the build directory under
  268. ``build/MathFunctions/MathExtensions``. You should find that, unlike
  269. ``MathFunctions``, no archives are produced for any of the object libraries.
  270. Solution
  271. --------
  272. First we will add links for all the object libraries to ``MathFunctions``.
  273. These are ``PUBLIC``, because we want the objects to be added to the
  274. ``MathFunctions`` library as part of its own build step, and we want the
  275. headers to be available to consumers of the library.
  276. Then we add the ``MathExtensions`` subdirectoy to the project.
  277. .. raw:: html
  278. <details><summary>TODO 8-9: Click to show/hide answer</summary>
  279. .. literalinclude:: Step6/MathFunctions/CMakeLists.txt
  280. :caption: TODO 8: MathFunctions/CMakeLists.txt
  281. :name: MathFunctions/CMakeLists.txt-link-objects
  282. :language: cmake
  283. :start-at: target_link_libraries(
  284. :end-at: )
  285. .. literalinclude:: Step6/MathFunctions/CMakeLists.txt
  286. :caption: TODO 9: MathFunctions/CMakeLists.txt
  287. :name: MathFunctions/CMakeLists.txt-add-objs
  288. :language: cmake
  289. :start-at: add_subdirectory(MathExtensions
  290. :end-at: add_subdirectory(MathExtensions
  291. .. raw:: html
  292. </details>
  293. To make the extensions available to consumers, we include their headers in the
  294. ``MathFunctions.h`` header.
  295. .. raw:: html
  296. <details><summary>TODO 10: Click to show/hide answer</summary>
  297. .. literalinclude:: Step6/MathFunctions/MathFunctions.h
  298. :caption: TODO 10: MathFunctions/MathFunctions.h
  299. :name: MathFunctions/MathFunctions.h-include-objects
  300. :language: c++
  301. :start-at: OpAdd
  302. :end-at: OpSub
  303. .. raw:: html
  304. </details>
  305. Finally we can take advantage of the extensions in the ``Tutorial`` program.
  306. .. raw:: html
  307. <details><summary>TODO 11: Click to show/hide answer</summary>
  308. .. literalinclude:: Step6/Tutorial/Tutorial.cxx
  309. :caption: TODO 11: Tutorial/Tutorial.cxx
  310. :name: Tutorial/Tutorial.cxx-use-objects
  311. :language: c++
  312. :start-at: OpMul
  313. :end-at: checkValue);
  314. :dedent: 2
  315. .. raw:: html
  316. </details>