CMake 重要知识点总结

Github 练习

语法特性

  • 变量使用 ${} 方式取值,但是在 IF 控制语句中是直接使用变量名。
  • 指令(参数 1 参数 2...) ,参数使用括弧括起,参数之间使用空格或分号分开。
  • 指令是大小写无关的,参数和变量是大小写相关的。
  • 无行结束符,注释使用 #

编译工程

设置编译规则

  • 包含源文件的子文件夹包含 CMakeLists.txt 文件,主目录的 CMakeLists.txt 通过 add_subdirectory 添加子目录即可。
  • 包含源文件的子文件夹未包含 CMakeLists.txt 文件,子目录编译规则体现在主目录的 CMakeLists.txt 中。

构建方式

  • 内部构建(in-source build)会在同级目录下产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源文件放在一起会显得杂乱无章,不推荐使用。
  • 外部构建(out-of-source build)会将编译输出文件与源文件放到不同目录中,推荐使用。

常用变量

路径

  • PROJECT_NAME :返回通过 PROJECT 指令定义的项目名称
  • CMAKE_BINARY_DIRPROJECT_BINARY_DIR<projectname>_BINARY_DIR :如果是 in-source build,指的就是工程顶层目录,如果是 out-of-source build编译,指的是工程编译发生的目录
  • CMAKE_SOURCE_DIRPROJECT_SOURCE_DIR<projectname>_SOURCE_DIR :不论采用何种编译方式,都是工程顶层目录
  • CMAKE_CURRENT_SOURCE_DIR :指的是当前处理的 CMakeLists.txt 所在的路径
  • CMAKE_CURRRENT_BINARY_DIR :如果是 in-source build,与 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source build,指的是 target 编译目录,add_subdirectory 可以修改这个变量的值,设置 EXECUTABLE_OUTPUT_PATH 并不会对这个变量造成影响
  • CMAKE_CURRENT_LIST_FILE :输出调用这个变量的 CMakeLists.txt 的完整路径
  • CMAKE_CURRENT_LIST_LINE :输出这个变量所在的行
  • EXECUTABLE_OUTPUT_PATH :可执行文件输出的存放路径
  • LIBRARY_OUTPUT_PATH :库文件输出的存放路径
  • CMAKE_INSTALL_PREFIX :目标文件的安装目录,默认值为 /usr/local/bin
  • CMAKE_MODULE_PATH :自定义的 CMake 模块的路径
  • CMAKE_INCLUDE_CURRENT_DIR :自动添加 CMAKE_CURRENT_SOURCE_DIRCMAKE_CURRRENT_BINARY_DIR 到当前的 CMakeLists.txt 中处理
  • CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE :将工程提供的头文件目录始终置于系统文件目录之前

系統信息

  • CMAKE_MAJOR_VERSION :CMake 的主版本号,例如 2.3.6 中的 2
  • CMAKE_MINOR_VERSION :CMake 的次版本号,例如 2.3.6 中的 3
  • CMAKE_PATCH_VERSION :CMake 的补丁等级,例如 2.3.6 中的 6
  • CMAKE_SYSTEM_NAME :系统名称,包含版本号,例如 Linux-2.6.28
  • CMAKE_SYSTEM_NAME :系统名称,不包含版本号,例如 Linux
  • CMAKE_SYSTEM_VERSION :系统版本号,例如 2.6.28
  • CMAKE_SYSTEM_PROCESSOR :处理器名称,例如 I386
  • UNIX :在所有的类 UNIX 平台为 TRUE,包括 MacOS 和 Cygwin
  • WIN32 :在所有的 win32 平台为 TRUE,包括 Cygwin

开关选项

  • CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS :开关选项,用来控制 if else 的书写方式
  • BUILD_SHARED_LIBS :开关选项,默认为静态库

编译选项

  • CMAKE_BUILD_TYPE :编译类型,Debug, Release
  • CMAKE_C_COMPILER :指定 C 编译器
  • CMAKE_CXX_COMPILER :指定 C++ 编译器
  • CMAKE_C_FLAGS :设置 C 编译选项,也可以通过 add_definitions() 添加
  • CMAKE_CXX_FLAGS :设置 C++ 编译选项,也可以通过 add_definitions() 添加

环境变量

  • $ENV{NAME}:调用系统的环境变量。
  • SET(ENV{VAR} VALUE):设置环境变量。

重要指令

cmake_minimum_required

指定 CMake 的最小版本要求。

语法:

cmake_minimum_required(VERSION versionNumber [FATAL_ERROR])

示例:

cmake_minimum_required(VERSION 2.8.3) # 如果 cmake 版本小与 2.8.3,则出现严重错误,整个过程中止

project

定义工程名称,并可指定工程支持的语言。支持的语言列表是可以忽略的,默认情况表示支持所有语言。

语法:

project(projectname [CXX] [C] [Java])

隐含定义了两个变量 <projectname>_BINARY_DIR<projectname>_SOURCE_DIR

示例:

project(test)

set

显式的定义变量。

语法:

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

示例:

set(SRC hello.cpp)

message

向终端输出用户定义的信息。

语法:

MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
  • SEND_ERROR:产生错误,生成过程被跳过
  • SATUS:输出前缀为 - 的信息
  • FATAL_ERROR:立即终止所有 cmake 过程

示例:

message("PROJECT_NAME=",${PROJECT_NAME})

add_compile_options

添加编译参数。

语法:

add_compile_options()

示例:

add_compile_options(-Wall -std=c++11 -O2)

include_directories

向工程添加多个特定的头文件搜索路径。

语法:

include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)

示例:

include_directories(/usr/include/ ./include)

add_library

生成库文件。

语法:

add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 … sourceN)
  • SHARED:动态库
  • STATIC:静态库
  • MODULE:在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待
  • EXCLUDE_FROM_ALL:这个库不会被默认构建,除非有其他的组件依赖或者手工构建

示例:

add_library(hello SHARED ${SRC})

set_target_properties

设置输出的名称,对于动态库,可以用于指定动态库的版本和 API 版本。

语法:

set_target_properties(target1 target2 ... PROPERTIES pro1 value1 pro2 value2 ...)

获取属性可以使用:

get_target_property(VAR target property)

示例:

set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1) # VERSION 指代动态库版本,SOVERSION 指代 API 版本

link_directories

向工程添加多个特定的库文件搜索路径。

语法:

link_directories(dir1 dir2 …)  

示例:

link_directories(/usr/lib ./lib)

target_link_libraries

为 target 添加需要链接的共享库。

语法:

target_link_libraries(target library1 <debug | optimized> library2…)

示例:

target_link_libraries(main hello)
# 也可以写成
target_link_libraries(main libhello.so)
# 链接静态库
target_link_libraries(main libhello.a)

install

安装文件

语法:

安装目标文件

install(TARGETS targets [[ARCHIVE | LIBRARY | RUNTIME]
    [DESTIONATION <dir>]
    [PERMISSIONS permissions..]
    [CONFIGURATIONS [Debug | Release | ...]]
    [COMPONENT <component>]
    [OPTIONAL]
    [...])
  • ARCHIVE :静态库文件
  • LIBRARY :动态库文件
  • RUNTIME :可执行文件
  • DESTINATION :定义安装路径,如果是绝对路径则覆盖 CMAKE_INSTALL_PERFIX,否则是指相对 CMAKE_INSTALL_PERFIX 的相对路径

示例:

install(TARGETS myrun mylib mystaticlib
        RUNTIME DESTINATION bin
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION libstatic
    )

上面的例子将:

  • 二进制文件 myrun 安装到 ${CMAKE_INSTALL_PERFIX}/bin 目录
  • 二进制文件 libmylib 安装到 ${CMAKE_INSTALL_PERFIX}/lib 目录
  • 二进制文件 liblibstatic 安装到 ${CMAKE_INSTALL_PERFIX}/libstatic 目录

安装普通文件

install(FILES files ... DESTINATION <dir>
        [PERMISSIONS permissions...]
        [CONFIGURATUONS [Debug | Release | ...]]
        [COMPONENT <component>]
        [RENAME <name>]
        [OPTIONAL])

可以指定权限,如果不指定,则默认是 644 权限。

目录安装

install(DIRECTORY dirs... DESTINATION <dir>
    [FILE_PERMISSIONS permissions...]
    [DIRECTORY_PERMISSIONS permissions...]
    [USE_SOURCE_PERMISSIONS]
    [CONFIGURATIONS [Debug | Release]...]
    [COMPONENT <component>]
    [[PATTERN <pattern> | REGEX <regex>] [EXCLUDE] [PERMISSIONS permissions...]]
    [...])

add_executable

增加可执行文件。

语法:

add_library(exename source1 source2 … sourceN)

示例:

add_executable(main main.cpp)

add_subdirectory

向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。

语法:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

示例:

add_subdirectory(src)

aux_source_directory

发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。

语法:

aux_source_directory(dir VARIABLE)

示例:

aux_source_directory(. SRC)

add_definitions

向 C/C++ 编译器添加 -D 定义。

语法:

add_definitions(-DMACRO1 -DMACRO2)

示例:

add_definitions(-DENABLE_DEBUG)

使用 cmake 指定时也可以定义宏:

cmake -DENABLE_DEBUG=on
cmake -DENABLE_DEBUG=off

add_dependencies

定义 target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建。

语法:

add_dependencies(target -name depend -target1 depend -target2 ...)

示例:

add_dependencies(main a.so b.so)

add_test 与 enable_testing

enable_testing() 用来控制 Makefile 是否构建 test 目标,一般放在工程的主 CMakeLists.txt 中。

语法:

add_test(testname Exename arg1 arg2 ...)

testname 是自定义的 test 名称,Exename 可以是构建的目标文件也可以是外部脚本等。如果没有在同一个 CMakeLists.txt 中打开 enable_testing() ,任何 add_test 都是无效的。

示例:

add_test(mytest ${PROJECT_BINARY_DIR}/bin/main)
enable_testing()

生成 Makefile 后,就可以运行 make test 来执行测试。

file

文件操作指令。

语法:

file(WRITE filename "message to write...")
file(APPEND filename "message to append...")
file(READ filename variable)
file(REMOVE [directories]...)
file(REMOVE_RECURSE [directories]...)
file(MAKE_DIRECTORY [directories]...)

include

载入 CMakeLists.txt 文件或者预定义的 cmake 模块。载入的内容在处理到 include 时直接执行。如果指定的是一个模块,将会在 CMAKE_MODULE_PATH 中搜索。

语法:

include(file1 [OPTIONAL])
include(module [OPTIONAL])
  • OPTIONAL :文件不存在时也不会产生错误。

find

find_file(<VAR> name1 path1 path2 ...) # VAR 变量代表找到的文件全路径,包含文件名
find_library(<VAR> name1 path1 path2 ...) # VAR 变量代表找到的库全路径,包含文件名
find_path(<VAR> name1 path1 path2 ...) # VAR 变量代表包含这个文件的路径
find_program(<VAR> name1 path1 path2 ...) # VAR 变量代表包含这个程序的全路径
find_package(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED | COMPONENTS] [components...]]) # 调用预定义在 CMAKE_MODULE_PATH 下的 Find<name>.cmake 模块
  • find_package 的 REQUIRED 指定这个共享库是不是工程必须的。

控制指令

if

语法:

if(expression)
    command1(args...)
    ...
else if(expression)
    command2(args...)
    ...
else(expression)
    command3(args...)
    ...
endif(expression)
  • 凡是出现 if 的地方一定要有对应的 endif
  • 出现 elseif 的地方,endif 是可选的
  • 如果 set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)else()endif()expression 可以省略

示例:

if(var) # 如果变量不是:空,N,NO,OFF,FALSE,NOTFOUND 或者 <var>_NOTFOUND 时,表达式为真
if(NOT var) # 与上述条件相反
if(var1 AND var2) 
if(var1 OR var2)
if(COMMAND cmd) # 给定的 cmd 确是是命令并且可以调用为真
if(EXISTS dir) 
if(EXISTS file)
if(file1 IS_NEWER_THAN file2) # 当 file1 比 file2 新为真
if(IS_DIRECTORY dirname) # 当 dirname 是目录时为真
if(variable MATCHES regex)
if(string MATCHES regex)


# 数字比较表达式
if(variable LESS number)
if(string LESS number)
if(variable GREATER number)
if(string GREATER number)
if(variable EQUAL number)
if(string EQUAL number)


# 按字母顺序排列进行比较
if(variable STRLESS string)
if(string STRLESS string)
if(variable STRGREATER string)
if(string STRGREATER string)
if(variable STREQUAL string)
if(string STREQUAL string)


if(DEFINED variable) # 如果变量被定义,为真


if(WIN32)
    ...
elseif(UNIX)
    ...
elseif(APPLE)
    ...
endif(WIN32)

while

语法:

while(condition)
    command1(args...)
    command2(args...)
endwhile(condition)

其真假判断条件可以参考 if 指令。

for_each

for_each 有三种使用形式:

列表

语法:

foreach(loop_var arg1 arg2...)
    command1(args ...)
    command2(args ...)
endforeach(loop_var)

示例:

aux_source_directory(. SRC_LIST)
foreach(F ${SRC_LIST})
    message(${F})
endforeach(F)

范围

语法:

foreach(loop_var RANGE total)
endforeach(loop_var)

示例:

foreach(VAR RANGE 10)
    message(${VAR})
endforeach()

范围和步进

语法:

foreach(loop_var RANGE start stop [step])
endforeach(loop_var)

start 开始到 stop 结束,以 step 为步进。

示例:

foreach(A RANGE 5 15 3)
    message(${A})
endforeach(A)

模块的使用和自定义模块

使用 FindCURL 模块

方法一:

include_directories(/usr/inclue) # 头文件目录
target_link_libraries(curTest curl) # 指定链接库

方法二:

find_package(CURL)
if(CURL_FOUND)
    include_directories(${CURL_INCLUDE_DIR})
    target_link_libraries(curTest ${CURL_LIBRARY})
else(CURL_FOUND)
    message(FATAL_ERROR "curl library not found")
endif(CURL_FOUND)

自定义模块

定义 cmake/FindHELLO.cmake 模块

find_path(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello)
find_library(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/lib)
if(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
    set(HELLO_FOUND TRUE)
endif(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
if(HELLO_FOUND)
    if(NOT HELLO_FIND_QUIETLY)
        message(STATUS "Found Hello:${HELLO_LIBRARY}")
    endif(NOT HELLO_FIND_QUIETLY)
else(HELLO_FOUND)
    if(HELLO_FIND_REQUIRED)
        message(FATAL "Could not find hello library")
    endif(HELLO_FIND_REQUIRED)
endif(HELLO_FOUND)

使用:

find_package(HELLO)
if(HELLO_FOUND)
    add_executable(hello main.c)
    include_directories(${HELLO_INCLUDE})
    target_link_libraries(hello ${HELLO_LIBRARY})
endif(HELLO_FOUND)

主工程中应该加入:

set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

欢迎关注WX:CPP Programmer
回复 "资料",收取相关学习资料和电子书。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容