【CMake】基本指令

参考资料

Linux下CMake简明教程
CMAKE常用指令

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_PATHPROJECT_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
  1. 这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。
  2. 也就是在 in source build时,他跟 CMAKE_BINARY_DIR 等变量一致。
  3. PROJECT_SOURCE_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。
  • CMAKE_C_COMPILER:指定C编译器
  • CMAKE_CXX_COMPILER:指定C++编译器
  • EXECUTABLE_OUTPUT_PATH:可执行文件输出的存放路径
  • LIBRARY_OUTPUT_PATH:库文件输出的存放路径

3 CMake编译工程

  • CMake目录结构:项目主目录存在一个CMakeLists.txt文件

两种方式设置编译规则:

  1. 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory
    添加子目录即可;
  2. 包含源文件的子文件夹未包含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
  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇光_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值