续文章 CMake入门实践之语法(1)
8. 变量 EXECUTABLE_OUTPUT_PATH, LIBRARY_OUTPUT_PATH
EXECUTABLE_OUTPUT_PATH 为生成可执行文件路径, LIBRARY_OUTPUT_PATH为生成库文件路径。我们可以通过 SET 指令对其进行设置最终目标二进制的位置,即最终生成的工程可执行文件与最终的共享库,而不会包含生成的中间文件。
命令如下:
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
9. LINK_DIRECTORIES
格式:
LINK_DIRECTORIES(directory1 directory2 ...)
# 该指令用于添加外部库的搜索路径
10. TARGET_LINK_LIBARRIES
格式:
TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ..)
# target:目标文件
# library1, ... , library N :链接外部库文件
# 指定链接目标文件时需要 链接的外部库,效果类似 gcc 编译参数 -L ,解决外部库依赖的问题。
11. MESSAGE
向终端输出用户定义的信息和变量值,格式:
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display” …)
# SEND_ERROR : 产生错误,生成过程被跳过
# STATUS : 输出前缀为 - 的信息
# FATAL_ERROR : 立即终止所有 cmake 过程
12. SET_TARGET_PROPERTIES
设置目标的某些属性,改变它们的构建方式。格式:
SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1 prop2 value2 ...)
该指令为一个目标设置属性,语法是列出所有用户想要变更的文件,然后提供想要设置的值。用户可以使用任何想用的属性与对应的值,并在随后的代码中调用 GET_TARGET_PROPERTY 命令取出属性的值。
影响目标输出文件按的属性 PROPERTY 详述如下:
-- PREFIX, SUFFIX :PREFIX 覆盖了默认的目标名前缀(如lib); SUFFIX 覆盖了默认的目标名后缀(如 .so)。
-- IMPORT_PREFIX,IMPORT_SUFFIX:与 PREFIX, SUFFIX是等价的属性,但针对的是 DLL 导入库(即共享库目标)。
-- OUTPUT_NAME :构建目标时, OUTPUT_NAME 用来设置目标的真实名称。
-- LINK_FLAGS : 为一个目标的链接阶段添加额外标志。LINK_FLAGS_<CONFIG> 将为配置 <CONFIG> 添加链接标志,如DEBUG/RELEASE/MINSIZEREL/RELWITHDEBINFO。
-- COMPILE_FLAGS : 设置附加的编译器标志,在构建目标内的源文件时被用到。
-- LINKER_LANGUAGE :改变链接可执行文件或共享库的工具。默认是设置与库中文件相匹配的语言。 CXX和C是该属性的公共值。
-- VERSION, SOVERSION : VERSION 指定构建的版本号, SOVERSION 指定构建的 API 版本号。构建或安装时,如果平台支持符号链接,且链接器支持 so 名称,那么恰当的符号链接将会被创建。如果只指定两者中的一个,缺失的另一个假定为具有相同的版本号。
示例1:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
示例2:
SET_TARGET_PROPERTIES(hello PROPERTEIES VERSION 1.2 SOVERSION 1)
13. AUX_SOURCE_DIRECTORY
查找某个路径下的所有源文件,并将源文件列表存储到一个变量中。
格式:
AUX_SOURCE_DIRECTORY(< dir > < variable >)
示例:
AUX_SOURCE_DIRECTORY(. SRC_LIST)
# 该指令将当前目录下的文件列表全部存入变量 SRC_LIST
14. INSTALL
INSTALL 命令可以按照对象的不同分为三种类型:目标文件、非目标文件、目录。
-- 目标文件
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION < dir >]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT < component >]
[OPTIONAL]
] [...])
# TARGETS targets :targets 即为我们通过 ADD_EXECUTABLE 或 ADD_LIBRARY 定义的目标文件,可能
# 是可执行的二进制、动态库、静态库;
# DESTINATION <dir> : dir 即为定义的安装路径。安装路径可以是 绝对/相对路径。
# 如果路径以 / 开头,则是绝对路径,且绝对路径的情况下, CMAKE_INATLL_PREFIX 就失效了。
# 注:如果希望使用 CMAKE_INSTALL_PREFIX 定义安装路径,就需要使用相对路径,这时候安装后的路径就是
# ${CMAKE_INSTALL_PREFIX}/<dir>
# 略去其他参数详解,待续
-- 非目标文件
.sh 脚本文件,即为典型的非目标文件的额可执行程序。
格式:
INSTALL(PROGRAMS files... DESTINATION < dir >
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT < component >]
[RENAME < name >] [OPTIONAL])
# 使用方法基本和上述目标文件指令的 INSTALL 相同,
# 唯一不同的是,安装非目标文件之后的权限为 OWNER_EXECTE, 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 dirs : dirs 是所在源文件目录的相对路径。
# 但需要注意的是: abc 与 abc/ 有很大的区别:
# 若是 abc, 则该目录将被安装为目标路径 abc;
# 若是 abc/,则代表将该目录内容安装到目标路径,但不包括该目录本身
示例:
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*" PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ)
# 该指令的执行结果是:
# -- 将 icons 目录安装到 <prefix>/share/myproj
# -- 将scripts/ 中的内容安装到 <prefix>/share/myproj
# -- 不包含目录名 CVS 的目录
# -- 对于 scripts/* 文件指定权限为 OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ
三、基本控制语法
1. IF
IF...ELSE... 语法格式基本类似于 C语言,大致如下:
IF (expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE (expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF (expression)
# 其中,一定要有 ENDIF 与 IF 对应。
(1)IF 基本用法
-- IF(expression), expression 不为:空,0, N, NO, FALSE, NOTFOUND 或 <var>_NOTFOUND时,为真
-- IF(not exp),与上面相反;
-- 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 dir) 当 dir 是目录时,为真
--IF(DEFINE var) 如果变量被定义,为真
--IF(var MATCHES regex) 此处 var 可以用 var名,也可以用 ${var}
--IF(string MATCHES regex) 当给定变量或字符串能匹配正则表达式 regex 时,为真
示例:
IF ("hello" MATCHES "ell")
MESSAGE("true")
ENDIF("hello" MATCHES "ell")
(2) 数字比较表达式
-- IF(var LESS number)
-- IF(var GREATER number)
-- IF(var EQUAL number)
(3) 字母表顺序比较
-- IF(var1 STRLESS var2)
-- IF(var1 STRGREATER var2)
-- IF (var1 STREQUAL var2)
示例1.1:
IF(WIN32)
MESSAGE(STATUS "This is windows.")
ELSE(WIN32)
MESSAGE(STATUS "This is not windows.")
ENDIF(WIN32)
# 上述代码可以控制不同平台进行不同的控制
注:也许 ELSE(WIN32) 之类的语句阅读起来不舒服,这时候可以加上语句:
SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
示例1.2:
SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
IF(WIN32)
MESSAGE(STATUS "This is windows.")
ELSE()
MESSAGE(STATUS "This is not windows.")
ENDIF()
示例2:配合 ELSEIF使用,不同平台上的控制
IF(WIN32)
#do something related to WIN32
ELSEIF(UNIX)
#do something related to UNIX
ELSEIF(APPLE)
#do something related to APPLE
ENDIF (WIN32)
2. WHILE
语法结构:
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)
#真假判断条件参考 IF 指令
3. FOREACH
FOREACH 有三种使用形式的语法,且每个 FOREACH 都需要一个 ENDFOREACH() 与之匹配。
(1) 列表语法
FOREACH(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
示例:
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
MESSAGE(${F})
ENDFOREACH(F)
# 将当前路径下的所有源文件列表赋值给变量 SRC_LIST, 任何遍历 SRC_LIST 中的文件,并持续输出信息
# 信息内容是当前路径下所有源文件的名称
(2)范围语法
FOREACH(loop_var RANGE total)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
示例:
FOREACH(VAR RANGE 10)
MESSAGE(${VAR})
ENDFOREACH(VAR)
# 示例从 0 到 total,此处为10, 以 1 为步进
# 此处输出为: 012345678910
(3)范围步进语法
FOREACH(loop_var RANGE start stop [step])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
# 从 start 开始,到 stop 结束, 以 step 为步进
示例:
FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
# 此处输出为:581114