123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- Step 3: Adding Usage Requirements for a Library
- ===============================================
- Exercise 1 - Adding Usage Requirements for a Library
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- :ref:`Usage requirements <Target Usage Requirements>` of a target parameters
- allow for far better control over a library or executable's link and include
- line while also giving more control over the transitive property of targets
- inside CMake. The primary commands that
- leverage usage requirements are:
- * :command:`target_compile_definitions`
- * :command:`target_compile_options`
- * :command:`target_include_directories`
- * :command:`target_link_directories`
- * :command:`target_link_options`
- * :command:`target_precompile_headers`
- * :command:`target_sources`
- Goal
- ----
- Add usage requirements for a library.
- Helpful Materials
- -----------------
- * :variable:`CMAKE_CURRENT_SOURCE_DIR`
- Files to Edit
- -------------
- * ``MathFunctions/CMakeLists.txt``
- * ``CMakeLists.txt``
- Getting Started
- ---------------
- In this exercise, we will refactor our code from
- :guide:`tutorial/Adding a Library` to use the modern CMake approach. We will
- let our library define its own usage requirements so they are passed
- transitively to other targets as necessary. In this case, ``MathFunctions``
- will specify any needed include directories itself. Then, the consuming target
- ``Tutorial`` simply needs to link to ``MathFunctions`` and not worry about
- any additional include directories.
- The starting source code is provided in the ``Step3`` directory. In this
- exercise, complete ``TODO 1`` through ``TODO 3``.
- First, add a call to :command:`target_include_directories` in
- ``MathFunctions/CMakeLists``. Remember that
- :variable:`CMAKE_CURRENT_SOURCE_DIR` is the path to the source directory
- currently being processed.
- Then, update (and simplify!) the call to
- :command:`target_include_directories` in the top-level ``CMakeLists.txt``.
- Build and Run
- -------------
- Make a new directory called ``Step3_build``, run the :manual:`cmake
- <cmake(1)>` executable or the :manual:`cmake-gui <cmake-gui(1)>` to
- configure the project and then build it with your chosen build tool or by
- using :option:`cmake --build . <cmake --build>` from the build directory.
- Here's a refresher of what that looks like from the command line:
- .. code-block:: console
- mkdir Step3_build
- cd Step3_build
- cmake ../Step3
- cmake --build .
- Next, use the newly built ``Tutorial`` and verify that it is working as
- expected.
- Solution
- --------
- Let's update the code from the previous step to use the modern CMake
- approach of usage requirements.
- We want to state that anybody linking to ``MathFunctions`` needs to include
- the current source directory, while ``MathFunctions`` itself doesn't. This
- can be expressed with an ``INTERFACE`` usage requirement. Remember
- ``INTERFACE`` means things that consumers require but the producer doesn't.
- At the end of ``MathFunctions/CMakeLists.txt``, use
- :command:`target_include_directories` with the ``INTERFACE`` keyword, as
- follows:
- .. raw:: html
- <details><summary>TODO 1: Click to show/hide answer</summary>
- .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
- :caption: TODO 1: MathFunctions/CMakeLists.txt
- :name: MathFunctions/CMakeLists.txt-target_include_directories-INTERFACE
- :language: cmake
- :start-after: # to find MathFunctions.h
- :end-before: # should we use our own
- .. raw:: html
- </details>
- Now that we've specified usage requirements for ``MathFunctions`` we can
- safely remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level
- ``CMakeLists.txt``.
- Remove this line:
- .. raw:: html
- <details><summary>TODO 2: Click to show/hide answer</summary>
- .. literalinclude:: Step3/CMakeLists.txt
- :caption: TODO 2: CMakeLists.txt
- :name: CMakeLists.txt-remove-EXTRA_INCLUDES
- :language: cmake
- :start-after: add_subdirectory(MathFunctions)
- :end-before: # add the executable
- .. raw:: html
- </details>
- And remove ``EXTRA_INCLUDES`` from ``target_include_directories``:
- .. raw:: html
- <details><summary>TODO 3: Click to show/hide answer</summary>
- .. literalinclude:: Step4/CMakeLists.txt
- :caption: TODO 3: CMakeLists.txt
- :name: CMakeLists.txt-target_include_directories-remove-EXTRA_INCLUDES
- :language: cmake
- :start-after: # so that we will find TutorialConfig.h
- .. raw:: html
- </details>
- Notice that with this technique, the only thing our executable target does to
- use our library is call :command:`target_link_libraries` with the name
- of the library target. In larger projects, the classic method of specifying
- library dependencies manually becomes very complicated very quickly.
- Exercise 2 - Setting the C++ Standard with Interface Libraries
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Now that we have switched our code to a more modern approach, let's demonstrate
- a modern technique to set properties to multiple targets.
- Let's refactor our existing code to use an ``INTERFACE`` library. We will
- use that library in the next step to demonstrate a common use for
- :manual:`generator expressions <cmake-generator-expressions(7)>`.
- Goal
- ----
- Add an ``INTERFACE`` library target to specify the required C++ standard.
- Helpful Resources
- -----------------
- * :command:`add_library`
- * :command:`target_compile_features`
- * :command:`target_link_libraries`
- Files to Edit
- -------------
- * ``CMakeLists.txt``
- * ``MathFunctions/CMakeLists.txt``
- Getting Started
- ---------------
- In this exercise, we will refactor our code to use an ``INTERFACE`` library to
- specify the C++ standard.
- Start this exercise from what we left at the end of Step3 exercise 1. You will
- have to complete ``TODO 4`` through ``TODO 7``.
- Start by editing the top level ``CMakeLists.txt`` file. Construct an
- ``INTERFACE`` library target called ``tutorial_compiler_flags`` and
- specify ``cxx_std_11`` as a target compiler feature.
- Modify ``CMakeLists.txt`` and ``MathFunctions/CMakeLists.txt`` so that all
- targets have a :command:`target_link_libraries` call to
- ``tutorial_compiler_flags``.
- Build and Run
- -------------
- Since we have our build directory already configured from Exercise 1, simply
- rebuild our code by calling the following:
- .. code-block:: console
- cd Step3_build
- cmake --build .
- Next, use the newly built ``Tutorial`` and verify that it is working as
- expected.
- Solution
- --------
- Let's update our code from the previous step to use interface libraries
- to set our C++ requirements.
- To start, we need to remove the two :command:`set` calls on the variables
- :variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`.
- The specific lines to remove are as follows:
- .. literalinclude:: Step3/CMakeLists.txt
- :caption: CMakeLists.txt
- :name: CMakeLists.txt-CXX_STANDARD-variable-remove
- :language: cmake
- :start-after: # specify the C++ standard
- :end-before: # configure a header file
- Next, we need to create an interface library, ``tutorial_compiler_flags``. And
- then use :command:`target_compile_features` to add the compiler feature
- ``cxx_std_11``.
- .. raw:: html
- <details><summary>TODO 4: Click to show/hide answer</summary>
- .. literalinclude:: Step4/CMakeLists.txt
- :caption: TODO 4: CMakeLists.txt
- :name: CMakeLists.txt-cxx_std-feature
- :language: cmake
- :start-after: # specify the C++ standard
- :end-before: # TODO 2: Create helper
- .. raw:: html
- </details>
- Finally, with our interface library set up, we need to link our
- executable ``Tutorial``, our ``SqrtLibrary`` library and our ``MathFunctions``
- library to our new ``tutorial_compiler_flags`` library. Respectively, the code
- will look like this:
- .. raw:: html
- <details><summary>TODO 5: Click to show/hide answer</summary>
- .. literalinclude:: Step4/CMakeLists.txt
- :caption: TODO 5: CMakeLists.txt
- :name: CMakeLists.txt-target_link_libraries-step4
- :language: cmake
- :start-after: add_executable(Tutorial tutorial.cxx)
- :end-before: # add the binary tree to the search path for include file
- .. raw:: html
- </details>
- this:
- .. raw:: html
- <details><summary>TODO 6: Click to show/hide answer</summary>
- .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
- :caption: TODO 6: MathFunctions/CMakeLists.txt
- :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
- :language: cmake
- :start-after: # link SqrtLibrary to tutorial_compiler_flags
- :end-before: target_link_libraries(MathFunctions
- .. raw:: html
- </details>
- and this:
- .. raw:: html
- <details><summary>TODO 7: Click to show/hide answer</summary>
- .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
- :caption: TODO 7: MathFunctions/CMakeLists.txt
- :name: MathFunctions-SqrtLibrary-target_link_libraries-step4
- :language: cmake
- :start-after: # link MathFunctions to tutorial_compiler_flags
- .. raw:: html
- </details>
- With this, all of our code still requires C++ 11 to build. Notice
- though that with this method, it gives us the ability to be specific about
- which targets get specific requirements. In addition, we create a single
- source of truth in our interface library.
|