Cmake Practice学习笔记
PROJECT指令:
PROJEXT(projectname [CXX] [C] [Java] )
例如:PROJECT (HELLO)
功能:用来定义工程名称,并可指定工程支持的语言,支持列表可以忽略,默认支持所有语言。
该指令使用后隐式的定义了两个变量:
<projectname>_BINARY_DIR
,表示编译路径,等价于HELLO_BINARY_DIR
<projectname>_SOURCE_DIR
,表示**工程路径,**等价于HELLO_SOURCE_DIR
此外,cmake系统默认预定义了:
PROJECT_BINARY_DIR
,等价于HELLO_BINARY_DIR
PROJECT_SOURCE_DIR
,等价于HELLO_SOURCE_DIR
两者区别在于:PROJECT永远指向当前工程名字,而另一种方法在修改工程名以后要全部修改,建议用PROJECT
SET指令
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
例如:SET(SRC_LIST main.c)
功能:SET命令可以用来显示定义变量,在cmake中引用变量的方式是**${}**;
MWSSAGE指令
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"...)
例如:MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
功能:用来向终端输出用户定义的信息
信息的类型包括三类:
SEND_ERROR
:产生错误,生成过程被跳过。SATUS
:输出前缀为“—”的信息。FATAL_ERROR
:立即终止cmake过程。
ADD_EXECUTABLE指令
ADD_EXECUTABLE(可执行文件,src1,src2,src3...);
例如:ADD_EXECUTABLE(hello ${SRC_LIST})
功能:定义这个工程生成一个名为hello的可执行文件,相关源文件是SRC_LIST中定义的源文件列表
ADD_SUBDIRECTORY 指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
例如:ADD_SUBDIRECTORY(src bin)
功能:用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL
将这个目录从编译过程中排除。
我们可以使用SET
指令来重新定义一下两个变量来指定最终目标二进制的位置:
EXECUTABLE_OUTPUT_PATH
:最终可执行文件的位置LIBRARY_OUTPUT_PATH
:最终库的位置
例如:
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
注意:在哪里ADD_EXECUTABLE
或 ADD_LIBRARY
,如果需要改变目标存放路径,就在哪里加入上述的定义。
INSTALL指令
目标文件的安装:INSTALL(TARGETS targets...)
。
普通文件的安装:INSTALL(FILES files...)
非目标文件的可执行文件的安装:INSTALL(PROGRAMS files...)
目录的安装:INSTALL(DIRECTORY dirs...)
功能:将编译好的文件安装到用户的目录中,通过变量**CMAKE_INSTALL_PREFIX
**可以修改目录起始位置,其默认是/usr/local
,
ADD_LIBRARY指令
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
例如:ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
功能:将对应源文件编译成动态、静态库
注:书写库名字时,cmake系统会自动生成libxxx.x
不需要自己写,对应的lib和后缀。
SET_TARGET_PROPERTIES指令
SET_TARGET_PROPERTIES(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)
功能:设置生成的库的属性
注:cmake在构建一个新的target
时会尝试清楚已经使用这个名字的库,为了回避这个问题可以利用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)
设置动态库版本号和API版本号:
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION
为动态库版本号,SOVERSION
为API版本号
GET_TARGET_PROPERTY指令
GET_TARGET_PROPERTY(VAR target property)
例如:GET_TARGET_PROPERTY(OUTPUT_VALUE hello_static OUTPUT_NAME)
功能:获取target
的对应属性到变量VAR
。
INCLUDE_DIRECTORIES指令
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
例如:INCLUDE_DIRECTORIES(/usr/include/hello)
功能:引入头文件搜索路径,自己构建的头文件不一定会在linux 默认的搜索路径下,所以需要添加。
CMAKE_INCLUDE_DIRECTORIES_BEFORE
,通过SET
这个cmake 变量为
on`,可以将添加的头文件搜索路径放在已有路径的前面。- 通过·
AFTER
或者BEFORE
参数,也可以控制是追加还是置前。
LINK_DIRECTORIES指令
LINK_DIRECTORIES(directory1 directory2 ...)
例如:INCLUDE_DIRECTORIES(/usr/include/hello)
功能:添加非标准的共享库搜索路径。
注:在工程内部同时存在共享库和可执行二进制,在编译时就需要指定一下这些共享库的路径
TARGET_LINK_LIBRARIES 指令
TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ...)
例如:TARGET_LINK_LIBRARIES(main libhello.a)
功能:用来为 arget
添加需要链接的共享库,可以是可执行文件也可以是共享库
cmake常用变量和常用环境变量
cmake变量引用方式:
一般使用${}
进行变量引用,在IF
语句中直接使用变量名而不是${}
取值。
自定义变量的方式:
- 隐式定义:使用指令以后会自动生成变量,例如
PROJECT
指令后会自动生成<projectname>_BINARY_DIR
- 显示定义:使用命令
SET
来进行。
camke常用变量:
CMAKE_BINARY_DIR,PROJECT_BINARY_DIR,<projectname>_BINARY_DIR
:- 如果in-source 编译,则指的是“工程顶层目录”。
- 如果out-of-source 编译,则指的是工程编译发生的目录
PROJECT_BINARY_DIR
另外两个有所区别
CMAKE_SOOURCE_DIR,PROJECT_SOURCE_DIR,<projectname_SOURCE_DIR>
:- 无论是那种编译方式,三个变量都代表
工程顶层目录
。
- 无论是那种编译方式,三个变量都代表
CMAKE_CURRENT_SOURCE_DIR
:- 指的是当前处理的CMakeLists.txt所在的路径。
CMAKE_CURRRENT_BINARY_DIR
:- 如果是 in-source 编译,跟
CMAKE_CURRENT_SOURCE_DIR
一致。 - 如果是 out-of-source 编译,他指的是 target 编译目录。
- 如果是 in-source 编译,跟
CMAKE_CURRENT_LIST_FILE
:- 输出调用这个变量的CMakeLists.txt的完整路径。
CMAKE_CURRENT_LIST_LINE
:- 输出这个变量所在的行。
CMAKE_MODULE_PATH
:- 定义自己的cmake模块的路径。
EXECUTABLE_OUTPUT_PATH
与LIBRARY_OUTPUT_PATH
:- 分别用来重新定义最终结果的存放目录。
PROJECT_NAME
:- 定义的项目名称。
cmake调用环境变量的方式:
通过使用$ENV{NAME}
来调用系统的环境变量。
通过使用SET(ENV{NAME} VALUE)
的方式设置环境变量的值。
cmake环境变量
CMAKE_INCLUDE_CURRENT_DIR
:- 自动添加
CMAKE_CURRENT_BINARY_DIR
和CMAKE_CURRENT_SOURCE_DIR
到当前处理的CMakeLists.txt
.
- 自动添加
CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE
:- 将工程提供的头文件目录始终至于系统头文件目录的前面,当你定义的头文件确实跟系统发生冲突时可以提供一些帮助。
CMAKE_INCLUDE_PATH
和CMAKE_LIBRARY_PATH
:CMAKE_INCLUDE_PATH
:指定使用FIND_PATH
时的搜索路径,单独设置该变量没有用CMAKE_LIBRARY_PATH
:指定使用FIND_PATH
时的搜索路径,单独设置该变量没有用
系统信息
CMAKE_MAJOR_VERSION
,CMAKE 主版本号,比如 2.4.6 中的 2CMAKE_MINOR_VERSION
,CMAKE 次版本号,比如 2.4.6 中的 4CMAKE_PATCH_VERSION
,CMAKE 补丁等级,比如 2.4.6 中的 6CMAKE_SYSTEM
,系统名称,比如 Linux-2.6.22CMAKE_SYSTEM_NAME
,不包含版本的系统名,比如 LinuxCMAKE_SYSTEM_VERSION
,系统版本,比如 2.6.22CMAKE_SYSTEM_PROCESSOR
,处理器名称,比如 i686.UNIX
,在所有的类 UNIX 平台为 TRUE,包括 OS X 和 cygwinWIN32
,在所有的 win32 平台为 TRUE,包括 cygwin
主要的开关选项
CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS
:- 用来控制
IF ELSE
语句的书写方式。
- 用来控制
BUILD_SHARED_LIBS
:- 用来控制默认的库编译方式,如果不进行设置,使用
ADD_LIBRARY
并没有指定库类型的情况下,默认编译生成的库都是静态库。
- 用来控制默认的库编译方式,如果不进行设置,使用
CMAKE_C_FLAGS
:- 设置 C 编译选项,也可以通过指令
ADD_DEFINITIONS()
添加。
- 设置 C 编译选项,也可以通过指令
CMAKE_CXX_FLAGS
:- 设置 C++编译选项,也可以通过指令
ADD_DEFINITIONS()
添加。
- 设置 C++编译选项,也可以通过指令
cmake常用指令
ADD_DEFINITIONS指令
向 C/C++编译器添加-D
定义.
ADD_DEPENDENCIES指令
ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)
定义 target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建。
ENABLE_TESTING指令
ENABLE_TESTING()
ENABLE_TESTING
指令用来控制 Makefile 是否构建 test 目标,涉及工程所有目录。
ADD_TEST指令
ADD_TEST(testname Exename arg1 arg2 ...)
不知道有什么用,好像是执行生成对应文件的测试,ADD_TEST
只有在调用ENABLE_TESTING
才会生效。
AUX_SOURCE_DIRECTORY指令
AUX_SOURCE_DIRECTORY(dir VARIABLE)
:
作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表
CMAKE_MINIMUM_REQUIRED指令
CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])
指定需要cmake的最低版本。
EXEC_PROGRAM指令
EXEC_PROGRAM(Executable [directory in which to run] [ARGS ] [OUTPUT_VARIABLE <var>] [RETURN_VALUE ])
在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行。
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)
对文件进行操作。
INCLUDE 指令
INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
用来载入 CMakeLists.txt 文件,也用于载入预定义的 cmake 模块,载入的内容将在处理到 INCLUDE 语句是直接执行。
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_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [componets...]])
用来调用预定义在 CMAKE_MODULE_PATH
下的 Find<name>.cmake
模块
控制指令
IF 指令
IF(expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE(expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF(expression)
表达式使用方法:
IF(var)
,如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND
或<var>_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(DEFINED variable)
,如果变量被定义,为真。
WHILE指令
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)
真假判断参考IF
.
FOREACH指令
以列表形式执行:
FOREACH(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
ENDFOREACH(loop_var)
以范围的形式执行:
FOREACH(loop_var RANGE total)
ENDFOREACH(loop_var)
注:以一自增
以范围和步进的方式:
FOREACH(loop_var RANGE start stop [step])
ENDFOREACH(loop_var)
从 start 开始到 stop 结束,以 step 为步进
模块
这些基本的指令使用起来起始很麻烦,为此cmake设计成可扩展模式,可以为一些通用的模块编写扩展cmake,这样以后就可以直接调用而不用自己取写如何编译那些东西,编译的模块化?哈哈哈
每一个模块都会定义以下几个变量
<name>_FOUND
<name>_INCLUDE_DIR
or<name>_INCLUDES
<name>_LIBRARY
or<name>_LIBRARIES
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)
一般使用上面这种结构,先引入模块,再对模块进行相应的操作。