一、Cmake 常用语句
1.1 程序的编译和执行
源程序经过预处理、编译、汇编、链接步骤后,才能生成可执行程序。
- 预处理:条件编译,头文件包含,宏替换的处理,刪除注释,生成.i文件。
gcc -E hello_world.c -o hello_world.i
- 编译:将预处理后的文件转换成汇编语言,生成.s文件
gcc -S
- 汇编:汇编变为目标代码(机器代码)生成.o的文件
gcc -c hello_world.s -o hello_world.o
- 链接:连接目标代码,生成可执行程序
gcc hello.world.o -o hello_world
1.2 cmake
CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译源码生成可执行程序或共享库(so(shared object))。
cmake 指向CMakeLists.txt所在的目录,例如cmake .. 表示CMakeLists.txt在当前目录的上一级目录。cmake后会生成很多编译的中间文件以及makefile文件。
cmake的特点:开源、跨平台、可扩展、简化编译构建过程和编译过程。
(1)cmake 变量
CMake支持简单的变量,它们或者是字符串,或者是字符串的列表。引用一个变量的语法是 ${VAR_NAME}
set(V 1 2 3) # V的值是1 2 3
command(${V}) # 等价于command(1 2 3)
要把一个列表变量作为整体传递,只需要加上双引号即可:
command("${V}") # 等价于command("1 2 3")
(2)cmake 实现原理
CMake包含一系列重要的概念抽象,包括目标(Targets)、生成器(Generators)、命令(Commands)等,这些命令均被实现为C++类。理解这些概念后才能编写高效的CMakeLists文件。
- 源文件:对应了典型的C/C++源代码;
- 目标:多个源文件联合成为目标,目标通常是可执行文件或者库;
- 目录:表示源码树中的一个目录,常常包含一个CMakeLists.txt文件,一或多个目标与之关联;
- 本地生成器(Local generator):每个目录有一个本地生成器,负责为此目录生成Makefiles,或者工程文件;
- 全局生成器(Global generator):所有本地生成器共享一个全局生成器,后者负责监管构建过程,全局生成器由CMake本身创建并驱动。
CMake的执行开始时,会创建一个cmake对象并把命令行参数传递给它。cmake对象管理整体的配置过程,持有构建过程的全局信息(例如缓存值)。cmake会依据用户的选择来创建合适的全局生成器(VS、Makefiles等等),并把构建过程的控制权转交给全局生成器(调用configure和generate方法)。
全局生成器负责管理配置信息,并生成所有Makefiles/工程文件。一般情况下全局生成器把具体工作委托给本地生成器执行,全局生成器为每个目录创建一个本地生成器。
(3)目标
Makefile对象中存放的最重要的对象是目标(Targets),目标代表可执行文件、库、实用工具等。每个 add_library 、 add_executable 、 add_custom_target 命令都会创建一个目标。
# 创建一个静态库,包含两个源文件
add_library(foo STATIC foo1.c foo2.c)
1.3 cmake 常见语法
(1)cmake 版本号设置cmake_minimum_required
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
(2)设置项目信息 project
# 项目信息
project (Demo1)
(3)生成目标 add_executable
# 指定生成目标
add_executable(Demo main.cc)
# 指定多个源文件,生成目标
add_executable(Demo main.cc MathFunctions.cc)
aux_source_directory,该命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
(4)添加子目录add_subdirectory
add_subdirectory
指明本项目包含一个子目录 ,这样 子目录下的 CMakeLists.txt 文件和源代码也会被处理
# 如在主目录下有个子目录math,添加 math 子目录
add_subdirectory(math)
(5)指明可执行程序所需要的链接库 TARGET_LINK_LIBRARIES (设置要链接的库文件的名称)
语法:TARGET_LINK_LIBRARIES(targetlibrary1 <debug | optimized> library2 ..)
TARGET_LINK_LIBRARIES(myProject hello),连接libhello.so库
TARGET_LINK_LIBRARIES(myProject libhello.a)
TARGET_LINK_LIBRARIES(myProject libhello.so)
命令 target_link_libraries
指明可执行文件 Demo 需要连接一个名为 MathFunctions 的链接库
# 添加链接库
target_link_libraries(Demo MathFunctions)
- 如果目标的头文件中包含了依赖的头文件(源文件间接包含),那么这里就是PUBLIC
- 如果目标仅源文件中包含了依赖的头文件,那么这里就是PRIVATE
- 如果目标的头文件包含依赖,但源文件未包含,那么这里就是INTERFACE
(6)设置变量 set
set(SRC_LIB "${CMAKE_SOURCE_DIR}/src/main/jnilibs")
定义了一个变量SRC_LIB,并且变量的值为${CMAKE_SOURCE_DIR}/src/main/jnilibs,其中CMAKE_SOURCE_DIR 是一个cmake内置变量,指定了CMakeLists.txt所在的目录
(7)输出信息 message
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 错误, 终止所有处理过程;
(8)将自定义构建规则添加到生成的构建系统 add_custom_command
add_custom_command
- 如果使用OUTPUT参数,需要在目标的构建中指定依赖于该OUTPUT;
- 如果使用TARGET参数,直接指定目标就可以了
add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1 [ARGS] [args1...]``
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS [depends...]]
[BYPRODUCTS [files...]]
[IMPLICIT_DEPENDS <lang1> depend1
[<lang2> depend2] ...]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[DEPFILE depfile]
[JOB_POOL job_pool]
[VERBATIM] [APPEND] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS])
add_custom_command(TARGET <target>
PRE_BUILD | PRE_LINK | POST_BUILD
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL])
- OUTPUT:指定命令预期产生的输出文件。如果输出文件的名称是相对路径,即相对于当前的构建的源目录路径;
- COMMAND:指定要在构建时执行的命令行;
- DEPENDS:指定命令所依赖的文件;
- COMMENT:在构建时执行命令之前显示给定消息;
- WORKING_DIRECTORY:使用给定的当前工作目录执行命令。如果它是相对路径,它将相对于对应于当前源目录的构建树目录;
- DEPFILE:为生成器指定一个.d depfile .d文件保存通常由自定义命令本身发出的依赖关系;
- MAIN_DEPENDENCY:指定命令的主要输入源文件;
- BYPRODUCTS:指定命令预期产生的文件。
cmake_minimum_required(VERSION 3.0)
project(test)
add_custom_command(OUTPUT COPY_RES
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/config ${CMAKE_CURRENT_SOURCE_DIR}/etc
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/log.txt ${CMAKE_CURRENT_SOURCE_DIR}/etc
)
add_custom_target(CopyTask ALL DEPENDS COPY_RES)
(9)添加需要链接的库文件目录LINK_DIRECTORIES
link_directories(directory1 directory2 ...)
它相当于g++
命令的-L
选项的作用,也相当于环境变量中增加LD_LIBRARY_PATH
的路径的作用。
link_directories("/lib/directory/")
(10)LINK_LIBRARIES (添加需要链接的库文件路径,注意这里是全路径)
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
(11)添加宏定义,代码中控制编译add_definitions
cmake的宏定义分为全局宏定义和针对某个目标定义宏定义。
定义全局宏定义:
# 定义宏MACRO_NAME,注意前面的-D
add_definitions(-DMACRO_NAME)
# 赋值
add_definitions(-DMACRO_NAME=${value})
应用举例:
在工程CMakeLists.txt 中,使用add_definitions()函数控制代码的开启和关闭。
option(TEST_DEBUG "option for debug" OFF)
if (TEST_DEBUG)
add_definitions(-DTEST_DEBUG)
endif(TEST_DEBUG)
option用法 /*TEST_DEBUG为编译开关,中间的字符串为描述信息,ON/OFF 为默认选项*/
运行构建项目的时候可以添加参数控制宏的开启和关闭
cmake -DTEST_DEBUG = on .. #打开
源码中使用该宏进行控制条件编译
#ifdef TEST_DEBUG
...
...
#else
...
#endif
option: 为选项开关
针对某个目标添加宏定义:
# 仅仅能用于 add_executable() 或 add_library() 添加的目标
# 格式:
target_compile_definitions(<target>
# PUBLIC、INTERFACE可以将宏定义传递给target的PUBLIC、INTERFACE条目
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
# 示例:
target_compile_definitions(hello PRIVATE A=1 B=0)
# 你也可以直接设置目标属性 COMPILE_DEFINITIONS
应用举例
target_compile_definitions(目标进程
PUBLIC
宏名称
)
(12)set_target_properties
设置目标的一些属性来改变它们构建的方式。
set_target_properties(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
(13)target_compile_definitions
为目标增加编译定义。
target_compile_definitions(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]
)
(14) target_include_directories
将给定目录加给编译器搜索到的包含文件
target_include_directories(<target> [SYSTEM] [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]
BEFORE or AFTER 可以加在前面或者后面. 如果设定 SYSTEM 选项 编译器认定为系统包含目录。
(15) find_package引入外部依赖包
https://zhuanlan.zhihu.com/p/97369704?utm_source=wechat_session
(16)add_compile_options
设置编译选项可以通过add_compile_options
命令,也可以通过set命令修改CMAKE_CXX_FLAGS
或CMAKE_C_FLAGS。
add_compile_options
命令添加的编译选项是针对所有编译器的(包括c和c++编译器),而set命令设置CMAKE_C_FLAGS
或CMAKE_CXX_FLAGS
变量则是分别只针对c和c++编译器的。
#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
add_compile_options(-std=c++11)
message(STATUS "optional:-std=c++11")
endif(CMAKE_COMPILER_IS_GNUCXX)
只针对c++编译器添加这个option。
#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
message(STATUS "optional:-std=c++11")
endif(CMAKE_COMPILER_IS_GNUCXX)
(17)list 列表操作
list(LENGTH <list> <output variable>)
list(GET <list> <element index> [<element index> ...]
<output variable>)
list(APPEND <list> [<element> ...])
list(FIND <list> <value> <output variable>)
list(INSERT <list> <element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value> [<value> ...])
list(REMOVE_AT <list> <index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)
说明:
LENGTH will return a given list’s length.#返回列表的长度
GET will return list of elements specified by indices from the list.#读取列表指定索引
APPEND will append elements to the list. #将子元素追加到列表
FIND will return the index of the element specified in the list or -1 if it wasn’t found.
INSERT will insert elements to the list to the specified location.
REMOVE_AT and REMOVE_ITEM will remove items from the list. The difference is that REMOVE_ITEM will remove the given items, while REMOVE_AT will remove the items at the given indices.
REMOVE_DUPLICATES will remove duplicated items in the list.
REVERSE reverses the contents of the list in-place.
SORT sorts the list in-place alphabetically.
(18)file
file GLOB命令主要用于匹配规则在指定的目录内匹配到所需要的文件,命令行格式:
file(GLOB <variable> [LIST_DIRECTORIES true[false] [RELATIVE <path> ] [CONFIGURE_DEPENDS] [<globbing-expression> ...])
如:寻找当前路径下的cpp文件,且返回的结果中为/public/home的相对路径,
file(GLOB TEST_RESULT LIST_DIRECT true RELATIVE /public/home *.cpp)
message("--------TEST_RESULT: ${TEST_RESULT}")
获取指定路径下全部源文件,按照绝对路径的形式
FILE (GLOB_RECURSE PROJECT_SRC ${PROJECT_SOURCE_DIR}/*.hpp ${PROJECT_SOURCE_DIR}/*.cpp)
(19)install
install用于指定在安装时运行的规则。它可以用来安装很多内容,可以包括目标二进制、动态库、静态库以及文件、目录、脚本等
install(TARGETS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
如:
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
可执行二进制myrun
安装到${CMAKE_INSTALL_BINDIR}
目录,动态库libmylib.so
安装到${CMAKE_INSTALL_LIBDIR}
目录,静态库libmystaticlib.a
安装到${CMAKE_INSTALL_LIBDIR}
目录。
二、Cmake 流程控制语句
CMake支持条件、循环控制结构,同时支持子过程(macro、function)
2.1 if-else-endif
if (condition)
command1
else(condition)
command2
endif(condition)
使用 elseif语句
if(MSVC80)
#...
elseif(MSVC90)
#...
elseif(APPLE)
#...
endif()
判断条件:
2.2 foreach
foreach (item list)
# do something with item
endforeach (item)
此命令用于迭代一个列表,第一个参数是每次迭代使用变量的名称,其余参数为被迭代的列表。
2.3 while
while(${COUNT} LESS 2000)
set(TASK_COUNT, ${COUNT})
endwhile()
三、Cmake 实战
3.1 搭建cmake 环境
使用阿里云 centos 8.0 环境
(1)安装cmake
yum install cmake
查看安装情况:
(2)安装gcc-c++
yum install gcc-c++
3.2 基础实例
(1)构建c++ 工程
#include <iostream>
using namespace std;
int main() {
return 0;
}
(2)需要在同一目录下放置CMakeLists.txt文件,编写如下内容:
# 需要最小的CMake版本
cmake_minimum_required(VERSION 3.3)
# 工程的名称,会作为MSVS的Workspace的名字
project(intellij_taste)
# 全局变量:CMAKE_SOURCE_DIR CMake的起始目录,即源码的根目录
# 全局变量:PROJECT_NAME 工程的名称
# 全局变量:PROJECT_SOURCE_DIR 工程的源码根目录的完整路径
# 全局变量:构建输出目录。默认的,对于内部构建,此变量的值等于CMAKE_SOURCE_DIR;否则等于构建树的根目录
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin) # ${}语法用于引用变量 自动创建bin目录
# 全局变量:可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# 全局变量:库文件的输出路径
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# 设置头文件位置
include_directories("${PROJECT_SOURCE_DIR}")
# 设置C++标志位
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# 设置源文件集合
set(SOURCE_FILES main.cpp)
# 添加需要构建的可执行文件,第二个以及后续参数是用于构建此文件的源码文件
add_executable(intellij_taste ${SOURCE_FILES})
(3)执行cmake
# 创建一个build子目录作为构建树
mkdir build
cd build
cmake ..
# 在build/bin子目录中生成可执行文件:
# cmake --build <dir> [options] [-- [native-options]]
cmake --build build -- -j3 # --表示把其余选项传递给底层构建工具
# 注意,亦可使用底层构建系统,例如make命令或者MSVC的IDE
cd build
make -j3
四、Cmake 生成VS工程
4.1 基本语法
(1)source_group
定义一组源文件到项目文件中. 主要用来配置Visual Studio中的文件标签. 任何被列出的或者与正则表达式匹配的文件都将被放入组中. 如
source_group(name [REGULAR_EXPRESSION regex] [FILES src1 src2 ...])
应用举例:
source_group(test\\vs项目文件集名称 REGULAR_EXPRESSION "代码目录/.+\.(h|inc|cc)")
参考文献:
【1】Cmake知识----编写CMakeLists.txt文件编译C/C++程序 : Cmake知识----编写CMakeLists.txt文件编译C/C++程序 - horsetail - 博客园
【2】Makefile和Cmake的联系与区别 : Makefile和Cmake的联系与区别_hjxu2016的博客-CSDN博客_cmake和makefile区别
【3】CMake 入门实战(精):CMake 入门实战(精)_起风了-CSDN博客_cmake 入门
【4】CMake之message()函数的使用和打印变量值: CMake之message()函数的使用和打印变量值_斧冰-CSDN博客_cmake message
【5】Cmake 内置变量: Cmake 内置变量_测试-CSDN博客
【6】Cmake官网: Documentation | CMake
【7】【CMake】cmake的add_custom_command和add_custom_target指令: 【CMake】cmake的add_custom_command和add_custom_target指令_Yngz_Miao的博客-CSDN博客_add_custom_command
【8】CMake 添加需要链接的库文件目录LINK_DIRECTORIES:CMake 添加需要链接的库文件目录LINK_DIRECTORIES | 浩瀚宇宙 灿烂星空
【9】link_directories, LINK_LIBRARIES, target_link_libraries使用总结: link_directories, LINK_LIBRARIES, target_link_libraries使用总结_w_w的专栏-CSDN博客
【10】linux 下 g++编译程序时,-I(大写i) 与-L(大写l)-l(小写l) 的作用: linux 下 g++编译程序时,-I(大写i) 与-L(大写l)-l(小写l) 的作用_月落及晨曦-CSDN博客
gcc -I(大写的i),-L(大写L)和-l(小写的L)详解:gcc -I(大写的i),-L(大写L)和-l(小写的L)详解_TMD_MCU的博客-CSDN博客
【11】CMAKE 中add_definitions的用法: CMAKE 中add_definitions的用法_邦戈邦戈栗子的博客-CSDN博客
【12】add_library,target_link_libraries,set_target_properties,target_link_libraries使用联系: add_library,target_link_libraries,set_target_properties,target_link_libraries使用联系_michaelhan3的博客-CSDN博客
【13】cmake CMakeLists.txt 命令 add_compile_options、add_definitions、target_compile_definitions、build_command: cmake CMakeLists.txt 命令 add_compile_options、add_definitions、target_compile_definitions、build_command_whatday的专栏-CSDN博客
【14】CMake-scope-PRIVATE-PUBLIC-INTERFACE: CMake-scope-PRIVATE-PUBLIC-INTERFACE_行走-CSDN博客
【15】cmake的四个命令:add_compile_options、add_definitions、target_compile_definitions、build_command...: cmake的四个命令:add_compile_options、add_definitions、target_compile_definitions、build_command..._DragonWar%的博客-CSDN博客
【16】CMake学习笔记:绿色记忆:CMake学习笔记
【17】CMake 入门1/5:基于阿里云 ECS搭建体验环境: CMake 入门1/5:基于阿里云 ECS搭建体验环境_weixin_33947521的博客-CSDN博客
【18】cmake:设置编译选项的讲究(add_compile_options和CMAKE_CXX_FLAGS的区别)_10km的博客-CSDN博客_add_compile_options