target_compile_definitions()
target_compile_options()
target_include_directories()
target_link_directories()
target_link_options()
target_precompile_headers()
target_sources()
CMAKE_CURRENT_SOURCE_DIR
The path to the source directory currently being processed.
This is the full path to the source directory that is currently being processed by cmake.
When run in cmake -P script mode, CMake sets the variables CMAKE_BINARY_DIR, CMAKE_SOURCE_DIR, CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR to the current working directory.
步骤:
MathFunctions/CMakeLists.txt
1 INTERFACE是指消费者需要,但是生产者不需要的 这条指令之前都是外部的cmakelist文件,现在里面的也写了,之前都是寻找名为Tutorial
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
2 外层的cmakelists
他前面用了个
target_include_directories(Tutorial PUBLIC 确认库的头文件的位置
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
这里要把相应的去掉
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}
)
部分余下的应该长这样
# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
请注意,使用此技术,我们的可执行目标使用库所做的唯一事情就是使用库目标的名称调用target_link_libraries()。在较大的项目中,手动指定库依赖项的经典方法很快就会变得非常复杂。
里面的cmakelists
add_library(MathFunctions MathFunctions.cxx)
# TODO 1: State that anybody linking to MathFunctions needs to include the
# current source directory, while MathFunctions itself doesn't.
# Hint: Use target_include_directories with the INTERFACE keyword
# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if (USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
# library that just does sqrt
add_library(SqrtLibrary STATIC
mysqrt.cxx
)
# TODO 7: Link SqrtLibrary to tutorial_compiler_flags
target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
endif()
# TODO 6: Link MathFunctions to tutorial_compiler_flags
在 CMake 中,我们通常将一个库或可执行文件的源代码分为多个目录进行管理,每个目录都有自己的 CMakeLists.txt
文件来描述这个目录内的源文件和编译选项等信息。这样做的好处是可以让项目结构更加清晰,同时也便于后期的维护和修改。
当一个目录要作为库的源码目录时,可以使用 add_library()
命令来创建一个库,并通过 add_subdirectory()
命令引入内层的 CMakeLists.txt
文件,以描述该库所在目录内的源文件及其生成的库文件等信息。
一般情况下,内层的 CMakeLists.txt
文件中只需包含定义相关的变量语句、编译选项、源文件列表等信息以及 add_library()
命令即可。
例如:
外层 CMakeLists.txt 文件:
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# 添加子目录
add_subdirectory(MyLibrary)
内层 CMakeLists.txt 文件:
# 定义源文件列表
set(SRCS foo.cpp bar.cpp)
# 定义编译选项
add_compile_options(-Wall -Wextra)
# 构建库
add_library(MyLibrary ${SRCS})
在上面的例子中,内层的 CMakeLists.txt
文件只有三条语句:定义了 SRCS
变量来保存库的源文件列表,使用 add_compile_options()
定义编译选项,最后使用 add_library()
命令来创建库。由于该目录只包含库的源码,因此并不需要添加可执行文件。
在这种情况下,因为内层的 CMakeLists.txt
文件只是为一个库定义了相关的信息,而没有要生成可执行文件等目标,所以不需要包含 cmake_minimum_required()
和 project()
这两条命令。但是在一般情况下,每个 CMakeLists.txt 文件中都应该包含 cmake_minimum_required()
和 project()
命令,用于指定 CMake 最低版本和项目名称等信息。
在 CMake 中,target_include_directories()
命令用于为一个目标(库或可执行文件)添加头文件搜索路径,以便在编译时能够找到需要的头文件。
该命令的用法如下:
target_include_directories( [SYSTEM] [directories...])
其中:
表示要添加头文件搜索路径的目标名称;[SYSTEM]
是一个可选的关键字,表示这些路径是系统头文件路径(即放在系统标准头文件目录中);
是一个可选的标记,表示要将这些路径添加到哪个范围中:
INTERFACE
表示这些路径会被添加到包括依赖
的所有目标中,但不会被添加到
本身;PUBLIC
表示这些路径会被添加到
和其依赖项中;PRIVATE
表示这些路径仅会被添加到
中,而不会添加到其依赖项中;[directories...]
表示要添加的头文件搜索路径列表。例如,以下代码将 MyLibrary
这个目标的头文件搜索路径设置为 include
目录:
add_library(MyLibrary foo.cpp bar.cpp)
target_include_directories(MyLibrary PRIVATE include)
在上面的例子中,我们使用 add_library()
命令创建了一个名为 MyLibrary
的库,并使用 target_include_directories()
命令将头文件搜索路径添加到该目标中。由于使用了 PRIVATE
标记,因此这些路径只会被添加到 MyLibrary
目标中,而不会被添加到其依赖项中。
使用 target_include_directories()
命令可以方便地管理头文件搜索路径,避免了手动编写编译选项的繁琐操作,并且提高了 CMake 项目的可维护性。
这行代码使用 CMake 的 list()
和 append()
函数将一个路径添加到名为 EXTRA_INCLUDES
的列表变量中。这个路径是由 ${PROJECT_SOURCE_DIR}
和 /MathFunctions
拼接而成的,表示项目源码目录下的 MathFunctions 目录的路径。
具体来说,list()
函数用于操作 CMake 中的列表变量,可以进行许多列表操作,例如:创建、添加、删除、查找等。而 append()
函数则是 list()
函数中用于添加元素的一种方式,它能够在列表末尾添加一个或多个元素,这些元素可以是常量、变量、表达式,也可以是其他的列表。
因此,这行代码的含义是:将 ${PROJECT_SOURCE_DIR}/MathFunctions
这个路径添加到名为 EXTRA_INCLUDES
的列表变量的末尾。可供后续的 CMake 命令使用,比如 target_include_directories()
命令中对于 EXTRA_INCLUDES
列表变量的引用。
例如,以下代码添加了一个名为 MyLibrary
的库,并将 ${PROJECT_SOURCE_DIR}/MathFunctions
加入该库的头文件搜索路径:
add_library(MyLibrary foo.cpp bar.cpp)
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
target_include_directories(MyLibrary PRIVATE ${EXTRA_INCLUDES})
在上面的例子中,我们使用 add_library()
命令创建了一个名为 MyLibrary
的库,并使用 list()
和 append()
函数将 ${PROJECT_SOURCE_DIR}/MathFunctions
这个路径添加到名为 EXTRA_INCLUDES
的列表变量中。然后,我们使用 target_include_directories()
命令将 EXTRA_INCLUDES
列表变量中的路径加入到 MyLibrary
库的头文件搜索路径中。
这种做法的好处在于,将头文件搜索路径和源码目录分离开来,使得 CMake 项目更加清晰易懂。同时也方便后续的维护和修改,只需要修改 EXTRA_INCLUDES
列表中的路径即可,而不用修改每一次调用 target_include_directories()
命令的路径参数。
相当于 在内部增加 文件调用
然后把外部的文件调用删除
这样的话外部的文件只相当于做了一件事,那就是用target link libraries
tutorial_compiler_flags
是一个 CMake 变量,用于存储 Tutorial 项目编译器相关的标志。这个变量可以在 CMakeLists.txt 文件中定义,例如:
set(tutorial_compiler_flags "-std=c++11 -Wall")
在 target_link_libraries()
命令中使用 tutorial_compiler_flags
变量时,它会被自动传递给链接器和编译器,以供编译和链接使用。
在这个例子中,通过将 tutorial_compiler_flags
的值添加到 Tutorial
目标的链接命令中,可以确保 MathFunctions
库及其他依赖项也使用与 Tutorial
相同的编译器标志进行编译,并且这些标志能够正确地传递给链接器,从而确保生成的执行文件具有正确的特性和行为。
通常情况下,tutorial_compiler_flags
中会包含一些编译器标志,如 -std=c++11
表示使用 C++ 11 标准编译,-Wall
表示开启所有警告。这些标志可以提高代码质量和可移植性,并帮助我们尽早发现代码中的问题,从而更好地进行调试和优化。
使用一个现代的方法为多个目标设置特征
1 删除
CMakeLists.txt
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
CMakeLists.txt 创建接口库,并添加
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) 把所有的库文件都连接到cpp11
MathFunctions/CMakeLists.txt
target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
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.
里面的cmakelists
add_library(MathFunctions MathFunctions.cxx)
# TODO 1: State that anybody linking to MathFunctions needs to include the
# current source directory, while MathFunctions itself doesn't.
# Hint: Use target_include_directories with the INTERFACE keyword
# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if (USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
# library that just does sqrt
add_library(SqrtLibrary STATIC
mysqrt.cxx
)
# TODO 7: Link SqrtLibrary to tutorial_compiler_flags
target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
endif()
# TODO 6: Link MathFunctions to tutorial_compiler_flags
通过该部,具体为哪些文件配置编辑方式等等,并且唯一确定从一个地方改
在 CMake 中,add_library()
命令还可以用于创建一个仅包含编译器选项的库,而不是实际的代码库。
在这个例子中,add_library(tutorial_compiler_flags INTERFACE)
创建了一个名为 tutorial_compiler_flags
的空库(没有真正的源文件),其中的 INTERFACE
表示该库不生成任何产物,只包含一些接口元素,如编译器选项、头文件目录等。这些接口元素可以被其他目标使用,以便它们也能享受到这些选项。
因此,add_library(tutorial_compiler_flags INTERFACE)
命令的作用是向项目中添加一个新的库 tutorial_compiler_flags
,并将其设置为一个 INTERFACE
库。这个库没有实际的源文件或目标文件,但提供了一些编译器选项,可供其他目标进行调用。具体来说,它可以被链接到其他目标(比如可执行文件或共享库)中,以使这些目标也使用与 tutorial_compiler_flags
相同的编译器选项。
例如,在上一个问题的回答中,我们提到了 target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
命令,其中的 tutorial_compiler_flags
就可以指向一个 INTERFACE
类型的库,以确保 Tutorial
目标及其依赖项都使用相同的编译器选项进行编译。这可以通过以下方式实现:
add_library(tutorial_compiler_flags INTERFACE)
target_compile_options(tutorial_compiler_flags INTERFACE -std=c++11 -Wall)
在上面的代码中,我们首先定义了一个名为 tutorial_compiler_flags
的 INTERFACE
类型库,并使用 target_compile_options()
命令将 -std=c++11
和 -Wall
两个编译器选项添加到该库的接口中。然后,在其他目标(如 Tutorial
)中使用 target_link_libraries()
命令时,就可以将 tutorial_compiler_flags
库与目标一起链接,以确保它们都使用了 -std=c++11
和 -Wall
标志进行编译。
在 CMake 中,target_compile_features()
和 target_compile_options()
命令都可以用于向目标(如库或可执行文件)添加编译器选项。不过它们的作用有所不同。
target_compile_options()
命令是用来设置编译器选项的,这些选项会被传递给编译器进行编译操作。例如,可以使用以下命令来设置 -Wall
和 -O2
编译选项:
target_compile_options(MyLibrary PRIVATE -Wall -O2)
另一方面, target_compile_features()
命令指定了目标代码所需的 C++ 标准特性。例如,可以使用以下命令来设置目标代码需要支持 C++ 11 标准特性:
target_compile_features(MyLibrary PUBLIC cxx_std_11)
在这种情况下,CMake 将确保使用编译器支持 C++ 11 标准特性进行编译,并启用与此标准相关的所有特性和功能。这可以用于确保所有代码都遵循相同的 C++ 标准,并能够在不同的平台和编译器上正确、一致地运行。
需要注意的是,target_compile_features()
命令通常比 target_compile_options()
更为高级,因为它包含了许多更复杂和深层次的编译器特性和选项。所以在使用 target_compile_features()
命令时,需要确保所选的编译器和平台都支持所需的特性和选项。
在 CMake 中,target_compile_definitions()
命令用于向目标(如库或可执行文件)添加预定义的宏定义。在 target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
这个命令中,我们向名为 MathFunctions
的目标添加了一个名为 USE_MYMATH
的预定义宏定义。
这意味着,在编译 MathFunctions
目标时,CMake 将会传递这个预定义宏给编译器,并启用相应的代码。在代码中,可以使用这个宏进行条件编译,例如:
#ifdef USE_MYMATH
#include "mymath.h"
#else
#include
#endif
这个代码片段表示,如果 USE_MYMATH
宏被定义,则包含 mymath.h
头文件;否则,包含
头文件。
需要注意的是,这里使用了 PRIVATE
作为 target_compile_definitions()
的第二个参数。这表示,预定义宏定义将仅适用于此目标及其直接依赖项。如果想要让该预定义宏定义适用于整个项目,可以使用 PUBLIC
或 INTERFACE
选项。
例如,如果将 target_compile_definitions(MathFunctions PUBLIC "USE_MYMATH")
命令改成 target_compile_definitions(MathFunctions INTERFACE "USE_MYMATH")
,则表示这个预定义宏定义会被添加到 MathFunctions 库的接口中,从而可以被其他目标(如 Tutorial 库)使用。这时,预定义宏定义也将成为这些目标的一部分,并在编译期间传递给它们。
在 CMake 中,target_compile_definitions()
命令用于向目标(如库或可执行文件)添加预定义的宏定义。这些宏定义可以在代码中使用,并会在编译期间传递给编译器。例如,可以使用以下命令来定义一个名为 DEBUG
的预编译宏:
target_compile_definitions(MyLibrary PRIVATE DEBUG)
这将把 DEBUG
宏定义传递给编译器,并在编译时启用相应的调试代码。还可以为预定义宏定义提供特定的值,例如:
target_compile_definitions(MyLibrary PRIVATE VERSION_MAJOR=2 VERSION_MINOR=1)
这将定义名为 VERSION_MAJOR
和 VERSION_MINOR
的两个宏,并分别将它们设置为 2
和 1
。在代码中,可以使用这些宏来获取版本号等信息。
需要注意的是,预定义宏的语法和定义方式因编译器而异。因此,建议在使用预定义宏时,先了解所选编译器的文档和规范。
生成表达式用于生成编译系统的配置信息
A common usage of generator expressions is to conditionally add compiler flags, such as those for language levels or warnings. A nice pattern is to associate this information to an INTERFACE target allowing this information to propagate.
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
set(gcc_like_cxx "$" )
set(msvc_cxx "$" )
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
"$<${msvc_cxx}:-W3>"
)
修改
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$>"
"$<${msvc_cxx}:$>"
)
在 CMake 中,set()
命令用于设置变量的值。在这个例子中,我们定义了两个变量,即 gcc_like_cxx
和 msvc_cxx
。
这里使用了 CMake 的生成表达式语法来设置变量的值。生成表达式是一种特殊的语法,用于在编译期间生成代码,并根据不同的条件生成不同的代码。在这个例子中,我们使用了 $
生成表达式来生成不同的变量值,具体含义如下:
$
:对于 C++ 语言和 ARMClang、AppleClang、Clang、GNU 和 LCC 编译器,该值为真;否则为假。这意味着,如果编译器是任何一种标准的 C++ 编译器,则 gcc_like_cxx
变量将被设置为真。$
:对于 C++ 语言和 MSVC 编译器,该值为真;否则为假。这意味着,如果编译器是 MSVC,则 msvc_cxx
变量将被设置为真。这些变量的作用是用于在不同的编译器上进行条件编译或设置不同的编译选项。在实际使用中,可以使用这些变量来进行条件判断,例如:
if (gcc_like_cxx)
# 设置 GCC 编译器选项
else()
# 设置其他编译器选项
endif()
这样代码就可以根据不同的编译器进行适配和优化。需要注意的是,生成表达式语法是 CMake 中比较高级和复杂的特性,使用前需要了解其语法和规范,并确保所选编译器支持相应的特性。
在 CMake 中,target_compile_options()
命令用于向目标(如库或可执行文件)添加编译选项。在这个例子中,我们使用了 target_compile_options(tutorial_compiler_flags ...)
命令来向名为 tutorial_compiler_flags
的接口目标添加编译选项。
这里使用了 CMake 的生成表达式语法来针对不同的编译器和操作系统设置不同的编译选项。具体的编译选项如下:
<${gcc_like_cxx}>
为真),设置 -Wall
、-Wextra
、-Wshadow
、-Wformat=2
和 -Wunused
编译选项。这些选项用于开启额外的警告信息和错误检查,并帮助发现常见的代码问题和潜在风险。<${msvc_cxx}>
为真),设置 -W3
编译选项。这个选项用于开启所有警告信息。需要注意的是,这里使用了 INTERFACE
选项,表示这些编译选项将作为接口的一部分传递给依赖于该接口的目标。这样,所有依赖于 tutorial_compiler_flags
接口的目标都可以共享这些相同的编译选项。
另外,这里使用了 $<${gcc_like_cxx}>
和 $<${msvc_cxx}>
生成表达式来控制编译选项的启用和禁用。如果 gcc_like_cxx
或 msvc_cxx
变量为真,则相应的编译选项将被启用;否则将被禁用。
总体来说,这个命令可以帮助我们以统一的方式为整个项目设置编译选项,并确保不同的编译器和操作系统都能获得正确的编译选项和警告信息。
这是一个 CMake 的生成表达式,用于在 MSVC 编译器下开启警告选项 -W3
。
具体来说,这个生成表达式包含了两个部分:
$<${msvc_cxx}>
表示如果当前编译器是 MSVC,则该表达式为真(非零);否则为假(零)。$
表示如果该目标正在构建,则该表达式为真(非零);否则为假(零)。这里使用了 BUILD_INTERFACE
,表示只有在构建过程中才使用这个选项。这种方式可以避免在安装或导出时将该选项传播到其他目标。两个表达式通过冒号连接起来,表示只有当两个表达式都为真时,才会添加编译选项 -W3
。因此,这个命令只会在 MSVC 编译器下,并且当前正在构建该目标时,才会开启 -W3
警告选项。
在 CMake 中,BUILD_INTERFACE
是一个特殊的关键字/标识符(identifier),它用于指定目标属性的接口(interface)。具体来说,BUILD_INTERFACE
用于指定那些只有在构建过程中使用的属性。这些属性包括编译选项、头文件路径、链接选项等等。
使用 BUILD_INTERFACE
可以帮助我们避免将构建过程中使用的属性传递到安装或导出的目标中,从而保持其独立性和可移植性。例如,在编写库时,我们可以使用 BUILD_INTERFACE
来指定库的头文件路径和依赖库的链接选项,这些设置仅在构建库时使用,而不会影响用户使用该库。
在 target_include_directories()
和 target_link_libraries()
等命令中,可以使用 $
语法来添加 BUILD_INTERFACE 标识符。例如:
target_include_directories(mylib PUBLIC $)
target_link_libraries(mylib PRIVATE $)
这里使用了 $
语法来指定只有在构建库时才需要使用的头文件路径和依赖库名称。当将该库安装或导出到其他目标时,这些特殊的设置将被忽略,只有普通的头文件路径和链接库名称会被传递。