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的差异:
- marco没有SCOPE的概念,只是字符串的替换,有些类似于C语言中的define,在预处理阶段就进行了字符串的替换,通过v1的值的打印即可看出
- 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