1、各种关系

在各种开源项目中,经常会发现项目中除了代码源文件,还包含了 CMakeList.txtMakefile 文件,在项目的编译时候需要用到的命令有 cmakemake。我们本次想搞清楚他们之前的关系以及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相关推荐

  1. Android录音-SoundTouch移植到Android

    Android录音-SoundTouch移植到Android 文章目录 Android录音-SoundTouch移植到Android 一.SoundTouch介绍 二.移植SoundTouch(And ...

  2. 使用哈工大LTP进行文本命名实体识别并保存到txt

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/broccoli2/article/de ...

  3. TVM示例展示 README.md,Makefile,CMakeLists.txt

    TVM示例展示 README.md,Makefile,CMakeLists.txt TVM/README.md Open Deep Learning Compiler Stack Documentat ...

  4. Python:数据导入、爬虫:csv,excel,sql,html,txt

    ''' 来源:天善智能韦玮老师课堂笔记 作者:Dust 数据导入 ·导入csv数据csv是一种常见的数据存储格式,基本上我们遇到的数据都可以转为这种存储格式.在Python数据分析中,我们可以使用pa ...

  5. python 创建.txt的文件 并写内容到里面

    如果是在ubuntu 系统 可以使用os.mknod, mknod 在windows 上面支持 所以在window 上面创建.txt 的记事本 ,使用open 最好 , 具体代码如下: def cre ...

  6. java 读取txt,java读取大文件

    java 读取txt,java读取大文件 package com.bbcmart.util; import java.io.File; import java.io.RandomAccessFile; ...

  7. vb mysql数据导入到mssql,[请教]怎样把*.txt文本的数据导入sql数据库中?

    我分两步走,先将文本文件导入到grid中,然后再上传到数据库.但是我测试下列代码来将文本文件导入时总是出错,不能成功导入,代码如下:Private Sub Command1_Click() On Er ...

  8. 手机php转换txt,PHP 实现的将图片转换为TXT

    PHP 实现的将图片转换为TXT /* 2015年10月19日10:24:59 */ // 打开一幅图像 $file_name='d:\ascii_dora.png'; $chars = " ...

  9. android读取工程目录下的文件,Android编程实现读取工程中的txt文件功能

    本文实例讲述了Android编程实现读取工程中的txt文件功能.分享给大家供大家参考,具体如下: 1. 众所周知,Android的res文件夹是用来存储资源的,可以在res文件夹下建立一个raw文件夹 ...

最新文章

  1. 线粒体|GetOrganelle组装软件
  2. redis数据结构之一:链表
  3. Android应用程序模块:应用、任务、进程和线程
  4. spm oracle cloud,oracle11g新特点——SQLPlanManagement(SPM)-Oracle
  5. 原文翻译:深度学习测试题(L1 W1 测试题)
  6. raid 物理盘缓存状态_服务器raid卡、磁盘缓存的配置策略
  7. python变量名可以包含的字符有问号吗,带问号文字的Python正则表达式
  8. 简书自动生成目录小工具
  9. iis php http500,IIS7.5 PHP环境HTTP经常500错误处理方法
  10. Dart的日期时间操作
  11. java代码post接口请求 用 hutool工具类
  12. SqlServer中使用游标进行双重遍历
  13. mysql大数据高并发处理
  14. 最全Mac系统快捷键一览
  15. linux如何磁盘管理工具下载,Linux_Linux系统的磁盘空间管理工具, 1.fdisk -lDis - phpStudy...
  16. 什么是有监督学习?看这里。
  17. html返回的状态值,iradon函数返回的是什么值
  18. specification jpa 复杂查询
  19. html5 dreamlive,DREAM LIVE 5th Tour Stargazer即将开演
  20. Frps一键安装脚本,带Frpc Windows便捷启动脚本

热门文章

  1. python3问题-easy_install使用
  2. 汽车ECU的内部构成与功能模块
  3. 唯样商城:氧化膜电阻和碳膜电阻有哪些区别
  4. 微信小程序中自定义函数的学习使用
  5. 采用飞狼刀模插件的炸字功能将文字导入protel99se
  6. 腾讯云服务器 linux系统 安装桌面环境
  7. EXCEL 图表相关,如何在一个图表里有2种图形?比如折线+ 柱状图?
  8. Web中图片的使用,下载与切图
  9. Linux安装与使用:
  10. 全国行政区域api 最新最全的全国行政区域查询