一、cmake命令
1、基本用法
cmake [选项] <源码路径>
CMake可执行程序是CMake的命令行界面。它可以用脚本对工程进行配置。工程配置设置可以在命令行中使用-D选项指定。使用-i选项,cmake将通过提示交互式地完成该设置。
CMake是一个跨平台的构建系统生成工具。它使用平台无关的CMake清单文件CMakeLists.txt,指定工程的构建过程;源码树的每个路径下都有这个文件。CMake产生一个适用于具体平台的构建系统,用户使用这个系统构建自己的工程。
-D:创建一个CMake的缓存条目
当cmake第一次运行于一个空的构建数时,它会创建一个CMakeCache.txt文件,并且使用可定制的工程设置来填充这个文件。这个选项可以用来指定优先级高于工程的默认值的工程设置值。这个参数可以被重复多次,用来填充所需要数量的缓存条目(cache entries)。
-E: CMake命令行模式
为了真正做到与平台无关,CMake提供了一系列可以用于所有系统上的的命令。以-E参数运行CMake会帮助你获得这些命令的用法。可以使用的命令有:chdir, copy, copy_if_different copy_directory, compare_files, echo, echo_append, environment, make_directory, md5sum, remove_directory, remove, tar, time, touch, touch_nocreate, write_regv, delete_regv, comspec, create_symlink。
例如:
#改变当前的目录,然后执行命令
chdir <dir> <cmd> [<arg>...]
-P : 处理脚本模式
将给定的cmake文件按照CMake语言编写的脚本进行处理。如果要使用-D选项定义变量,-D选项必须在-P选项之前。
参考:
https://www.cnblogs.com/lsgxeva/p/9454443.html
https://cmake.org/cmake/help/v3.6/manual/cmake.1.html?highlight=chdir
2、install
install用于指定在安装时运行的规则。它可以用来安装很多内容,可以包括目标二进制、动态库、静态库以及文件、目录、脚本等:
install(TARGETS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
3、include
作用:从文件或模块加载并运行CMake代码。
可以指定载入一个文件,如果定义的是一个模块,那么将在 CMAKE_MODULE_PATH 中搜 索这个模块并载入,如果没有指定CMAKE_MODULE_PATH,那么从当前的CMakeLists.txt所在的目录查找。 载入的内容将在处理到 INCLUDE 语句是直接执行。
用法:
include(<file|module> [OPTIONAL] [RESULT_VARIABLE ]
[NO_POLICY_SCOPE])
#例如,加载预定义的cmake模块
include (tools.cmake)
说明:
- 变量读写范围为调用此指令的调用者的范围,因此,具有动态范围即取决于调用者;
- 如果“OPTIONAL”被指定,则指定的文件不存在时,不会产生任何错误;
- 如果“RESULT_VARIABLE”被给予了变量,则会赋值给变量完整的文件名(即<file|module>)或者在失败时赋值为“NOTFOUND ”。
参考:
https://blog.csdn.net/RobotLife/article/details/85416226
4、config_file
指令说明:
configure_file(<input> <output>
[COPYONLY] [ESCAPE_QUOTES] [@ONLY]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
configure_file 主要实现如下两个功能:
- 将 文件里面的内容全部复制到 文件中;
- 根据参数规则,替换 @VAR@ 或 ${VAR} 变量;
参数解析:
(1) COPYONLY
仅拷贝 文件里面的内容到 文件, 不进行变量的替换;
(2) ESCAPE_QUOTES
使用反斜杠(C语言风格)来进行转义;
(3) @ONLY
限制替换, 仅仅替换 @VAR@ 变量, 不替换 ${VAR} 变量
(4) NEWLINE_STYLE
指定输入文件的新行格式, 例如:Unix 中使用的是 \n, windows 中使用的 \r\n
注意: COPYONLY 和 NEWLINE_STYLE 是冲突的,不能同时使用;
参考:https://www.cnblogs.com/gaox97329498/p/10952732.html
5、set_property和get_property
set_property: 在指定域中设置一个命名属性
set_property(<GLOBAL |
DIRECTORY [dir] |
TARGET [target1 [target2 ...]] |
SOURCE [src1 [src2 ...]] |
TEST [test1 [test2 ...]] |
CACHE [entry1 [entry2 ...]]>
[APPEND][APPEND_STRING]
PROPERTY <name>[value1 [value2 ...]])
在某个域中对零个或多个对象设置一个属性。第一个参数决定该属性设置所在的域。它必须为下面中的其中之一:
- GLOBAL域是唯一的,并且不接特殊的任何名字。
- DIRECTORY域默认为当前目录,但也可以用全路径或相对路径指定其他的目录(前提是该目录已经被CMake处理)。
- TARGET域可命名零或多个已经存在的目标。
- SOURCE域可命名零或多个源文件。注意:源文件属性只对在相同目录下的目标是可见的(CMakeLists.txt)。
- TEST域可命名零或多个已存在的测试。
- CACHE域必须命名零或多个已存在条目的cache。
必选项PROPERTY后面紧跟着要设置的属性的名字。其他的参数用于构建以分号隔开的列表形式的属性值。如果指定了APPEND选项,则指定的列表将会追加到任何已存在的属性值当中。如果指定了APPEND_STRING选项,则会将值作为字符串追加到任何已存在的属性值。
get_property: 获取一个属性值
get_property(<variable>
<GLOBAL |
DIRECTORY [dir] |
TARGET <target> |
SOURCE <source> |
TEST <test> |
CACHE <entry> |
VARIABLE>
PROPERTY <name>
[SET | DEFINED |BRIEF_DOCS | FULL_DOCS])
相关域的说明与set_property意义相同。
必选项PROPERTY后面紧跟着要获取的属性的名字。如果指定了SET选项,则变量会被设置为一个布尔值,表明该属性是否已设置。如果指定了DEFINED选项,则变量也会被设置为一个布尔值,表明该属性是否已定义(如通过define_property)。如果定义了BRIEF_DOCS或FULL_DOCS选项,则该变量被设置为一个字符串,包含了对请求的属性的文档。如果该属性没有相关文件,则会返回NOTFOUND。
参考:
https://blog.csdn.net/fuyajun01/article/details/9036485
5、set
CMake 变量包含 常规变量Normal Variables和全局变量Cache Variables。通过 set 指令可以设置两种不同的变量。也可以在 CMake 脚本中使用和设置环境变量。set(ENV{} …),本文重点讲述 CMake 脚本语言特有的两种变量。
(1)两种变量的定义参考
Normal Variables
set(<variable> <value>... [PARENT_SCOPE])
这个命令来设置的变量就是 Normal Variables。例如 set(MY_VAL “666”) ,此时 MY_VAL 变量的值就是 666。
Cache Variables
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
这个命令来设置的变量就是 Cache Variables。例如 set(MY_CACHE_VAL “666” CACHE STRING INTERNAL),此时 MY_CACHE_VAL 就是一个 CACHE 变量。
CMake 规定,有一个与 Cache 变量同名的 Normal 变量出现时,后面使用这个变量的值都是以 Normal 为准,如果没有同名的 Normal 变量,CMake 才会自动使用 Cache 变量。
set(TAF_WEB_HOST "" CACHE STRING "set web host")
IF (TAF_WEB_HOST STREQUAL "")
set(TAF_WEB_HOST "http://taf.test.com")
ENDIF ()
缓存变量由如下特点:
- 缓存变量是全局作用域, 变量的值会从CMakeCache.txt中获取,缓存变量在写入CMakeCache.txt以后, 每次都从这个缓存文件中读取该值, 而不会重新调用set(… CACHE …)!!!(Cache变量只会初始化一次,在CMakeCache.txt中存储以后, 多次调用set(… CACHE …)不起作用!!!)。
- 缓存变量的常规初始化的方式是set(abc “789” CACHE STRING “”)。
- 当常规变量和缓存变量重名时, 如果缓存变量声明在后面, 会覆盖常规变量。
- 除了使用set(… CACHE …)可以声明缓存, 也可以在命令行使用-Dxxx=xxx修改缓存变量的值!!!
- 如果某个Cache变量已经存在于CMakeCache.txt中, 代码中的set(… CACHE …)将不会对这个值有影响, 如果我们在代码中需要强制修改Cache变量的值, 需要使用FORCE字段 — set(abc “123” CACHE STRING “” FORCE)。
参考:
https://www.jianshu.com/p/72b07f886820
https://www.cnblogs.com/ncuneugcj/p/9756324.html
二、常用变量
1、CMAKE_BINARY_DIR和CMAKE_SOURCE_DIR
好多网上的文章,都说CMAKE_BINARY_DIR和CMAKE_SOURCE_DIR是等价的。实际不然。一般来说,都是这样用
cmake ./
上面的方式一般称之为内部编译,这样CMAKE_BINARY_DIR和CMAKE_SOURCE_DIR是等价的。也就是当前源码的目录。如果执行cmake的时候,并不在源码的路径的话,比如:
cmake ../src
这样编译一般称之为外部编译,外部编译的好处是cmake生成的文件和编译出来的东西,就不放在源码路径下了,保证了源码路径的干净整洁。比如可以在src的同级目录下建立build目录。然后在build目录下执行cmake …/src。这样编译出来的东西和cmake生成的东西,都放到了build目录下了。
CMAKE_BINARY_DIR=全路径/build
CMAKE_SOURCE_DIR=全路径/src
2、PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR
CMAKE_BINARY_DIR和PROJECT_BINARY_DIR是不等价的,CMAKE_SOURCE_DIR和PROJECT_SOURCE_DIR也是不等价的。CMAKE_BINARY_DIR和CMAKE_SOURCE_DIR永远都是工程的顶层目录,而PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR则是CMakeList.txt编译发生的目录。
在此之前,有必要介绍一个最基本的命令:
project(projectname [CXX] [C] [Java])
指定工程名称,并可指定工程支持的语言。支持语言列表可忽略,默认支持所有语言。执行project命令后立即会常见两个cmake变量_BINARY_DIR和_SOURCE_DIR,同时也重新赋值了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR这两个变量。具体为当前CMakeList.txt所在的源目录和编译目录(build)。
3、CMAKE_CURRENT_SOURCE_DIR和CMAKE_CURRENT_BINARY_DIR
路径指向当前正在处理的源目录(CMakeLists.txt所在目录),这是一个指向源目录的完全路径,当前正在被cmake处理。
CMAKE_CURRENT_SOURCE_DIR和CMAKE_CURRENT_BINARY_DIR不同于PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR是编译发生的目录,只有在执行project命令后才会重置为CMakeList.txt所在的目录,而 CMAKE_CURRENT_SOURCE_DIR和CMAKE_CURRENT_BINARY_DIR是当前处理的源目录,无论是否执行project命令,都是当前CMakeList.txt所在的目录。
4、CMAKE_CURRENT
cmake可执行程序程序的完整路径,例如:
/usr/local/bin/cmake
5、CMAKE_ARCHIVE_OUTPUT_DIRECTORY,CMAKE_LIBRARY_OUTPUT_DIRECTORY和CMAKE_RUNTIME_OUTPUT_DIRECTORY
CMAKE_ARCHIVE_OUTPUT_DIRECTORY:编译后.a文件存储位置。
CMAKE_LIBRARY_OUTPUT_DIRECTORY:动态链接文件.so存储位置。
CMAKE_RUNTIME_OUTPUT_DIRECTORY:可执行文件存储位置。
例如:
#编译的可执行程序输出目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
以上三行命令最好写在根(root)CmakeLists.txt的里面。
参考:
https://www.cnblogs.com/gatsby123/p/10885555.html
https://cmake.org/cmake/help/v3.0/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html
https://blog.csdn.net/weixin_34043301/article/details/92424326
https://blog.csdn.net/u012487272/article/details/12882283
6、CMAKE_VERBOSE_MAKEFILE
为了在cmake生成Makefile后执行make的时候输出详细的编译命令,可以在CMakeList.txt内加入:
SET( CMAKE_VERBOSE_MAKEFILE on )
或者执行make时
$ make VERBOSE=1
7、系统信息
系统信息相关的变量(内置变量):
(1)在不同的平台编译的时候,会用到一些系统内置的变量,比如操作系统名称,版本号之类
CMAKE_MAJOR_VERSION,CMAKE 主版本号,比如 2.4.6 中的 2
CMAKE_MINOR_VERSION,CMAKE 次版本号,比如 2.4.6 中的 4
CMAKE_PATCH_VERSION,CMAKE 补丁等级,比如 2.4.6 中的 6
CMAKE_SYSTEM,系统名称,比如 Linux-2.6.22
CMAKE_SYSTEM_NAME,不包含版本的系统名,比如 Linux
CMAKE_SYSTEM_VERSION,系统版本,比如 2.6.22
CMAKE_SYSTEM_PROCESSOR,处理器名称,比如 i686.
(2)系统标志,下面的变量都是BOOL类型的,如果与当前系统或编译器相符,值为true,反之为false
UNIX
WIN32 for MINGW,CYGWIN,MSYS
APPLE
BORLAND
WATCOM
MSVC,MSVC_IDE,CMAKE_COMPILER_2005,MSVC60/70/71/80/90/10,针对不同的Visual C++
CMAKE_COMPILER_IS_GUNCXX/CMAKE_COMPILER_IS_GUNCC
UNIX,在所有的类 UNIX 平台为 TRUE,包括 OS X 和 cygwin
WIN32,在所有的 win32 平台为 TRUE,包括 cygwin
(3)编译时选项
BUIlD_SHARED_LIBS:将所有程序库的target设置成共享库
CMAKE_BUIlD_TYPE:控制构建类型,以下为可选参数
None:default;Debug:生成调试信息;Release:发布版本,进行最佳化,需要注意这个值不会在configure的时候自动初始化,需要手动指定
CMAKE_C_FLAGS
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE
CMAKE_CXX_FLAGS
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_RELEASE
8、编译选项相关的变量
CMAKE_C_FLAGS:内置的 C 编译选项,也可以通过指令 ADD_DEFINITIONS()添加。
CMAKE_CXX_FLAGS:内置的 C++编译选项,也可以通过指令 ADD_DEFINITIONS()添加。
例如:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++11 -Wno-deprecated -fno-strict-aliasing -Wno-overloaded-virtual")
CMake 中有一个变量 CMAKE_BUILD_TYPE ,可以的取值是 Debug,Release,Rel,WithDebInfo 和 MinSizeRel。当这个变量值为 Debug 的时候,CMake 会使用变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字符串作为编译选项生成 Makefile ,当这个变量值为 Release 的时候,工程会使用变量 CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 选项生成 Makefile。
CMAKE_CXX_FLAGS和CMAKE_C_FLAGS是公共的编译选项,即不管CMAKE_BUILD_TYPE是什么值,也不管是CMAKE_CXX_FLAGS_DEBUG还是CMAKE_CXX_FLAGS_RELEASE,那么最终的编译中都包括CMAKE_CXX_FLAGS和CMAKE_C_FLAGS定义的编译选项。
例如:
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "set build type to release default")
IF (CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Release")
ENDIF ()
#公共编译选项
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++11 -Wno-deprecated -fno-strict-aliasing -Wno-overloaded-virtual")
#__FILE__宏不带路径名
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D__FILE__='\"$(notdir $(abspath $<))\"'")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -Wall -g")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O2 -Wall -fno-strict-aliasing")
9、CMAKE_INSTALL_PREFIX
CMAKE_INSTALL_PREFIX 变量类似于 configure 脚本的 –prefix,常见的使用方法看 起来是这个样子:
#命令行设置
cmake -DCMAKE_INSTALL_PREFIX=/usr .
#设置变量
set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/release" CACHE PATH "Installation Directory" FORCE)
install安装命令
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])
DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用 CMAKE_INSTALL_PREFIX 来 定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是:
${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>
三、常用命令
1、set
#编译选项 Release 还是 Debug
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "set build type to release default")
set(Ext "")
IF (CMAKE_BUILD_TYPE STREQUAL "" OR CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_BUILD_TYPE "Release")
set(Ext ".O2")
ENDIF()
message("CMAKE_BUILD_TYPE TYPE = " ${CMAKE_BUILD_TYPE})
message("Ext = " ${Ext})
其中:
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "set build type to release default")
执行命令:
cmake ../project/ -DCMAKE_BUILD_TYPE=Debug
只设置CMAKE_BUILD_TYPE的缓存值,如果后续没有set这个值,那么这个值取cache中的值,否则为新set的值。如果CMake 中默认有这个CMAKE_BUILD_TYPE变量,那么此时就是赋值,没有的话,CMake 就会默认创建了一个全局 Cache 变量然后赋值。
参考:
https://www.cnblogs.com/ncuneugcj/p/9756324.html
2、aux_source_directory
查找源文件并保存到相应的变量中:
#查找当前目录下所有源文件并保存至SRC_LIST变量中
aux_source_directory(. SRC_LIST)
3、file
file(WRITE filename "message towrite"... )
WRITE 将一则信息写入文件’filename’中,如果该文件存在,它会覆盖它,如果不存在,它会创建该文件。
file(APPEND filename "message to write"... )
APPEND 如同WRITE,区别在于它将信息内容追加到文件末尾。
file(READ filename variable [LIMIT numBytes] [OFFSEToffset] [HEX])
READ 会读取文件的内容并将其存入到变量中。它会在给定的偏移量处开始读取最多numBytes个字节。如果指定了HEX参数,二进制数据将会被转换成十进制表示形式并存储到变量中。
file(<MD5|SHA1|SHA224|SHA256|SHA384|SHA512> filenamevariable)
MD5, SHA1, SHA224, SHA256, SHA384, 和SHA512 会计算出文件内容对应的加密散列。
file(STRINGS filename variable [LIMIT_COUNT num]
[LIMIT_INPUT numBytes] [LIMIT_OUTPUTnumBytes]
[LENGTH_MINIMUM numBytes] [LENGTH_MAXIMUMnumBytes]
[NEWLINE_CONSUME] [REGEX regex]
[NO_HEX_CONVERSION])
STRINGS 从文件中解析出ASCII字符串列表并存储在变量中。文件中的二进制数据将被忽略。回车符(CR)也会被忽略。也能解析Intel Hex和Motorola S-record文件,这两种文件在读取是会自动转换为二进制格式,可以使用参数NO_HEX_CONVERSION 禁用这种自动转换。LIMIT_COUNT设置可返回的最大数量的字符串。LIMIT_INPUT 设置从输入文件中可读取的最大字节数。LIMIT_OUTPUT设置了存储在输出变量中最大的字节数。 LENGTH_MINIMUM设置了返回的字符串的最小长度。小于这个长度的字符串将被忽略。 LENGTH_MAXIMUM 设置返回的字符串的最大长度。大于这个长度的字符串将被切分为长度不大于于最大长度值的子字符串。NEWLINE_CONSUME 允许换行符包含进字符串中而不是截断它们。REGEX 指定了返回的字符串必须匹配的正则表达式的模式。典型用法:
file(STRINGS myfile.txt myfile)
将输入文件的每行内容存储在变量"myfile"中。
file(GLOB variable [RELATIVE path] [globbingexpressions]...)
GLOB 会产生一个由所有匹配globbing表达式的文件组成的列表,并将其保存到变量中(注意返回的是文件列表而不是文件内容)。Globbing 表达式与正则表达式类似,但更简单。如果指定了RELATIVE 标记,返回的结果将是与指定的路径相对的路径构成的列表。 (通常不推荐使用GLOB命令来从源码树中收集源文件列表。原因是:如果CMakeLists.txt文件没有改变,即便在该源码树中添加或删除文件,产生的构建系统也不会知道何时该要求CMake重新产生构建文件。globbing 表达式包括:
file(GLOB_RECURSE variable [RELATIVE path] [FOLLOW_SYMLINKS] [globbingexpressions]...)
#例如:遍历当前子目录
file(GLOB children RELATIVE ${curdir} ${curdir}/*)
GLOB_RECURSE 与GLOB类似,区别在于它会遍历匹配目录的所有文件以及子目录下面的文件。对于属于符号链接的子目录,只有FOLLOW_SYMLINKS指定一或者cmake策略CMP0009没有设置为NEW时,才会遍历这些目录。
4、get_filename_component
get_filename_component(<VAR> FileName
PATH|ABSOLUTE|NAME|EXT|NAME_WE|REALPATH
[CACHE])
将变量设置为路径(PATH),文件名(NAME),文件扩展名(EXT),去掉扩展名的文件名(NAME_WE),完整路径(ABSOLUTE),或者所有符号链接被解析出的完整路径(REALPATH)。注意,路径会被转换为Unix的反斜杠(/),并且没有结尾的反斜杠。该命令已经考虑了最长的文件扩展名。如果指定了CACHE选项,得到的变量会被加到cache中。
5、add_library
生成动态库或者静态库,默认情况下会在build的相应目录下面生成库。
语法:
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
source1 [source2 ...])
:库的名字,直接写名字即可,不要写lib,会自动加上前缀的哈。
[STATIC | SHARED | MODULE] :类型有三种。
- SHARED,动态库
- STATIC,静态库
- MODULE,在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。
EXCLUDE_FROM_ALL:这个库不会被默认构建,除非有其他的组件依赖或者手工构建。
使用:
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
注意,一般我们使用的静态库/动态库只是后缀名不同而已,上面构建的libhello.so与libhello_static.a,显然名字不同哦。这时你会有一个想法,那我把hello_static改成hello,结果是不可行的,静态库无法构建。重名会忽略第二条指令。
解决方法:改libhello_static.a的属性–输出名字
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
这样就可以生成libhello.so与libhello.a了。关于动态库的版本号:
#VERSION 指代动态库版本,SOVERSION 指代 API 版本。
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
参考:
https://blog.csdn.net/weixin_39956356/article/details/100504979
6、add_dependenices
对于编译时遇到的依赖问题,很多时候我们只需要一句target_link_libraries就可以搞定。但是CMake还有另外一个command,add_dependencies。这个什么时候用呢?
一般来说用不到。用到的情况就是两个targets有依赖关系(通过target_link_libraries解决)并且依赖库也是通过编译源码产生的。这时候一句add_dependencies可以在直接编译上层target时,自动检查下层依赖库是否已经生成。没有的话先编译下层依赖库,然后再编译上层target,最后link depend target。
命令语法:
add_dependencies(target-name depend-target1 depend-target2 …)
命令简述:用于指定某个目标(可执行文件或者库文件)依赖于其他的目标。这里的target-name目标必须是 add_executable、add_library、add_custom_target 命令创建的目标。
7、add_custom_comand
增加自定义的构建规则到生成的构建系统中。对于add_custom_command,有两种使用形式。第一种形式是增加一个自定义的命令用来产生一个输出。
add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1[ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS[depends...]]
[IMPLICIT_DEPENDS<lang1> depend1 ...]
[WORKING_DIRECTORY dir]
[COMMENT comment] [VERBATIM] [APPEND])
不要同时在多个相互独立的目标中执行上述命令产生相同的文件,主要是为了防止冲突产生。如果有多条命令,它们将会按顺序执行。ARGS是为了向后兼容,使用过程中可以忽略。MAIN_DEPENDENCY完全是可选的,它主要是针对Visual Studio给出的一个建议。在Makefile中,它会产生一个这样的新目标:
OUTPUT: MAIN_DEPENDENCY DEPENDS
COMMAND
第二种形式是为某个目标如库或可执行程序添加一个自定义命令。这对于要在构建一个目标之前或之后执行一些操作非常有用。该命令本身会成为目标的一部分,仅在目标本身被构建时才会执行。如果该目标已经构建,命令将不会执行。
add_custom_command(TARGET target
PRE_BUILD | PRE_LINK| POST_BUILD
COMMAND command1[ARGS] [args1...]
[COMMAND command2[ARGS] [args2...] ...]
[WORKING_DIRECTORY dir]
[COMMENT comment][VERBATIM])
命令执行的时机由如下参数决定:
- PRE_BUILD - 命令将会在其他依赖项执行前执行
- PRE_LINK - 命令将会在其他依赖项执行完后执行
+POST_BUILD - 命令将会在目标构建完后执行。
其中,PRE_BUILD只被Visual Studio 7及之后的版本支持,其他所有的构建文件产生器将视PRE_BUILD为PRE_LINK。如果指定了WORKING_DIRECTORY,那么命令将会在指定的目录下执行。如果是相对路径,那么该路径将被解释为与当前源码目录对应的构建目录相对的路径。 如果设置了COMMENT,那么在编译时,命令执行前会将COMMENT的内容当做信息输出。如果指定了APPEND ,那么COMMAND 和 DEPENDS 选项的值将会被追加到第一个指定的输出对应的自定义命令中。目前,如果指定了APPEND选项,那么COMMENT, WORKING_DIRECTORY, 和 MAIN_DEPENDENCY选项将会忽略。但是将来可能会使用。如果指定了VERBATIM选项,那么,所有传递到命令的参数将会被适当地转义,这样命令接受到的参数将不会改变。建议使用VERBATIM选项,如果客制命令的输出不是创建一个存储在磁盘上的文件,需要使用命令SET_SOURCE_FILES_PROPERTIES把它标记为SYMBOLIC。
IMPLICIT_DEPENDS选项请求扫描一个输入文件的隐含依赖项。特定的语言会指明对应编程语言,它会使用相应的依赖项扫描器。目前仅支持C和CXX语言依赖项扫描器。目前IMPLICIT_DEPENDS 选项仅被Makefile产生器支持,其它构建文件的产生器将会忽略该选项。
如果COMMAND指定了一个可执行的目标(由ADD_EXECUTABLE创建),那么它会自动地被在构建时创建的可执行文件路径替换。另外,也会添加一个目标级的依赖,使得可执行目标总会在使用了该客制命令的任何目标之前构建。然而,它不会增加一个文件级的依赖,这种依赖会使得只要该可执行程序被重新编译,该客制命令也会重新运行。
DEPENDS选项指定了该命令所依赖的文件。如果任何依赖项是同一目录中其他另一个客制命令 OUTPUT(CMakeLists.txt)。那么CMake会自动地将其引入到执行该客制命令的目标中来。如果没有指定DEPENDS,那么只要OUTPUT不见了,该命令就会运行。如果该命令并没有实际去创建OUTPUT,那么该规则总是执行。如果DEPENDS指定了任何一个目标 (由ADD_* 系列命令创建) ,那么就会创建一个目标级的依赖以确保该目标比任何使用该客制命令的目标要先构建。另外,如果该目标是一个可执行文件或是一个库,那么就会创建一个文件级的依赖,这样会使得只要该目标重新编译,该客制命令就会重新运行。
**注:**add_custom_command命令生成输出文件并不会立即执行,只会在make的时候才会执行。
参考:
https://cmake.org/cmake/help/v3.0/command/add_custom_command.html
https://blog.csdn.net/fuyajun01/article/details/8907207
8、add_custom_target
增加一个没有输出的目标,使得它总是被构建。
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[WORKING_DIRECTORY dir]
[COMMENT comment] [VERBATIM]
[SOURCES src1 [src2...]])
增加一个指定名字的目标,并执行指定的命令。该目标没有输出文件,总是被认为是过期的,即使是在试图用目标的名字创建一个文件。使用ADD_CUSTOM_COMMAND命令来创建一个具有依赖项的文件。默认情况下,没有任何目标会依赖该自定义目标。使用ADD_DEPENDENCIES 来添加依赖项或成为别的目标的依赖项。如果指定了ALL选项,那就表明该目标会被添加到默认的构建目标,使得它每次都被运行。(该命令的名称不能命名为 ALL). 命令和参数都是可选的,如果没有指定,将会创建一个空目标。如果设置了WORKING_DIRECTORY ,那么该命令将会在指定的目录中运行。如果它是个相对路径,那它会被解析为相对于当前源码目录对应的构建目录。如果设置了 COMMENT,在构建的时候,该值会被当成信息在执行该命令之前显示。DEPENDS参数可以是文件和同一目录中的其他客制命令的输出。
如果指定了VERBATIM, 所有传递给命令的参数将会被适当地转义。建议使用该选项。
SOURCES选项指定了包含进该客制目标的额外的源文件。即使这些源文件没有构建规则,但是它们会被增加到IDE的工程文件中以方便编辑。
下面以生成jce文件的实际例子来说明:
#JCE2CPP: "/usr/local/taf/cpp/tools/jce2cpp"
#JCE_FILE:"${CMAKE_CURRENT_SOURCE_DIR}/CommonData.jce"
#JCE_GEN_DIR: "${CMAKE_CURRENT_SOURCE_DIR}"
#CUR_JCE_GEN:"${CMAKE_CURRENT_SOURCE_DIR}/CommonData.h"
#set(JCE_GEN ${JCE_GEN} ${CUR_JCE_GEN})
#JCE2CPP_INCULDE: "/home/tafjce/HQSys/LIBS/hqtools"
#JCE2CPP_FLAG: ""
#TARGET: "jce_all"
add_custom_command(
OUTPUT ${CUR_JCE_GEN}
WORKING_DIRECTORY ${JCE_GEN_DIR}
COMMAND ${JCE2CPP} ${JCE_FILE} ${JCE2CPP_FLAG} ${JCE2CPP_INCULDE}
DEPENDS ${JCE2CPP} ${JCE_FILE}
)
add_custom_target(${TARGET} ALL DEPENDS ${JCE_GEN})
DEPENDS ${JCE2CPP} ${JCE_FILE}:这里之所以有 ${JCE2CPP},JCE2CPP虽然是可执行程序,单也是一个文件,故也是一个依赖项。
add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1[ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCYdepend]
[DEPENDS[depends...]]
[IMPLICIT_DEPENDS<lang1> depend1 ...]
[WORKING_DIRECTORYdir]
[COMMENT comment] [VERBATIM] [APPEND])
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[WORKING_DIRECTORY dir]
[COMMENT comment] [VERBATIM]
[SOURCES src1 [src2...]])
这里add_custom_command没有指定TARGET,故需要add_custom_target配合才能立即执行。
参考:
https://cmake.org/cmake/help/v3.0/command/add_custom_target.html
https://blog.csdn.net/fuyajun01/article/details/8907207
9、list
用途:提供一些列表操作
list(LENGTH <list><output variable>)
list(GET <list> <elementindex> [<element index> ...] <output variable>)
list(APPEND <list><element> [<element> ...])
list(FIND <list> <value><output variable>)
list(INSERT <list><element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value>[<value> ...])
list(REMOVE_AT <list><index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)
选项说明:
- LENGTH返回列表的长度
- GET返回列表中指定下标的元素
- APPEND添加新元素到列表中
- INSERT 将新元素插入到列表中指定的位置
- REMOVE_ITEM从列表中删除某个元素
- REMOVE_AT从列表中删除指定下标的元素
- REMOVE_DUPLICATES从列表中删除重复的元素
- REVERSE 将列表的内容实地反转,改变的是列表本身,而不是其副本
- SORT 将列表按字母顺序实地排序,改变的是列表本身,而不是其副本
列表的子命令APPEND, INSERT, REMOVE_AT, REMOVE_ITEM,REMOVE_DUPLICATES, REVERSE以及SORT在当前的CMake变量域创建一些新值。与SET命令类似,即使列表本身是在父域中定义的,LIST命令也只会在当前域创建新的变量值,为了将这些操作的结果向上传递,需要通过SET PARENT_SCOPE, SET CACHE INTERNAL或其他值域扩展的方法。
注意:cmake中的列表是以分号隔开的一组字符串。可以使用set命令创建一个列表。例如:set(var a b c d e)创建了一个这样的列表:a;b;c;d;e。 set(var “a b c d e”)创建了一个字符串或只有一个元素的列表。
当指定索引值时,为大于或等于0的值。它从列表的开始处索引,0代表列表的第一个元素。如果为小于或等于-1的值,它从列表的结尾处索引,-1代表列表的最后一个元素。
10、set_directory_properties
设置某个路径的一种属性。
set_directory_properties(PROPERTIES prop1 value1 prop2 value2)
为当前的路径及其子路径设置一种属性。如果该属性不存在,CMake将会报告一个错误。属性包括:INCLUDE_DIRECTORIES, LINK_DIRECTORIES, INCLUDE_REGULAR_EXPRESSION, 以及ADDITIONAL_MAKE_CLEAN_FILES共四种。ADDITIONAL_MAKE_CLEAN_FILES是一个文件名的list,其中包含有"make clean"阶段会被清除掉的文件。
11、include_directories,link_directories, link_libraries和target_link_libraries
INCLUDE_DIRECTORIES作用是添加头文件目录,它相当于g++选项中的-I参数的作用,也相当于环境变量中增加路径到CPLUS_INCLUDE_PATH变量的作用(这里特指c++。c和Java中用法类似)。
比如:
include_directories("/opt/MATLAB/R2012a/extern/include")
LINK_DIRECTORIES作用是添加需要链接的库文件目录
语法:
link_directories(directory1 directory2 ...)
它相当于g++命令的-L选项的作用,也相当于环境变量中增加LD_LIBRARY_PATH的路径的作用。比如:
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
也可以写成:
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
TARGET_LINK_LIBRARIES 作用是设置要链接的库文件的名称,语法:
TARGET_LINK_LIBRARIES(targetlibrary1 <debug | optimized> library2 ..)
比如(以下写法,包括备注中的都可以):
#连接libhello.so库
TARGET_LINK_LIBRARIES(myProject hello)
TARGET_LINK_LIBRARIES(myProject libhello.a)
TARGET_LINK_LIBRARIES(myProject libhello.so)
再如:
TARGET_LINK_LIBRARIES(myProject libeng.so)
TARGET_LINK_LIBRARIES(myProject eng)
TARGET_LINK_LIBRARIES(myProject -leng)
一个简单的示例(以下CMakeLists.txt效果相当,在ubuntu 12.04 + g++4.6下测试编译通过):
方式一:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
include_directories("/opt/MATLAB/R2012a/extern/include")
#directly link to the libraries.
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
#equals to below
#LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
add_executable(myProject main.cpp)
方式二:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
include_directories("/opt/MATLAB/R2012a/extern/include")
LINK_DIRECTORIES("/opt/MATLAB/R2012a/bin/glnxa64")
add_executable(myProject main.cpp)
target_link_libraries(myProject eng mx)
#equals to below
#target_link_libraries(myProject -leng -lmx)
#target_link_libraries(myProject libeng.so libmx.so)
参考:
https://blog.csdn.net/Tommy_wxie/article/details/77698627?utm_source=blogxgwz4
12、execute_process
CMake可以通过execute_process调用shell命令或者脚本,其原型如下:
execute_process(COMMAND <cmd1> [args1...]]
[COMMAND <cmd2> [args2...] [...]]
[WORKING_DIRECTORY <directory>]
[TIMEOUT <seconds>]
[RESULT_VARIABLE <variable>]
[OUTPUT_VARIABLE <variable>]
[ERROR_VARIABLE <variable>]
[INPUT_FILE <file>]
[OUTPUT_FILE <file>]
[ERROR_FILE <file>]
[OUTPUT_QUIET]
[ERROR_QUIET]
[OUTPUT_STRIP_TRAILING_WHITESPACE]
按指定的先后顺序运行一个或多个命令,每个进程的输出通过管道连接作为下一个进程的输入。所有的进程使用单个的标准错误输出管道。如果指定了WORKING_DIRECTORY,则指定的目录将作为子进程当前的工作目录。如果指定了TIMEOUT值,则如果在指定的时间内(以秒为单位计算,允许有小数位)子进程执行仍未完成,则将会被中断。如果指定了RESULT_VARIABLE变量,则最后命令执行的结果将保存在该变量中,它是最后一个子进程执行完后的返回值或描述某种错误信息的字符串。如果指定了OUTPUT_VARIABLE或ERROR_VARIABLE变量,则该变量会分别保存标准输出和标准错误输出的内容。如果指定的变量是同一个,则输出会按产生的先后顺序保存在该变量中。如果指定了INPUT_FILE,UTPUT_FILE或ERROR_FILE等文件名,则它们会分别与第一个子进程的标准输入,最后一个子进程的标准输出以及所有子进程的标准错误输出相关联。如果指定了OUTPUT_QUIET或ERROR_QUIET,则会忽略标准输出和错误输出。如果在同一管道中同时指定了多个OUTPUT_*或ERROR_*选项,则优先级顺序是未知的(应避免这种情况)。如果未指定任何OUTPUT_*或ERROR_*选项,则命令CMake所在进程共享输出管道。
例如,执行shell命令:
execute_process(COMMAND <一句shell命令> WORKING_DIRECTORY <这句shell命令执行的工作目录>)
执行shell脚本:
execute_process(COMMAND sh test.sh WORKING_DIRECTORY <test.sh所在目录>)
参考:
https://blog.csdn.net/qq_28584889/article/details/97758450
13、add_subdirectory
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前的工程添加存放源文件的目录,并可以指定中间二进制和目标二进制存放的位置。 EXCLUDE_FROM_ALL参数的含义是将这个目录从编译的过程中排除,比如,工程中的example,可以就需要工程构建完成后,再进入example目录单独进行构建。
有这样一种情况。自己写了一个库,需要写测试程序。类似如下结构:
hello-world/
├── CMakeLists.txt
├── main.c
├── test
│ ├── CMakeLists.txt
│ └── main.c
├── hello
│ ├── CMakeLists.txt
│ ├── hello.c
│ └── hello.h
└── world
├── CMakeLists.txt
├── world.c
└── world.h
hello/ 目录生成 libhello.so,world/ 目录生成 libworld.so,test/ 目录存储测试程序,测试上述两个库功能是否正常。请注意 test/ 与 hello/ 和 world/ 的目录层级关系。我们只关注 test/CMakeLists.txt 文件。第一反应,是这么写:
cmake_minimum_required(VERSION 3.5)
project(test C)
#库目录
set(TOP_DIR ${CMAKE_CURRENT_LIST_DIR}/../)
add_subdirectory(${TOP_DIR}/hello)
add_subdirectory(${TOP_DIR}/world)
add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} PRIVATE hello)
target_link_libraries(${PROJECT_NAME} PRIVATE world)
target_include_directories(${PROJECT_NAME} PRIVATE ${TOP_DIR}/hello)
target_include_directories(${PROJECT_NAME} PRIVATE ${TOP_DIR}/world)
编译结果:
-- The C compiler identification is GNU 5.3.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
CMake Error at CMakeLists.txt:10 (add_subdirectory):
add_subdirectory not given a binary directory but the given source
directory "/home/sdc/pro/hello-world/hello" is not a subdirectory of
"/home/sdc/pro/hello-world/test". When specifying an out-of-tree source a
binary directory must be explicitly specified.
CMake Error at CMakeLists.txt:11 (add_subdirectory):
add_subdirectory not given a binary directory but the given source
directory "/home/sdc/pro/hello-world/world" is not a subdirectory of
"/home/sdc/pro/hello-world/test". When specifying an out-of-tree source a
binary directory must be explicitly specified.
-- Configuring incomplete, errors occurred!
See also "/home/sdc/pro/hello-world/test/build/CMakeFiles/CMakeOutput.log".
add_subdirectory 发生错误。
错误原因: hello/ 目录不是 test/ 的子目录。
解决方法: 显式指定 binary directory。
在使用 add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])命令时,如果 source_dir 不是当前目录(CMakeLists.txt 所在目录,例子中的 test/ 目录)的子目录,那么就需要显式指定 [binary_dir] 参数,用于存储 source_dir 相关文件。
修改CMakeLists.txt 文件中的 add_subdirectory()命令为如下方式即可:
#指定 [binary_dir] 参数
add_subdirectory(${TOP_DIR}/hello hello_binary_dir)
add_subdirectory(${TOP_DIR}/world world_binary_dir)
**说明:**例子中的 hello/ 和world/ 目录与 test/ 目录是平级关系。实际上,只要不是上下级关系(比如,hello/ 和 world/ 目录在其他目录下或者是 test/ 目录的上级),在使用 add_subdirectory() 时都需要指定 [binary_dir] 参数。
14、set_target_properties
set_target_properties 设置目标的一些属性来改变它们构建的方式。
set_target_properties(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
为一个目标设置属性。该命令的语法是列出所有你想要变更的文件,然后提供你想要设置的值。你能够使用任何你想要的属性/值对,并且在随后的代码中调用GET_TARGET_PROPERTY命令取出属性的值。
影响一个目标输出文件的名字的属性详述如下。PREFIX和SUFFIX属性覆盖了默认的目标名前缀(比如lib)和后缀(比如.so)。IMPORT_PREFIX和IMPORT_SUFFIX是与之等价的属性,不过针对的是DLL(共享库目标)的导入库。在构建目标时,OUTPUT_NAME属性设置目标的真实名字,并且可以用来辅助创建两个具有相同名字的目标,即使CMake需要唯一的逻辑目标名。例如:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
LINK_FLAGS属性可以用来为一个目标的链接阶段添加额外的标志。LINK_FLAGS_将为配置添加链接标志,例如DEBUG,RELEASE,MINSIZEREL,RELWITHDEBINFO。COMPILE_FLAGS属性可以设置附加的编译器标志,它们会在构建目标内的源文件时被用到。它也可以用来传递附加的预处理器定义。
参考:
http://www.wendangku.net/doc/73ab3fcad5bbfd0a79567372-15.html
https://www.cnblogs.com/coderfenghc/archive/2012/10/20/2712806.html
15、option
提供一个用户可以任选的选项。语法如下:
option(<option_variable> "help string describing option"
[initial value])
提供选项让用户选择是 ON 或者 OFF ,如果没有提供初始化值,使用OFF,也就是说默认的值是OFF。
注意:这个option命令和你本地是否存在编译缓存的关系很大。所以,如果你有关于 option 的改变,那么请你务必清理 CMakeCache.txt 和 CMakeFiles 文件夹。还有,请使用标准的 [initial value] 值 ON 或者 OFF。
参考:
https://www.cnblogs.com/the-capricornus/p/4717566.html
https://www.jianshu.com/p/035bc18f8f62
四、编译过程
1、cmake会立即执行的操作
(1)set命令
set命令在cmake的时候会立即执行,例如:
set(CLEAR_INCLUDE "clear-install.cmake")
(2)file命令
file命令在cmake的时候会立即执行,例如:
set(CLEAR_INCLUDE "clear-install.cmake")
FILE(WRITE ${CLEAR_INCLUDE} "EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_INSTALL_PREFIX}/include)\n")
注:如果没有指定路径,那么文件默认就是当前CMakeLists.txt所在的目录。
(3)config_file命令
复制文件操作
configure_file(${CMAKE_SOURCE_DIR}/tc_version.h.in
${CMAKE_SOURCE_DIR}/taf-cpp-util/include/util/tc_version.h
)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)