CMakeCMakeList.txt
1、各种关系
在各种开源项目中,经常会发现项目中除了代码源文件,还包含了 CMakeList.txt、 Makefile 文件,在项目的编译时候需要用到的命令有 cmake 、 make。我们本次想搞清楚他们之前的关系以及CMakeList的语法规则。
正常情况下,我们编写程序的大体流程为:
1)用编辑器(vim、emacs等)编写源代码文件(.h、.cpp等);
2)用编译器编译代码生成目标文件(.o等);
3)用链接器连接目标文件生成可执行文件(.exe等)。
一个程序在编写时,可能需要编写很多的代码文件,以及依赖很多第三方的库。在这种情况下,手动依次编译每个文件会变的非常麻烦,效率低下。
make 是一个自动化的批量编译工具,可以实现用一个命令构建整个工程的目的。但是其执行需要依赖一个规则文件,这个文件就是 Makefile。 Makefile 文件里详细描述了构建的细节(文件的依赖关系,编译的先后顺序等)。
对于一个大工程来说,编写 Makefile 文件也是一项非常复杂的事情。
cmake(Cross-platform Make)是一个可以自动生成 Makefile 文件的工具,当然它不只能生成 Makefile ,还能跨平台生成主流IDE(VS, xcode…)构建工程所需的 project 文件。 cmake 的执行同样需要依赖规则文件,这个文件就是 CMakeLists.txt 。
所以,为了能够把一堆c,cc,cpp,h,hpp代码变成可运行的库或者程序,核心还是Makefile,CMakefiles.txt纯粹是为了降低不懂Unix体系的人去编译C/C++代码做的一个自动系统和环境检测和设置工具,同时帮你生成Makefile。
综上所述,大致可以总结 cmake, make, CMakeList.txt, Makefile 之间的关系,如下图所示:
2、CMakeList.txt规则
鉴于目前在开发中,主要遇到的是使用CMake和CMakeList.txt使用的问题,重点介绍下CMakeList.txt的编写规则。
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
CMake的所有的语句都写在一个叫:CMakeLists.txt的文件中。当CMakeLists.txt文件确定后,可以用ccmake命令对相关 的变量值进行配置。这个命令必须指向CMakeLists.txt所在的目录。配置完成之后,应用cmake命令生成相应的makefile(在Unix like系统下)或者 project文件(指定用window下的相应编程工具编译时)
2.1 常用命令
2.11 指定 cmake 的最小版本
这行命令是可选的,可以不写这句话,但在有些情况下,如果 CMakeLists.txt 文件中使用了一些高版本 cmake 特有的一些命令的时候,就需要加上这样一行,提醒用户升级到该版本之后再执行 cmake。
cmake_minimum_required(VERSION 3.4.1)
2.12 设置项目名称
这个命令不是强制性的,但最好都加上。它会引入两个变量 demo_BINARY_DIR 和 demo_SOURCE_DIR,同时,cmake 自动定义了两个等价的变量 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR。
project(demo)
2.13 设置编译类型
add_executable(demo test.cpp) # 生成可执行文件add_library(common STATIC test.cpp) # 生成静态库add_library(common SHARED test.cpp) # 生成动态库或共享库
add_library 默认生成是静态库,通过以上命令生成文件名字
在 Linux /Mac下是:
demo
libcommon.a
libcommon.so
在 Windows 下是:
demo.exe
common.lib
common.dll
2.14 执行编译包含的源文件
2.14.1 明确指定包含哪些源文件
源文件少时可以这么做
add_library(demo demo.cpp test.cpp util.cpp)
2.14.2 搜索所有的 cpp 文件
aux_source_directory(dir VAR) 发现一个目录下所有的源代码文件并将列表存储在一个变量中。
aux_source_directory(. SRC_LIST) # 搜索当前目录下的所有.cpp文件,存储在变量SRC_LIST中add_library(demo ${SRC_LIST})
2.14.3 自定义搜索规则
file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp") #查找当前目录下、及protocol目录下所有cpp文件,保存在变量SEC_LIST中add_library(demo ${SRC_LIST})# 或者file(GLOB SRC_LIST "*.cpp") #查找当前目录下所有cpp文件,保存在变量SRC_LIST中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})
2.15 查找指定的库文件
ind_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 )
2.16 设置包含的目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}${CMAKE_CURRENT_BINARY_DIR}${CMAKE_CURRENT_SOURCE_DIR}/include)
Linux和Mac下还可以通过如下方式设置包含的目录
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}")
2.17 设置链接库搜索目录
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs)
Linux和Mac下还可以通过如下方式设置链接库搜索目录
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CURRENT_SOURCE_DIR}/libs")
2.18 设置 target 需要链接的库
target_link_libraries( # 目标库demo# 目标库需要链接的库# log-lib 是上面 find_library 指定的变量名${log-lib} )
在 Windows 下,系统会根据链接库目录,搜索xxx.lib 文件,Linux 下会搜索 xxx.so 或者 xxx.a 文件,如果都存在会优先链接动态库(so 后缀)
2.18.1 指定链接静态库或动态库
target_link_libraries(demo libface.a) # 链接libface.atarget_link_libraries(demo libface.so) # 链接libface.so
2.18.2 指定全路径
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a)
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.so)
2.18.3 指定链接多个库
target_link_libraries(demo${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.aboost_system.aboost_threadpthread)
2.19 设置变量
2.19.1 set直接设置变量的值
set(SRC_LIST main.cpp test.cpp) # 两个cpp文件,存储在变量SRC_LIST中add_executable(demo ${SRC_LIST})
2.19.2 set追加设置变量的值
set(SRC_LIST main.cpp)set(SRC_LIST ${SRC_LIST} test.cpp)add_executable(demo ${SRC_LIST})
2.19.3 list追加或删除变量的值
set(SRC_LIST main.cpp)list(APPEND SRC_LIST test.cpp)list(REMOVE_ITEM SRC_LIST main.cpp)add_executable(demo ${SRC_LIST})
2.110 条件控制
2.110.1 if…elseif…else…endif
逻辑判断和比较:if (expression):expression 不为空(0,N,NO,OFF,FALSE,NOTFOUND)时为真if (not exp):与上面相反if (var1 AND var2)if (var1 OR var2)if (COMMAND cmd):如果 cmd 确实是命令并可调用为真if (EXISTS dir) if (EXISTS file):如果目录或文件存在为真if (file1 IS_NEWER_THAN file2):当 file1 比 file2 新,或 file1/file2 中有一个不存在时为真,文件名需使用全路径if (IS_DIRECTORY dir):当 dir 是目录时为真if (DEFINED var):如果变量被定义为真if (var MATCHES regex):给定的变量或者字符串能够匹配正则表达式 regex 时为真,此处 var 可以用 var 名,也可以用 ${var}if (string MATCHES regex)数字比较:if (variable LESS number):LESS 小于if (string LESS number)if (variable GREATER number):GREATER 大于if (string GREATER number)if (variable EQUAL number):EQUAL 等于if (string EQUAL number)字母表顺序比较:if (variable STRLESS string)if (string STRLESS string)if (variable STRGREATER string)if (string STRGREATER string)if (variable STREQUAL string)if (string STREQUAL string)
2.1100.2 while .. endwhile
while(condition)...endwhile()
2.110.3 foreach…endforeach
foreach(loop_var RANGE start stop [step])...endforeach(loop_var)foreach(i RANGE 1 9 2) # 从1到9,步长为2message(${i})endforeach(i)# 输出:13579
2.111 打印信息
message(${PROJECT_SOURCE_DIR})message("build with debug mode")message(WARNING "this is warnning message")message(FATAL_ERROR "this build has many error") # FATAL_ERROR 会导致编译失败
2.112 包含其他cmake文件
include(./common.cmake) # 指定包含文件的全路径include(def) # 在搜索路径中搜索def.cmake文件set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # 设置include的搜索路径
2.2 常用变量
2.21 预定义变量
PROJECT_SOURCE_DIR:工程的根目录
PROJECT_BINARY_DIR:运行 cmake 命令的目录,通常是 ${PROJECT_SOURCE_DIR}/build
PROJECT_NAME:返回通过 project 命令定义的项目名称
CMAKE_CURRENT_SOURCE_DIR:当前处理的 CMakeLists.txt 所在的路径
CMAKE_CURRENT_BINARY_DIR:target 编译目录
CMAKE_CURRENT_LIST_DIR:CMakeLists.txt 的完整路径
CMAKE_CURRENT_LIST_LINE:当前所在的行
CMAKE_MODULE_PATH:定义自己的 cmake 模块所在的路径,SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块
EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH:重新定义目标链接库文件的存放位置
2.22 环境变量
使用环境变量
$ENV{Name}
写入环境变量
set(ENV{Name} value) # 这里没有“$”符号
2.23 系统变量
CMAKE_MAJOR_VERSION:cmake 主版本号,比如 3.4.1 中的 3
CMAKE_MINOR_VERSION:cmake 次版本号,比如 3.4.1 中的 4
CMAKE_PATCH_VERSION:cmake 补丁等级,比如 3.4.1 中的 1
CMAKE_SYSTEM:系统名称,比如 Linux-2.6.22
CMAKE_SYSTEM_NAME:不包含版本的系统名,比如 Linux
CMAKE_SYSTEM_VERSION:系统版本,比如 2.6.22
CMAKE_SYSTEM_PROCESSOR:处理器名称,比如 i686
UNIX:在所有的类 UNIX 平台下该值为 TRUE,包括 OS X 和 cygwin
WIN32:在所有的 win32 平台下该值为 TRUE,包括 cygwin
2.24 主要开关选项
BUILD_SHARED_LIBS:这个开关用来控制默认的库编译方式,如果不进行设置,使用 add_library 又没有指定库类型的情况下,默认编译生成的库都是静态库。如果 set(BUILD_SHARED_LIBS ON) 后,默认生成的为动态库
CMAKE_C_FLAGS:设置 C 编译选项,也可以通过指令 add_definitions() 添加
CMAKE_CXX_FLAGS:设置 C++ 编译选项,也可以通过指令 add_definitions() 添加
add_definitions(-DENABLE_DEBUG -DABC) # 参数之间用空格分隔
3 实例搞起
3.1 单个源文件
cmaketest1.zip
以一段简单C程序为例
#include <stdio.h>int main() {printf("某程序员退休后决定练习书法,于是重金购买文房四宝。一日,饭后突生雅兴,一番研墨拟纸,并点上上好檀香。定神片刻,泼墨挥毫,郑重地写下一行字:\n");printf("Hello World!\n");return 0;}
新建文件 CMakeLists.txt(命名必须是 CMakeLists.txt,注意大小写和不要漏字母)
编译可执行文件
project(HELLO)add_executable(hello test.c) # 生成可执行文件#add_library(common STATIC test.c) # 生成静态库#add_library(common SHARED test.c) # 生成动态库或共享库
直接在当前文件夹执行cmake ..
弹出提示
原来这个命令会去上级目录找CMakeLists.txt
一般会新建一个build目录,cd到build目录
先执行cmake ..
再执行make
可以看到在build目录中产出了可执行文件,并且能执行成功
编译静态库
project(HELLO)#add_executable(hello test.c) # 生成可执行文件add_library(common STATIC test.c) # 生成静态库#add_library(common SHARED test.c) # 生成动态库或共享库
编译动态库
project(HELLO)#add_executable(hello test.c) # 生成可执行文件#add_library(common STATIC test.c) # 生成静态库add_library(common SHARED test.c) # 生成动态库或共享库
但是发现编译出来的是Mac下的动态库文件格式,dylib
如何才能编译出来so格式的动态库呢,需要把CMAKE_SYSTEM_NAME设置为Android
set(CMAKE_SYSTEM_NAME Android)set(CMAKE_ANDROID_NDK "/Users/guozhenqian/Develop/android-ndk-r22b")set(CMAKE_TOOLCHAIN_FILE "/Users/guozhenqian/Develop/android-ndk-r22b/build/cmake/android.toolchain.cmake")set(ANDROID_TOOLCHAIN clang)set(ANDROID_NATIVE_API_LEVEL 14)set(ANDROID_ABI "armeabi-v7a")set(CMAKE_BUILD_TYPE Release)set(CMAKE_CROSSCOMPILING "TRUE")set(CMAKE_CXX_STANDARD 11) #c++标准project(HELLO)#add_executable(hello test.c) # 生成可执行文件#add_library(common STATIC test.c) # 生成静态库add_library(common SHARED test.c) # 生成动态库或共享库
3.2 复杂项目(多个目录,多个源文件,多个项目)
关于多目录+自定义编译选项的,参考这篇文章足够了
CMake入门-04-自定义编译选项_binglingziyu的博客-CSDN博客_cmake 自定义选项
但是这篇文章有个错误的地方,导致自定义编译选项不生效
option一定要在configure_file之前定义
cmaketest_3.zip
cmake_minimum_required(VERSION 3.15.0)# 项目名project(hello)# 查找当前目录下的所有源文件,并将名称保存到 SRC_LIST 变量aux_source_directory(. SRC_LIST)# 是否使用自己的 MathFunctions 库# 这里设置的变量 USE_MYMATH、中间的提示文字、默认值,在 ccmake 命令中会展示# 这个option一定要在configure_file之前,不然生成的config.h文件默认是没有define的option (USE_MYMATH"Use provided math implementation"ON)# 加入一个配置头文件,用于处理 CMake 对源码的设置configure_file ("${PROJECT_SOURCE_DIR}/config.h.in""${PROJECT_SOURCE_DIR}/config.h")# 是否加入 MathFunctions 库if (USE_MYMATH)# 添加头文件路径include_directories ("${PROJECT_SOURCE_DIR}/math")# 添加 math 子目录 (math 目录里必须有 CMakeLists.txt)add_subdirectory (math)set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)endif (USE_MYMATH)# 指定生成目标add_executable(hello ${SRC_LIST} ${MATH_SRC_LIST})# 添加链接库target_link_libraries(hello ${EXTRA_LIBS})
3.3 复杂项目编译动态库
在cmaketest_3的基础上升级,编译出来动态库
cmaketest_4.zip
cmake_minimum_required(VERSION 3.15.0)# 设置Android环境set(CMAKE_SYSTEM_NAME Android)set(CMAKE_ANDROID_NDK "/Users/guozhenqian/Develop/android-ndk-r22b")set(CMAKE_TOOLCHAIN_FILE "/Users/guozhenqian/Develop/android-ndk-r22b/build/cmake/android.toolchain.cmake")set(ANDROID_TOOLCHAIN clang)set(ANDROID_NATIVE_API_LEVEL 14)set(ANDROID_ABI "armeabi-v7a")set(CMAKE_BUILD_TYPE Release)set(CMAKE_CROSSCOMPILING "TRUE")set(CMAKE_CXX_STANDARD 11) #c++标准# 项目名project(hello)# 查找当前目录下的所有源文件,并将名称保存到 SRC_LIST 变量aux_source_directory(. SRC_LIST)# 打印一下SRC_LIST的值看看MESSAGE( STATUS "SRC_LIST contains = ${SRC_LIST}")# 查找 math 目录下的所有源文件,并将名称保存到 MATH_SRC_LIST 变量# aux_source_directory(${PROJECT_SOURCE_DIR}/math MATH_SRC_LIST)# 是否使用自己的 MathFunctions 库# 这里设置的变量 USE_MYMATH、中间的提示文字、默认值,在 ccmake 命令中会展示# 这个option一定要在configure_file之前,不然生成的config.h文件默认是没有define的option (USE_MYMATH "Use provided math implementation" ON)# 加入一个配置头文件,用于处理 CMake 对源码的设置configure_file ("${PROJECT_SOURCE_DIR}/config.h.in""${PROJECT_SOURCE_DIR}/config.h")# 是否加入 MathFunctions 库if (USE_MYMATH)# 添加头文件路径include_directories ("${PROJECT_SOURCE_DIR}/math")# 添加 math 子目录 (math 目录里必须有 CMakeLists.txt)add_subdirectory (math)# 把MathFunctions追加设置到EXTRA_LIBS变量中set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)MESSAGE( STATUS "EXTRA_LIBS contains = ${EXTRA_LIBS}")endif (USE_MYMATH)# 生成动态库add_library(hello SHARED ${SRC_LIST})# 添加链接库target_link_libraries(hello ${EXTRA_LIBS})
码字不易,如果对您有帮助,还请赏个赞哇!
参考文章:
1、cmake, make, CMakeLists.txt, Makefile简介 cmake, make, CMakeLists.txt, Makefile简介_Northan的博客-CSDN博客_cmakelists.txt makefile
2、Makefile和CMakefiles.txt的那些事儿 https://www.jianshu.com/p/f51adf8430f0
3、CMakeLists.txt 语法介绍与实例演练(最后一个例子有问题) CMakeLists.txt 语法介绍与实例演练_阿飞__的博客-CSDN博客_cmakelist
4、cmake学习4--自定义编译选项 cmake学习4--自定义编译选项_小珊瑚的爸爸的博客-CSDN博客
5、CMake入门实战(这篇才正宗,别的都是抄的吧)CMake&CMakeList.txt
CMakeCMakeList.txt相关推荐
- Android录音-SoundTouch移植到Android
Android录音-SoundTouch移植到Android 文章目录 Android录音-SoundTouch移植到Android 一.SoundTouch介绍 二.移植SoundTouch(And ...
- 使用哈工大LTP进行文本命名实体识别并保存到txt
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/broccoli2/article/de ...
- TVM示例展示 README.md,Makefile,CMakeLists.txt
TVM示例展示 README.md,Makefile,CMakeLists.txt TVM/README.md Open Deep Learning Compiler Stack Documentat ...
- Python:数据导入、爬虫:csv,excel,sql,html,txt
''' 来源:天善智能韦玮老师课堂笔记 作者:Dust 数据导入 ·导入csv数据csv是一种常见的数据存储格式,基本上我们遇到的数据都可以转为这种存储格式.在Python数据分析中,我们可以使用pa ...
- python 创建.txt的文件 并写内容到里面
如果是在ubuntu 系统 可以使用os.mknod, mknod 在windows 上面支持 所以在window 上面创建.txt 的记事本 ,使用open 最好 , 具体代码如下: def cre ...
- java 读取txt,java读取大文件
java 读取txt,java读取大文件 package com.bbcmart.util; import java.io.File; import java.io.RandomAccessFile; ...
- vb mysql数据导入到mssql,[请教]怎样把*.txt文本的数据导入sql数据库中?
我分两步走,先将文本文件导入到grid中,然后再上传到数据库.但是我测试下列代码来将文本文件导入时总是出错,不能成功导入,代码如下:Private Sub Command1_Click() On Er ...
- 手机php转换txt,PHP 实现的将图片转换为TXT
PHP 实现的将图片转换为TXT /* 2015年10月19日10:24:59 */ // 打开一幅图像 $file_name='d:\ascii_dora.png'; $chars = " ...
- android读取工程目录下的文件,Android编程实现读取工程中的txt文件功能
本文实例讲述了Android编程实现读取工程中的txt文件功能.分享给大家供大家参考,具体如下: 1. 众所周知,Android的res文件夹是用来存储资源的,可以在res文件夹下建立一个raw文件夹 ...
最新文章
- 线粒体|GetOrganelle组装软件
- redis数据结构之一:链表
- Android应用程序模块:应用、任务、进程和线程
- spm oracle cloud,oracle11g新特点——SQLPlanManagement(SPM)-Oracle
- 原文翻译:深度学习测试题(L1 W1 测试题)
- raid 物理盘缓存状态_服务器raid卡、磁盘缓存的配置策略
- python变量名可以包含的字符有问号吗,带问号文字的Python正则表达式
- 简书自动生成目录小工具
- iis php http500,IIS7.5 PHP环境HTTP经常500错误处理方法
- Dart的日期时间操作
- java代码post接口请求 用 hutool工具类
- SqlServer中使用游标进行双重遍历
- mysql大数据高并发处理
- 最全Mac系统快捷键一览
- linux如何磁盘管理工具下载,Linux_Linux系统的磁盘空间管理工具, 1.fdisk -lDis - phpStudy...
- 什么是有监督学习?看这里。
- html返回的状态值,iradon函数返回的是什么值
- specification jpa 复杂查询
- html5 dreamlive,DREAM LIVE 5th Tour Stargazer即将开演
- Frps一键安装脚本,带Frpc Windows便捷启动脚本