CMake搭建项目工程(2)-CMake控制命令、函数与宏、安装、模块

CMake+GoogleTest搭建项目工程(1)-C/C++编译及CMake那些事
本篇主要阐述CMake的其他常用命令,并给出一个示例代码。

CMake控制命令

if else endif指令
if(expression)
    command(……)
else(expression)
    command(……)
endif(expression)
if elseif endif指令
if(expression_1)
   command(……)
elseif(expression_2)
   command(……)
elseif(expression_3)
   command(……)
endif(expression_1)

if的判断条件:
如果expression是变量,则当expression为:空、0、N、 NO、 OFF、 FALSE、 NOTFOUND 、expression_NOTFOUND 时,表达式为假,其余为真。
同时支持与(and)、或(or)、非(not)的语义,与常规语言的与或非含义一致。

while指令
while(expression)
    command(……)
endwhile(expression)       

与常规编程语言的while一致。

foreach
//var依次取列表arg中的值
foreach(var arg1 arg2……)
    command(……)
endforeach(var)
//var从0到total以1为步长增加
foreach(var range total)
    command(……)
endforeach(var)
//var从start到end, 以step为步长增加
foreach(var range start end step)
    command(……)
endforeach(var)

CMake宏与函数

  • CMake的函数与宏可用的变量
name description
ARGC 函数实参的个数
ARGV 所有实参列表
ARGN 保留函数形参列表以后的所有参数列表。
ARGV0 函数第1个实参
ARGV1 函数第2个实参
依次类推 依次类推
function
function(<name> [arg1 [arg2 [arg3 ...]]])
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
endfunction(<name>)
macro
macro(<name> [arg1 [arg2 [arg3 ...]]])
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
endmacro(<name>)
macro与function的区别

macro是CMake的宏定义,function为CMake的函数,其差异与C语言中宏和函数的差异类似,我们对比以下代码:

cmake_minimum_required(VERSION 2.8)
set(v1 "ABC")
set(v2 "XYZ")

macro(Macro v1 v2)
  message("ARGC = ${ARGC} ARGV = ${ARGV}")
  message("ARGV0 = ${ARGV0} ARGV1 = ${ARGV1} ARGN = ${ARGN}")
  message("----------------------------")
  message("v1 = ${v1} v2 = ${v2}")
  set(v1 "abc")
  set(v2 "xyz")
  message("v1 = ${v1} v2 = ${v2}")
endmacro()

Macro(${v1} v2 x y z)
message("v1 = ${v1} v2 = ${v2}")

message("----------------------------")

set(v1 "ABC")
set(v2 "XYZ")
function(Function  v1 v2)
  message("v1 = ${v1} v2 = ${v2}")
  set(v1 "abc")
  set(v2 "xyz" PARENT_SCOPE)
  message("v1 = ${v1} v2 = ${v2}")
endfunction()

Function(${v1} v2)
message("v1 = ${v1} v2 = ${v2}")

它的输出结果为

ARGC = 5 ARGV = ABC;v2;x;y;z
ARGV0 = ABC ARGV1 = v2 ARGN = x;y;z
----------------------------
v1 = ABC v2 = v2
v1 = ABC v2 = v2
v1 = abc v2 = xyz
----------------------------
v1 = ABC v2 = v2
v1 = abc v2 = v2
v1 = ABC v2 = xyz

通过上面的代码,我们首先可以看到marco与function的可用变量ARG*,同时可以看出marco与function的差异:

  1. marco没有SCOPE的概念,只是字符串的替换,有些类似于C语言中的define,在预处理阶段就进行了字符串的替换,通过v1的值的打印即可看出
  2. function有SCOPE的概念,虽然marco与function都完成了v2的赋值,但marco是字符串的替换,而function的入参写为v2形式,并且需要加上PARENT_SCOPE。

CMake安装 (install)

CMake install一般用于库、头文件的安装,当然也可以安装目录,我们写好CMakeLists.txt后在build目录执行cmake生成makefile,之后执行make进行编译,编译是不会进行安装的(这也是install用于安装文件与file的差别),需要额外执行make install进行安装。

目标文件的安装
INSTALL(TARGETS targets... [[ARCHIVE|LIBRARY|RUNTIME]
                [DESTINATION <dir>]
                [PERMISSIONS permissions...]
                [CONFIGURATIONS
                [Debug|Release|...]]
                [COMPONENT <component>]
                [OPTIONAL]] )

作用:目标文件的安装,目标文件TARGETS 后面跟的可以是二进制可执行文件(ADD_EXECUTABLE的target)或库文件(ADD_LIBRARY的target)。目标类型分为三种,ARCHIVE特指静态库,LIBRARY特指动态库,RUNTIME 特指可执行目标二进制。可以一次安装多个目标文件。

普通文件的安装
INSTALL(FILES files... DESTINATION <dir>
              [PERMISSIONS permissions...]
              [CONFIGURATIONS [Debug|Release|...]]
              [COMPONENT <component>]
              [RENAME <name>] [OPTIONAL])

作用:一般文件的安装,可以指定访问权限,如果默认不定义权限PERMISSIONS,安装后的权限为:OWNER_WRITE & OWNER_READ & GROUP_READ & WORLD_READ。

非目标文件的可执行程序安装
INSTALL(PROGRAMS files... DESTINATION <dir>
     [PERMISSIONS permissions...]
     [CONFIGURATIONS [Debug|Release|...]]
     [COMPONENT <component>]
     [RENAME <name>] [OPTIONAL])

作用:非目标文件的可执行程序安装,默认的权限为OWNER_EXECUTE & GROUP_EXECUTE & WORLD_EXECUTE。

目录的安装
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.h,Add.cc 和CMakeLists.txt分别如下:(需求将Add.h安装到/usr/local/WalkeR-ZG/include目录下,将静态库文件libadd.a安装到/usr/local/WalkeR-ZG/lib目录下)
Add.h:

int add(int a, int b);

Add.cc:

#include "Add.h"
int add(int a, int b){
    return a + b;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
set(CMAKE_INSTALL_PREFIX /usr/local/WalkeR-ZG)
add_library(add STATIC Add.cc)
target_include_directories(add PRIVATE include "${PROJECT_SOURCE_DIR}")
install(TARGETS add ARCHIVE DESTINATION lib)
install(FILES Add.h DESTINATION include)

CMake模块

CMake模块主要的作用是当我们需要使用外部库时,需要知道外部库的头文件和链接库的位置,CMake模块主要就给出了头文件及链接库的查找方法。
模块使用find_package(xxx)被调用,每个模块会定义如下变量:

  • xxx__FOUND:用来判断模块是否被找到,如果没有找到,根据工程的需要关闭某些特性、给出提醒或者中止编译。
  • xxx_INCLUDE_DIR: 头文件的目录
  • xxx_LIBRARY (xxx_LIBRARIES):库文件目录

find_package(xxx)用来调用预定义在CMAKE_MODULE_PATH下的Findxxx.cmake模块,你也可以自己定义Find<name> 模块,通过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录供工程使用。
结合上面给出的代码编译安装的Add的头文件及静态库,编写一个cmake的模块。目录结构如下:
顶层目录是demo目录,存在一个子目录cmake用于放模块。代码和CMakeLists.txt直接放在demo目录下。
main.cc:

#include <Add.h>
#include <iostream>

int main(){
    int a = add(3, 4);
    std::cout<<a<<std::endl;
    return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
find_package(Add)
if(Add_FOUND)
    add_executable(demo main.cc)
    target_include_directories(demo PRIVATE Add_INCLUDE_DIR)
    target_link_libraries(demo Add_LIBRARY)
else(Add_FOUND)
    message(FATAL_ERROR "Add cannot find!!!")
endif(Add_FOUND)

cmake/FindAdd.cmake:

find_path(Add_INCLUDE_DIR Add.h /usr/local/WalkeR-ZG/include)
find_library(Add_LIBRARY libadd.a /usr/local/WalkeR-ZG/lib)
if (Add_INCLUDE_DIR AND Add_LIBRARY)
    set(Add_FOUND TRUE)
endif(Add_INCLUDE_DIR AND Add_LIBRARY)

if(Add_FOUND)
    if(NOT Add_FIND_QUIETLY)
    message(STATUS "Found Add: ${Add_LIBRARY}")
    endif(NOT Add_FIND_QUIETLY)
endif(Add_FOUND)

CMake的内容粗略阐述完毕。

WalkeR_ZG

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

推荐阅读更多精彩内容