-
1. Creating Library Programs -
2. Controlling Compilation via Option Definitions -
3. Defining Macros Used in Source Code -
4. Linking Library Programs
The directory structure for Step 2 is as follows:
├──CMakeLists.txt // Top-level cmake
├──MathFunctions
│ ├──CMakeLists.txt // Subdirectory cmake
│ ├──MathFunctions.cxx
│ ├──MathFunctions.h
│ ├── mysqrt.cxx
│ └── mysqrt.h
├──TutorialConfig.h.in
└── tutorial.cxx
We can see that we will introduce two CMakeLists.txt files:
-
1. Top-level CMakeLists.txt: This is the entry point for the entire project, responsible for configuring the basic settings, defining global variables and options, and adding subdirectories. -
2. Subdirectory CMakeLists.txt: Used to define and manage the build logic for specific submodules or subprojects.
Creating Library Programs
First, use the add_library command in the subdirectory CMakeLists.txt to create the library:
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)
Specifically, creating a Normal Library is done as follows:
add_library(<name>[<type>][EXCLUDE_FROM_ALL]<sources>...)
<span><name></span>
corresponds to the name of the library (unique within the project), and <span><type></span>
currently supports three standard values:
-
1. STATIC: Create a static library -
2. SHARED: Create a dynamic library linked at compile time -
3. MODULE: Create a dynamic library loaded at runtime
If the value of <span><type></span>
is not specified, it will default to STATIC or SHARED, depending on the value of BUILD_SHARED_LIBS.
CMake does not predefine BUILD_SHARED_LIBS; it needs to be manually set using the option command:
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
If not set, it defaults to OFF, meaning the library type is STATIC.
One line of code in the subdirectory CMakeLists.txt is sufficient, whereas for the top-level CMakeLists.txt, three lines of code need to be added, modified to:
cmake_minimum_required(VERSION 3.10)
project(Tutorial VERSION 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
configure_file(TutorialConfig.h.in TutorialConfig.h)
# New
add_subdirectory(MathFunctions)
add_executable(Tutorial tutorial.cxx)
# New
target_link_libraries(Tutorial PUBLIC MathFunctions)
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
# New
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
In addition to setting the include paths, two new commands have appeared:
-
1. add_subdirectory: Used to include the CMakeLists.txt file from the subdirectory into the current project’s build process, supporting modular development. -
2. target_link_libraries: Used to specify other libraries that a target (such as an executable or library) needs to link. It ensures the compiler knows how to find and link these dependency libraries, thus correctly generating the final binary file.
Thus, the top-level CMakeLists.txt for Step 2 is complete.
Adding Compilation Options
Using macros for conditional compilation is a common practice in C/C++ programming, especially when adjusting code behavior based on different build configurations or platform features.
The effect we want to achieve here is to provide a command-line compilation option <span>USE_MYMATH</span>
. When this option is OFF, the standard library’s square root function is called; when ON, the user-implemented square root function is called, with the default using the user-implemented square root function. As follows:
# Call the standard library's square root function
cmake .. -DUSE_MYMATH=OFF
# Use the user-implemented square root function
cmake .. -DUSE_MYMATH=ON
For command-line options, we can define them using option, with the default set to ON.
In the source code, there are currently two commands that can be used for macros:
-
1. target_compile_definitions: Command to set compilation definitions (i.e., macros) for a specified target. -
2. add_definitions: Adds compilation definitions for the entire project. This method is not recommended for new projects, as it affects all targets and is not as precise as target_compile_definitions.
The final subdirectory CMakeLists.txt is as follows:
add_library(MathFunctions MathFunctions.cxx)
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
add_library(SqrtLibrary STATIC
mysqrt.cxx
)
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()
Note that here we also used the <span>if</span>
scripting command, which we will not elaborate on here; we will introduce it later. In the code, we use it as follows:
#include "MathFunctions.h"
#include <cmath>
#ifdef USE_MYMATH
# include "mysqrt.h"
#endif
namespace mathfunctions {
double sqrt(double x)
{
#ifdef USE_MYMATH
return detail::mysqrt(x);
#else
return std::sqrt(x);
#endif
}
}
Thus, Step 2 is concluded. In the upcoming Step 3, we will refactor the CMakeLists.txt completed in Step 2 to achieve finer control over linking libraries or executable files and include paths.