文章目录
参考资料
sudo apt install cmake
cmake -version # 查看cmake版本
一、同一目录下单个源文件
add_executable
# 第一行意思是表示cmake的最低版本要求是2.8
cmake_minimum_required (VERSION 2.8)
# 第二行是表示本工程信息,也就是工程名叫demo
project (demo)
# 表示最终要生成的elf文件的名字叫main,使用的源文件是main.c
add_executable(main main.c)
make clean # 如果想重新生成main,输入make clean就可以删除main这个elf文件。
二、同一目录下多个源文件
- 第一种方法
cmake_minimum_required (VERSION 2.8)
project (demo)
add_executable(main main.c testFunc.c)
如果在同一目录下有多个源文件,那么只要在add_executable
里把所有源文件都添加进去就可以了。但是如果有一百个源文件,再这样做就有点坑了,无法体现cmake的优越性,cmake提供了一个命令可以把指定目录下所有的源文件存储在一个变量中,这个命令就是 aux_source_directory(dir var)
# 第一个参数dir是指定目录,第二个参数var是用于存放源文件列表的变量。
aux_source_directory(dir var)
aux_source_directory
- 第二种方法
cmake_minimum_required (VERSION 2.8)
project (demo)
aux_source_directory(. SRC_LIST)
add_executable(main ${SRC_LIST})
-
使用
aux_source_directory
把当前目录下的源文件存列表存放到变量SRC_LIST
里,然后在add_executable
里调用SRC_LIST
(注意调用变量时的写法)。 -
aux_source_directory()
也存在弊端,它会把指定目录下的所有源文件都加进来,可能会加入一些我们不需要的文件,此时我们可以使用set
命令去新建变量来存放需要的源文件,如下
set( SRC_LIST ./main.c ./testFunc1.c ./testFunc.c)
cmake_minimum_required (VERSION 2.8)
project (demo)
set( SRC_LIST
./main.c
./testFunc1.c
./testFunc.c)
add_executable(main ${SRC_LIST})
三、不同目录下多个源文件
include_directories
cmake_minimum_required (VERSION 2.8)
project (demo)
# 该命令是用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔
include_directories (test_func test_func1)
aux_source_directory (test_func SRC_LIST)
aux_source_directory (test_func1 SRC_LIST1)
add_executable (main main.c ${SRC_LIST} ${SRC_LIST1})
因为main.c
里include了testFunc.h和testFunc1.h,如果没有这个命令来指定头文件所在位置,就会无法编译。当然,也可以在main.c里使用include来指定路径,如下
#include "test_func/testFunc.h"
#include "test_func1/testFunc1.h"
四、正规一点的组织结构
add_subdirectory
- 正规一点来说,一般会把源文件放到
src
目录下,把头文件放入到include
文件下,生成的对象文件放入到build
目录下,最终输出的elf
文件会放到bin
目录下,这样整个结构更加清晰。
我们在最外层目录下新建一个CMakeLists.txt,内容如下:
cmake_minimum_required (VERSION 2.8)
project (demo)
# 这个命令可以向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置,具体用法可以百度。
add_subdirectory (src)
这里指定src
目录下存放了源文件,当执行cmake时,就会进入src目录下去找src目录下的CMakeLists.txt,所以在src目录下也建立一个CMakeLists.txt,内容如下:
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
aux_source_directory (. SRC_LIST)
include_directories (../include)
add_executable (main ${SRC_LIST})
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
这里又出现一个新的命令set,是用于定义变量的,EXECUTABLE_OUT_PATH
和PROJECT_SOURCE_DIR
是CMake自带的预定义变量,其意义如下,
EXECUTABLE_OUTPUT_PATH
:目标二进制可执行文件的存放位置PROJECT_SOURCE_DIR
:工程的根目录
所以,这里set
的意思是把存放elf
文件的位置设置为工程根目录下的bin
目录。(cmake有很多预定义变量,详细的可以网上搜索一下)
添加好以上这2个CMakeLists.txt后,整体文件结构如下:
- 另外一种写法:
前面的工程使用了2个CMakeLists.txt,最外层的CMakeLists.txt用于掌控全局,使用add_subdirectory
来控制其它目录下的CMakeLists.txt的运行。
上面的例子也可以只使用一个CMakeLists.txt,把最外层的CMakeLists.txt内容改成如下:
cmake_minimum_required (VERSION 2.8)
project (demo)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
aux_source_directory (src SRC_LIST)
include_directories (include)
add_executable (main ${SRC_LIST})
五、动态库和静态库的编译控制
add_library
set_target_properties
有时只需要编译出动态库和静态库,然后等着让其它程序去使用。
cmake_minimum_required (VERSION 3.5)
project (demo)
set (SRC_LIST ${PROJECT_SOURCE_DIR}/testFunc/testFunc.c)
# 生成动态库或静态库(第1个参数指定库的名字;第2个参数决定是动态还是静态,如果没有就默认静态;第3个参数指定生成库的源文件)
add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})
set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
这里又出现了新的命令和预定义变量:
add_library
: 生成动态库或静态库(第1个参数指定库的名字;第2个参数决定是动态还是静态,如果没有就默认静态;第3个参数指定生成库的源文件)set_target_properties
: 设置最终生成的库的名称,还有其它功能,如设置库的版本号等等LIBRARY_OUTPUT_PATH
: 库文件的默认输出路径,这里设置为工程目录下的lib目录
前面使用set_target_properties
重新定义了库的输出名称,如果不使用set_target_properties
也可以,那么库的名称就是add_library
里定义的名称,只是连续2次使用add_library
指定库名称时(第一个参数),这个名称不能相同,而set_target_properties
可以把名称设置为相同,只是最终生成的库文件后缀不同(一个是.so
,一个是.a
),这样相对来说会好看点。
六、对库进行链接
工程目录下的CMakeLists.txt内容如下:
cmake_minimum_required (VERSION 3.5)
project (demo)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set (SRC_LIST ${PROJECT_SOURCE_DIR}/src/main.c)
# find testFunc.h
include_directories (${PROJECT_SOURCE_DIR}/testFunc/inc)
# 在指定目录下查找指定库,并把库的绝对路径存放到变量里,其第一个参数是变量名称,第二个参数是库名称,第三个参数是HINTS,第4个参数是路径,其它用法可以参考cmake文档
find_library(TESTFUNC_LIB testFunc HINTS ${PROJECT_SOURCE_DIR}/testFunc/lib)
add_executable (main ${SRC_LIST})
target_link_libraries (main ${TESTFUNC_LIB})
find_library
target_link_libraries
这里出现2个新的命令:
find_library
: 在指定目录下查找指定库,并把库的绝对路径存放到变量里,其第一个参数是变量名称,第二个参数是库名称,第三个参数是HINTS,第4个参数是路径,其它用法可以参考cmake文档target_link_libraries
: 把目标文件与库文件进行链接
使用find_library
的好处是在执行cmake ..
时就会去查找库是否存在,这样可以提前发现错误,不用等到链接时。
- 在
lib
目录下有testFunc的静态库和动态库,find_library(TESTFUNC_LIB testFunc ...
默认是查找动态库,如果想直接指定使用动态库还是静态库,可以写成find_library(TESTFUNC_LIB libtestFunc.so ...
或者find_library(TESTFUNC_LIB libtestFunc.a ...
- 查看
elf
文件使用了哪些库,可以使用readelf -d ./xx
来查看 - 之前本节教程使用的是库查找方法是
link_directories
,但是很多读者反映运行时有问题,本人去官方文档上查了下,发现不建议使用了,推荐使用find_library
或者find_package
七、添加编译选项
add_compile_options(-std=c++11 -Wall)
有时编译程序时想添加一些编译选项,如-Wall
,-std=c++11
等,就可以使用add_compile_options
来进行操作。
cmake_minimum_required (VERSION 2.8)
project (demo)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_compile_options(-std=c++11 -Wall)
add_executable(main main.cpp)
八、添加控制选项
有时希望在编译代码时只编译一些指定的源码,可以使用cmake的option
命令,主要遇到的情况分为2种:
- 本来要生成多个bin或库文件,现在只想生成部分指定的bin或库文件
- 对于同一个bin文件,只想编译其中部分代码(使用宏来控制)
1 第一种情况
option(MYDEBUG “enable debug compilation” OFF)
外层的CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.5)
project(demo)
# 第一个参数是这个option的名字,第二个参数是字符串,用来描述这个option是来干嘛的,第三个是option的值,ON或OFF,也可以不写,不写就是默认OFF。
option(MYDEBUG "enable debug compilation" OFF)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_subdirectory(src)
这里使用了option命令,其第一个参数是这个option的名字,第二个参数是字符串,用来描述这个option是来干嘛的,第三个是option的值,ON或OFF,也可以不写,不写就是默认OFF。
然后编写src目录下的CMakeLists.txt,如下:
cmake_minimum_required (VERSION 3.5)
add_executable(main1 main1.c)
if (MYDEBUG)
add_executable(main2 main2.c)
else()
message(STATUS "Currently is not in debug mode")
endif()
注意,这里使用了if-else来根据option
来决定是否编译main2.c
每次想改变MYDEBUG
时都需要去修改CMakeLists.txt,有点麻烦,其实可以通过cmake的命令行去操作,例如我们想把MYDEBUG设置为OFF,先cd到build目录,然后输入cmake .. -DMYDEBUG=ON
,这样就可以编译出main1和main2 (在bin目录下)
2 第二种情况
#include <stdio.h>
int main(void)
{
#ifdef WWW1
printf("hello world1\n");
#endif
#ifdef WWW2
printf("hello world2\n");
#endif
return 0;
}
可以通过定义宏来控制打印的信息,我们CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.5)
project(demo)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
option(WWW1 "print one message" OFF)
option(WWW2 "print another message" OFF)
if (WWW1)
add_definitions(-DWWW1)
endif()
if (WWW2)
add_definitions(-DWWW2)
endif()
add_executable(main main.c)
这里把option的名字保持和main.c里的宏名称一致,这样更加直观,也可以选择不同的名字。通过与add_definitions()
的配合,就可以控制单个bin文件的打印输出了。
cd到build目录下执行cmake .. && make
,然后到bin目录下执行./main
,可以看到打印为空,
接着分别按照下面指令去执行,然后查看打印效果:
cmake .. -DWWW1=ON -DWWW2=OFF && make
cmake .. -DWWW1=OFF -DWWW2=ON && make
cmake .. -DWWW1=ON -DWWW2=ON && make
这里有个小坑要注意下:假设有2个options叫A和B,先调用cmake设置了A,下次再调用cmake去设置B,如果没有删除上次执行cmake时产生的缓存文件,那么这次虽然没设置A,也会默认使用A上次的option值。
所以如果option有变化,要么删除上次执行cmake时产生的缓存文件,要么把所有的option都显式的指定其值。
九、指令汇总
1 基本指令
# c++标准
set(CMAKE_CXX_STANDARD 14)
# 查找指定目录下的所有源文件,然后将结果存进指定变量名
aux_source_directory(<dir> <variable>)
# 指定运行此配置文件所需的 CMake 的最低版本
cmake_minimum_required(xx)
# 该命令表示项目的名称
project(name)
# 添加链接库,指明可执行文件 main 需要连接一个名为 library_name 的链接库
target_link_libraries(target_name library_name)
# 用于输出信息
message(“message to display”)
2 add 指令
# 向 C/C++编译器添加-D 定义, 参数之间用空格分割, 如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量设置
add_definitions(-xx -xxx)
# 添加 math 子目录,指明本项目包含一个子目录 math,这样 math 目录下的 CMakeLists.txt 文件和源代码也会被处理
add_subdirectory(math)
# 添加target依赖, 确保在编译本 target 之前,其他的 target 已经被构建。
add_dependencies(target-name depend-target1 depend-target2 ...)
# 生成可执行文件
add_executable(target_name main.cpp)
# 生成动态静态库
add_library(libname [SHARED|STATIC|MODULE] source1 source2 ...)
3 测试指令
# 用来控制 Makefile 是否构建 test 目标,涉及工程所有目录, 一般情况这个指令放在工程的主CMakeLists.txt 中
enable_testing()
# 添加测试的 target 以及传入参数, make test 执行测试
add_test(testname Exename arg1 arg2 ...)
4 find 指令
4.1 find_path
如果找到则将路径保存在 VAR 中(此路径为一个绝对路径),如果没有找到则结果为 NOTFOUND。默认的情况下,VAR 会被保存在 Cache 中,这时候我们需要清除 VAR 才可以进行下一次查询(使用 unset 命令)
# 用于查找包含文件 name1 的路径
find_path(<VAR> name1 [path1 path2 …])
# 使用范例
find_path(LUA_INCLUDE_PATH lua.h ${LUA_INCLUDE_FIND_PATH})
if(NOT LUA_INCLUDE_PATH)
message(SEND_ERROR "Header file lua.h not found")
endif()
4.2 find_library, find_package
- find_library类似find_package,常用find_package,参见find_package
- find_package 可以被用来在系统中自动查找配置构建工程所需的程序库。CMake 自带的模块文件里有大半是对各种常见开源库的 find_package 支持,支持库的种类非常多。
find_package原理
find_package 可以根据一些预先定义好的变量[1]去搜索库文件,就是去搜索一个叫 Find.cmake 的文件,并将搜索结果存入到特定变量[1]中以供使用者调用。
[1] 这些变量有关目标库的设置,比如 xxx_ROOT,xxx_INCLUDEDIR 之类的,你可以修改这些变量去帮助 find_package 找到库
[2] 这些变量就是 find_package 的搜索结果,比如 xxx_FOUND,xxx_LIBRARY_DIRS 等
具体可以查看CMake_modules里对应库的说明
搜索路径
cmake会先在${CMAKE_MODULE_PATH}
和/share/cmake-x.y/Modules/
下寻找Find.cmake,如果是linux系统会从/usr/local/lib
下搜索模块,如果是windows会从环境变量目录以及注册表的模块目录下搜索模块
格式
find_package(<package> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
QUIET
,如果找不到包,将不会显示消息REQUIRED
,如果找不到包,将停止处理并显示错误消息COMPONENTS
,或REQUIRED之后,列出项目所需组件列表
如何寻找支持的库
- 在CMake_modules寻找支持的库
- 在对应库的官方文档里可能有相关cmake配置
如何导入库
#前面可能需要设置一些其他变量
#比如要导入xxx库
find_package(xxx)
#包含头文件,find_package把头文件地址保存在了xxx_INCLUDE_DIRS中
include_directories(${xxx_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} files)
#连接库文件
target_link_libraries(your_target_name ${xxx_LIBRARIES})
5 file 指令
# 写一条消息到名为filename的文件中。如果文件已经存在,该命令会覆盖已有的文件;如果文件不存在,它将创建该文件
file(WRITE filename "message to write"... )
# 写一条消息到名为filename的文件中,附加到文件末尾
file(APPEND filename "message to write"... )
# 拷贝文件
file(COPY files... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS]
[FILES_MATCHING]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
# READ选项将会读一个文件中的内容并将其存储在变量里。读文件的位置从offset开始,最多读numBytes个字节。如果指定了HEX参数,二进制代码将会转换为十六进制表达方式,并存储在变量里。
file(READ filename variable [LIMIT numBytes] [OFFSET offset] [HEX])
# 从一个文件中将一个ASCII字符串的list解析出来,然后存储在variable变量中,文件中的二进制数据会被忽略。回车换行符会被忽略,并通过换行符分割list
file(STRINGS filename variable [LIMIT_COUNT num]
[LIMIT_INPUT numBytes] [LIMIT_OUTPUT numBytes]
[LENGTH_MINIMUM numBytes] [LENGTH_MAXIMUM numBytes]
[NEWLINE_CONSUME] [REGEX regex]
[NO_HEX_CONVERSION])
file(RENAME <oldname> <newname>)
# 类似rm,包括在子路径下的文件
file(REMOVE [file1 ...])
# 类似rm -r,删除给定的文件以及目录,包括非空目录
file(REMOVE_RECURSE [file1 ...])
# 类似于mkdir,如果父目录不存在时,同样也会创建
file(MAKE_DIRECTORY [directory1 directory2 ...])
# 确定从direcroty参数到指定文件的相对路径
file(RELATIVE_PATH variable directory file)
file(DOWNLOAD url file [TIMEOUT timeout] [STATUS status] [LOG log] [EXPECTED_MD5 sum] [SHOW_PROGRESS])
6 基本语法
6.1 赋值语句
# 通过 set 命令构建一个 listVAR
set(VAR a b c)
使用语法 ${VariableName}
来访问名字为 VariableName 的变量的值(变量名区分大小写)
需要注意的是,即使在字符串中也可以使用 ${VariableName}
来访问变量的值
6.2 条件控制
#条件控制命令为 if 命令
if(expression)
#...
elseif(expression2)
#...
else()
#...
endif()
对于 if(string) 来说:
- 如果 string 为(不区分大小写)1、ON、YES、TRUE、Y、非 0 的数则表示真
- 如果 string 为(不区分大小写)0、OFF、NO、FALSE、N、IGNORE、空字符串、以 -NOTFOUND 结尾的字符串则表示假
- 如果 string 不符合上面两种情况,则 string 被认为是一个变量的名字。变量的值为第二条所述的各值则表示假,否则表示真。
6.3 操作符
# 为真的前提是 expression 为假
if(NOT expression)
# 为真的前提是 expr1 和 expr2 都为真
if(expr1 AND expr2)
# 为真的前提是 expr1 或者 expr2 为真
if(expr1 OR expr2)
# 为真的前提是存在 command-name 命令、宏或函数且能够被调用
if(COMMAND command-name)
# 为真的前提是存在 name 的文件或者目录(应该使用绝对路径)
if(EXISTS name)
# 为真的前提是 file1 比 file2 新或者 file1、file2 中有一个文件不存在(应该使用绝对路径)
if(file1 IS_NEWER_THAN file2)
# 为真的前提是 directory-name 表示的是一个目录(应该使用绝对路径)
if(IS_DIRECTORY directory-name)
# 为真的前提是变量值或者字符串匹配 regex 正则表达式
if(variable|string MATCHES regex)
# 为真的前提是变量值或者字符串为有效的数字且满足小于(大于、等于)的条件
if(variable|string LESS variable|string)
if(variable|string GREATER variable|string)
if(variable|string EQUAL variable|string)
# 为真的前提是变量值或者字符串以字典序满足小于(大于、等于)的条件
if(variable|string STRLESS variable|string)
if(variable|string STRGREATERvariable|string)
if(variable|string STREQUALvariable|string)
# 为真的前提是 variable 表示的变量被定义了。
if(DEFINED variable)
6.4 循环语句
# foreach 循环范例:
set(VAR a b c)
foreach(f ${VAR})
message(${f})
endforeach()
# while 循环范例:
set(VAR 5)
while(${VAR} GREATER 0)
message(${VAR})
math(EXPR VAR "${VAR} - 1")
endwhile()
7 预定义变量
- CMake 中有许多显式或隐式的预定义好的变量,也可以自己定义变量
二进制目录
CMAKE_BINARY_DIR
, PROJECT_BINARY_DIR
, <projectname>_BINARY_DIR
这三个变量指代的内容是一致的,如果是 in-source 编译,指的就是工程顶层目录,如果是 out-of-source 编译,指的是工程编译发生的目录
源码目录
CMAKE_SOURCE_DIR
, PROJECT_SOURCE_DIR
, <projectname>_SOURCE_DIR
这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。
CMAKE_CURRENT_SOURCE_DIR
指的是当前处理的 CMakeLists.txt 所在的路径
CMAKE_CURRRENT_BINARY_DIR
如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR
一致,如果是 out-of-source 编译, 他指的是 target 编译目录
变量所在处
CMAKE_CURRENT_LIST_FILE
, CMAKE_CURRENT_LIST_LINE
输出调用这个变量的 CMakeLists.txt 的完整路径, 所在行
CMAKE_MODULE_PATH
这个变量用来定义自己的 cmake 模块所在的路径。如果你的工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理CMakeLists.txt 时找到这些模块,你需要通过 SET
指令,将自己的 cmake 模块路径设置一下。比如SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
这时候你就可以通过 INCLUDE 指令来调用自己的模块了。
out-of-source 目录
EXECUTABLE_OUTPUT_PATH
, LIBRARY_OUTPUT_PATH
分别用来重新定义最终结果的存放目录
PROJECT_NAME
返回通过 PROJECT 指令定义的项目名称。
CMAKE_BUILD_TYPE
设置Release, Debug
变量的传递
set
用set命令定义的变量能传递到子目录,同级目录和父目录则不能传递 set(var_name var1 var2 ...)
set_property,get_property
使用set_property
实现共享变量的方法,不会将变量写入CMakeCache.txt,而是写入内存中
# GLOBAL 代表全局
set_property(GLOBAL PROPERTY var_name var1 var2 ...)
# 将 var_name_src 的值读入 var_name_temp 中
get_property(var_name_temp GLOBAL PROPERTY var_name_src)
汇总
- 基本语法格式:指令(参数 1 参数 2…)
- 参数使用括弧括起
- 参数之间使用空格或分号分开
- 指令是大小写无关的,参数和变量是大小写相关的
set(HELLO hello.cpp)
add_executable(hello main.cpp hello.cpp)
ADD_EXECUTABLE(hello main.cpp ${HELLO})
- 变量使用
${}
方式取值,但是在 IF 控制语句中是直接使用变量名
1 重要指令
cmake_minimum_required
- 指定CMake的最小版本要求- 语法:
cmake_minimum_required(VERSION versionNumber [FATAL_ERROR])
- 语法:
# CMake最小版本要求为2.8.3
cmake_minimum_required(VERSION 2.8.3)
-
project
- 定义工程名称,并可指定工程支持的语言- 语法:
project(projectname [CXX] [C] [Java])
- 语法:
# 指定工程名为HELLOWORLD
project(HELLOWORLD)
-
set
- 显式的定义变量- 语法:
set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
- 语法:
# 定义SRC变量,其值为sayhello.cpp hello.cpp
set(SRC sayhello.cpp hello.cpp)
-
include_directories
- 向工程添加多个特定的头文件搜索路径 —>相当于指定g++
编译器的-I
参数- 语法:
include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
- 语法:
# 将/usr/include/myincludefolder 和 ./include 添加到头文件搜索路径
include_directories(/usr/include/myincludefolder ./include)
-
link_directories
- 向工程添加多个特定的库文件搜索路径 —>相当于指定g++
编译器的-L
参数- 语法:
link_directories(dir1 dir2 ...)
- 语法:
# 将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径
link_directories(/usr/lib/mylibfolder ./lib)
-
add_library
- 生成库文件- 语法:
add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
- 语法:
# 通过变量 SRC 生成 libhello.so 共享库
add_library(hello SHARED ${SRC})
-
add_compile_options
- 添加编译参数- 语法:
add_compile_options()
- 语法:
# 添加编译参数 -Wall -std=c++11 -O2
add_compile_options(-Wall -std=c++11 -o2)
-
add_executable
- 生成可执行文件- 语法:
add_executable(exename source1 source2 ... sourceN)
- 语法:
# 编译main.cpp生成可执行文件main
add_executable(main main.cpp)
-
target_link_libraries
- 为 target 添加需要链接的共享库 —>相同于指定g++
编译器-l
参数- 语法:
target_link_libraries(target library1<debug | optimized> library2...)
- 语法:
# 将hello动态库文件链接到可执行文件main
target_link_libraries(main hello)
-
add_subdirectory
- 向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制 存放的位置- 语法:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
- 语法:
# 添加src子目录,src中需有一个CMakeLists.txt
add_subdirectory(src)
-
aux_source_directory
- 发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指 令临时被用来自动构建源文件列表- 语法:
aux_source_directory(dir VARIABLE)
- 语法:
# 定义SRC变量,其值为当前目录下所有的源代码文件
aux_source_directory(. SRC)
# 编译SRC变量所代表的源代码文件,生成main可执行文件
add_executable(main ${SRC})
2 CMake常用变量
CMAKE_C_FLAGS
gcc编译选项CMAKE_CXX_FLAGS
g++编译选项
# 在CMAKE_CXX_FLAGS编译选项后追加-std=c++11
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
CMAKE_BUILD_TYPE
编译类型(Debug, Release)
# 设定编译类型为debug,调试时需要选择debug
set(CMAKE_BUILD_TYPE Debug)
# 设定编译类型为release,发布时需要选择release
set(CMAKE_BUILD_TYPE Release)
-
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
_BINARY_DIR
1. 这三个变量指代的内容是一致的。
2. 如果是in source build
,指的就是工程顶层目录
3. 如果是out-of-source
编译,指的是工程编译发生的目录
4.PROJECT_BINARY_DIR
跟其他指令稍有区别,不过现在,你可以理解为他们是一致
的。
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
_SOURCE_DIR
- 这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。
- 也就是在
in source build
时,他跟CMAKE_BINARY_DIR
等变量一致。PROJECT_SOURCE_DIR
跟其他指令稍有区别,现在,你可以理解为他们是一致的。
CMAKE_C_COMPILER
:指定C编译器CMAKE_CXX_COMPILER
:指定C++编译器EXECUTABLE_OUTPUT_PATH
:可执行文件输出的存放路径LIBRARY_OUTPUT_PATH
:库文件输出的存放路径
3 CMake编译工程
- CMake目录结构:项目主目录存在一个CMakeLists.txt文件
两种方式设置编译规则:
- 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过
add_subdirectory
添加子目录即可; - 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的
CMakeLists.txt中;
3.1 两种构建方式
-
内部构建(in-source build):不推荐使用
内部构建会在同级目录下产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源文件放在一起会显得杂乱无章。
## 内部构建
# 在当前目录下,编译本目录的CMakeLists.txt,生成Makefile和其他文件
cmake .
# 执行make命令,生成target
make
-
外部构建(out-of-source build):推荐使用
将编译输出文件与源文件放到不同目录中
## 外部构建
# 1. 在当前目录下,创建build文件夹
mkdir build
# 2. 进入到build文件夹
cd build
# 3. 编译上级目录的CMakeLists.txt,生成Makefile和其他文件
cmake ..
# 4. 执行make命令,生成target
make