引言
CMake学习笔记
0 CMake
- cmake:高级编译配置工具:当多个人用不同的语言或者编译器开发一个项目,最终要输出一个可执行文件或者共享库,所有的操作通过编译CMakeLists.txt完成
1 CMake一个HelloWord
- 编写cpp程序
- 写CMakeLists.txt
- 使用cmake,生成makefile文件
- 使用make编译
- 最终生成了Hello的可执行程序
1 | // 1. main.cpp |
2 CMake语法介绍
2.1 PROJECT关键字
- 指定工程的名字和支持的语言,默认支持所有语言
- PROJECT (HELLO)
- 指定了工程的名字,并且支持所有语言(建议)
- PROJECT (HELLO CXX)
- 指定了工程的名字,并且支持语言是C++
- PROJECT (HELLO C CXX)
- 指定了工程的名字,并且支持语言是C和C++
- PROJECT (HELLO)
- 该指令隐式定义了两个CMAKE的变量
- <projectname>_BINARY_DIR,本例中是 HELLO_BINARY_DIR
- <projectname>_SOURCE_DIR,本例中是 HELLO_SOURCE_DIR
- MESSAGE关键字可以直接使用者两个变量
- 问题:如果改了工程名,这两个变量名也会改变
- 解决:定义两个预定义变量:PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,这两个变量和HELLO_BINARY_DIR,HELLO_SOURCE_DIR是一致的
2.2 SET关键字
- 显示的指定变量的
- SET(SRC_LIST main.cpp) SRC_LIST变量就包含了main.cpp
- SET(SRC_LIST main.cpp t1.cpp t2.cpp)
2.3 MESSAGE关键字
- 向终端输出用户自定义的信息
- 主要包含三种信息:
- SEND_ERROR, 产生错误,生成过程被跳过
- SATUS, 输出前缀为—的信息
- FATAL_ERROR, 立即终止所有 cmake 过程
2.4 ADD_EXECUTABLE关键字
- 生成可执行文件
- ADD_EXECUTABLE(hello ${SRC_LIST}), 生成的可执行文件名是hello,源文件读取变量SRC_LIST中的内容
- 也可以直接写 ADD_EXECUTABLE(hello main.cpp)
- ADD_EXECUTABLE(hello ${SRC_LIST}), 生成的可执行文件名是hello,源文件读取变量SRC_LIST中的内容
1 | // 上面的CMakeLists.txt可简化写为 |
- 注意:工程名的 HELLO 和生成的可执行文件 hello 没有任何关系
2.5 语法的基本原则
- 变量使用${}方式取值
- 但在 IF 控制语句中是直接使用变量名
- 指令(参数 1 参数 2…) 参数使用括弧括起,参数之间使用空格或分号分开
- 例子 ADD_EXECUTABLE(hello main.cpp func.cpp)或者ADD_EXECUTABLE(hello main.cpp;func.cpp)
- 指令是大小写无关的,参数和变量是大小写相关的
- 推荐全部使用大写
- 注意事项
- SET(SRC_LIST main.cpp) 可以写成 SET(SRC_LIST “main.cpp”)
- 如果源文件名中含有空格,就必须要加双引号
- ADD_EXECUTABLE(hello main) 不写后缀会自动去找.c和.cpp(不推荐)
- SET(SRC_LIST main.cpp) 可以写成 SET(SRC_LIST “main.cpp”)
3 内部构建和外部构建
- 上述例子就是内部构建,生产的临时文件特别多,不方便清理
- 外部构建会把生成的临时文件放在build目录下,不会对源文件有任何影响
- 强烈使用外部构建方式
3.1 外部构建方式举例
- 建立一个build目录,可以在任何地方(建议在当前目录下)
- 进入build目录, 运行
cmake ....表示上一级目录,也可以写CMakeLists.txt所在的绝对路径,生产的文件都在build目录下
- 在build目录下,运行make来构建工程
- 注意外部构建的两个变量
- HELLO_SOURCE_DIR 工程路径
- HELLO_BINARY_DIR 编译路径(build目录)
3.2 将目标文件放入构建目录的 bin 子目录
1 | // 项目的结构 |
- 每个目录下都要有一个CMakeLists.txt说明
1 | // 外层CMakeLists.txt |
3.2.1 ADD_SUBDIRECTORY 指令
-
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
-
用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置
- EXCLUDE_FROM_ALL函数是将写的目录从编译中排除,如程序中的example
- ADD_SUBDIRECTORY(src bin)
- 将 src 子目录加入工程并指定编译输出(包含编译中间结果)路径为bin 目录
- 如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在build/src 目录
3.2.2 更改二进制的保存路径
- 指定最终的目标二进制的位置: SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
- 哪里要改变目标存放路径,就在哪里加入上述的定义,所以应该在src下的CMakeLists.txt下写
4 安装
- 一种是从代码编译后直接 make install 安装
- 一种是从打包时的指定目录进行安装
- 简单的可以这样指定目录:make install DESTDIR=/tmp/test
- 稍微复杂一点可以这样指定目录:./configure –prefix=/usr
- CMake语法新内容:
- INSTALL可以安装:二进制、动态库、静态库以及文件、目录、脚本等
- CMAKE一个新的变量:CMAKE_INSTALL_PREFIX
4.1安装文件COPYRIGHT和README
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)- FILES:文件
- DESTINATION:
- 可以写绝对路径
- 可以写相对路径,相对路径实际路径是:
${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>- CMAKE_INSTALL_PREFIX 默认是在 /usr/local/
cmake -DCMAKE_INSTALL_PREFIX=/usr 在cmake的时候指定CMAKE_INSTALL_PREFIX变量的路径
4.2 安装脚本runhello.sh
- PROGRAMS:非目标文件的可执行程序安装(比如脚本之类)
INSTALL(PROGRAMS runhello.sh DESTINATION bin)- 实际安装到的是 /usr/bin
4.3 安装 doc 中的 hello.txt
- 在 doc 目录建立CMakeLists.txt ,通过install下的file
- 直接在工程目录通过
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)- DIRECTORY 后面连接的是所在 Source 目录的相对路径
- abc 和 abc/有很大的区别
- 目录名不以/结尾:这个目录将被安装为目标路径下的
- 目录名以/结尾:将这个目录中的内容安装到目标路径
4.4 安装过程
1 | cmake .. |
5 静态库和动态库的构建
-
任务
- 建立一个静态库和动态库,提供 HelloFunc 函数供其他程序编程使用,HelloFunc 向终端输出 Hello World 字符串
- 安装头文件与共享库
-
静态库和动态库的区别
- 静态库的扩展名一般为“.a”或“.lib”;动态库的扩展名一般为“.so”或“.dll”。
- 静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行
- 动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行。
1 | // ------- |
5.1 同时构建静态和动态库
1 | // 静态库的后缀是.a |
5.1.1 ADD_LIBRARY
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})- hello:正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
- SHARED, 动态库 STATIC, 静态库
- ${LIBHELLO_SRC} :源文件
5.1.2 SET_TARGET_PROPERTIES
- 可以用来设置输出的名称
- 对于动态库,还可以用来指定动态库版本和 API 版本
1 | // ------------------ |
5.1.3 动态库的版本号
- 一般动态库都有一个版本号的关联
libhello.so.1.2libhello.so ->libhello.so.1libhello.so.1->libhello.so.1.2
- CMakeLists.txt 插入的内容
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)- VERSION 指代动态库版本,SOVERSION 指代 API 版本
5.2 安装共享库和头文件
- 将 hello 的共享库安装到<prefix>/lib目录
- 将 hello.h 安装到<prefix>/include/hello 目录
1 | //文件放到该目录下 |
- 注意:安装的时候,指定一下路径,放到系统下:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
5.3 使用外部共享库和头文件
- 新建一个目录来使用外部共享库和头文件
1 | // 项目结构 |
5.3.1 解决:make后头文件找不到的问题
PS:include <hello/hello.h> 这样include是可以,这么做的话,就没啥好讲的了
关键字:INCLUDE_DIRECTORIES 这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割
在CMakeLists.txt中加入头文件搜索路径
INCLUDE_DIRECTORIES(/usr/include/hello)
5.3.2 解决:找到引用的函数问题
- 报错信息:undefined reference to `HelloFunc()’
- 关键字:LINK_DIRECTORIES 添加非标准的共享库搜索路径
- 指定第三方库所在路径,LINK_DIRECTORIES(/home/myproject/libs)
- 关键字:TARGET_LINK_LIBRARIES 添加需要链接的共享库
- 使用TARGET_LINK_LIBRARIES的时候,只需要给出动态链接库的名字就行了
- 在CMakeLists.txt中插入链接共享库,主要要插在executable的后面
- 链接静态库:
TARGET_LINK_LIBRARIES(main libhello.a)
5.3.3 特殊的环境变量
- CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH
- 环境变量而不是 cmake 变量
- 指明include路径的方法(两种方法)
- 使用了绝对路径INCLUDE_DIRECTORIES(/usr/include/hello)来指明include路径的位置
- 使用环境变量export CMAKE_INCLUDE_PATH=/usr/include/hello
6 其他内容
- 生产debug版本的方法:
cmake .. -DCMAKE_BUILD_TYPE=debug
1 | <!-- 遍历src下的所有cpp文件的main函数进行构建 --> |