CMAKE

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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

在 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 指明本项目包含一个子目录 <source_dir>这样 <source_dir>目录下的 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)

<projectname>_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_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
      [[REQUIRED|COMPONENTS] [componets...]])
    
    用来调用预定义在CMAKE_MODULE_PATH下的Find<name>.cmake模块你也可以自己定义Find<name>模块通过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录中供工程使用我们在后面的章节会详细介绍FIND_PACKAGE的使用方法和Find模块的编写。

FIND_LIBRARY示例

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

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}/<DESTINATION定义的路径>

普通文件的安装

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目录安装到<prefix>/share/myprojscripts/中的内容安装到<prefix>/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)如果变量不是空0N, 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("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 最低版本号要求
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})

编译共享库

通过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_INCLUDE_PATH>
以此为例CMAKE_LIBRARY_PATH可以用在FIND_LIBRARY中。
同样因为这些变量直接为FIND_指令所使用所以所有使用FIND_指令的cmake模块都会受益。

常用变量和环境变量

变量引用方式

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

自定义变量的方式

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

cmake常用变量

  1. CMAKE_BINARY_DIR
    PROJECT_BINARY_DIR
    <projectname>_BINARY_DIR
    这三个变量指代的内容是一致的如果是in source编译指得就是工程顶层目录如果是out-of-source编译指的是工程编译发生的目录。PROJECT_BINARY_DIR跟其他指令稍有区别现在你可以理解为他们是一致的。
  2. CMAKE_SOURCE_DIR
    PROJECT_SOURCE_DIR
    <projectname>_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_VERSIONCMAKE主版本号比如2.4.6中的2
  2. CMAKE_MINOR_VERSIONCMAKE次版本号比如2.4.6中的4
  3. CMAKE_PATCH_VERSIONCMAKE补丁等级比如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<name>.cmake模块使用方法一般如上例所示
每一个模块都会定义以下几个变量

  • <name>_FOUND
  • <name>_INCLUDE_DIR or <name>_INCLUDES
  • <name>_LIBRARY or <name>_LIBRARIES

你可以通过<name>_FOUND来判断模块是否被找到如果没有找到按照工程的需要关闭某些特性、给出提醒或者中止编译上面的例子就是报出致命错误并终止构建。
如果<name>_FOUND为真则将<name>_INCLUDE_DIR加入INCLUDE_DIRECTORIES
<name>_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(<name> [<arg1> ...])
  <commands>
endfunction()

在函数体内用${arg1}来取出参数值。

return([PROPAGATE <var-name>...])

如果return()被调用了控制权被返还给函数的调用者

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6