CMake使用手册
简介
一、安装方式
CMake 安装路径参见 官网,安装方式分为 源码编译 和 二进制方式,分别下载对应的系统安装文件安装即可。对于 Ubuntu 系统可直接使用如下命令:
sudo apt install cmake
二、常用变量及引用方法
2.1 常用变量
- 引用方式是使用 "${ }",在 IF 中直接使用变量名即可
- 自定义变量使用 SET(OBJ_NAME xxxx),使用方式为 "${OBJ_NAME}"
- cmake 常用变量:
- CMAKE_BINARY_DIR ,PROJECT_BINARY_DIR:这两个变量内容一致,如果是内部编译,就指的是工程的顶级目录,如果是外部编译,指的就是工程编译发生的目录
- CMAKE_SOURCE_DIR, PROJECT_SOURCE_DIR:这两个变量内容一致,都指的是工程的顶级目录
- CMAKE_CURRENT_BINARY_DIR:外部编译时,指的是target目录,内部编译时,指的是顶级目录
- CMAKE_CURRENT_SOURCE_DIR:CMakeList.txt所在的目录
- CMAKE_CURRENT_LIST_DIR:CMakeList.txt的完整路径
- CMAKE_CURRENT_LIST_LINE:当前所在的行
- CMAKE_MODULE_PATH:如果工程复杂,可能需要编写一些cmake模块,这里通过SET指定这个变量
- LIBRARY_OUTPUT_DIR, BINARY_OUTPUT_DIR:库和可执行的最终存放目录
- EXECUTABLE_OUTPUT_PATH: 重新定义目标二进制可执行文件的存放位置
- PROJECT_NAME, CMAKE_PROJECT_NAME:前者是当前CMakeList.txt里设置的project_name,后者是整个项目配置的project_name
2.2 cmake中调用环境变量
1. Using $ENV{NAME} : 调用系统环境变量,我们也可以使用 "SET(ENV{NAME} value)". 需要注意的是这里"ENV"没有"$".
2. CMAKE_INCLUDE_CURRENT_DIR 等同于 INCLUDE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
2.3 其他的内置变量
- BUILD_SHARED_LIBS: set the default value when using ADD_LIBRARY()
- CMAKE_C_FLAGS: set compiler for c language
- CMAKE_CXX_FLAGS: set compiler for c++ language
2.4 区分debug和release
在工程build目录下执行如下命令,再执行make
cmake .. -DCMAKE_BUILD_TYPE=DEBUG|RELEASE
或者在顶级CMakeList.txt里加入:
set(CMAKE_BUILD_TYPE Debug|Release|MinSizeRel|RelWithDebInfo)
2.5 指定编译32bit或64bit程序
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
2.6 获取所有系统环境变量
# 在CMakeList.txt中使用
execute_process(
COMMAND ${CMAKE_COMMAND} -E environment
)
# 在终端中使用
cmake -E environment
2.7 获取绝对路径和父目录
# 获取文件绝对路径
get_filename_component(FULL_NAME "${FILE}" ABSOLUTE)
# 获取文件父路径
get_filename_component(PARENT_DIR "${FULL_NAME}" PATH)
2.8 判断操作系统平台及Win是否为32位
if(CMAKE_SYSTEM_NAME MATCHES "Linux") // 注意区分大写
message(STATUS "Linux platorm!")
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
if(CMAKE_CL_64)
message(STATUS "Windows Win64 platform!")
else()
message(STATUS "Windows Win32 platform!")
endif(CMAKE_CL_64)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
message(STATUS "FreeBSD platform!")
else()
message(STATUS "other platform!")
endif(CMAKE_SYSTEM_NAME MATCHES "Linux")
三、使用方法
3.1 基本使用
序号 | 描述 |
---|---|
样例一 | 单个源文件 main.cpp |
样例二 | 分解成多个文件:main.cpp utils.cpp utils.h |
样例三 | 先生成一个静态库,链接该库 |
样例四 | 源文件存在于不同目录 |
样例五 | 控制生成的程序和库所在的目录 |
在项目文件夹下创建 CMakeLists.txt 文件,添加设置
3.1.1 样例一 [单文件]
假设存在 main.cpp、utils.cpp 之类文件
// 指定 cmake 的最小版本
cmake_minimum_required(VERSION 3.4.1)
# 设置工程名字和版本
project(Test VERSION 1.0)
# 设置编译类型
add_executable(Test main.cpp) # 生成可执行文件
add_library(common STATIC utils.cpp) # 生成静态库
add_library(common SHARED utils.cpp) # 生成动态库或共享库
这里注意的是在 Linux 下编译的产物分别是 Test、libcommon.a、libcommon.so;在 Windows 下是 Test.exe、common.lib、common.dll;
创建 build 文件夹,进入后执行
- cmake .. //生成Makefile文件
- make //使用make命令进行编译
3.1.2 样例二 [多文件]
# 指定 cmake 的最小版本
cmake_minimum_required(VERSION 3.4.1)
# 设置工程名字和版本
project(Test VERSION 1.0)
# 方式一:可直接包含多文件
#add_executable(Test main.cpp utils.cpp) # 生成可执行文件
# 方式二:set设置变量
set(SRC_LIST main.cpp utils.cpp)
# 方式三:使用AUX_SOURCE_DIRECTORY搜索当前目录下的文件
#AUX_SOURCE_DIRECTORY(. SRC_LIST)
add_executable(demo ${SRC_LIST})
#设置头文件搜索路径(和此txt同个路径的头文件无需设置),可选
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include)
3.1.3 样例三 [链接生成的静态库]
# 指定 cmake 的最小版本
cmake_minimum_required(VERSION 3.4.1)
# 设置工程名字和版本
project(Test VERSION 1.0)
add_library(libutils utils.cpp)
# 设置编译类型
add_executable(Test main.cpp) # 生成可执行文件
# 添加了一个新的目标 libutils,并将其链接进Test程序
target_link_libraries(Test libutils)
#设置头文件搜索路径(和此txt同个路径的头文件无需设置),可选
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include)
3.1.4 样例四 [源文件存在不同的目录]
文件目录如下:
+
|
+--- CMakeList.txt
+--+ src/
| |
| +--- main.c
| /--- CMakeList.txt
|
+--+ libhello/
| |
| +--- hello.h
| +--- hello.c
| /--- CMakeList.txt
|
/--+ build/
- 顶层的CMakeList.txt 文件
cmake_minimum_required(VERSION 3.16)
project(HELLO)
# 添加子目录
add_subdirectory(src)
add_subdirectory(libhello)
- src 中的 CMakeList.txt 文件
# 包含头文件文件
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/libhello)
set(APP_SRC main.cpp
${PROJECT_SOURCE_DIR}/utils.cpp)
add_executable(hello ${PROJECT_SOURCE_DIR}/utils.cpp main.cpp)
target_link_libraries(hello libhello)
- libhello 中的 CMakeList.txt 文件
set(LIB_SRC hello.cpp)
add_library(libhello ${LIB_SRC})
# 前文中add_executable占据了 hello 所以这里取 libhello会导致生成 libhello.lib 或者 liblibhell.a 所以配置如下属性
# 则生成 hello.lib(或libhello.a)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")
则可以在 build 目录中执行编译命令,可以得到:
- build/src/hello
- build/libhello/libhello.a
3.1.5 样例五 [控制生成的程序和库所在的目录]
如果让可执行文件在 bin 目录,库文件在 lib 目录如下结构所示:
+ build/
|
+--+ bin/
| |
| /--- hello
|
/--+ lib/
|
/--- libhello.a
- 方法一:修改顶级的 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.16)
project(HELLO)
# 添加子目录
add_subdirectory(src bin)
add_subdirectory(libhello lib)
- 方法二: 修改其他两个文件
src/CMakeLists.txt 文件:
# 包含头文件
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/libhello)
set(APP_SRC main.cpp
${PROJECT_SOURCE_DIR}/utils.cpp)
add_executable(hello ${PROJECT_SOURCE_DIR}/utils.cpp main.cpp)
# 这里设置 可执行文件 路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
target_link_libraries(hello libhello)
libhello/CMakeLists.txt 文件:
set(LIB_SRC hello.cpp)
add_library(libhello ${LIB_SRC})
# 前文中add_executable占据了 hello 所以这里取 libhello会导致生成 libhello.lib 或者 liblibhell.a 所以配置如下属性
# 则生成 hello.lib(或libhello.a)
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")
# 设置库文件路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
四、搜索路径
4.1 自定义搜索规则
file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp")
add_library(demo ${SRC_LIST})
# 或者
file(GLOB SRC_LIST "*.cpp")
file(GLOB SRC_PROTOCOL_LIST "protocol/*.cpp")
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# 或者
file(GLOB_RECURSE SRC_LIST "*.cpp") #递归搜索
FILE(GLOB SRC_PROTOCOL RELATIVE "protocol" "*.cpp") # 相对protocol目录下搜索
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# 或者
aux_source_directory(. SRC_LIST)
aux_source_directory(protocol SRC_PROTOCOL_LIST)
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
4.2 查找指定的库文件
find_library(VAR name path)查找到指定的预编译库,并将它的路径存储在变量中。
默认的搜索路径为 cmake 包含的系统库,因此如果是 NDK 的公共库只需要指定库的 name 即可。类似的命令还有 find_file()、find_path()、find_program()、find_package()。
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
4.3 设置链接库搜索目录
link_directories(
${CMAKE_CURRENT_SOURCE_DIR}/libs
)
4.4 设置 target 需要链接的库
target_link_libraries( # 目标库
demo
# 目标库需要链接的库
# log-lib 是上面 find_library 指定的变量名
${log-lib} )
4.5 指定链接动态库或静态库
target_link_libraries(demo libface.a) # 链接libface.a
target_link_libraries(demo libface.so) # 链接libface.so
4.6 set 追加设置变量的值
set(SRC_LIST main.cpp)
set(SRC_LIST ${SRC_LIST} test.cpp)
add_executable(demo ${SRC_LIST})
4.7 list 追加或者删除变量的值
set(SRC_LIST main.cpp)
list(APPEND SRC_LIST test.cpp)
list(REMOVE_ITEM SRC_LIST main.cpp)
add_executable(demo ${SRC_LIST})
五、自定义编译选项
根据配置选择是否编译相关的库,例如MathFunctions 库设为一个可选的库,如果该选项为 ON ,就使用该库定义的数学函数来进行运算,否则就调用标准库中的数学函数库
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
"Use provided math implementation" ON)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/math")
add_subdirectory (math)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})
- configure_file 命令用于加入一个配置头文件 config.h ,这个文件由 cmake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成
- option 命令添加了一个 USE_MYMATH 选项,并且默认值为 ON 。根据 USE_MYMATH 变量的值来决定是否使用我们自己编写的 MathFunctions 库
修改 main.cc 文件,让其根据 USE_MYMATH 的预定义值来决定是否调用标准库还是MathFunctions 库:
#include "config.h"
#ifdef USE_MYMATH
#include "math/MathFunctions.h"
#else
#include <math.h>
#endif
int main(int argc, char *argv[])
{
if (argc < 3){
printf("Usage: %s base exponent \n", argv[0]);
return 1;
}
double base = atof(argv[1]);
int exponent = atoi(argv[2]);
#ifdef USE_MYMATH
printf("Now we use our own Math library. \n");
double result = power(base, exponent);
#else
printf("Now we use the standard library. \n");
double result = pow(base, exponent);
#endif
printf("%g ^ %d is %g\n", base, exponent, result);
return 0;
}
编写 config.h.in 文件
注意 main.cc 的第一行,这里引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:
#cmakedefine USE_MYMATH
这样 cmake 会自动根据 CMakeLists.txt 配置文件中的设置自动生成 config.h 文件。
六、交叉编译成Android使用的库
CMakeLists.txt 中内容不变,只需要编写 confirgure.sh 脚本:
#!/bin/bash
rm -rf build
mkdir build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=/home/zsk/Android/Sdk/ndk/20.0.5594570/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=/home/zsk/Android/Sdk/ndk/20.0.5594570 \
-DANDROID_ABI=armeabi-v7a \
-DANDROID_TOOLCHAIN=clang++ \
-DANDROID_PLATFORM=android-21 \
-DCMAKE_BUILD_TYPE=debug \
..
make
DCMAKE_TOOLCHAIN_FILE:指定ndk交叉编译链工具的路径,在ndk16版本以上,ndk自带cmake交叉编译工具链。
DANDROID_NDK:NDK的安装跟目录
DANDROID_ABI:各大平台:如armeabi-v7a、x86、mips等。android下基本编译一个armeabi-v7a就可以了,当然也可以加一个x86,方便在虚拟机上调试。
DANDROID_TOOLCHAIN:表示交叉编译链类型,取值gcc或者clang,clang++;gcc已经被废弃
DANDROID_PLATFORM:定义最低api版本
DCMAKE_BUILD_TYPE:定义构建类型,取值Debug或Release,Release
AS平台方式:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
// 指定一些编译选项
cppFlags "-std=c++11 -frtti -fexceptions"
...
// 也可以使用下面这种语法向变量传递参数:
// arguments "-D变量名=参数".
arguments "-DANDROID_ARM_NEON=TRUE",
// 使用下面这种语法向变量传递多个参数(参数之间使用空格隔开):
// arguments "-D变量名=参数1 参数2"
"-DANDROID_CPP_FEATURES=rtti exceptions"
// 指定ABI
abiFilters "armeabi-v7a" , "arm64-v8a"
}
}
}
buildTypes {...}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
- 在 defaultConfig 外面的 externalNativeBuild 里面的 cmake 指明了 CMakeList.txt 的路径(在本项目下, 和是 build.gradle 在同一个目录里面)。
- 在 defaultConfig 里面的 externalNativeBuild 里面的 cmake 主要填写的是CMake的命令参数。即由 arguments 中的参数最后转化成一个可执行的 CMake 的命令,可以在如下路径查看:
app/.cxx/cmake/debug/{架构}/CMakeFiles/build_command.txt
参考
[ 1 ] : https://blog.csdn.net/afei__/article/details/81201039#t4
[ 2 ] : https://www.jianshu.com/p/7477a8b3923f
[ 3 ] : https://blog.csdn.net/afei__/article/details/81201039#t21
[ 4 ] : https://www.jianshu.com/p/cb4f8136a265
[ 5 ] : https://www.jianshu.com/p/ce15d2f197a7
[ 6 ] : https://www.jianshu.com/p/f3da16a89f39
[ 7 ] : https://developer.android.google.cn/ndk/guides/cmake