文章目录
CMake
CMake是一个项目构建工具,是跨平台的。CMake允许开发者指定整个工程的编译流程,并根据编译平台,自动生成本地化的Makefile和工程文件,最后用户只需make
编译即可,可以把CMake当作自动生成Makefile的工具,编译流程如下:
CMake优点:
- 跨平台
- 能够管理大型项目
- 简化编译构建过程和编译过程
- 可扩展,可以为cmake编写特定功能的模块,扩充cmake功能
CMake的使用
CMake支持大写、小写、混合大小写的命令
-
注释:
#
、#[[]]
# 行注释 #[[块 注 释]]
-
cmake CMakeLists.txt所在路径
:在当前路径./build
执行cmake ..
命令后生成了一个Makefile
文件,在执行make
命令,就可以对项目进行构建得到所需的可执行程序(默认可执行程序生成在./build目录下). ├── CMakeLists.txt ├── add.c ├── build │ ├── CMakeCache.txt │ ├── CMakeFiles │ ├── Makefile │ └── cmake_install.cmake ├── head.h ├── main.c └── sub.c
在执行
cmake
命令时指定宏的值:cmake CMakeLists.txt路径 -DCMAKE_CXX_STANDARD=11
-
cmake_minimum_required(VERSION 3.0)
:指定使用的cmake最低版本 -
project
:定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言project(<PROJECT-NAME> [<language-name>...]) project(<PROJECT-NAME> [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] [DESCRIPTION <project-description-string>] [HOMEPAGE_URL <url-string>] [LANGUAGES <language-name>...]) project(csapp)
-
add_executable(可执行程序名 源文件名称)
:生成一个可执行程序,源文件可以有多个,并用空格或;
间隔 -
set
:定义变量or设置宏or拼接字符串set(变量名 [变量值] [CACHE TYPE DOCSTRING [FORCE]]) # 定义变量 set(SRC_LIST main.c;add.c;sub.c) add_executable(app ${SRC_LIST}) # 设置宏CMAKE_CXX_STANDARD的值指定cxx版本 set(CMAKE_CXX_STANDARD 11) # 设置宏EXECUTABLE_OUTPUT_PATH的值指定可执行程序输出路径,如果此路径不存在,会自动生成 # 由于可执行程序是基于cmake命令生成的makefile文件然后再执行make命令得到的,此处的相对路径./是makefile所在目录 set(EXECUTABLE_OUTPUT_PATH ../bin) # 拼接字符串,将从第二个参数开始往后所有的字符串进行拼接,最后将结果存储到第一个参数中,如果第一个参数中原来有数据会对原数据就行覆盖 set(变量名1 ${变量名1} ${变量名2})
-
aux_source_directory
:查找指定路径下的所有源文件aux_source_directory(<要搜索的目录> <将要搜索的目录下搜索到的源文件列表存储到该变量中>) CMAKE_CURRENT_SOURCE_DIR:宏表示当前访问的CMakeLists.txt文件所在路径 PROJECT_SOURCE_DIR:宏就是在使用cmake命令时,后面紧跟对的目录,一般是工程的根目录
-
file
:搜索文件,得到的是文件的绝对路径file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型) GLOB:将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中 GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中 # 搜索当前目录的src目录下的所有源文件,并存储到变量中,要搜索的文件路径和类型可加也可不加双引号 file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c) file(GLOB MAIN_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c")
-
include_directories
:设置要包含的头文件路径include_directories(head_path)
-
add_definitions
:宏定义add_definitions(-D宏名称)
-
message
:日志(显式一条消息),CMake的命令行工具会在stdout
上显式STATUS
消息,在stderr
上显式其它所有消息。CMake的GUI会在它的log区域显式所有消息。CMake警告和错误消息的文本显示使用的是一种简单的标记语言,文本没有缩进,超过长度的行会回卷,段落之间以新行作为分隔符。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 错误, 终止所有处理过程 # 输出一般日志信息 message(STATUS "source path: ${PROJECT_SOURCE_DIR}") # 输出警告信息 message(WARNING "source path: ${PROJECT_SOURCE_DIR}") # 输出错误信息 message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")
-
list
:列表操作# 通过索引获取元素,[0……][……-1],边界越线报错 list(GET <list> <element index> [<element index> ...] <output variable>) # 根据值查找元素,未找到-1 list(FIND <list> <value> <output variable>) # 将元素追加到列表中 list(APPEND <list> [<element> ...]) # 在指定位置index插入元素 list(INSERT <list> <element_index> <element> [<element> ...]) # 将元素插入到0位置 list (PREPEND <list> [<element> ...]) # 根据索引从列表中移除元素 list (REMOVE_AT <list> <index> [<index> ...]) # 根据值从列表中移除元素 list(REMOVE_ITEM <list> <value> [<value> ...]) # 将列表中最后一个元素移除 list (POP_BACK <list> [<out-var>...]) # 将列表中第一个元素移除 list (POP_FRONT <list> [<out-var>...]) # 列表元素去重 list (REMOVE_DUPLICATES <list>) # 列表翻转 list(REVERSE <list>) # 获取list长度 list(LENGTH <list> <output variable>) # 将列表中的元素用连接符glue连接起来组成一个字符串 list (JOIN <list> <glue> <output variable>) # 列表排序 list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>]) COMPARE:指定排序方法,有如下几种值可选: STRING:按照字母顺序进行排序,为默认的排序方法 FILE_BASENAME:如果是一系列路径名,会使用basename进行排序 NATURAL:使用自然数顺序排序 CASE:指明是否大小写敏感。有如下几种值可选: SENSITIVE: 按照大小写敏感的方式进行排序,为默认值 INSENSITIVE:按照大小写不敏感方式进行排序 ORDER:指明排序的顺序。有如下几种值可选: ASCENDING:按照升序排列,为默认值 DESCENDING:按照降序排列
静&动态库
静态库:在Linux中,静态库名称分为三部分:lib
+ 库名字
+ .a
,下面只需要指定库名字即可,另外两部分生成该文件时自动添加。静态库会被打包到可执行程序中,可执行程序启动后,静态库也就随之加载到内存中了。
add_library(库名字 STATIC 源文件1 [源文件2] ...)
动态库:在Linux中,动态库名称分为三部分:lib
+ 库名字
+ .so
,此处只需要指定出库名字即可,另外两部分生成该文件时自动添加。如果生成的库比较大,建议动态库。只有可执行程序调用了动态库中的函数的时候,动态库才会被加载到内存中,多个进程可以共享内存中的同一个动态库(共享库)。
add_library(库名字 SHARED 源文件1 [源文件2] ...)
设置库生成路径:
由于在Linux下生成的动态库默认是有可执行权限的,可按照生成可执行程序的方式指定生成目录。
由于在Linux下生成的静态库默认是不具有可执行权限的,所有在指定静态库生成路径的时候就不能使用EXECUTABLE_OUTPUT_PATH
宏了,应该使用LIBRARY_OUTPUT_PATH
,这个宏对静/动态库文件都适用
-
适用于动态库:
# 设置宏EXECUTABLE_OUTPUT_PATH的值指定动态库的输出路径 set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
-
都适用:
# 设置静/动态库生成路径 set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
库链接
-
link_libraries
:链接静态库link_libraries(<static lib> [<static lib>...]) static lib可以是:①全名libxxx.a;②xxx # 如果静态库不是系统提供的(自制or使用第三方提供的静态库)可能会出现静态库找不到的情况,此时可以指定静态库路径,此命令也可指定动态库路径 link_directories(<lib path>)
-
target_link_libraries
:链接动态库,也可链接静态库target_link_libraries( <target> <PRIVATE|PUBLIC|INTERFACE> <item>... [<PRIVATE|PUBLIC|INTERFACE> <item>...]...) target:指定要加载动态库的文件名字,该文件可能是一个源文件、动态库文件、可执行文件 PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC。
动态库具有传递性:如果A连接了B,C链接了A,那么相当于C连接了A和B,C可以使用A和B中定义的方法。
如果各个动态库之间没有依赖关系,无需做任何设置,三者没有区别,一般无需指定,使用默认的PUBLIC即可:
public
:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。PRIVATE
:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库INTERFACE
:在interface后面引入的库不会被链接到前面的target中,只会导出符号。
链接系统/第三方动态库:在
cmake
中要指定链接的动态库的时候,应该将命令写到生成了可执行文件之后:target_link_libraries(可执行程序名 库名)
嵌套的CMake
在构建大型项目的时候可以给每个源代码目录都添加一个CMakeLists.txt
文件(头文件目录不需要)。
嵌套的CMake是一个树状结构,最顶层的CMakeLists.txt是根节点,其次都是子节点。
- 根节点CMakeLists.txt中的变量全局有效
- 父节点CMakeLists.txt中的变量可以在子节点中使用
- 子节点CMakeLists.txt中的变量只能在当前节点中使用
add_subdirectory
:建立CMake中父子关系
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
source_dir:指定CMakeLists.txt源文件和源代码文件的位置,即指定子目录
binary_dir:指定输出文件路径,一般不需要指定
EXCLUDE_FROM_ALL:在子路径下的目标默认不会被包含到父路径的ALL目标里,并且也会被排除在IDE工程文件之外
用户必须显式构建在子路径下的目标
示例
目录结构:对于calc和sort目录中的源文件来说,可以将它们先编译成库文件(可以是静态库也可以是动态库)然后在提供给测试文件使用即可。
$ tree
.
├── build
├── calc
│ ├── add.cpp
│ ├── CMakeLists.txt
│ ├── div.cpp
│ ├── mult.cpp
│ └── sub.cpp
├── CMakeLists.txt
├── include
│ ├── calc.h
│ └── sort.h
├── sort
│ ├── CMakeLists.txt
│ ├── insert.cpp
│ └── select.cpp
├── test1
│ ├── calc.cpp
│ └── CMakeLists.txt
└── test2
├── CMakeLists.txt
└── sort.cpp
6 directories, 15 files
根目录:
cmake_minimum_required(VERSION 3.0)
project(test)
# 定义变量
# 静态库生成的路径
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 测试程序生成的路径
set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 头文件目录
set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 静态库的名字
set(CALC_LIB calc)
set(SORT_LIB sort)
# 可执行程序的名字
set(APP_NAME_1 test1)
set(APP_NAME_2 test2)
# 添加子目录
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)
calc目录:
cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${CALC_LIB} STATIC ${SRC})
sort目录:
cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})
test1目录:
cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
link_directories(${LIB_PATH})
link_libraries(${CALC_LIB})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
add_executable(${APP_NAME_1} ${SRC})
test2目录:
cmake_minimum_required(VERSION 3.0)
project(SORTTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
link_directories(${LIB_PATH})
add_executable(${APP_NAME_2} ${SRC})
target_link_libraries(${APP_NAME_2} ${SORT_LIB})
在项目中,如果将程序中的某个模块制作成了动态库或者静态库并且在CMakeLists.txt 中指定了库的输出目录,而后其它模块又需要加载这个生成的库文件,此时直接使用就可以了,如果没有指定库的输出路径或者需要直接加载外部提供的库文件,此时就需要使用 link_directories 将库文件路径指定出来。
控制流程
条件
在进行条件判断的时候,如果有多个条件,那么可以写多个elseif
,最后一个条件可以使用else
,但是开始和结束是必须要成对出现的,分别为:if
和endif
。
if(<condition>)
<commands>
elseif(<condition>) # 可选快, 可以重复
<commands>
else() # 可选快
<commands>
endif()
循环
foreach(<loop_var> <items>)
<commands>
endforeach()
while(<condition>)
<commands>
endwhile()
预定义宏
宏 | 功能 |
---|---|
PROJECT_SOURCE_DIR | 使用cmake命令后紧跟的目录,一般是工程的根目录 |
PROJECT_BINARY_DIR | 执行cmake命令的目录 |
CMAKE_CURRENT_SOURCE_DIR | 当前处理的CMakeLists.txt所在的路径 |
CMAKE_CURRENT_BINARY_DIR | target编译目录 |
EXECUTABLE_OUTPUT_PATH | 重新定义目标二进制可执行文件的从存放路径 |
LIBRARY_OUTPUT_PATH | 重新定义目标链接库文件的存放位置 |
PROJECT_NAME | 返回通过PROJECT指令定义的项目名称 |
CMAKE_BINARY_DIR | 项目实际构建路径,假设在build 目录进行的构建,那么得到的就是这个目录的路径 |
参考:
- https://www.subingwen.cn/cmake/CMake-primer/