CMAKE

文章目录

  • 在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
  • cmake指令
    • 设置变量
    • ADD_DEFINITIONS
    • ADD_DEPENDENCIES
    • AUX_SOURCE_DIRECTORY
    • ADD_SUBDIRECTORY
    • INCLUDE指令
    • FIND_指令
      • FIND_PACKAGE
        • Search mode
        • 基本语法
        • 完整语法
    • FetchContent
      • FetchContent_Declare
      • FetchContent_MakeAvailable
    • FILE指令
    • 安装命令
    • 控制指令
      • IF指令
      • WHILE指令
      • FOREACH指令
  • 自定义编译选项
    • 修改CMakeList文件
    • 编写 config.h.in 文件
    • 修改 main.cpp 文件
  • 编译共享库
    • 动态库版本号
  • 使用外部共享库和头文件
    • 引入头文件搜索路径
    • 为target添加共享库
    • 特殊的环境变量
  • 常用变量和环境变量
    • 变量引用方式
    • 自定义变量的方式
    • cmake常用变量
    • cmake调用环境变量
    • cmake环境变量
      • 系统信息
      • 主要的开关选项:
  • 模块的使用和自定义模块
    • 编写属于自己的FindHello模块
  • 函数式编程
    • function
    • macro
      • 调用方式
      • macro与funtion的区别
      • macro中参数的处理方式
    • block
  • list operations
    • Reading
    • Search
    • Modification
    • Ordering
  • String operations
    • Search and Replace
    • Regex正则表达式
    • Manipulation
    • Comparison

Cmake是一个跨平台的软件构建工具,主要功能是告诉编译器如何工作。
它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。CMake 是一个比 GNU Make,QT 的 qmake,微软的 MS nmake,BSD Make(pmake) 更高级的编译配置工具。

在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

1.编写 CMake 配置/文件 CMakeLists.txt 。
2.执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile(ccmake 和 cmake 的区别在于前者提供了一个交互式的界面)。其中, PATH 是 CMakeLists.txt 所在的目录。
3.使用 make 命令进行编译。

cmake指令

cmake指令不区分大小写

设置变量

SET(<variable> [<value>] [CACHE TYPE DOCSTRING [FORCE]]);

ADD_DEFINITIONS

向C/C++编译器添加-D定义,比如:
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC),参数之间用空格分割。
如果你的代码中定义了#ifdef ENABLE_DEBUG #endif,这个代码块就会生效。
如果要添加其他的编译器开关,可以通过CMAKE_C_FLAGS变量和CMAKE_CXX_FLAGS
量设置。

ADD_DEPENDENCIES

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

ADD_DEPENDENCIES(<target-name> <depend-target1> <depend-target2> ...)

AUX_SOURCE_DIRECTORY

基本语法是:

AUX_SOURCE_DIRECTORY(<dir> VARIABLE)

作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前cmake还不能自动发现新添加的源文件。
比如

AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})

你也可以通过后面提到的FOREACH指令来处理这个LIST
AUX_SOURCE_DIRECTORY命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。其语法如下:

AUX_SOURCE_DIRECTORY(<dir> <variable>)

ADD_SUBDIRECTORY

add_subdirectory(<source_dir> [<binary_dir>] [EXCLUDE_FROM_ALL])

命令 ADD_SUBDIRECTORY 指明本项目包含一个子目录 ,这样 目录下的 CMakeLists.txt 文件和源代码也会被处理,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL参数的含义是将这个目录从编译过程中排除,比如,工程的example,可能就需要工程构建完成后,再进入example目录单独进行构建。
add_subdirectory指令(不论是否指定编译输出目录),我们都可以通过set指令重新定义EXECUTABLE_OUTPUT_PATHLIBRARY_OUTPUT_PATH变量来指定最终的目标二进制的位置(指最终生成的可执行文件或者最终的共享库,不包含编译生成的中间文件)

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

_BINARY_DIRPROJECT_BINARY_DIR变量,他们指的编译发生的当前目录,如果是内部编译,就相当于PROJECT_SOURCE_DIR也就是工程代码所在目录,如果是外部编译,指的是外部编译所在目录,也就是build目录。

INCLUDE指令

用来载入CMakeLists.txt文件,也用于载入预定义的cmake模块.

INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])

OPTIONAL参数的作用是文件不存在也不会产生错误。
你可以指定载入一个文件,如果定义的是一个模块,那么将在CMAKE_MODULE_PATH中搜
索这个模块并载入。
载入的内容将在处理到INCLUDE语句是直接执行。

FIND_指令

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_LIBRARY示例:

FIND_LIBRARY(libX X11 /usr/lib)
IF(NOT libX)
MESSAGE(FATAL_ERROR “libX not found”)
ENDIF(NOT libX)

FIND_PACKAGE

Search mode

  • Module mode
    在这个模式,CMake搜索形如Find.cmake的文件,首先在路径CMAKE_MODULE_PATH下搜索,然后在CMake installation提供的Find Modules中寻找。文件如果找到,就会被CMake读取并执行,来寻找软件包,检查版本信息和产生需要的信息。你也可以自己定义Find模块,通过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录中供工程使用。
    只有find_packge基本语法命令支持Module mode。

  • Config mode
    在这个模式,CMake搜索-config.cmakeConfig.cmake。如果版本被指定,则CMake会搜索形如-config-version.cmakeConfigVersion.cmake 的文件。
    基本语法命令和完整语法命令都支持Config mode。

  • FetchContent redirection mode
    New in version 3.24: find_package()的调用可以被重定向到FetchContent模块提供的软件包内部。

如果使用基本语法命令,find_packge()会首先以Module mode 搜索,如果未找到则再以Config mode搜索。如果使用完整语法命令,则只会以Config mode搜索。

find_package查找路径对应的环境变量如下:

_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH

基本语法

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [REGISTRY_VIEW  (64|32|64_32|32_64|HOST|TARGET|BOTH)]
             [GLOBAL]
             [NO_POLICY_SCOPE]
             [BYPASS_PROVIDER])

用来调用预定义在CMAKE_MODULE_PATH下的Find.cmake模块,我们在后面的章节会详细介绍FIND_PACKAGE的使用方法和Find模块的编写。

完整语法

find_package(<PackageName> [version] [EXACT] [QUIET]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [CONFIG|NO_MODULE]
             [GLOBAL]
             [NO_POLICY_SCOPE]
             [BYPASS_PROVIDER]
             [NAMES name1 [name2 ...]]
             [CONFIGS config1 [config2 ...]]
             [HINTS path1 [path2 ... ]]
             [PATHS path1 [path2 ... ]]
             [REGISTRY_VIEW  (64|32|64_32|32_64|HOST|TARGET|BOTH)]
             [PATH_SUFFIXES suffix1 [suffix2 ...]]
             [NO_DEFAULT_PATH]
             [NO_PACKAGE_ROOT_PATH]
             [NO_CMAKE_PATH]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_PACKAGE_REGISTRY]
             [NO_CMAKE_BUILDS_PATH] # Deprecated; does nothing.
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_INSTALL_PREFIX]
             [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH])

CONFIGNO_MODULE选项是等价的。名为_DIR的缓存条目被创建用来保存configuration file存在的目录。

FetchContent

该模块可以通过ExternalProject模块支持的任何方法在配置阶段填充内容。ExternalProjectproject_add()在构建阶段下载所需的内容,而FetchContent模块使需要的内容立即可用,从而允许在配置阶段就能够在类似add_subdirectory(),include()file()的操作中使用需要的内容。

FetchContent_Declare

FetchContent_Declare(
  <name>
  <contentOptions>...
  [SYSTEM]
  [OVERRIDE_FIND_PACKAGE |
   FIND_PACKAGE_ARGS args...]
)

选项可以是任何ExternalProject_Add()可以理解的download,update,patch选项。
在多数情况下,
In most cases, 是一系列定义下载方式和方式特定的信息比如 commit tag或者archive hash。

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)

FetchContent_Declare(
  myCompanyIcons
  URL      https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
  URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
)

FetchContent_Declare(
  myCompanyCertificates
  SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs
  SVN_REVISION   -r12345
)

FetchContent_MakeAvailable

New in version 3.14.

FetchContent_MakeAvailable(<name1> [<name2>...])

该命令确保在返回时所有列出的依赖项可用。对于每个依赖项,都必须有一个FetchContent_Declare() 调用。

FILE指令

文件操作指令,基本语法为:

FILE(WRITE filename "message to write"... )
FILE(APPEND filename "message to write"... )
FILE(READ filename variable)
FILE(GLOB  variable [RELATIVE path] [globbing
expressions]...)
FILE(GLOB_RECURSE variable [RELATIVE path]
[globbing expressions]...)
FILE(REMOVE [directory]...)
FILE(REMOVE_RECURSE [directory]...)
FILE(MAKE_DIRECTORY [directory]...)
FILE(RELATIVE_PATH variable directory file)
FILE(TO_CMAKE_PATH path result)
FILE(TO_NATIVE_PATH path result)
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo1)
# 指定生成目标
add_executable(<exe_name> ${EXE_SRCS})
  • cmake_minimum_required:指定运行此配置文件所需的 CMake 的最低版本;
  • project:参数值是 Demo1,该命令表示项目的名称是 Demo1 。
  • add_executable:将名为 main.cc 的源文件编译成一个名称为 Demo 的可执行文件。

安装命令

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}/

普通文件的安装:

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

这里主要介绍其中的DIRECTORYPATTERN以及PERMISSIONS参数。
DIRECTORY后面连接的是所在Source目录的相对路径,但务必注意: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

控制指令

IF指令

基本语法为:

IF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE(expression)
# ELSE section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF(expression)

另外一个指令是ELSEIF,总体把握一个原则,凡是出现IF的地方一定要有对应的
ENDIF.出现ELSEIF的地方,ENDIF是可选的。
表达式的使用方法如下:

  • IF(var),如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND或
    _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新,或者file1/file2其中有一个不存在时为真,文件名请使用完整路径。
  • IF(IS_DIRECTORY dirname),当dirname是目录时,为真。
  • IF(variable MATCHES regex)
    IF(string MATCHES regex)
    当给定的变量或者字符串能够匹配正则表达式regex时为真。比如:
    IF("hello" MATCHES "ell")
    	MESSAGE("true")
    ENDIF("hello" MATCHES "ell")
    

数字比较表达式

  • 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),如果变量被定义,为真。

SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
这时候就可以写成:

IF(WIN32)
ELSE()
ENDIF()

WHILE指令

WHILE指令的语法是:

WHILE(condition)
	COMMAND1(ARGS ...)
	COMMAND2(ARGS ...)
	...
ENDWHILE(condition)

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

FOREACH指令

FOREACH指令的使用方法有三种形式:

  1. 列表
    FOREACH(loop_var arg1 arg2 ...)
    	COMMAND1(ARGS ...)
    	COMMAND2(ARGS ...)
    	...
    ENDFOREACH(loop_var)
    
    像我们前面使用的AUX_SOURCE_DIRECTORY的例子
    AUX_SOURCE_DIRECTORY(. SRC_LIST)
    FOREACH(F ${SRC_LIST})
    	MESSAGE(${F})
    ENDFOREACH(F)
    
  2. 范围
    FOREACH(loop_var RANGE total)
    ENDFOREACH(loop_var)
    
    从0到total以1为步进
    举例如下:
    FOREACH(VAR RANGE 10)
    	MESSAGE(${VAR})
    ENDFOREACH(VAR)
    #输出
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
  3. 范围和步进
    FOREACH(loop_var RANGE start stop [step])
    ENDFOREACH(loop_var)
    
    从start开始到stop结束,以step为步进,
    举例如下
    FOREACH(A RANGE 5 15 3)
    	MESSAGE(${A})
    ENDFOREACH(A)
    #输出
    5
    8
    11
    14
    

这个指令需要注意的是,直到遇到ENDFOREACH指令,整个语句块才会得到真正的执行。

自定义编译选项

CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。

修改CMakeList文件

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo4)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
  "${PROJECT_SOURCE_DIR}/config.h.in"
  "${PROJECT_BINARY_DIR}/config.h"
  )
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
       "Use provided math implementation" ON)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/math")
  add_subdirectory (math)  
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo  ${EXTRA_LIBS})
  1. 第7行的 configure_file 命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。
  2. 第13行的 option 命令添加了一个 USE_MYMATH 选项,并且默认值为 ON 。
  3. 第17行根据 USE_MYMATH 变量的值来决定是否使用我们自己编写的 MathFunctions 库。

编写 config.h.in 文件

上面的程序值得注意的是第2行,这里引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:

#cmakedefine USE_MYMATH

这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件。

修改 main.cpp 文件

...
#ifdef USE_MYMATH
	#include "math/MathFunction.h"
#else
	#include 
#endif
...

编译共享库

通过set(LIBRARY_OUTPUT_PATH <路径>)来指定共享库输出的位置。

add_library(name [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL] <source1 source2 ... sourceN>)

cmake系统会自动为你生成libname.X
类型有三种:

  • SHARED,动态库
  • STATIC,静态库
  • MODULE,在使用dyld的系统有效,如果不支持dyld,则被当作SHARED对待。

EXCLUDE_FROM_ALL参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。

name作为target是不能重名的,这意味着,我们无法使用add_library 同时构建同名的静态库和动态库:

add_library(hello STATIC ${LIBHELLO_SRC})
add_library(hello SHARED ${LIBHELLO_SRC})

我们想要名字相同的静态库和动态库,因为target名称是唯一的,所以,我们肯定不能通过ADD_LIBRARY指令来实现了。这时候我们需要用到另外一个指令:

SET_TARGET_PROPERTIES,其基本语法是:

set_target_properties(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)

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

add_library(hello SHARED ${LIBHELLO_SRC})
add_library(hello_static STATIC ${LIBHELLO_SRC})
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")

但是这样也不能同时得到libhello.so/libhello.a两个库。可以发现libhello.a已经构建完成,位于build/lib目录中,但是libhello.so去消失了。这个问题的原因是:cmake在构建一
个新的target时,会尝试清理掉其他使用这个名字的库,因为,在构建libhello.a时,就会清理掉libhello.so.
为了回避这个问题,需要再次使用set_target_properties定义
CLEAN_DIRECT_OUTPUT属性。

set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT  1)
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

这时候,我们再次进行构建,会发现build/lib目录中同时生成了libhello.solibhello.a

动态库版本号

为了实现动态库版本号,我们仍然需要使用set_target_properties指令。
具体使用方法如下:

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

VERSION指代动态库版本,SOVERSION指代API版本。

使用外部共享库和头文件

引入头文件搜索路径

include_directories([AFTER|BEFORE] [SYSTEM] <dir1> <dir2> ...)

这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的
后面,你可以通过两种方式来进行控制搜索路径添加的方式:

  1. CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过SET这个cmake变量为on,可以
    将添加的头文件搜索路径放在已有路径的前面。
  2. 通过AFTER或者BEFORE参数,也可以控制是追加还是置前。

为target添加共享库

我们现在需要完成的任务是将目标文件链接到libhello,这里我们需要引入两个新的指令
link_directoriestarget_link_library

link_directories的全部语法是:

link_directories(<directory1> <directory2> ...)

这个指令非常简单,添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可执行二进制,在编译时就需要指定一下这些共享库的路径。

target_link_libraries的全部语法是:

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

这个指令可以用来为target添加需要链接的共享库,本例中是一个可执行文件,但是同样可以用于为自己编写的共享库添加共享库链接。

特殊的环境变量

CMAKE_INCLUDE_PATHCMAKE_LIBRARY_PATH是环境变量而不是cmake变量。
使用方法是要在bash中用export或者在csh中使用set命令设置或者CMAKE_INCLUDE_PATH=/home/include cmake ..等方式。

这里简单说明一下,FIND_PATH用来在指定路径中搜索文件名,比如:

find_path(myHeader NAMES hello.h PATHS /usr/include
/usr/include/hello)

这里我们没有指定路径,但是,cmake仍然可以帮我们找到hello.h存放的路径,就是因
为我们设置了环境变量CMAKE_INCLUDE_PATH
如果你不使用FIND_PATHCMAKE_INCLUDE_PATH变量的设置是没有作用的,你不能指
望它会直接为编译器命令添加参数-I
以此为例,CMAKE_LIBRARY_PATH可以用在FIND_LIBRARY中。
同样,因为这些变量直接为FIND_指令所使用,所以所有使用FIND_指令的cmake模块都会受益。

常用变量和环境变量

变量引用方式

使用${}进行变量的引用。在IF等语句中,是直接使用变量名而不通过${}取值

自定义变量的方式

主要有隐式定义和显式定义两种,前面举了一个隐式定义的例子,就是PROJECT指令,他会隐式的定义_BINARY_DIR_SOURCE_DIR两个变量。
显式定义的例子我们前面也提到了,使用SET指令,就可以构建一个自定义变量了。

cmake常用变量

  1. CMAKE_BINARY_DIR
    PROJECT_BINARY_DIR
    _BINARY_DIR
    这三个变量指代的内容是一致的,如果是in source编译,指得就是工程顶层目录,如果是out-of-source编译,指的是工程编译发生的目录。PROJECT_BINARY_DIR跟其他指令稍有区别,现在,你可以理解为他们是一致的。
  2. CMAKE_SOURCE_DIR
    PROJECT_SOURCE_DIR
    _SOURCE_DIR
    这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。也就是在in source编译时,他跟CMAKE_BINARY_DIR等变量一致。PROJECT_SOURCE_DIR跟其他指令稍有区别,现在,你可以理解为他们是一致的。
  3. CMAKE_CURRENT_SOURCE_DIR
    指的是当前处理的CMakeLists.txt所在的路径,比如上面我们提到的src子目录。
  4. CMAKE_CURRRENT_BINARY_DIR
    如果是in-source编译,它跟CMAKE_CURRENT_SOURCE_DIR一致,如果是out-of-source编译,他指的是target编译目录。
    使用我们上面提到的ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。
    使用SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。
  5. CMAKE_CURRENT_LIST_FILE
    输出调用这个变量的CMakeLists.txt的完整路径
  6. CMAKE_CURRENT_LIST_LINE
    输出这个变量所在的行
  7. CMAKE_MODULE_PATH
    这个变量用来定义自己的cmake模块所在的路径。如果你的工程比较复杂,有可能会自己编写一些cmake模块,这些cmake模块是随你的工程发布的,为了让cmake在处理CMakeLists.txt时找到这些模块,你需要通过SET指令,将自己的cmake模块路径设置一下。
    比如
    SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
    
    这时候你就可以通过INCLUDE指令来调用自己的模块了。
  8. EXECUTABLE_OUTPUT_PATHLIBRARY_OUTPUT_PATH
    分别用来重新定义最终结果的存放目录,前面我们已经提到了这两个变量。
  9. PROJECT_NAME
    返回通过PROJECT指令定义的项目名称。

cmake调用环境变量

使用$ENV{NAME}指令就可以调用系统的环境变量了。
比如

MESSAGE(STATUS “HOME dir: $ENV{HOME})

设置环境变量的方式是:

SET(ENV{变量名})

cmake环境变量

  1. CMAKE_INCLUDE_CURRENT_DIR
    自动添加CMAKE_CURRENT_BINARY_DIRCMAKE_CURRENT_SOURCE_DIR到当前处理的CMakeLists.txt。相当于在每个CMakeLists.txt加入:

    INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
    
  2. CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE
    将工程提供的头文件目录始终至于系统头文件目录的前面,当你定义的头文件确实跟系统发
    生冲突时可以提供一些帮助。

  3. CMAKE_INCLUDE_PATHCMAKE_LIBRARY_PATH我们在上一节已经提及。

系统信息

  1. CMAKE_MAJOR_VERSION,CMAKE主版本号,比如2.4.6中的2
  2. CMAKE_MINOR_VERSION,CMAKE次版本号,比如2.4.6中的4
  3. CMAKE_PATCH_VERSION,CMAKE补丁等级,比如2.4.6中的6
  4. CMAKE_SYSTEM,系统名称,比如Linux-2.6.22
  5. CMAKE_SYSTEM_NAME,不包含版本的系统名,比如Linux
  6. CMAKE_SYSTEM_VERSION,系统版本,比如2.6.22
  7. CMAKE_SYSTEM_PROCESSOR,处理器名称,比如i686.
  8. UNIX,在所有的类UNIX平台为TRUE,包括OS X和cygwin
  9. WIN32,在所有的win32平台为TRUE,包括cygwin

主要的开关选项:

  1. CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS,用来控制IF ELSE语句的书写方式,在
    下一节语法部分会讲到。
  2. BUILD_SHARED_LIBS
    这个开关用来控制默认的库编译方式,如果不进行设置,使用ADD_LIBRARY并没有指定库
    类型的情况下,默认编译生成的库都是静态库。
    如果SET(BUILD_SHARED_LIBS ON)后,默认生成的为动态库。
  3. CMAKE_C_FLAGS
    设置C编译选项,也可以通过指令ADD_DEFINITIONS()添加。
  4. CMAKE_CXX_FLAGS
    设置C++编译选项,也可以通过指令ADD_DEFINITIONS()添加。

模块的使用和自定义模块

FIND_PACKAGE(CURL)
IF(CURL_FOUND)
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR ”CURL library not found”)
ENDIF(CURL_FOUND)

对于系统预定义的Find.cmake模块,使用方法一般如上例所示:
每一个模块都会定义以下几个变量

  • _FOUND
  • _INCLUDE_DIR or _INCLUDES
  • _LIBRARY or _LIBRARIES

你可以通过_FOUND来判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。
如果_FOUND为真,则将_INCLUDE_DIR加入INCLUDE_DIRECTORIES
_LIBRARY加入TARGET_LINK_LIBRARIES中。

编写属于自己的FindHello模块

  1. 定义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_ERROR "Could not find hello library")
	ENDIF (HELLO_FIND_REQUIRED)
ENDIF (HELLO_FOUND)

针对上面的模块让我们再来回顾一下FIND_PACKAGE指令:

FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets...]])
  • QUIET参数,对应与我们编写的FindHELLO.cmake中的HELLO_FIND_QUIETLY,如果不指定
    这个参数,就会执行:

    MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")
    
  • REQUIRED参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。对应于FindHELLO.cmake模块中的HELLO_FIND_REQUIRED变量。

  1. 使用HELLO模块
FIND_PACKAGE(HELLO)
IF(HELLO_FOUND)
ADD_EXECUTABLE(hello main.c)
INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY})
ENDIF(HELLO_FOUND)

为了能够让工程找到FindHELLO.cmake模块(存放在工程中的cmake目录)
我们在主工程文件CMakeLists.txt中加入:

SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

函数式编程

function

function(<name> [<arg1> ...])
  <commands>
endfunction()

在函数体内,用${arg1}来取出参数值。
除了正式参数外,还可以使用额外的预置变量:

  • ARGC 传递到该函数的参数数量
  • 以及ARGV0ARGV1ARGV2,…它们将具有传递的参数的实际值。这有助于通过可选参数创建功能
  • ARGV保留了传递给该function的所有参数的列表,ARGN保留了最后一个预期参数后的参数列表。
return([PROPAGATE <var-name>...])

当此命令出现在被included的文件中( 通过include()find_package() ),它会导致当前文件的处理停止,并将控制返回到包含它的文件。
如果return()在函数中被调用了,控制权被返还给函数的调用者。

此选项在父目录或函数调用者范围中设置或消除指定变量。

macro

宏用来记录一串命令,以便以后调用

macro(<name> [<arg1> ...])
  <commands>
endmacro()

调用方式

macro(foo)
 <commands>
endmacro()

foo()
Foo()
FOO()
cmake_language(CALL foo)

macro与funtion的区别

functionARGN, ARGC, ARGV and ARGV0, ARGV1, … 是真正的变量,在macro中, 它们是替代字符串。
macro()function()不同,它直接将代码就地展开,两者就如同C中的预处理和函数的关系,因此macro()不能处理return()

macro中参数的处理方式

由于在macro中ARGN, ARGC, ARGV, ARGV0不是变量,所以无法使用这样的语句

if(ARGV1) # ARGV1 is not a variable
if(DEFINED ARGV2) # ARGV2 is not a variable
if(ARGC GREATER 2) # ARGC is not a variable
foreach(loop_var IN LISTS ARGN) # ARGN is not a variable

正确方式:

if(${ARGV1}) 
if(${ARGC} GREATER 2) 
set(list_var "${ARGN}")
foreach(loop_var IN LISTS list_var)

block

block([SCOPE_FOR [POLICIES] [VARIABLES] ] [PROPAGATE <var-name>...])
  <commands>
endblock()

block()与endblock()之间的所有命令均不会被调用,直到遇到endblock(),block里的代码在请求的域内被调用,执行完代码后block创建的域被销毁。

list operations

Reading

list(LENGTH <list> <output variable>)

Returns the list’s length.

list(GET <list> <element index> [<element index> ...] <output variable>)

Returns the list of elements specified by indices from the list.

list(JOIN <list> <glue> <output variable>)

Returns a string joining all list’s elements using the glue string.

list(SUBLIST <list> <begin> <length> <output variable>)

Returns a sublist of the given list. If is 0, an empty list will be returned. If is -1 or the list is smaller than + then the remaining elements of the list starting at will be returned.

Search

list(FIND <list> <value> <output variable>)

Returns the index of the element specified in the list or -1 if it wasn’t found.

Modification

list(APPEND <list> [<element> ...])

Appends elements to the list. If no variable named exists in the current scope its value is treated as empty and the elements are appended to that empty list.

list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
list(POP_BACK <list> [<out-var>...])
list(POP_FRONT <list> [<out-var>...])

Ordering

list(REVERSE <list>)

Reverses the contents of the list in-place.

list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])

Sorts the list in-place alphabetically.

Use the COMPARE keyword to select the comparison method for sorting. The option should be one of:

  • STRING: Sorts a list of strings alphabetically. This is the default behavior if the COMPARE option is not given.

  • FILE_BASENAME: Sorts a list of pathnames of files by their basenames.

  • NATURAL: Sorts a list of strings using natural order (see strverscmp(3) manual), i.e. such that contiguous digits are compared as whole numbers. For example: the following list 10.0 1.1 2.1 8.0 2.0 3.1 will be sorted as 1.1 2.0 2.1 3.1 8.0 10.0 if the NATURAL comparison is selected where it will be sorted as 1.1 10.0 2.0 2.1 3.1 8.0 with the STRING comparison.

Use the CASE keyword to select a case sensitive or case insensitive sort mode. The option should be one of:

  • SENSITIVE: List items are sorted in a case-sensitive manner. This is the default behavior if the CASE option is not given.

  • INSENSITIVE: List items are sorted case insensitively. The order of items which differ only by upper/lowercase is not specified.

To control the sort order, the ORDER keyword can be given. The option should be one of:

  • ASCENDING: Sorts the list in ascending order. This is the default behavior when the ORDER option is not given.

  • DESCENDING: Sorts the list in descending order.

String operations

Search and Replace

string(FIND <string> <substring> <output_variable> [REVERSE])

Return the position where the given was found in the supplied . If the REVERSE flag was used, the command will search for the position of the last occurrence of the specified . If the is not found, a position of -1 is returned.

The string(FIND) subcommand treats all strings as ASCII-only characters. The index stored in will also be counted in bytes, so strings containing multi-byte characters may lead to unexpected results.

string(REPLACE <match_string>
       <replace_string> <output_variable>
       <input> [<input>...])

Replace all occurrences of in the with and store the result in the .

string(REGEX MATCH <regular_expression>
       <output_variable> <input> [<input>...])

Match the once and store the match in the . All arguments are concatenated before matching. Regular expressions are specified in the subsection just below.

string(REGEX MATCHALL <regular_expression>
       <output_variable> <input> [<input>...])

Match the as many times as possible and store the matches in the as a list. All arguments are concatenated before matching.

string(REGEX REPLACE <regular_expression>
       <replacement_expression> <output_variable>
       <input> [<input>...])

Match the as many times as possible and substitute the for the match in the output. All arguments are concatenated before matching.

The may refer to parenthesis-delimited subexpressions of the match using \1, \2, …, \9. Note that two backslashes (\\1) are required in CMake code to get a backslash through argument parsing.

Regex正则表达式

^ 句首
$ 句尾
. 匹配任意单个字符
\匹配单个字符
[ ] 匹配任意方括号内的字符
[^ ] 匹配任意不再方括号内的字符
- 在方括号中指定范围,例如 [a-f]代表[abcdef]
* 匹配前一个模式零或更多次
+ 匹配前一个模式一或更多次
?匹配前一个模式零或一次
| 匹配|两侧的任一模式
() 像数学运算中的括号一样,可以用来改变运算优先级

Manipulation

string(APPEND <string_variable> [<input>...])

Append all the arguments to the string.

string(PREPEND <string_variable> [<input>...])

Prepend all the arguments to the string.

string(CONCAT <output_variable> [<input>...])

Concatenate all the arguments together and store the result in the named .

string(JOIN <glue> <output_variable> [<input>...])

Join all the arguments together using the string and store the result in the named .

string(TOLOWER <string> <output_variable>)
string(TOUPPER <string> <output_variable>)

Convert to lower/upper characters.

string(LENGTH  )

Store in an a given string’s length in bytes. Note that this means if contains multi-byte characters, the result stored in will not be the number of characters.

string(SUBSTRING <string> <begin> <length> <output_variable>)

Store in an a substring of a given . If is -1 the remainder of the string starting at will be returned.
Both and are counted in bytes, so care must be exercised if could contain multi-byte characters.

string(STRIP <string> <output_variable>)

删除头和尾的空格

string(REPEAT <string> <count> <output_variable>)

Produce the output string as the input repeated times.

Comparison

string(COMPARE LESS <string1> <string2> <output_variable>)
string(COMPARE GREATER <string1> <string2> <output_variable>)
string(COMPARE EQUAL <string1> <string2> <output_variable>)
string(COMPARE NOTEQUAL <string1> <string2> <output_variable>)
string(COMPARE LESS_EQUAL <string1> <string2> <output_variable>)
string(COMPARE GREATER_EQUAL <string1> <string2> <output_variable>)

Compare the strings and store true or false in the .

你可能感兴趣的:(计算机,windows,linux)