CMake基础知识

CMake 简要内容

CMake 是cross platform make的简写,从这里你完全可以看出,CMake是基于Make来实现相关的内容的,换句话说,CMake就是在Make的基础上抽象出来的更高级的框架。

CMakeLists.txt的编译test.cpp生成test可执行文件的基本例子:

cmake_minimum_required(VERSION 2.8.10)
SET(PROJECT_NAME test)
project(${PROJECT_NAME})
add_executable(test test.cpp)  

通过以下Shell Command:

mkdir -p build && cd build && cmake .. && make 

通过上文的shell命令,其实你也已经发现了,cmake会生成Makefile,然后我们需要调用make来生成可执行文件。

CMakeLists.txt 编写要点

常用的cmake指令解释

cmake_minimum_required(VERSION xxx) #cmake最小版本需求,新版本的cmake改了很多东西,提升了便利性,也可能让你自己挖坑了
project(xxx) #设置此项目的名称
add_executable(target target_source_codes) #生成可执行文件target ,后面填写的是生成此可执行文件所依赖的源文件列表。
SET(var_name var_value)# 设置一个名字var_name 的变量,同时给此变量赋值为var_value
MESSAGE("MSG") #类比echo 打印消息
option(var_name "comment" var_value) #给变量var_name赋值为var_value,comment是此变量的注释,和SET 有类似的功效,用于给某变量设置默认值
include_directories(xxx) #添加include路径,也就是 gcc -I xxx 的意思,或者vs ide中添加头文件包含目录
add_subdirectory(xxx) #调用xxx子目录的CMakeLists.txt执行
add_compile_options(xxx) #给编译器添加xxx参数,但是貌似没有什么用,我一般不这样添加参数,不直接
link_directories(xxx) #给编译器添加库目录,也就是 gcc -L xxx 的意思,或者vs ide中添加库的包含目录
add_library(lib_name SHARED or STATIC lib_source_code) #和add_executable类似,生成库文件,SHARED代表动态库,STATIC代表静态库, 最后一个参数代表此库的源文件列表,此指令只有三个参数
target_link_libraries(target_name lib_name ...) #给目标添加依赖库,类似与gcc -l lib_name,此指令有两个用处,一个是给可执行target_name 添加库依赖,二是给库target_name 添加库依赖。

常见的cmake指令也就是上述的这些,还有部分比较常见的指令这里没有列出,我放到了下面单独讲解如:install()

cmake 流控制指令相关
条件语句
if(xxx)
...
elseif(xx)
...
else()
...
endif()
常见条件语句用法为:

if (va) va为bool型
if (va MATCHES xxx) va 是string类型,如果va包含了xxx,则此句为真

在CMake中if语法比较简单,if后面括号中的参数随着CMake版本的推进,在else和endif中也可以不用写了。

if(address)
else()
endif()

对于if语法,比较常用的就是字符串比较了,这里个人简单用到过两种,一种是这个变量有没有定义过。有两种写法可以验证

if(DEFINED address)
else()
endif()
# or 
if(address)
else()
endif()

两种都可以验证这个变量有没有定义过,注意:是定义过,比如你在CMake命令行中随便写了-Daddress=xxx,就表示定义过了,对里面的值没有任何要求。

第二中常用的用法就是字符串比较,比如

if(${address} STREQUAL "ON")
else()
endif()

#当然去掉大括号也是可以比较的

if(address STREQUAL "ON")
else()
endif()

这种就是比较字符串的值了,注意:在这个时候,你的脚本已经假设你对于address已经有了已被定义的默认要求,也就是说此时如果你没有定义过address变量,在正常处理cmake …的时候会报错,会提示无法识别的参数“STREQUAL”和"ON",这个需要特别注意。还有就是如果真的要加上大括号来取值,适当加上双引号可以防止出现一些不必要的麻烦。

有的同学会问了,如果我就想有个默认值呢?如果我就想,当我自己不去设这个,我希望它存在,被定义过,并且有个自己的默认值,有没有办法?

答案当然是有的,强大的CMake为我们准备了–option这个命令,给我们作为默认初始值并且作为定义值的候选。

option(address "This is a option for address" ON)

此时表示,如果用户没有定义过address,那我address的默认值就是ON,如果用户在命令行显示改变过address的值比如为OFF,那么在脚本中address的值就是OFF。

有一个需要注意,就是对于那些依赖于address有没有定义过的时候,如果你用了option你要特别注意,就是在脚本处理过程中,如果你没有在命令行中显示定义过address,那么在option之前的语句对于address都是没有定义过,直到到达了option,此时才真正定义了address.说起来不好理解,给个例子

if(address)
    message("defined address!!!!!!!!!!")
else()
    message("NOT defined address!!!!!!!!!")
endif()

option(address "hello world" ON)
message("option is ${address}")

if(address)
    message("defined address!!!!!!!!!!")
else()
    message("NOT defined address!!!!!!!!!")
endif()

输出结果为

NOT defined address!!!!!!!!!!
option is a
defined address!!!!!!!!!!

配合option和if一起使用,可以灵活多变的针对编译环境做各种定制化处理,包括项目中宏的各种替换和使用等等。这里我给大家开了个简单的头,希望这篇文章能够给你带来哪怕只有一点点的帮助就够了 : )
if 和option转载自:
https://www.cnblogs.com/lidabo/p/7359271.html

循环语句
foreach(va va_lists)
...
endforeach()

在foreach中,va的值会依次被va_lists的值替换

macro 和 function
macro(name arg ...)
...
endmacro()
function(name arg ...)
...
endfunction()

宏和函数效果都类似,唯一区别为function中的变量为局部的。

install 指令(主要是生成Makefile中的install target)
install(FILES flie DESTINATION dir_path) #执行make install时,把file拷贝到dir_path
install(PROGRAMS file DESTINATION dir_path) #执行make install时,把file拷贝到dir_path,并给予file可执行权限
INSTALL(TARGETS  ylib ylib_s
    RUNTIME DESTINATION xxx
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)
# 安装libylib.so到lib目录,安装libylib_s.a到lib目录,RUNTIME 是安装可执行文件到xxx目录,注意这个指令有个坑,我后面会说明这个问题。
configure_file指令
configure_file(fileA fileB @ONLY)
#把fileA 复制并重命名为fileB,此时,fileA中的@var@的值会被替换为cmakelists.txt 中var的值。@ONLY是只转换@va@这种变量
CMakeLists.txt常用的内置变量
CMAKE_INSTALL_PREFIX  #make install 的安装路径
CMAKE_BUILD_TYPE #生成的目标为debug或者release
CMAKE_C_FLAGS #gcc 的编译参数指定,这个非常好用,一般通过set 修改其值
CMAKE_CXX_FLAGS #g++ 和上面CMAKE_C_FLAGS 类似
CMAKE_CURRENT_SOURCE_DIR # 当前CMakeLists.txt所在的目录,主要用来定位某文件
CMAKE_CURRENT_BINARY_DIR # 当前CMakeLists.txt对应的编译时的目录
XXXConfig.cmake文件(cmake模块文件)编写以及引用
yLibConfig.cmake

find_path(yLib_INCLUDE_DIR NAMES ylib.h PATHS @CMAKE_INSTALL_PREFIX@/include) 

find_library(yLib_LIBRARY NAMES ylib PATHS @CMAKE_INSTALL_PREFIX@/lib) 
#find_library 会到@CMAKE_INSTALL_PREFIX@/lib目录查询libylib.so


set(yLib_FOUND TRUE) 
set(yLib_INCLUDE_DIRS ${yLib_INCLUDE_DIR}) 
set(yLib_LIBS ${yLib_LIBRARY}) 


mark_as_advanced(yLib_INCLUDE_DIRS yLib_LIBS )

XXX_INCLUDE_DIR 
XXX_LIBRARY 
XXX_FOUND 
XXX_INCLUDE_DIRS 
XXX_LIBS 

以上变量最好都定义了,不然find_package可能会报错
.cmake 文件就是定义了相关include变量和lib变量,没有什么其他的东西

调用:
set(yLib_DIR "@CMAKE_INSTALL_PREFIX@/cmake")
#设置.cmake 的目录所在
find_package(yLib REQUIRED)
#find_package会导入.cmake 中的相关变量,完成相关模块的导入
一个关于install()指令的深坑
INSTALL(TARGETS  ylib ylib_s
    #RUNTIME DESTINATION xxx
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)

#对于RUNTIME 和 LIBRARY 两种目标,在安装时候,cmake会默认给你移除掉目标文件中的gcc的Wl,rpath的值,导致某些库找不到的错误。
以下变量会影响此坑,更详细的信息去查查别的资料,我这里就不详细说明了。

#set(CMAKE_SKIP_BUILD_RPATH FALSE)                
#set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)        
#set(CMAKE_INSTALL_RPATH "")                      
#set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)    
#set(CMAKE_SKIP_INSTALL_RPATH TRUE)
#set(CMAKE_INSTALL_PATH "${CMAKE_INSTALL_PREFIX}/lib")

#set(CMAKE_SKIP_RPATH TRUE)
#set(CMAKE_SKIP_INSTALL_RPATH TRUE)

注意:cmake会直接修改你的二进制文件替换掉rpath的相关信息。默认替换的值是一个空值,也就是说移除掉了你设置的rpath的值

说明
以上只是介绍了cmake中常见的内容,而且很多内容只涉及到一般的使用方法,某些指令还有很多其他的操作,我这里没有介绍。如果需要了解更加详细的信息,我推荐各位去看cmake 的doc。

作者:Iflyinsky2013
来源:优快云
原文:https://blog.youkuaiyun.com/u011728480/article/details/81480668

INSTALL指令

安装的需要有两种,一种是从代码编译后直接make install安装,一种是打包时的指定目录安装。
这里需要引入一个新的cmake 指令 INSTALL和一个非常有用的变量CMAKE_INSTALL_PREFIX。
CMAKE_INSTALL_PREFIX变量类似于configure脚本的 –prefix,常见的使用方法看
起来是这个样子:

cmake -DCMAKE_INSTALL_PREFIX=/usr .

INSTALL指令用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及
文件、目录、脚本等。

INSTALL指令包含了各种安装类型,我们需要一个个分开解释:

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

参数中的TARGETS后面跟的就是我们通过ADD_EXECUTABLE或者ADD_LIBRARY定义的
目标文件,可能是可执行二进制、动态库、静态库。

目标类型也就相对应的有三种,ARCHIVE特指静态库,LIBRARY特指动态库,RUNTIME
特指可执行目标二进制。

DESTINATION定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候
CMAKE_INSTALL_PREFIX其实就无效了。如果你希望使用CMAKE_INSTALL_PREFIX来
定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是

${CMAKE_INSTALL_PREFIX}/<DESTINATION定义的路径>

举个简单的例子:

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

上面的例子会将:

可执行二进制myrun安装到${CMAKE_INSTALL_PREFIX}/bin目录 
动态库libmylib安装到${CMAKE_INSTALL_PREFIX}/lib目录 
静态库libmystaticlib安装到${CMAKE_INSTALL_PREFIX}/libstatic目录 
特别注意的是你不需要关心TARGETS具体生成的路径,只需要写上TARGETS名称就可以 
了。
普通文件的安装
INSTALL(FILES files... DESTINATION <dir>
         [PERMISSIONS permissions...]
         [CONFIGURATIONS [Debug|Release|...]]
         [COMPONENT <component>]
         [RENAME <name>] [OPTIONAL])

可用于安装一般文件,并可以指定访问权限,文件名是此指令所在路径下的相对路径。
如果默认不定义权限PERMISSIONS,

安装后的权限为,OWNER_WRITE,OWNER_READ,GROUP_READ,和WORLD_READ,即644权限。
非目标文件的可执行程序安装(比如脚本之类)
INSTALL(PROGRAMS files... DESTINATION <dir>
     [PERMISSIONS permissions...]
     [CONFIGURATIONS [Debug|Release|...]]
     [COMPONENT <component>]
     [RENAME <name>] [OPTIONAL])

跟上面的FILES指令使用方法一样,唯一的不同是安装后权限为:

OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE,即755权限

目录的安装

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...]] [...])

这里主要介绍其中的DIRECTORY、PATTERN以及PERMISSIONS参数。
DIRECTORY后面连接的是所在Source目录的相对路径,但务必注意:
abc和abc/有很大的区别。
abc意味着abc这个目录会安装在目标路径下;
abc/意味着abc这个目录的内容会被安装在目标路径下;

如果目录名不以/结尾,那么这个目录将被安装为目标路径下的abc,如果目录名以/结尾,
代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。
PATTERN用于使用正则表达式进行过滤,
PERMISSIONS用于指定PATTERN过滤后的文件权限。

我们来看一个例子:

INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
        PATTERN "CVS" EXCLUDE
        PATTERN "scripts/*"
        PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
        GROUP_EXECUTE GROUP_READ)

这条指令的执行结果是:
将icons目录安装到 /share/myproj,将scripts/中的内容安装到
/share/myproj
不包含目录名为CVS的目录,对于scripts/*文件指定权限为 OWNER_EXECUTE
OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ.

安装时cmake脚本的执行

INSTALL([[SCRIPT <file>] [CODE <code>]] [...])
SCRIPT参数用于在安装时调用cmake脚本文件(也就是<abc>.cmake文件)
CODE参数用于执行CMAKE指令,必须以双引号括起来。比如:
INSTALL(CODE "MESSAGE(\"Sample install message.\")")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值