Cmake学习实战-基础篇

一、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 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  depend1
                                    [ depend2] ...]
                   [WORKING_DIRECTORY dir]
                   [COMMENT comment]
                   [DEPFILE depfile]
                   [JOB_POOL job_pool]
                   [VERBATIM] [APPEND] [USES_TERMINAL]
                   [COMMAND_EXPAND_LISTS])
add_custom_command(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(
  # PUBLIC、INTERFACE可以将宏定义传递给target的PUBLIC、INTERFACE条目
   [items1...]
  [ [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(
                            [items1...]
                           [ [items2...] ...]
                          )

(14) target_include_directories

将给定目录加给编译器搜索到的包含文件

target_include_directories( [SYSTEM] [BEFORE]
   [items1...]
  [ [items2...] ...]

 BEFORE or AFTER 可以加在前面或者后面. 如果设定 SYSTEM 选项 编译器认定为系统包含目录。

Cmake学习实战-基础篇_第1张图片

(15) find_package引入外部依赖包

 https://zhuanlan.zhihu.com/p/97369704?utm_source=wechat_session

(16)add_compile_options

设置编译选项可以通过add_compile_options命令,也可以通过set命令修改CMAKE_CXX_FLAGSCMAKE_C_FLAGS。

add_compile_options命令添加的编译选项是针对所有编译器的(包括c和c++编译器),而set命令设置CMAKE_C_FLAGSCMAKE_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(GET   [ ...]
     )
list(APPEND  [ ...])
list(FIND   )
list(INSERT    [ ...])
list(REMOVE_ITEM   [ ...])
list(REMOVE_AT   [ ...])
list(REMOVE_DUPLICATES )
list(REVERSE )
list(SORT )

 说明:

LENGTH will return a given list’s length.#返回列表的长度

Cmake学习实战-基础篇_第2张图片

GET will return list of elements specified by indices from the list.#读取列表指定索引

Cmake学习实战-基础篇_第3张图片

 Cmake学习实战-基础篇_第4张图片

APPEND will append elements to the list.  #将子元素追加到列表

Cmake学习实战-基础篇_第5张图片

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.

其他参考:Cmake命令之list介绍 - 百度文库 

(18)file 

file GLOB命令主要用于匹配规则在指定的目录内匹配到所需要的文件,命令行格式:

file(GLOB  [LIST_DIRECTORIES true[false] [RELATIVE  ] [CONFIGURE_DEPENDS] [ ...])

 如:寻找当前路径下的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 ... [...])
install({FILES | PROGRAMS} ... [...])
install(DIRECTORY

... [...])
install(SCRIPT [...])
install(CODE [...])
install(EXPORT [...])

 

如:

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()

 判断条件:

Cmake学习实战-基础篇_第6张图片

 Cmake学习实战-基础篇_第7张图片

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++
 Cmake学习实战-基础篇_第8张图片

3.2 基础实例

(1)构建c++ 工程

#include 
 
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  [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

【19】CMAKE 批量获取目录下指定类型文件并转换为相对路径_黑色低级高中生的博客-CSDN博客

你可能感兴趣的:(C++,cmake)