Adding Usage Requirements for a Library — CMake 4.0.2 Documentation (original) (raw)

Exercise 1 - Adding Usage Requirements for a Library

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:

Goal

Add usage requirements for a library.

Helpful Materials

Files to Edit

Getting Started

In this exercise, we will refactor our code from[Adding a Library](Adding%20a%20Library.html#guide:tutorial/Adding a Library "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, MathFunctionswill specify any needed include directories itself. Then, the consuming targetTutorial 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 target_include_directories() inMathFunctions/CMakeLists. Remember thatCMAKE_CURRENT_SOURCE_DIR is the path to the source directory currently being processed.

Then, update (and simplify!) the call totarget_include_directories() in the top-level CMakeLists.txt.

Build and Run

Make a new directory called Step3_build, run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool or by using cmake --build . from the build directory. Here's a refresher of what that looks like from the command line:

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. RememberINTERFACE means things that consumers require but the producer doesn't.

At the end of MathFunctions/CMakeLists.txt, usetarget_include_directories() with the INTERFACE keyword, as follows:

TODO 1: Click to show/hide answer

TODO 1: MathFunctions/CMakeLists.txt

target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} )

Now that we've specified usage requirements for MathFunctions we can safely remove our uses of the EXTRA_INCLUDES variable from the top-levelCMakeLists.txt.

Remove this line:

TODO 2: Click to show/hide answer

And remove EXTRA_INCLUDES from target_include_directories:

TODO 3: Click to show/hide answer

Notice that with this technique, the only thing our executable target does to use our library is call 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 forgenerator expressions.

Goal

Add an INTERFACE library target to specify the required C++ standard.

Helpful Resources

Files to Edit

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 anINTERFACE 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 target_link_libraries() call totutorial_compiler_flags.

Build and Run

Since we have our build directory already configured from Exercise 1, simply rebuild our code by calling the following:

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 set() calls on the variablesCMAKE_CXX_STANDARD and CMAKE_CXX_STANDARD_REQUIRED. The specific lines to remove are as follows:

CMakeLists.txt

set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True)

Next, we need to create an interface library, tutorial_compiler_flags. And then use target_compile_features() to add the compiler featurecxx_std_11.

TODO 4: Click to show/hide answer

TODO 4: CMakeLists.txt

add_library(tutorial_compiler_flags INTERFACE) target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

Finally, with our interface library set up, we need to link our executable Tutorial, our SqrtLibrary library and our MathFunctionslibrary to our new tutorial_compiler_flags library. Respectively, the code will look like this:

TODO 5: Click to show/hide answer

TODO 5: CMakeLists.txt

target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)

this:

TODO 6: Click to show/hide answer

TODO 6: MathFunctions/CMakeLists.txt

target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)

and this:

TODO 7: Click to show/hide answer

TODO 7: MathFunctions/CMakeLists.txt

target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)

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.