CMake 常用总结一:CMake 单个文件目录
引言
CMake 实践帮助我们对 CMake 有一个系统全面的了解,并且有大量示例以供参考,至少在实际项目中可以让我们有能力看懂并修改项目中现有的 CMake 。
阅读完 CMake 实践文档,认为自己的任务也就结束了,可这样总感觉不是自己的东西,不如整理一下并吸收其中自己认为最有用的东西,这样也能极大的减轻自己的记忆负担。
与此同时 CMake 实践行文组织过于复杂,不方便遇到问题时快速查阅,所以我做了一些调整与总结,希望能够对读者更加友好。
本文不能代替 CMake 实践文档,有时间还是把 CMake 实践文档认真阅读一遍。
CMake 常用总结
CMake 背景
在 C/C++ 开发中,我们常听人聊 CMake,那么 CMake 到底是什么呢 ?——高级编译配置工具。
实际工作中,一个大型项目往往由多个人共同开发维护,其中的文件常常是不计其数,这些文件按类型、功能、模块分别放在若干个目录中,最终要输出一个可执行文件或者共享库(dll,so等等)。那如何实现这一目的呢?这时候神器就出现了—— CMake!所有操作都是通过编译 CMakeLists.txt 来完成。
CMake主要解决两个问题:
大量代码的关系维护
大项目中源代码比较多,手工维护、编译时间长而且编译命令复杂,难以记忆及维护。
把代码维护命令及编译命令写在 CMakeLists.txt 文件中,然后再用 cmake 工具解析此文件自动执行相应命令,可实现代码的合理编译。
减少重复编译时间
在改动其中一个文件的时候,能判断哪些文件被修改过,可以只对该文件进行重新编译,然后重新链接所有的目标文件,节省编译时间。
官方网站是 www.cmake.org,有兴趣的朋友可以通过访问官方网站获得更多关于 CMake的信息。
CMake 安装
关于 CMake 的安装可以参考:Ubuntu中安装Cmake,这里就不过多介绍了。
CMake 单个文件目录
原始工程结构
CMake 简单实例
先来一个简单地 CMake 实例,对 CMake 有一个简单、直接的了解!
创建一个 .cpp 文件,并命名为 main。
// # main.cpp#include <iostream>int main() {std::cout << "hello world" << std::endl; }
创建 CMakeLists.txt 文件(目前不需要了解具体内容,下文会详细介绍)。
# CMakeLists.txtPROJECT (HELLO) SET(SRC_LIST main.cpp)MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR}) MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})ADD_EXECUTABLE(hello ${SRC_LIST})
使用 cmake,生成 makefile 文件。
CMake 操作成功后,当前目录下会生成相应目录与文件,包括:CMakeFiles 目录,CMakeCache.txt 文件,cmake_install.cmake 文件,并且生成了Makefile。
现在不需要理会这些文件的作用,以后你也可以不去理会。最关键的是,它自动生成了Makefile,如下所示:
yxm@192:~/myshare/test1$ cmake . CMake Warning (dev) in CMakeLists.txt:Syntax Warning in cmake code at/home/yxm/myshare/CMakeLists.txt:9:37Argument not separated from preceding token by whitespace. This warning is for project developers. Use -Wno-dev to suppress it.-- The C compiler identification is GNU 5.4.0 -- The CXX compiler identification is GNU 5.4.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- This is BINARY dir /home/yxm/myshare -- This is SOURCE dir /home/yxm/myshare -- Configuring done -- Generating done -- Build files have been written to: /home/yxm/myshare yxm@192:~/myshare$ ls CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt main.cpp Makefile
cmake . 后面的点号代表本目录
使用 make 命令编译 Makefile 文件。
编译成功后,将会生成可执行文件,本案例中将生成 Hello 的可执行程序。
yxm@192:~/myshare/test1$ make Scanning dependencies of target hello [100%] Building CXX object CMakeFiles/hello.dir/main.cpp.o Linking CXX executable hello [100%] Built target hello yxm@192:~/myshare$ ls CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt hello main.cpp Makefile
运行程序获得运行结果。
yxm@192:~/myshare/test1$ ./hello hello world
CMake 语法
(1)PROJECT 语法
指令:PROJECT
语法:PROJECT(projectname [CXX] [C] [Java])
说明:用于指定⼯程名称,并可指定工程支持的语⾔(支持的语⾔列表可以忽略,默认支持所有语言) 。 这个指令隐式的定义了两个 CMake 变 量 :
<projectname>_BINARY_DIR:本例中是 HELLO_BINARY_DIR
<projectname>_SOURCE_DIR :本例中是 HELLO_SOURCE_DIR
问题:如果改了工程名,这两个变量名也会改变?
CMake 帮我们预定义 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR 变量。建议使用这两个变量,即使修改了⼯程名称,也不会影响这 两个变量。如果使⽤了<projectname>_SOURCE_DIR,修改⼯程名称后,需要同时修改这些变量。所以上文例子中 CMakeLists.txt 文件可以修改为:
MESSAGE(STATUS "This is BINARY dir " ${PROJECT_BINARY_DIR}) MESSAGE(STATUS "This is SOURCE dir " ${PROJECT_SOURCE_DIR})
(2)SET 语法
- 指令:SET
- 语法:SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
- 说明:⽤来显式的定义变量,⽐如 :
- 如果有单个源⽂件,可以定义成 SET(SRC_LIST main.c),SRC_LIST 变量就代表 main.cpp;
- 如果有多个源⽂件,也可以定义成 SET(SRC_LIST main.c t1.c t2.c),多个文件之间可以用空格或者分号来进行分割。
(3)MESSAGE 语法
指令:MESSAGE
语法:MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display” …)
说明:⽤于向终端输出⽤户定义的信息,它包含了三种类型:
SEND_ERROR:产⽣错误,⽣成过程被跳过。
STATUS:输出前缀为 – 的信息。
FATAL_ERROR:立即终止所有cmake过程。
(4)ADD_EXECUTABLE 语法
指令:ADD_EXECUTABLE
语法:ADD_EXECUTABLE([BINARY] [SOURCE_LIST])
说明:用于⽣成⼀个⽂件名为 [BINARY] 可执⾏⽂件 , 相关的源⽂件是 SOURCE_LIST 中定义的源⽂件列表。
ADD_EXECUTABLE(hello ${SRC_LIST}) 即生成的可执行文件名是hello,源文件读取变量SRC_LIST中的内容,也可以直接写 ADD_EXECUTABLE(hello main.cpp)
注意:工程名的 HELLO 和生成的可执行文件 hello 是没有任何关系的
CMake 语法的基本原则
(1)语法本原则
变量使用 ${} 方式取值,但是在 IF 控制语句中是直接使用变量名
指令(参数 1 参数 2…) 参数使用括弧括起,参数之间使用空格或分号分开。
以上面的 ADD_EXECUTABLE 指令为例,如果存在另外一个 func.cpp 源文件就要写成:ADD_EXECUTABLE(hello main.cpp func.cpp) 或者 ADD_EXECUTABLE(hello main.cpp;func.cpp)
指令是大小写无关的,参数和变量是大小写相关的。但推荐你全部使用大写指令
(2)语法注意事项
- SET(SRC_LIST main.cpp) 可以写成 SET(SRC_LIST “main.cpp”),如果源文件名中含有空格,就必须要加双引号。
- ADD_EXECUTABLE(hello main) 后缀可以不写,他会自动去找 .c 和 .cpp,最好不要这样写,可能会有这两个文件main.cpp和main。
(3)生产debug版本的方法
cmake … -DCMAKE_BUILD_TYPE=debug
生成可调试的版本之后,可以使用gdb工具对程序进行调试。
CMake 内部构建问题
上面的例子是单个文件目录结构,展示的是内部构建,即通过内部编译进行工程构建。这种方式会产生冗余的临时文件(中间文件),比如CMakeFiles 目录,CMakeCache.txt 文件,cmake_install.cmake 文件,这些文件占用空间、打乱原有目录结构且不方便清理。
改进工程结构
CMake 外部构建
如上文所述,对于 CMake而言,内部编译生成了一些无法自动删除的中间文件,所以引出了我们对外部编译的探讨。
外部构建,即通过外部编译进行工程构建,会把生成的临时文件放在build目录下,不会对原有的工程有任何影响,所有动作全部发生在编译目录,所以推荐大家使用外部构建方式,如下所示:
创建以下工程结构
yxm@192:~/myshare/test2$ tree . ├── build ├── CMakeLists.txt └── main.cpp
main.cpp 与上面的 main.cpp 内容相同。
进入 build,运行 cmake …(…表示上一级目录,也可以写CMakeLists.txt所在的绝对路径),产生的中间文件都在 build 目录下。
在 build 目录下,运行 make 命令编译 Makefile 文件。
注意外部构建的两个变量
- HELLO_SOURCE_DIR 还是工程路径,即 ~/myshare/test2
- HELLO_BINARY_DIR 则是编译路径,也就是 ~/myshare/test/bulid
CMake 工程结构的完善
实际工程中还会存在许多非源码文件,如何规范的放置这些文件呢?
- 为工程添加一个子目录 src,用来放置工程源代码
- 添加一个子目录 doc,用来放置这个工程的文档 hello.txt
- 在工程目录添加文本文件 COPYRIGHT(版权),README(看我的信息)
- 在工程目录添加一个 runhello.sh 脚本,用来调用 hello 二进制
- 将构建后的目标文件放入构建目录的 bin 子目录
- 将 doc 目录 的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/
(1)将目标文件放入构建目录的 bin 子目录
补充工程结构:每个目录下都要有一个CMakeLists.txt 说明。
yxm@192:~/myshare/test2$ tree . ├── build ├── CMakeLists.txt └── src├── CMakeLists.txt└── main.cpp
外层 CMakeLists.txt 内容如下:
PROJECT(HELLO) ADD_SUBDIRECTORY(src bin) # 详细见下文 CMake 语法中第一条
src 下的 CMakeLists.txt 内容如下:
ADD_EXECUTABLE(hello main.cpp)
外部编译过程:
- 进入 build,运行 cmake …
- 在 build 目录下,运行 make 命令编译 Makefile 文件,生成的可执行文件存放在 src/bin 目录下。
(2)换个地方保存目标二进制
SET 指令可以重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(本例中指最终生成的 hello 或者最终的共享库,不包含编译生成的中间文件)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
所以,上面两个指令分别定义了:可执行二进制的输出路径为build/bin 和库的输出路径为build/lib。本节我们没有提到共享库和静态库的构建,所以,你可以不考虑第二条指令。
(3)修改 CMake 支持安装
支持安装需要使用 CMake 一个新的指令:INSTALL;还需要使用 CMake 一个新的变量:CMAKE_INSTALL_PREFIX,该变量用于指定安装路径( 详细可以看下文 CMake 语法)。
补充工程结构:
yxm@192:~/myshare/test2$ tree . ├── build ├── CMakeLists.txt ├── COPYRIGHT ├── doc │ └── hello.txt ├── README ├── runhello.sh └── src├── CMakeLists.txt└── main.cpp
安装文件 COPYRIGHT (版权) 和 README (说明)
修改外层CMakeLists.txt,添加 INSTALL 指令
PROJECT(HELLO) ADD_SUBDIRECTORY(src bin) INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
FILES:文件
DESTINATION:路径
- 可以写绝对路径
- 可以写相对路径,实际路径是:${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>,CMAKE_INSTALL_PREFIX 默认是在 /usr/local/,所以 INSTALL 中的share/doc/cmake/ 就相当于/usr/local/share/doc/cmake/
安装脚本 runhello.sh
修改外层CMakeLists.txt,添加 INSTALL 指令
PROJECT(HELLO) ADD_SUBDIRECTORY(src bin) INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/) INSTALL(PROGRAMS runhello.sh DESTINATION bin)
PROGRAMS:非目标可执行程序安装 (比如脚本之类)
DESTINATION:参考上文第二点,上面实际应该安装到/usr/local/bin
安装目录 doc 以及目录中的 hello.txt,有两种方式:
一种是通过在 doc 目录建立CMakeLists.txt ,通过install下的file
另一种是直接在工程目录通过
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
修改外层CMakeLists.txt,添加 INSTALL 指令
PROJECT(HELLO) ADD_SUBDIRECTORY(src bin) INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/) INSTALL(PROGRAMS runhello.sh DESTINATION bin) INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
DIRECTORY 后面连接的是所在 Source 目录的相对路径。注意:abc 和 abc/ 区别:
- 目录名不以/结尾:这个目录将被安装为目标路径下的
- 目录名以/结尾:将这个目录中的内容安装到目标路径
DESTINATION:参考上文第二点
外部编译过程:
进入 build,运行 cmake …
在 build 目录下,运行 make 命令编译 Makefile 文件,生成的可执行文件存放在 src/bin 目录下。
在 build 目录下安装 make install,将安装一下文件:
安装有以下几种方法:
- 一种是从代码编译后直接 make install 安装,本例子就是使用这种方法
- 一种是打包时的指定目录安装。
- 简单的可以这样指定目录:make install DESTDIR=/tmp/test
- 稍微复杂一点可以这样指定目录:./configure –prefix=/usr
CMake 语法
(1)DCMAKE_INSTALL_PREFIX 语法
CMAKE_INSTALL_PREFIX 该变量用于表示传递安装⽬录,默认的安装目录是 /usr/local/;
也可以在 cmake 的时候指定CMAKE_INSTALL_PREFIX 变量的路径,如下:
cmake -DCMAKE_INSTALL_PREFIX=/usr # -D之后加不加空格都可
(2)ADD_SUBDIRECTORY 语法
指令:ADD_SUBDIRECTORY
语法:ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
说明:用于向当前⼯程添加存放源⽂件的⼦⽬录,并可以指定中间⼆进制和⽬标⼆进制存放的位置。
EXCLUDE_FROM_ALL参数的含义是将这个⽬录从编译过程中排除,⽐如⼯程的example,可能就需要⼯程构建完成后,再进⼊example⽬录单独进⾏构建(当然,你也可以通过定义依赖来解决此类问题) 。
上文中 ADD_SUBDIRECTORY(src bin) 是将 src 子目录加入工程并指定编译输出(包含编译中间结果)路径为 bin 目录;如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在build/src 目录。
(3)INSTALL 语法
INSTALL 指令⽤于定义安装规则,安装的内容可以包括⽬标⼆进制、动态库、静态库以及⽂件、⽬录、脚 本等。INSTALL 指令包含了各种安装类型,我们需要⼀个个分开解释:
类型:⽬标⽂件
语法:INSTALL(TARGETS targets…
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions…]
[CONFIGURATIONS
[Debug|Release|…]]
[COMPONENT <component>]
[OPTIONAL]
] […])
说明:参数中的 TARGETS 后⾯跟的就是我们通过 ADD_EXECUTABLE 或者ADD_LIBRARY 定义的⽬标⽂件,可能是可执⾏⼆进制、动态库、静态库。
⽬标类型也就相对应的有三种,ARCHIVE特指静态库,LIBRARY特指动态库,RUNTIME特指可执⾏⽬标⼆进制。
DESTINATION定义了安装的路径。
类型:普通⽂件
语法:INSTALL(FILES files… DESTINATION <dir>
[PERMISSIONS permissions…]
[CONFIGURATIONS [Debug|Release|…]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
说明:可⽤于安装⼀般⽂件,并可以指定访问权限,⽂件名是此指令所在路径下的相对路径。如果默认 不 定 义 权 限 PERMISSIONS , 安 装 后 的 权 限 为 : OWNER_WRITE, OWNER_READ, GROUP_READ,和WORLD_READ,即644权限。
类型:⾮⽬标⽂件的可执⾏程序(如脚本之类)
语法:INSTALL(PROGRAMS files… DESTINATION <dir>
[PERMISSIONS permissions…]
[CONFIGURATIONS [Debug|Release|…]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
说明:跟上⾯的 FILES 指令使⽤⽅法⼀样 , 唯⼀的不同是安装后权限为 : OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE,即755权限。
类型:⽬录
语法:INSTALL(DIRECTORY dirs… DESTINATION <dir>
[FILE_PERMISSIONS permissions…]
[DIRECTORY_PERMISSIONS permissions…]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|…]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions…]] […])
说明:主要介绍其中的DIRECTORY、PATTERN和PERMISSIONS参数:
- DIRECTORY:后⾯连接的是所在Source⽬录的相对路径。
- PATTERN:⽤于使⽤正则表达式进⾏过滤。
- PERMISSIONS:⽤于指定PATTERN过滤后的⽂件权限。
CMake 常用总结一:CMake 单个文件目录相关推荐
- cmake 常用变量和常用环境变量查表手册
cmake 常用变量和常用环境变量查表手册 一,cmake 变量引用的方式: 前面我们已经提到了,使用${}进行变量的引用.在 IF 等语句中,是直接使用变量名而不通过${}取值 二,cmake 自定 ...
- 【Tools】cmake 常用变量和常用环境变量查表手册---整理
原文链接:https://blog.csdn.net/gubenpeiyuan/article/details/8667279 一.cmake 变量引用的方式: 前面我们已经提到了,使用${}进行变量 ...
- cmake 常用变量和常用环境变量
一,cmake 变量引用的方式: 前面我们已经提到了,使用${}进行变量的引用.在 IF 等语句中,是直接使用变量名而不通过${}取值 二,cmake 自定义变量的方式: 主要有隐式定义和显式定义两种 ...
- 【使用CMake组织C++工程】2:CMake 常用命令和变量
前言 前面的文章介绍了一个最简单的CMake工程,这篇文章将介绍一个稍微复杂一些的CMake工程,结合这个工程总结一下在组织一个C/C++工程时最为常用的一些CMake命令和变量.对于涉及到的命令和变 ...
- CMake 常用的预定义变量
CMake 常用的预定义变量 PROJECT_NAME : 通过 project() 指定项目名称 PROJECT_SOURCE_DIR : 工程的根目录 PROJECT_BINARY_DIR : 执 ...
- CMake常用命令整理
CMake常用命令整理 转自:https://zhuanlan.zhihu.com/p/315768216 CMake 是什么我就不用再多说什么了,相信大家都有接触才会看一篇文章.对于不太熟悉的开发人 ...
- CMake 常用命令和变量
前言 前面的文章介绍了一个最简单的CMake工程,这篇文章将介绍一个稍微复杂一些的CMake工程,结合这个工程总结一下在组织一个C/C++工程时最为常用的一些CMake命令和变量.对于涉及到的命令和变 ...
- cmake学习笔记(2)--CMake常用的预定义变量
cmake常用的预定义变量不多,根据经验掌握如下几个就基本上够用了: PROJECT_NAME : 通过 project() 指定项目名称 PROJECT_SOURCE_DIR : 工程的根目录 PR ...
- cmake常用语法参考
<cmake常用语法参考> 利用cmake来构建C++工程是一个非常方便的选择,尤其是依赖的库比较多的时候,或者工程比较大的时候都非常方便,这里记录一些cmake常用的语法, Key ...
最新文章
- python判断密码是否正确_第一个python程序-判断登陆用户名和密码是否正确
- 国家服务器1eb硬盘,仅一个月:奇亚币已占用超过1EB存储空间
- boost::gil::view_is_mutable用法的测试程序
- Linux下svn新建用户,Linux下建立svn工程
- 在SAP Business Application Studio里创建Fiori应用的操作流程
- 流量节省模式 Android,这三种方法让你节省更多手机上网流量
- java处理中文字符串_Java实现读取文章中重复出现的中文字符串
- STM32位带区和位带别名区的浅谈
- mac版mysql 1290_Mac 终端下mysql load data infile 文件路径 into table 表名称 操作,[Error Code] 1290 - The MyS...
- 微信小程序如何零成本获客
- 如何查看浏览器Cookie数据(以360为例)
- java课程设计模拟科学计算器_JAVA课程设计科学计算器
- 练习5:MySQL数据插入、更新与删除
- qt中glMultiTexCoord2fARB报错
- numpy矩阵升维,拼接
- SAP JCo业务情景:在线发票
- Linux里面的oa环境是什么,Linux下oa环境搭建
- 小花梨的字符串 ——java 美登杯
- 北大生命学院邓宏魁课题组最新成果:建立全新胰岛移植策略,解决干细胞治疗糖尿病的关键难题...
- 库卡工业机器人负载曲线图_DC电机性能曲线图
热门文章
- 小米5x html,小米5X是什么接口_小米5X充电接口是什么-太平洋IT百科
- PPT制作——快捷键
- GNSS+IMU+MM车载高精度组合导航定位系统
- java怎么输入字符_java怎么输入一个字符
- 信息搜集 - 二层发现 Nmap
- 怎样用命令行方式添加打印机端口? (已解决)
- 游戏里常见的动态难度调节机制(DDA, dynamic difficulty adjustment)
- Cplex学术版安装+anaconda
- LINUX开机卡文件系统检查,Linux开机提示:timed out waiting for device dev-vdb1.device_Linux文件系统检查的依赖关系失败...
- 〖全域运营实战白宝书 - 高转化文案速成篇②〗- 快速找到产品卖点的N个小技巧