Cmake小总结

Posted H-w-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cmake小总结相关的知识,希望对你有一定的参考价值。

CMake入门

介绍

CMake是一个跨平台的编译(Build)工具,可以用简单的语句来描述所有平台的编译过程。

CMake能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。

CMake 不仅可以编译源代码、制作程序库、产生适配器(wrapper)、还可以用任意的顺序建构执行档。CMake 支持 in-place 建构(二进档和源代码在同一个目录树中)和 out-of-place 建构(二进档在别的目录里),因此可以很容易从同一个源代码目录树中建构出多个二进档。CMake 也支持静态与动态程式库的建构。

功能

CMake主要有两大功能:

Cmake是用来makefile的一个工具,读入所有源文件之后,自动生成makefile

1、配置和生成各大平台的工程(vs的vcxproj,Qt的Pro):

比如设置输出目录,设置编译对象的debug后缀,设置源码在工程中的那个文件夹(Filter),配置需要依赖的第三方的头文件目录,库目录等等属性。

2、生成makefile文件

计算机编译源文件的时候是一条指令一条指令的发送给编译器执行的,这样效率很低下,所以就产生了一种文件,把所有的命令写到一个文件中,这个文件就是makefileCMake生成了这个makefile之后,各大平台的编译器都会拿到这个makefile然后解析它。将他的命令解析出来一条一条执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xXMvyWr3-1663249533817)(image/cmake入门/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rWB5qWa5Li25qC85b-1,size_15,color_FFFFFF,t_70,g_se,x_16.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6KxzZogb-1663249533818)(image/cmake入门/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rWB5qWa5Li25qC85b-1,size_20,color_FFFFFF,t_70,g_se,x_16.png)]

一.常用命令

  1. 指定cmake最小版本
cmake_minimun_version(VERSION 3.4.1)
  1. 设置项目名称
project(demo)

该命令可选。会引入两个变量:

  • demo_BINARY_DIR
  • demo_SOURCE_DIR

同时,cmake自动定义了两个等价的变量PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR.
备注:

  1. CMAKE_BINART_DIR, PROJECT_BINARY_DIR, _BINARY_DIR:这三个变量的含义一样。
  • 如果是in source编译:指的就是工程的顶层目录(CMakeLists.txt所在目录)
  • 如果是out source编译:指的就是工程编译发生的目录(build目录)
  1. CMAKE_SOURCE_DIR, PROJECT_SOURCE_DIR, _SOURCE_DIR:这三个变量的含义一样。
    都是指工程的顶级目录。
  2. 设置编译类型
//生成可执行文件
add_executable(demo demo.cpp)
//生成静态库(默认)
add_library(common STATIC util.cpp)
//生成动态库
add_library(common SHARED util.cpp)

add_library:默认生成的是静态库

  1. 制定编译包含的源文件

1)明确制定包含的源文件

add_library(demo demo.cpp test.cpp util.cpp)

2)搜索所有cpp文件

//发现一个目录下所有的源代码文件,并将列表存储再一个变量中(不会递归遍历目录)
aux_source_directory(. SRC_LIST)
add_library(demo $SRC_LIST)

3)自定义搜索规则

file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp")
add_library(demo $SRC_LIST)
#或者
file(GLOB SRC_LIST "*.cpp")
file(GLOB SRC_PROTOCOL_LIST "protocol/*.cpp")
add_library(demo $SRC_LIST $SRC_PROTOCOL_LIST)
#或者
aux_source_directory(. SRC_LIST)
aux_source_directory(protocol SRC_PROTOCOL_LIST)
add_library(demo $SRC_LIST) $SRC_PROTOCOL_LIST)
  1. 查找指定的库文件
//find_library(VAR name path)查找到指定的预编译库,并将它的路径存储在变量中。
//默认的搜索路径为cmake 包含的系统库,因此如果是NDK的公共库只需要指定库的name即可。
find_library(
                  #sets the name of the path variable
                  log-lib
                  #Specifies the name of the NDK library that you want CMake to locate
                  log
)

类似的命令:find_file(), find_path(), find_program(), find_package()

  1. 设置包含的目录
include_directories(
    $CMAKE_CURRENT_SOURCE_DIR
    $CMAKE_CURRENT_SOURCE_DIR
    $CMAKE_CURRENT_SOURCE_DIR/include
)

//linux下另一种方式设置:包含的目录
set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -I$CMAKE_CURRENT_SOURCE_DIR")
  1. 设置链接库的搜索目录
link_directories(
    $CMAKE_CURRENT_SOURCE_DIR/libs
)

//linux下另一种方式设置:链接的目录
set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -L$CMAKE_CURRENT_SOURCE_DIR)/libs")
  1. 设置target需要连接的库
target_link_libraries(
    //目标库
    demo
    //目标库需要链接的库,log-lib是上面find_library指定的变量名
    $log-lib
)

linux下会搜索xxx.so或者xxx.a文件,如果都存在会优先链接动态库(.so)
1)指定连接动态库或静态库

target_link_libraries(demo libface.a)
target_link_libraries(demo libface.so)

2)指定全路径

target_link_libraries(demo $CMAKE_CURRENT_SOURCE_DIR/libs/libface.a)
target_link_libraries(demo $CMAKE_CURRENT_SOURCE_DIR/libs/libface.so)

3)指定链接多个库

target_link_libraries(demo
  $CMAKE_CURRENT_SOURCE_DIR/libs/libface.a
  boost_system.a
  boost_thread
  pthread
)
  1. 设置变量
    1)直接设置变量的值
set(SRC_LIST main.cpp test.cpp)
add_executable(demo $SRC_LIST)

2)追加设置变量的值

set(SRC_LIST main.cpp)
set(SRC_LIST $SRC_LIST test.cpp)
add_executable(demo $SRC_LIST)

3)list 追加或者删除变量的值

set(SRC_LIST main.cpp)
list(append SRC_LIST test.cpp)
list(REMOVE_ITEM SRC_LIST main.cpp)
add_executable(demo $SRC_LIST)
  1. 条件控制
    1)if…elseif…else…endif
//1.逻辑判断和比较
if(expression):expression 不为空.(0,N,NO,OFF,FALSE,NOTFOUND)时为真。
if(not exp):与上面相反
if(var1 AND var2)
if(var1 OR var2)
if(COMMAND cmd):如果cmd确实是命令,并且可调用为真
if(EXISTS dir)/ if(EXISTS):如果目录或文件存在为真
if(file1 IS_NEWER_THAN file2):当file1比file2新,或file1/file2中有一个不存在时为真,文件名需要使用全路径。
if(IS_DIRECTORY dir):当dir是目录时为真
if(DEFINED var):如果变量被定义为真
if(var MATCHES regex):给定的变量或者字符串能够匹配正则表达式regex时为真,此处var可以用var命名,也可以用$var
if(string MATCHES regex)

//2.数字比较
if(variable LESS number):LESS 小于
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)

2)while…endwhile

while(condition)
...
endwhile()

3)foreach…endforeach

foreach(loop var RANGE start stop [step])
...
endforeach
  1. 打印信息
message($PROJECT_SOURCE_DIR)
message("build with debug mode")
message(WARNING "this is warnning message")
message(FATAL_ERROR "this build has many error") #FATAL_ERROR会导致编译失败
  1. 包含其他cmake文件
//指定包含文件的全路径
include(./common.cmake) 
//在搜索路径中搜索def.cmake文件
include(def)
//设置include的搜索路径
set(CMAKE_MODULE_PATH $CMAKE_CURRENT_SOURCE_DIR/cmake)

二.常用变量

  1. 预定义变量
PROJECT_SOURCE_DIR:工程根目录
PROJECT_BINARY_DIR:运行cmake命令的目录,通常是$PROJECT_SOURCE_DIR/build
PROJECT_NAME:返回通过project命令定义的项目名称
CMAKE_CURRENT_SOURCE_DIR:当前处理的CMakeLists.txt所在的路径
CMAKE_CURRENT_BINARY_DIR:target 编译目录
CMAKE_CURRENT_LIST_DIR:CMakeLists.txt的完整路径
CMAKE_CURRENT_LIST_LINE:当前所在的行
CMAKE_MODULE_PATH:定义自己cmake模块所在的路径。SET(CMAKE_MODULE_PATH $PROJECT_SOURCE_DIR/cmake),然后可以用INCLUDE命令来调用自己的模块
EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH:重新定义目标链接库的存放位置
  1. 环境变量
    1)使用环境变量
    $ENVname
    2)写入环境变量
    set(ENVname value)
  2. 系统信息
CMAKE_MAJOR_VERSION:cmake主版本号(3.4.1:3)
CMAKE_MINIOR_VERSION:cmake次版本号(3.4.1:4)
CMAKE_PATCH_VERSION:cmake补丁等级(3.4.1:1)
CMAKE_SYSTEM:系统名称,比如Linux-2.6.22
CMAKE_SYSTEM_NAME:不包含版本的系统名,比如Linux
CMAKE_SYSTEM_VERSION:系统版本,比如2.6.22
CMAKE_SYSTEM_PROCESSOR:处理器名称,比如i686
UNIX:在所有的类UNIX平台下该值为TRUE,包括OS X和cygwin
WIN32:在所有的win32平台下该值为TRUE,包括cygwin
  1. 主要开关选项
BUILD_SHARED_LIBS:这个开关用来控制默认的库编译方式,如果不进行设置,使用add_library又没有指定库类型的情况下,默认编译生成的库都是静态库。如果set(BUILD_SHARED_LIBS ON)后,默认生成的是动态库。
CMAKE_C_FLAGS:设置C编译选项,也可以通过指令add_definitions()添加
CMAKE_CXX_FLAGS:设置C++编译选项,也可以通过指令add_definitions()添加。

三.常用复杂命令

  1. CMake对文件的操作:file命令
//1.WRITE:写一条消息到名位filename中,如果文件存在,则会覆盖原文件,如果文件不存在,则创建该文件
file(WRITE filename "message to write" ...)
//2.APPEND:和WRITE选项一样,只是APPEND会写到文件的末尾
file(APPEND filename "message to write" ...)
//3. READ选项会将读取的文件内容存放到变量variable,读取numBytes个字节,从offset位置开始,如果制定了[HEX]参宿,二进制代码就会转成十六进制。
file(READ filename variable [LIMIT numBytes] [OFFSET offset] [HEX])
//4. STRINGS:该命令在变量myfile中存储了一个list,该list每一项是myfile.txt中的一行文本
file(STRINGS myfile.txt myfile)
//5. GLOB:该选项将会位所有匹配表达式的文件生成一个文件list,并将该list存放在variable里面,文件名的查询表达式和正则表达式类似
file(GLOB variable [RELATIVE path] [globbing expression])
//6.GLOB_RECURSE:会生成一个类似于通常GLOB选项的list,不过该选项可以递归查找文件中的匹配项
file(GLOB_RECURSE variable [RELATIVE path][FOLLOW_SYMLINKS] [globbing expressions])
//7.RENAME:对同一个文件系统下的一个文件或者目录重命名
file(RENAME <oldname> <newname>)
//8.REMOVE:将会删除指定的文件,包括在字路径下的文件
file(REMOVE [file1...])
//9.REMOVE_RECURSE:该选项会删除戈丁的文件以及目录,包括非空目录
file(REMOVE_RECURSE [file1 ...])
//10. MAKE_DIRECTORY:创建指定的目录,如果其父目录不存在时,同样也会创建
file(MAKE_DIRECTORY [directory1 directory2])
//11.RELATIVE_PATH:该选项会确定从directory参数到指定文件的相对路径,然后存到变量variable中
file(RELATIVE_PATH variable directory file)
//12. TO_CMAKE_PATH:该选项会把path转换位一个以unix的/开通的cmake风格的路径
file(TO_CMAKE_PATH path result)
//13.TO_NATIVE_PATH:把cmake风格的路径转换为本地路径的风格
file(TO_NATIVE_PATH path result)
//14.DOWNLOAD:该命令将给定的url下载到指定的文件中,如果指定了LOG,下载的日志将会被输出到log中;如果指定了STATUS status选项,下载操作的装填就会被输出到status里面,该状态的返回值是一个长度为2的字符串,错误信息如果是0,就表示没有错误;如果指定了TIMEOUT timeout选项,time秒之后,操作就会直接退出;如果指定了EXPECTED_MD5 sum选项,下载操作会认证下载的文件的实际MD5和是否与预期值相匹配,如果不匹配,操作将返回一个错误;如果指定SHOW_PROGRESS,进度信息会被打印出来,直到操作完成。
file(DOWNLOAD url file [TIMEOUT timeout] [STATUS status] [LOG log] [EXPECTED_MD5 sum] [SHOW_PROGRESS])

2.运行其他命令

  • 在配置时运行命令
指令:execute_process
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][ERROR_STRIP_TRAILING_WHITESPACE]
)

1.作用:这条指令可以执行系统命令,将输出结果保存到cmake变量或文件中去,运行一个或多个给定的命令序列,每一个进程的标准输出通过管道流向下一个进程的标准输入。
2.参数解析:

- COMMAND:子进程的命令行,CMake使用操作系统的API直接执行子进程,所有的参数逐字传输,没有中间脚本参与。
- WORKING_DIRECTORY:指定的工作目录将会设置为子进程的工作目录。
- TIMEOUT:子进程如果再指定的秒数之内没有结束,就会被中断。
- RESULT_VARIABLE:变量被设置为包含子进程的运算结果,也就是命令执行的最后结果将会保存在这个变量之中,返回码将是来自最后一个子进程的整数或者一个错误描述字符串。
- OUTPUT_VARIABLE、ERROR_VARIABLE:输出变量和错误变量
- INPUT_FILE、OUTPUT_FILE、ERROR_FILE:输入文件、输出文件、错误文件
- OUTPUT_QUIET、ERROR_QUIT:输出忽略、错误忽略,标准输出和标准错误的结果将被默认忽略。

例子:
set(MAKE_CMD "/src/bin/make.bat")
MESSAGE("COMMAN:$MAKE_CMD")
execute_precess(
  COMMAND "$MAKE_CMD"
  RESULT_VARIABLE CMD_ERROR
  OUTPUT_FILE CMD_OUTPUT
)
MESSAGE(STATUS "CMD_ERROR:" $CMD_ERROR)
MESSAGE(STATUS "CMD_OUTPUT:"$CMD_OUTPUT)
  1. 在构建的时候运行命令
例子:
find_package(PythonInterp REQUIRED)
add_custom_command(OUTPUT
  "$CMAKE_CURRENT_BINARY_DIR/include/Generated.hpp"
  COMMAND "$PYTHON_EXECUTABLE"
  "$CMAKE_CURRENT_SOURCE_DIR/scripts/GenerateHeader.py" --argument DEPENDS some_target
  add_custom_target(generate_header ALL DEPENDS "$CMAKE_CURRENT_BINARY_DIR/include/Generated.hpp")
  install(FILES $CMAKE_CURRENT_BINARY_DIR/include/Generated.hpp DESTINATION include)
)

1.find_package:查找链接库
如果编译的过程使用了外部的库,事先并不知道其头文件和链接库的位置,得在编译命令中加上包含外部库的查找路径,CMake中使用find_package方法。

  • 查找***.cmake的顺序
    1)CMAKE_MODULE_PATH:Module模式
    首先会在该路径下查找Find.cmake。
    CMAKE通过CMAKE_MODULE_PATH这个变量来指定模块cmake所在路径。
    eg:set(CMAKE_MODULE_PATH $PROJECT_SOURCE_DIR/cmake)
    2)如果上面没找到:Config模式
    会在…/./cmake/packages 或者 …/usr/local/share/中的包目录查找<库名字大写>Config.cmake 或者 <库名字小写>-config.cmake。
    3)如果找到这个包,则可以通过在工程的顶层目录中的CMakeLists.txt中添加:include_directories(_INCLUDE_DIRS)来包含库的头文件。
    target_link_libraries(源文件 _LIBRARIES)将源文件以及库文件链接起来。

备注:
无论哪一种方式,只要找到xxx.cmake,xxx.cmake里面都会定义下面这些变量
_FOUND
_INCLUDE_DIRS 或者 _INCLUDES
_LIBRARIES 或者 _LIBRARIES 或者 _LIBS 或者 _DEFINITIONS
其中:就是库名

2.find_package

FIND_PACKAGE(<name> [version] [EXACT] [QUIT] [NO_MODULE] [[REQUIRED | COMPONENTS [components...]]])

version:需要一个版本号,给出这个参数而没有给出EXACT(准确的), 那个就是找到和给出的这个版本号相互兼容就符合条件。
EXACT:要求版本号必须和version给出的精确匹配。
QUIET:会禁掉查找的包没有被发现的金高信息。对应于Find<Name>.cmake模块里面的NAME_FIND_QUIETLY变量.
NO_MODULE:给出该命令之后,cmake将直接跳过Module模式的查找,直接使用Config模式查找。
REQUIRED:该选项表示如果没有找到需要的包就会停止并且报错。
COMPONENTS:在REQUIRED选项之后,或者没有指定REQUIRED选项但是指定了COMPONENTS选项,在COMPONENTS后面可以列出一些与包相关部分组件的清单。

3.搜索原理
Cmake可以支持很多外部内部的库。通过命令可以查看当前cmake支持的模块有哪些:cmake --help-module-list
find_package的搜索模式:

  • Module模式:搜索CMAkE_MODULE_PATH指定路径下FindXXX.cmake文件(XXX就是我们要搜索库的名称),这个CMAKE_MODULE_PATH变量是cmake预先定义,但是没有值。需要通过set()方式赋值。一旦赋值之后,cmake就会最高优先级的在这个变量里面查找,如果没有找到,就在自己的安装库里面去找FindXXX.cmake模块。找到之后,执行文件从而找到XXX库。其中:具体查找库并给XXX_INCLUDE_DIR和XXX_LIBRARIES这两个变量赋值的操作由FindXXX.cmake模块完成。
  • Config模式:如果Module模式没有找到,则启用Config模式查找,搜索XXX_DIR路径下XXXConfig.cmake文件,执行该文件从而找到XXX库。其中:查找库以及给XXX_INCLUDE_DIR和XXX_LIBRARIY赋值的操作都是由XXXConfig.cmake模块完成。
  • cmake采取的是Module模式,如果Module模式没有找到,则采用Config模式查找。
    find_package:本质是执行一个.cmake文件,相当于cmake的内置脚本,这个脚本将设置我们之前提到的相关的变量,相当于根据传经来的参数来使用一个查找模块,每一个常用的库在cmake里面就有一个对应查找的模块。

3.为某一个工程添加一个自定义的命令

1)为某一个工程添加一个自定义的命令

add_custom_command(TARGET target
PRE_BUILD | PRE_LINK| POST_BUILD
COMMAND command1[ARGS] [args1...]
[COMMAND command2[ARGS] [args2...] ...]
[WORKING_DIRECTORYdir]
[COMMENT comment][VERBATIM])

//1.执行命令的时间由第二个参数决定
1.PRE_BUILD:命令将会在其他依赖项执行前执行
2.PRE_LINK:命令将会再其他依赖项执行完成后执行
3.POST_BUILD:命令将会再目标构建完成后执行
eg:
add_custom_command (
TARGET $PROJECT_NAME
POST_BUILD
COMMAND $CMAKE_COMMAND -E sleep 5
)
当PROJECT_NAME被生成执行的时候就会执行COMMAND后面的命令。

2)添加自定义命令来产生一个输出

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

其中:ARGS选项是为了向后兼容,MAIN_DEPENDENCY选项是针对VS给的一个建议,这两个选项可以忽略。
COMMAND:指定一些在构建阶段执行的命令。如果指定多于一条的命令,他会按照顺序去执行。如果指定了一个可执行目标的名字,他会自动被再构建阶段创建的可执行文件的路径替换。
DEPENDS:指定目标依赖的文件,如果依赖的文件和CMakeLists.txt相同目录的文件,则命令就会再CMakeLists.txt文件目录执行。如果没有指定DEPENDS,则只要缺少OUTPUT,该命令就会执行。如果指定的位置和CMakeLists.txt不是同一位置,则会先去创建依赖关系,先去将依赖关系的目标或者命令先去编译。
WORKING_DIRECTORY:使用给定的当前目录执行命令,如果是相对路劲,则相对于当前源目录对应的目录结构进行解析。

以上是关于Cmake小总结的主要内容,如果未能解决你的问题,请参考以下文章

Cmake小总结

cmake用法及常用命令总结

cmake 在安装mysql5.5时为啥在一些配置项前加大写字母D?

我开发中总结的小技巧

CMake使用总结

cmake使用示例与整理总结