# Cmake
笔记
Cmake
允许开发者指定整个工程的编译流程,再根据编译平台,自动生成本地化的 makefile
和工程文件,只需 make
即可 编译
。一款自动生成 Makefile
的工具。程序执行流程:预处理,编译,汇编,链接与生成可执行文件
# Cmake
优点
跨平台
- 能够管理大型项目
- 简化编译构建过程和编译过程
- 可扩展:可以为
cmake
编写特定功能的模块,扩充 cmake
功能
# Cmake
使用
Cmake
不区分大小写。
# 注释
# 测试文件
| #include <stdio.h> |
| #include "head.h" |
| |
| int add(int a, int b) |
| { |
| return a+b; |
| } |
| #include <stdio.h> |
| #include "head.h" |
| |
| int subtract(int a, int b) |
| { |
| return a-b; |
| } |
| #include <stdio.h> |
| #include "head.h" |
| |
| int multiply(int a, int b) |
| { |
| return a*b; |
| } |
| #include <stdio.h> |
| #include "head.h" |
| |
| double divide(int a, int b) |
| { |
| return (double)a/b; |
| } |
| #ifndef _HEAD_H |
| #define _HEAD_H |
| |
| int add(int a, int b); |
| |
| int subtract(int a, int b); |
| |
| int multiply(int a, int b); |
| |
| double divide(int a, int b); |
| #endif |
| #include <stdio.h> |
| #include "head.h" |
| |
| int main() |
| { |
| int a = 20; |
| int b = 12; |
| printf("a = %d, b = %d\n", a, b); |
| printf("a + b = %d\n", add(a, b)); |
| printf("a - b = %d\n", subtract(a, b)); |
| printf("a * b = %d\n", multiply(a, b)); |
| printf("a / b = %f\n", divide(a, b)); |
| return 0; |
| } |
# 添加 CMakeLists.txt
| |
| cmake_minimum_required(VERSION 3.0) |
| |
| 并可指定工程的版本, |
| 工程描述, |
| web主页地址, |
| 支持的语言, |
| |
| project(<PROJECT-NAME> [<language-name>...]) |
| project(<PROJECT-NAME> |
| [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] |
| [DESCRIPTION <project-description-string>] |
| [HOMEPAGE_URL <url-string>] |
| [LANGUAGES <language-name>...]) |
| |
| ]] |
| project(CALC) |
| |
| |
| add_executable(app add.c div.c main.c mult.c sub.c) |
| |
# 执行
| |
| cmake CMakeLists.txt文件所在路径 |
| |
| make |
| |
# 存入 build
| |
| mkdie build |
| cd build |
| |
| |
| cmake .. |
# 定义变量
| |
| set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]]) |
| set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c) |
| add_executable(app ${SRC_LIST}) |
# 指定使用的标准
| g++ *.cpp -std=c++11 -o app |
- 使用
CMakeLists.txt
中通过 set
命令指定
| |
| set(CMAKE_CXX_STANDARD 11) |
| cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11 |
# 指定输出路径
Cmake
指定可自行输出的路径,对应宏: EXECUTABLE_OUTPUT_PATH
,通过 set 命令进行设置值
| |
| set(HOME /home/robin/linux/Sort) |
| |
| set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin) |
# 搜索 文件
使用 aux_source_directory
命令或者 file
命令
| aux_source_directory(< dir > < variable >) |
dir
要搜索的目录variable
将从 dir
目录下搜索到的 源文件
列表存储到该变量中
| cmake_minimum_required(VERSION 3.0) |
| project(CALC) |
| include_directories(${PROJEcT_SOURCE_DIR}/include) |
| |
| aux_source_directory(${CMAKE_CURRENT_SOURCE_DIE}/src SRC_LIST) |
| add_executable(app ${SRC_LIST}) |
# file 文件名
| file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型) |
GLOB
: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中GLOB_RECURSE
: 递归
搜索指定目录,将搜索到的满足条件的 文件名
生成一个列表,并将其存储到变量中
搜索当前目录的 src
目录下所有的源文件,并存储到变量中
CMAKE_CURRENT_SOURCE_DIR
表示当前访问的 CMakeLists.txt
文件所在的路径,关于要搜索文件路径和类型可加双引号,也可不加
| file(GLOB MAIN_SRS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) |
| file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) |
# 包含头文件
| include_directories(headpath) |
# CMaeList.txt
文件内容
| cmake_minimum_required(VERSION 3.0) |
| project(CALC) |
| |
| set(CMAKE_CXX_STANDARD 11) |
| set(HOME /home/robin/Linux/calc) |
| set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin/) |
| |
| include_directories(${PROJECT_SOURCE_DIR}/include) |
| file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) |
| add_executable(app ${SRC_LIST}) |
# 制作静态库
Linux 静态库分为三个部分: lib
+ 库名称
+ .a
| |
| add_library(库名称 STATIC 源文件1 [源文件2] ...) |
| |
| |
| set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) |
| add_library(calc STATIC ${SRC_LIST}) |
| |
# 制作动态库
Linux
动态库名称分为 lib + 库名称 + .so
| |
| set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) |
| add_library(库名称 SHARED 源文件 [源文件2] ... ) |
# 链接静态库
| link_libraries(<static lib> [<static lib> ...]) |
- 参数 1, 指出要链接的静态库名称,可以是全名,也可以是去掉头 lib 和尾.a 之后的名字,
- 参数 2 要链接的其它静态库名称
如果该静态库 不是系统
提供的,此时可以将静态库的 路径指定
出来
| |
| link_directories(<lib path>) |
# 链接动态库
| target_link_libraries( |
| <target> |
| <PRIVATE|PUBLIC|INTERFACE> <item>... |
| [<PRIVATE|PUBLIC|INTERFACE> <item>...]...) |
| |
| |
由于动态链接库在可执行程序中并不会被打包到可执行程序中,应该将命令写到可执行文件之后。通过命令指定要链接的动态库位置,指定静态库位置使用的命令。
| cmake_minimum_required(VERSION 3.0) |
| project(TEST) |
| file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) |
| |
| include_directories(${PROJECT_SOURCE_DIR}/include) |
| |
| link_directories(${PROJECT_SOURCE_DIR}/lib) |
| |
| |
| add_executable(app ${SRC_LIST}) |
| |
| |
| |
| target_link_libraries(app pthread) |
| |
# 日志
CMake
的命令工具会在 stdout
上显示 STATUS
信息,在 stderr
上显示所有信息, CMake
的 GUI 会在它的 log 区域显示所有信息, CMake
警告和错误信息的文本显示使用一种简洁的标记语言,文本没有缩进,超过长度的行会回卷,段落之间以新行作为分隔符
输出信息,但 Cmake
是不会中断的
| message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...) |
- (无) :重要消息
STATUS
:非重要消息WARNING
: CMake
警告,会继续执行AUTHOR_WARNING
: CMake
警告 ( dev
), 会继续执行SEND_ERROR
: CMake
错误,继续执行,但是会跳过生成的步骤FATAL_ERROR
: CMake
非常严重错误, 终止所有处理过程
| |
| message(STATUS "source path: ${PROJECT_SOURCE_DIR}") |
# 变量操作
有时候 源文件
并不一定都在一个目录中,但最后要编译器到一起,通过 set
或 list
命令对变量进行 拼接
# 使用 set 拼接
| set(变量名1 ${变量名} ${变量名2} ... ) |
| |
# 使用 list 拼接
| |
| list(APPEND <list> [<element> ...]) |
| |
| |
| cmake_minimum_required(VERSION 3.0) |
| project(TEST) |
| set(TEMP "hello,world") |
| file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp) |
| file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp) |
| |
| list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP}) |
| message(STATUS "message: ${SRC_1}") |
| |
# 使用 list 移除
REMOVE_ITEM
| list(REMOVE_ITEM <list> <value> [<value> ...]) |
| |
| cmake_minimum_required(VERSION 3.0) |
| project(TEST) |
| set(TEMP "hello,world") |
| file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/*.cpp) |
| |
| message(STATUS "message: ${SRC_1}") |
| |
| list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp) |
| |
| message(STATUS "message: ${SRC_1}") |
# list
使用
| list(LENGTH <list> <output variable>) |
| |
| |
| |
| list(GET <list> <element index> [<element index> ...] <output variable>) |
| |
| |
| |
| |
| |
| |
| list(FIND <list> <value> <output variable>) |
| |
| |
| |
| |
| |
- 将列表中的元素用连接符 (字符串) 连接起来组成一个字符串
| list (JOIN <list> <glue> <output variable>) |
| |
| |
| |
| list (APPEND <list> [<element> ...]) |
| list(INSERT <list> <element_index> <element> [<element> ...]) |
| list (PREPEND <list> [<element> ...]) |
| list (POP_BACK <list> [<out-var>...]) |
| list (REMOVE_ITEM <list> <value> [<value> ...]) |
| list (REMOVE_DUPLICATES <list>) |
| list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>]) |
| |
| STRING: 按照字母顺序进行排序,为默认的排序方法 |
| FILE_BASENAME:如果是一系列路径名,会使用 basename 进行排序 |
| NATURAL:使用自然数顺序排序 |
| CASE:指明是否大小写敏感。有如下几种值可选: |
| SENSITIVE: 按照大小写敏感的方式进行排序,为默认值 |
| INSENSITIVE:按照大小写不敏感方式进行排序 |
| ORDER:指明排序的顺序。有如下几种值可选: |
| ASCENDING: 按照升序排列,为默认值 |
| DESCENDING:按照降序排列 ]] |
# 宏定义
| |
| gcc test.c -DDEBUG -o app |
# 预宏定义列表
宏 | 功能 |
---|
PROJECT_SOURCE_DIR | 使用 cmake 命令后紧跟的目录,一般是工程的根目录 |
PROJECT_BINARY_DIR | 执行 cmake 命令的目录 |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的 CMakeLists.txt 所在的路径 |
CMAKE_CURRENT_BINARY_DIR | target 编译目录 |
EXECUTABLE_OUTPUT_PATH | 重新定义目标二进制可执行文件的存放位置 |
LIBRARY_OUTPUT_PATH | 重新定义目标链接库文件的存放位置 |
PROJECT_NAME | 返回通过 PROJECT 指令定义的项目名称 |
CMAKE_BINARY_DIR | 项目实际构建路径,假设在 build 目录进行的构建,那么得到的就是这个目录的路径 |
# 嵌套 CMake
若项目较大,化繁为简为每个源码目录都添加一个 CMakeLists.txt
文件,使文件不太复杂,更领会,更容易维护
# 节点关系
- Linux 目录是树状结构,最顶层的
CMakeLists.txt
是 根
节点,其次使子节点。 - 根节点
CMakeLists.txt
的变量 全局有效
- 父节点变量可以在子节点中使用
- 子节点变量只能在当前节点中使用
# 添加子目录
| add_subdirectory(source_dir,[binary_dir] [EXCLUDE_FROM_ALL]) |
source_dir
:z 指定 CMakeLists.txt
源文件和代码文件的位置,指定子目录binary_dir
:指定输出文件的路径,一般不需要指定,忽略即可EXCLUDE_FROM_ALL
: 在子路径下默认不会被包含到父路径的 ALL 目录里,并且也会被排除在 IDE
工程文件之外,必须显式构建在子路径下的目标
# 目录结构
| $ tree |
| . |
| ├── build |
| ├── calc |
| │ ├── add.cpp |
| │ ├── CMakeLists.txt |
| │ ├── div.cpp |
| │ ├── mult.cpp |
| │ └── sub.cpp |
| ├── CMakeLists.txt |
| ├── include |
| │ ├── calc.h |
| │ └── sort.h |
| ├── sort |
| │ ├── CMakeLists.txt |
| │ ├── insert.cpp |
| │ └── select.cpp |
| ├── test1 |
| │ ├── calc.cpp |
| │ └── CMakeLists.txt |
| └── test2 |
| ├── CMakeLists.txt |
| └── sort.cpp |
| |
| 6 directories, 15 files |
| cmake_minimum_required(VERSION 3.0) |
| project(test) |
| |
| |
| set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) |
| |
| set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin) |
| |
| set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include) |
| |
| set(CALC_LIB calc) |
| set(SORT_LIB sort) |
| |
| set(APP_NAME_1 test1) |
| set(APP_NAME_2 test2) |
| |
| add_subdirectory(calc) |
| add_subdirectory(sort) |
| add_subdirectory(test1) |
| add_subdirectory(test2) |
| cmake_minimum_required(VERSION 3.0) |
| project(CALCLIB) |
| |
| aux_source_directory(./ SRC) |
| |
| include_directories(${HEAD_PATH}) |
| |
| set(LIBRARY_OUTPUT_PATH ${LIB_PATH}) |
| |
| add_library(${CALC_LIB} STATIC ${SRC}) |
| cmake_minimum_required(VERSION 3.0) |
| project(SORTLIB) |
| aux_source_directory(./ SRC) |
| include_directories(${HEAD_PATH}) |
| set(LIBRARY_OUTPUT_PATH ${LIB_PATH}) |
| |
| add_library(${SORT_LIB} SHARED ${SRC}) |
| cmake_minimum_required(VERSION 3.0) |
| project(CALCTEST) |
| aux_source_directory(./ SRC) |
| |
| include_directories(${HEAD_PATH}) |
| |
| link_libraries(${CALC_LIB}) |
| |
| set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH}) |
| |
| add_executable(${APP_NAME_1} ${SRC}) |
| cmake_minimum_required(VERSION 3.0) |
| project(SORTTEST) |
| aux_source_directory(./ SRC) |
| |
| include_directories(${HEAD_PATH}) |
| set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH}) |
| |
| add_executable(${APP_NAME_2} ${SRC}) |
| |
| target_link_libraries(${APP_NAME_2} ${SORT_LIB}) |
若某个程序某个模块生成的动态库 / 静态库,在 CMakeLists.txt 制定了库的输出路径,其它库需要加载,直接使用即可。
如果没有指定,就需要 link_directories 将库文件路径指定出来
cmake
# 流程控制
| if(<condition>) |
| <commands> |
| elseif(<condition>) |
| <commands> |
| else() |
| <commands> |
| endif() |
| if(<expression>) |
| if(NOT <condition>) |
| if(<cond1> AND <cond2>) |
| if(<cond1> OR <cond2>) |
| |
| if(<variable|string> LESS <variable|string>) |
| if(<variable|string> GREATER <variable|string>) |
| if(<variable|string> EQUAL <variable|string>) |
| if(<variable|string> LESS_EQUAL <variable|string>) |
| if(<variable|string> GREATER_EQUAL <variable|string>) |
| |
| if(<variable|string> STRLESS <variable|string>) |
| if(<variable|string> STRGREATER <variable|string>) |
| if(<variable|string> STREQUAL <variable|string>) |
| if(<variable|string> STRLESS_EQUAL <variable|string>) |
| if(<variable|string> STRGREATER_EQUAL <variable|string>) |
| |
| if(EXISTS path-to-file-or-directory) |
| if(IS_DIRECTORY path) |
| if(IS_SYMLINK file-name) |
| if(IS_ABSOLUTE path) |
| if(<variable|string> IN_LIST <variable>) |
| if(<variable|string> PATH_EQUAL <variable|string>) |
foreach 可以对 items 中的数据进行遍历,通过 loop_var 将遍历到当前的值取出
| foreach(<loop_var> <items>) |
| <commands> |
| endforeach() |
| |
| |
| |
| foreach(<loop_var> RANGE <stop>) |
| |
| foreach(<loop_var> RANGE <start> <stop> [<step>]) |
| |
| |
| |
| set(WORD a b c d) |
| set(NAME ace sabo luffy) |
| |
| |
| foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]]) |
| while(<condition>) |
| <commands> |
| endwhile() |
# 参考资料: