初识CMake:学习笔记与速查
# CMake学习
主要参考CMake菜谱 (opens new window)教程总结的学习笔记,仅供笔者参考,完整教程请移步CMake菜谱 (opens new window) 😃
# 从可执行文件到库
# 编译单个文件
# 设置CMake所需的最低版本
cmake_minimum_required (VERSION 3.8)
# 声明项目的名称和支持的编程语言(CXX代表C++)
project(hello_cmake_world LANGUAGES CXX)
# 将源代码添加到此项目的可执行文件。
add_executable (hello-cmake "hello_cmake_world.cpp")
该文件与源文件hello_cmake_world.cpp
放在一起,执行
mkdir -p build
cd build
cmake ..
cmake --build .
注意:
CMake语言不区分大小写,但是参数是区分大小写的
CMake中,C++是默认的编程语言。不过,建议使用
LANGUAGES
选项在project
命令中显式地声明项目的语言使用
cmake -help
查看完整的帮助信息# mkdir -p sirius_build # cd sirius_build # cmake .. # 以上构建步骤可用跨平台命令代替 # -H表示当前目录中搜索根CMakeLists.txt文件 # -Bsirius_build告诉CMake在一个名为sirius_build的目录中生成所有的文件 cmake -H. -Bsirius_build
# 构建和链接静态库
cmake_minimum_required (VERSION 3.8)
project(hello_cmake_world LANGUAGES CXX)
# 创建目标——静态库。库的名称和源码文件名相同
add_library(message
STATIC
"Message.hpp"
"Message.cpp"
)
add_executable (hello-cmake "hello_cmake_world.cpp")
# 最后需要将目标库链接到可执行目标
target_link_libraries(hello-cmake message)
# 构建和链接动态库
# 与构建静态库一样,只需将STATIC改为SHARED
add_library(message
SHARED
"Message.hpp"
"Message.cpp"
)
如果考虑跨平台,有两种方法:
可为动态库的头文件添加:
#pragma once /* 添加一下内容以实现跨平台 */ #ifdef _WIN32 #ifdef MessageDLL #define MessageDLL __declspec(dllexport) #else #define MessageDLL __declspec(dllimport) #endif #else #define MessageDLL #endif //////////END//////////// class MessageDLL Message { public: // ... DO SOMETING ... };
在
CMakeLists.txt
里添加set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
在 Windows 平台中,导出动态库时除了会生成
.dll
动态库之外还会生成一个.lib
文件。这个.lib
文件和静态库的.lib
文件不同,它里面不保存代码生成的二进制文件,而是所有需要导出符号的符号表。如果希望将一个符号(symbol)导出(这里的符号可以指类、函数等各种类型):
方法一:需要在其前面加上
__declspec(dllexport)
标志。这样这个符号的相关信息就会存在于.lib
中的符号表中; 方法二:在CMakeLists.txt
里添加set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# 条件句控制编译
先看一个例子:
cmake_minimum_required(VERSION 3.8)
project(hello_cmake_world LANGUAGES CXX)
# 引入变量USE_LIBRARY,message打印相关信息
set(USE_LIBRARY OFF)
message(STATUS "Compile sources into a library? ${USE_LIBRARY}")
# BUILD_SHARED_LIBS在下面第二点有具体解释
set(BUILD_SHARED_LIBS OFF)
# 引入变量包括*.hpp\*.cpp文件
list(APPEND _sources "Message.hpp" "Message.cpp")
# 逻辑控制编译
if (USE_LIBRARY)
add_library(message ${_sources})
add_executable(hello-cmake "hello_cmake_world.cpp")
target_link_libraries(hello-cmake message)
else()
add_executable(hello-cmake "hello_cmake_world.cpp" ${_sources})
endif()
- 使用
set
为变量设置值,其中逻辑变量可以是以下几种之一:- 逻辑
true
:1
、ON
、YES
、true
、Y
或非零数
; - 逻辑
false
:0
、OFF
、NO
、false
、N
、IGNORE
、NOTFOUND
、空字符串
或以-NOTFOUND
为后缀。
- 逻辑
BUILD_SHARED_LIBS
是CMake的一个全局标志,可以决定为生成动态库还是静态库。如果为false
或未定义,将生成一个静态库;_sources
变量是一个局部变量,不应该在当前范围之外使用,可以在名称前加下划线。
# 显示选项
上一节中(条件句控制编译)用硬编码控制逻辑,只能从内部修改。
本节使用
option()
命令,以选项的形式在外部设置编译逻辑
语法:
option
可接受三个参数:
option(<option_variable> "help string" [initial value])
<option_variable>
变量名"help string"
在终端可见的帮助性文字[initial value]
选项的默认值,可以是ON
或OFF
。
因而对于上一节中的CMakeLists.txt
,只需将set(USE_LIBRARY OFF)
使用option()
代替,并设置默认值即可:
option(USE_LIBRARY "Compile sources into a library" OFF)
现在,可以通过CMake的-D
CLI选项,将信息传递给CMake来切换库的行为
cmake -D USE_LIBRARY=ON ..
-D
开关用于为CMake设置任何类型的变量:逻辑变量、路径等等。
如果有依赖关系,如:用户设置不构建库,但设置了构建动态库,此时应该不构建库。CMake提供cmake_dependent_option()
命令用来定义依赖于其他选项的选项:
需要引入
CMakeDependentOption
模块
语法:
cmake_dependent_option
可接受五个参数:
cmake_dependent_option(<OPTION_NAME> "help string" <default_value> <depends> <force_value>)
<OPTION_NAME>
变量名"help string"
帮助性文字<default_value>
默认值<depends>
该值为true
时 开启此选项并设置为默认值,否则强制该选项默认值为 <force_value><depends>
可以用分号代表与的关系,例如:"ENABLE_BUILD;NOT DISABLE_TESTING"
表示(ENABLE_BUILD
&& (!DISABLE_TESTING
) )
因而添加是否编译为库
和是否编译为动态库
两个选项时,可修改为:
option(USE_LIBRARY "Compile sources into a library" OFF)
include(CMakeDependentOption)
# 当USE_LIBRARY为ON时,MAKE_STATIC_LIBRARY是;MAKE_STATIC_LIBRARY是
# 当USE_LIBRARY为ON时,MAKE_STATIC_LIBRARY是;MAKE_STATIC_LIBRARY是
cmake_dependent_option(
MAKE_STATIC_LIBRARY "Compile sources into a static library" OFF
"USE_LIBRARY" ON
)
cmake_dependent_option(
MAKE_SHARED_LIBRARY "Compile sources into a shared library" ON
"USE_LIBRARY" ON
)
# 设置编译器选项
参考如下一个例子:
cmake_minimum_required(VERSION 3.8)
project(recipe-08 LANGUAGES CXX)
message("C++ compiler flags: ${CMAKE_CXX_FLAGS}")
# 准备编译选项列表
list(APPEND flags "-fPIC" "-Wall")
if(NOT WIN32)
list(APPEND flags "-Wextra" "-Wpedantic")
endif()
add_library(geometry
STATIC
geometry_circle.cpp
geometry_circle.hpp
geometry_polygon.cpp
geometry_polygon.hpp
geometry_rhombus.cpp
geometry_rhombus.hpp
geometry_square.cpp
geometry_square.hpp
)
# 设置编译选项
target_compile_options(geometry
PRIVATE
${flags}
)
add_executable(compute-areas compute-areas.cpp)
target_compile_options(compute-areas
PRIVATE
"-fPIC"
)
target_link_libraries(compute-areas geometry)
- 编译选项
target_compile_options()
可以添加三个级别的可见性:INTERFACE
、PUBLIC
和PRIVATE
;- PRIVATE,编译选项应用于指定的目标,不会传递给与目标相关的目标。
- INTERFACE,给定的编译选项将只应用于指定目标,并传递给与目标相关的目标。
- PUBLIC,编译选项将应用于指定目标和使用它的目标。
# 语言设定标准
set_target_properties(animals
PROPERTIES
CXX_STANDARD 14
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE 1
)
这里为目标设置了一些属性:
- CXX_STANDARD,会设置我们想要的C++标准。
- CXX_EXTENSIONS,告诉CMake,只启用
ISO C++
标准的编译器标志,而不使用特定编译器的扩展。 - CXX_STANDARD_REQUIRED,指定所选标准的版本。如果这个版本不可用,CMake将停止配置并出现错误。当这个属性被设置为
OFF
时,CMake将寻找下一个标准的最新版本,直到一个合适的标志。这意味着,首先查找C++20
,然后是C++17
,然后是C++14
。
如果语言标准是所有目标共享的全局属性,那么可以将
CMAKE_<LANG>_STANDARD
、CMAKE_<LANG>_EXTENSIONS
和CMAKE_<LANG>_STANDARD_REQUIRED
变量设置为相应的值。所有目标上的对应属性都将使用这些设置。
# 关于目标的一些选项汇总
target_include_directories(myapp PUBLIC /path) # 添加头文件搜索目录
target_link_libraries(myapp PUBLIC hellolib) # 添加要链接的库
target_add_definitions(myapp PUBLIC MY_MACRO=1) # 添加一个宏定义
target_add_definitions(myapp PUBLIC -DMY_MACRO=1) # 与MY_MACRO=1等价
target_compile_options(myapp PUBLIC -fopenmp) # 添加编译器命令行选项
target_sources(myapp PUBLIC hello.cpp other.cpp) # 添加要编译的源文件
## 以下指令不推荐使用
include_directories(/path/to/include)
link_directories(/opt/cuda) # 添加库的搜索路径
add_definitions(MY_MACRO=1)
add_compile_options(-fopenmp)
Related Issues not found
Please contact @Sirius0v0 to initialize the comment