Modern CMake文档 # 安装 - Windows安装 - 网址:[https://cmake.org/download/](https://cmake.org/download/) - macOS安装 - 网址:[https://cmake.org/download/](https://cmake.org/download/),下载CMake,并正常安装 - 安装完成之后,使用以下指令创建/usr/local/bin下的CMake的软连接 - sudo \"/Applications/CMake.app/Contents/bin/cmake-gui" \--install - 注意:执行此命令的时候确保CMake处于关闭状态 - 重新打开Terminal,即可正常使用 CMake 的各种指令了,也可以在应用程序列表中使用带 GUI 的 CMake 工具。 - Linux安装 - 网址:[https://cmake.org/download/](https://cmake.org/download/),下载对应版本的CMake(32位或者64位) - 将下载的安装包上传到Linux服务器,比如:/root - 输入以下命令进行解压 - tar -zxvf cmake-3.13.0-rc1-Linux-x86_64.tar.gz - 注意:后面是官网下载的对应版本的名字 - 把解压后的目录改名为:cmake - mv cmake-3.10.0-rc4-Linux-x86_64 cmake - 设置环境变量 - 使用指令"vi .bash_profile"来设置环境变量,找到PATH=\$PATH:\$\....这一行,后面添加CMake安装目录里面的bin目录的地址 - 如果是在/root目录安装的CMake,那添加的目录就是:/root/cmake/bin - 安装完毕,环境变量设置成功之后,命令行输入:cmake \--version检测是否安装成功 - 输出:cmake version 3.13,表示安装成功 # 使用CMake生成项目 - 使用Windows或者Linux生成项目 - 进入项目目录(CMakeLists.txt所在目录),新建一个build文件夹,因为CMake会产生很多自己的中间文件。 - 执行 :cmake ../ 就会在build目录产生项目文件,windows下面默认产生vs的项目。 - 如果要产生其他编译器的makefile,就需要使用-G指定编译器 - cmake -G \"MinGW Makefiles\" ../ - 可以使用cmake \--help 来查看使用的编译器的名字 - 生成项目工程文件或者makefile之后,就可以使用对应的编译器来编译项目 - 使用macOS生成项目 - mac下基本操作和windows、Linux相同,不过cmake命令使用的是:cmake .. (没有右斜杠) - 注意:(默认已经配置好环境变量) # CMake命令行选项的设置 - 指定构建系统生成器:-G - 使用:-G 命令可以指定编译器,当前平台支持的编译器名称可以通过帮助手册查看:cmake \--help, > 例如:cmake -G \"Visual Studio 15 2017\" ../ 使用vs2017构建工程 - CMakeCache.txt文件 - 当cmake第一次运行一个空的构建的时候,他就会创建一个CMakeCache.txt文件,文件里面存放了一些可以用来制定工程的设置,比如:变量、选项等 - 对于同一个变量,如果Cache文件里面有设置,那么CMakeLists文件里就会优先使用Cache文件里面的同名变量。 - CMakeLists里面通过设置了一个Cache里面没有的变量,那就将这个变量的值写入到Cache里面 - 例子: - SET(var 1024) > //变量var的值被设置成1024,如果变量var在Cache中已经存在,该命令不会覆盖cache里面的值 - SET(var 1024..CACHE..) > //如果var在Cache中存在,就优先使用Cache里面的值,如果不存在,就将该值写入Cache里面​ - SET(var..CACHE..FORCE) > //无论Cache里面是否存在,都始终使用该值 - 添加变量到Cache文件中:-D - 注意:-D后面不能有空格,例如:cmake -DCMAKE_BUILD_TYPE:STRING=Debug - 从Cache文件中删除变量:-U - 此选项和-D功能相反,从Cache文件中删除变量,支持使用\*和?通配符 - CMake命令行模式:-E - CMake提供了很多和平台无关的命令,在任何平台都可以使用:chdir, copy, copy_if_different等 - 可以使用:cmake -E help进行查询 - 打印运行的每一行CMake - 命令行选项中:\--trace,将打印运行的每一行CMake,例如windows下执行: cmake \--trace .. - 命令:\--trace-source=\"filename\"就会打印出有关filename的执行 - 设置编译参数 - add_definitions(-DENABLED),当在CMake里面添加该定义的时候,如果代码里面定义了#ifdef ENABLED [#endif相关的片段]{.underline},此时代码里面这一块代码就会生效 - //add_definitions( "-Wall -ansi --pedantic --g") - 该命令现已经被取代,使用: add_compile_definitions(WITH_OPENCV2) - 设置默认值命令:option - option命令可以帮助我们设置一个自定义的宏,如下: - option(MY-MESSAGE \"this is my message\" ON) - 第一个参数就是我们要设置的默认值的名字 - 第二个参数是对值的解释,类似于注释 - 第三个值是这个默认值的值,如果没有声明,CMake默认的是OFF - 使用:设置好之后我们在命令行去使用的时候,也可以去给他设定值:cmake -DMY-MESSAGE=on ../ - 注意:使用的时候我们应该在值的前面加"D" - 这条命令可将MY-MESSAGE的值设置为on,通过这个值我们可以去触发相关的判断 # CMake基础知识简介 - 最低版本 - 每一个CMake.txt的第一行都会写:cmake_minimum_required(VERSION 3.1),该命令指定了CMake的最低版本是3.1 - 命令名称cmake_minimum_required不区分大小写 - 设置版本范围: ``` cmake_minimum_required(VERSION 3.1\...3.12) ``` 该命令表示支持3.1至3.12之间的版本 - 判断CMake版本: ``` if(\${CMAKE_VERSION} VERSION_LESS 3.12) cmake_policy(VERSION \${CMAKE_MAJOR_VERSION}.\${CMAKE_MINOR_VERSION}) endif() ``` - 该命令表示:如果CMake版本小于3.12,则if块将为true,然后将设置为当前CMake版本;如果CMake版本高于3.12,if块为假​,cmake_minimum_required将被正确执行 - 注意:如果需要支持非命令行Windows版本则需在上面的if判断加上else分支,如下: ``` cmake_minimum_required(VERSION 3.1) if(${CMAKE_VERSION} VERSION_LESS 3.12) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) else() cmake_policy(VERSION 3.12) endif() ``` - 设置生成项目名称 - 使用的命令:project(MyProject) - 表示我们生成的工程名字叫做:MyProject - 命令还可以标识项目支持的语言,写法:project(MyProject\[C\] \[C++\]),不过通常将后面的参数省掉,因为默认支持所有语言 - 使用该指令之后系统会自动创建两个变量:\\_BINARY_DIR:二进制文件保存路径、\\_SOURCE_DIR:源代码路径 - 执行:project(MyProject),就是定义了一个项目的名称为:MyProject,对应的就会生成两个变量:\_BINARY_DIR和_SOURCE_DIR,但是cmake中其实已经有两个预定义的变量:PROJECT_BINARY_DIR和PROJECT_SOURCR_DIR - 关于两个变量是否相同,涉及到是内部构建还是外部构建 - 内部构建\ > cmake ./\ > make​ - 外部构建\ > mkdir build\ > cd ./build\ > cmake ../\ > make​​​ - 内部构建和外部构建的不同在于:cmake 的工作目录不同。内部构建会将cmake生成的中间文件和可执行文件放在和项目同一目录;外部构建的话,中间文件和可执行文件会放在build目录。 - PROJECT_SOURCE_DIR和_SOURCE_DIR无论内部构建还是外部构建,指向的内容都是一样的,都指向工程的根目录 - PROJECT_BINARY_DIR和_BINARY_DIR指向的相同内容,内部构建的时候指向CMakeLists.txt文件的目录,外部构建的,指向target编译的目录 - 生成可执行文件 - 语法:add_executable(exename srcname)\ > exename:生成的可执行文件的名字\ > srcname:以来的源文件​​ - 该命令指定生成exe的名字以及指出需要依赖的源文件的文件名 - 获取文件路径中的所有源文件 - 命令:aux_sourcr_directory(\ \) - 例子:aux_sourcr_directory(. DIR_SRCS),将当前目录下的源文件名字存放到变量DIR_SRCS里面 ,如果源文件比较多,直接用DIR_SRCS变量即可 - 生成可执行文件:add_executable(Demo \${DIR_SRCS}),将生成的可执行文件命名为:Demo.exe - 生成lib库 - 命令:add_library(libname \[SHARED\|STATIC\|MODULE\] \[EXCLUDE_FROM_ALL\] source1 source2 \... sourceN)\ > libname:生成的库文件的名字\ > ​\[SHARED\|STATIC\|MODULE\]:生成库文件的类型(动态库\|静态库\|模块)\ > \[EXCLUDE_FROM_ALL\]:有这个参数表示该库不会被默认构建​\ > source2 \... sourceN:生成库依赖的源文件,如果源文件比较多,可以使用​aux_sourcr_directory命令获取路径下所有源文件,具体章节参见:CMake基础知识简介-\>生成可执行文件-\>获取路径中所有源文件 - 例子:add_library(ALib SHARE alib.cpp) - 添加头文件目录 - 命令1:target_include_directories(\ \[SYSTEM\] \[BEFORE\] \ \[items1\...\] \[\ \[items2\...\] \...\])\ > 当我们添加子项目之后还需要设置一个include路径,例子:\ > eg:target_include_directories(RigelEditor PUBLIC ./include/rgeditor),表示给RigelEditor 这个子项目添加一个库文件的路径 - 命令2:include_directories(\[AFTER\|BEFORE\] \[SYSTEM\] dir1 \[dir2 ...\])\ > 参数解析:\ > ​\[AFTER\|BEFORE\]:指定了要添加路径是添加到原有列表之前还是之后\ > \[SYSTEM\]​:若指定了system参数,则把被包含的路径当做系统包含路径来处理\ > dir1 \[dir2 ...\]​把这些路径添加到CMakeLists及其子目录的CMakeLists的头文件包含项目中\ > 相当于g++选项中的-l的参数的作用\ > ​​举例:\ > include_directories(\"/opt/MATLAB/R2012a/extern/include\") - 两条指令的作用都是讲将include的目录添加到目标区别在于include_directories是CMake编译所有目标的目录进行添加,target_include_directories是将CMake编译的指定的特定目标的包含目录进行添加 - 添加需要链接的库文件路径 - 命令1:target_link_libraries(\ \[item1 \[item2 \[\...\]\]\] \[\[debug\|optimized\|general\] \\] \...) - 作用:为给定的目标设置链接时使用的库(设置要链接的库文件的名称) - eg:target_link_libraries(MyProject a b.a [c.so]{.underline})   //将若干库文件链接到hello中,target_link_libraries里的库文件的顺序符合gcc/g++链接顺序规则,即:被依赖的库放在依赖他的库的后面,如果顺序有错,链接将会报错 - 关键字:debug对应于调试配置 - 关键字:optimized对应于所有其他的配置类型 - 关键字:general对应于所有的配置(该属性是默认值) - 命令2:link_libraries - 作用:给当前工程链接需要的库文件(全路径) - eg:link_libraries((\"/opt/MATLAB/R2012a/bin/glnxa64/[libeng.so]{.underline}\")//必须添加带名字的全路径 - 区别:link_libraries和target_link_libraries命令的区别:target_link_libraries可以给工程或者库文件设置其需要链接的库文件,而且不需要填写全路径,但是link_libraries只能给工程添加依赖的库,而且必须添加全路径 - 添加需要链接的库文件目录 - 命令:link_directories(添加需要链接的库文件目录) - 语法:link_directories(directory1 directory2 \...) - 例子:link_directories(\"/opt/MATLAB/R2012a/bin/glnxa64\") - 指令的区别:指令的前缀带target,表示针对某一个目标进行设置,必须指明设置的目标;include_directories是在编译时用,指明.h文件的路径;link_directoeies是在链接时用的,指明链接库的路径;target_link_libraries是指明链接库的名字,也就是具体谁链接到哪个库。link_libraries不常用,因为必须指明带文件名全路径 - 控制目标属性 - 以上的几条命令的区分都是:是否带target前缀,在CMake里面,一个target有自己的属性集,如果我们没有显示的设置这些target的属性的话,CMake默认是由相关的全局属性来填充target的属性,我们如果需要单独的设置target的属性,需要使用命令:set_target_properties() - 命令格式\ > 格式:\ > ​set_target_properties(target1 target2 \...\ > PROPERTIES\ > 属性名称1 值\ > 属性名称2 值\ > \...\ > ) - 控制编译选项的属性是:COMPILE_FLAGS - 控制链接选项的属性是:LINK_FLAGS - 控制输出路径的属性:EXECUTABLE_OUTPUT_PATH(exe的输出路径)、LIBRARY_OUTPUT_PATH(库文件的输出路径) - 举例:\ > 命令:\ > ​set_target_properties(exe\ > PROPERTIES\ > LINK_FLAGS -static\ > LINK_FLAGS_RELEASE -s\ > ) - 这条指令会使得exe这个目标在所有的情况下都采用-static选项,而且在release build的时候 -static -s 选项。但是这个属性仅仅在exe这个target上面有效 - 变量和缓存 - 局部变量 - CMakeLists.txt相当于一个函数,第一个执行的CMakeLists.txt相当于主函数,正常设置的变量不能跨越CMakeLists.txt文件,相当于局部变量只在当前函数域里面作用一样, - 设置变量:set(MY_VARIABLE \"value\") - 变量的名称通常大写 - 访问变量:\${MY_VARIABLE} - 缓存变量 - 缓存变量就是cache变量,相当于全局变量,都是在第一个执行的CMakeLists.txt里面被设置的,不过在子项目的CMakeLists.txt文件里面也是可以修改这个变量的,此时会影响父目录的CMakeLists.txt,这些变量用来配置整个工程,配置好之后对整个工程使用。 - 设置缓存变量:set(MY_CACHE_VALUE \"cache_value\" CACHE INTERNAL \"THIS IS MY CACHE VALUE\")\ > //THIS IS MY CACHE VALUE,这个字符串相当于对变量的描述说明,不能省略,但可以自己随便定义 - 环境变量 - 设置环境变量:set(ENV{variable_name} value) - 获取环境变量:\$ENV{variable_name} - 内置变量 - CMake里面包含大量的内置变量,和自定义的变量相同,常用的有以下: - CMAKE_C\_COMPILER:指定C编译器 - CMAKE_CXX_COMPILER:指定C++编译器 - EXECUTABLE_OUTPUT_PATH:指定可执行文件的存放路径 - LIBRARY_OUTPUT_PATH:指定库文件的放置路径 - CMAKE_CURRENT_SOURCE_DIR:当前处理的CMakeLists.txt所在的路径 - CMAKE_BUILD_TYPE:控制构建的时候是Debug还是Release\ > 命令:set(CMAKE_BUILD_TYPE Debug) - CMAKE_SOURCR_DIR:无论外部构建还是内部构建,都指的是工程的顶层目录(参考project命令执行之后,生成的_SOURCR_DIR以及CMake预定义的变量PROJECT_SOURCE_DIR) - CMAKE_BINARY_DIR:内部构建指的是工程顶层目录,外部构建指的是工程发生编译的目录(参考project命令执行之后,生成的_BINARY_DIR以及CMake预定义的变量PROJECT_BINARY_DIR) - CMAKE_CURRENT_LIST_LINE:输出这个内置变量所在的行 - 缓存 - 缓存就是之前提到的CMakeCache文件,参见:CMake命令行选项的设置-\>CMakeCache.txt文件 - CMake基本控制语法 - If - 基本语法\ > if (expression)\ >     COMMAND1(ARGS \...)\ >     COMMAND2(ARGS \...)\ >     \...\ > else (expression)\ >     COMMAND1(ARGS \...)\ >     COMMAND2(ARGS \...)\ >     \...\ > endif (expression)\ > 注意:ENDIF要和IF对应​ - if (expression),expression不为:空,0,N,NO,OFF,FALSE,NOTFOUND或\< var \>\_NOTFOUND,为真 - IF (not exp),与上面相反 - if (var1 AND var2),var1且var2都为真,条件成立 - if (var1 OR var2),var1或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 (DEFINED var) 如果变量被定义,为真 - if (string MATCHES regex) 当给定变量或字符串能匹配正则表达式regex时,为真\ > 例:\ > IF (\"hello\" MATCHES \"ell\") \ > MESSAGE(\"true\") \ > ENDIF (\"hello\" MATCHES \"ell\") - 数字表达式 - if (var LESS number),var小于number为真 - if (var GREATER number),var大于number为真 - if (var EQUAL number),var等于number为真 - 字母表顺序比较 - IF (var1 STRLESS var2),var1字母顺序小于var2为真 - IF (var1 STRGREATER var2),var1字母顺序大于var2为真 - IF (var1 STREQUAL var2),var1和var2字母顺序相等为真 - While - 语法结构\ > WHILE(condition)\ > COMMAND1(ARGS \...)\ > COMMAND2(ARGS \...)\ > \...\ > ENDWHILE(condition) - 真假条件的判断参考if - Foreach - FOREACH有三种使用形式的语法,且每个FOREACH都需要一个ENDFOREACH()与之匹配。 - 列表循环 - 语法\ > FOREACH(loop_var arg1 arg2 \...)\ > COMMAND1(ARGS \...)\ > COMMAND2(ARGS \...)\ > \...\ > ENDFOREACH(loop_var)​ - 例子\ > eg:\ > ​AUX_SOURCE_DIRECTORY(. SRC_LIST)\ > FOREACH(F \${SRC_LIST})\ > MESSAGE(\${F})\ > ENDFOREACH(F) - 例子中,先将当前路径的源文件名放到变量SRC_LIST里面,然后遍历输出文件名 - 范围循环 - 语法\ > FOREACH(loop_var RANGE total)\ > COMMAND1(ARGS \...)\ > COMMAND2(ARGS \...)\ > \...\ > ENDFOREACH(loop_var) - 例子\ > eg:\ > ​FOREACH(VAR RANGE 100)\ > MESSAGE(\${VAR})\ > ENDFOREACH(VAR)\ > ​​ - 例子中默认起点为0,步进为1,作用就是输出:0\~100 - 范围步进循环 - 语法\ > FOREACH(loop_var RANGE start stop \[step\])\ > COMMAND1(ARGS \...)\ > COMMAND2(ARGS \...)\ > \...\ > ENDFOREACH(loop_var) - 例子\ > eg:\ > FOREACH(A RANGE 0 100 10)\ > MESSAGE(\${A})\ > ENDFOREACH(A)​ - 例子中,起点是0,终点是100,步进是10,输出:0,10,20,30,40,50,60,70,80,90,100 - 构建规范以及构建属性// - 用于指定构建规则以及程序使用要求的指令:target_include_directories(), target_compile_definitions(), target_compile_options() - 指令格式 - target_include_directories(\ \[SYSTEM\] \[BEFORE\]\ \[items1\...\] \[\ \[items2\...\] \...\])\ > Include的头文件的查找目录,也就是Gcc的\[-Idir\...\]选项 - target_compile_definitions(\ \ \[items1\...\]\[\ \[items2\...\] \...\])\ > 通过命令行定义的宏变量 - target_compile_options(\ \[BEFORE\] \ \[items1\...\] \[\ \[items2\...\] \...\]\ > gcc其他的一些编译选项指定,比如-fPIC\ > ​​ - -fPIC选项说明\ > 说明:-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),\ >   则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意\ >   位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。 - -ldir选项说明\ > 说明:在你是用 [#include]{.underline} \"file\" 的时候, gcc/g++ 会先在当前目录查找你所制定的头文件, 如果没有找到, 他会到缺省的头文件目录找, 如果使用 -I 制定了目录,他会先在你所制定的目录查找, 然后再按常规的顺序去找。 - 以上的额三个命令会生成INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS, COMPILE_OPTIONS变量的值,。或者 INTERFACE_INCLUDE_DIRECTORIES,INTERFACE_COMPILE_DEFINITIONS, INTERFACE_COMPILE_OPTIONS的值. - 这三个命令都有三种可选模式: PRIVATE, PUBLIC。 INTERFACE. PRIVATE模式仅填充不是接口的目标属性; INTERFACE模式仅填充接口目标的属性.PUBLIC模式填充这两种的目标属性。 - - 宏和函数 - CMake里面可以定义自己的函数(function)和宏(macro) - 区别1:范围。函数是有范围的,而宏没有。如果希望函数设置的变量在函数的外部也可以看见,就需要使用PARENT_SCOPE来修饰,但是函数对于变量的控制会比较好,不会有变量泄露 - 例子 - 宏(macro)\ > eg:\ > ​macro( \[arg1 \[arg2 \[arg3 \...\]\]\])\ >      COMMAND1(ARGS \...)\ >      COMMAND2(ARGS \...)\ >      \...\ > endmacro() - 函数(function)\ > eg:\ > ​function( \[arg1 \[arg2 \[arg3 \...\]\]\])\ >      COMMAND1(ARGS \...)\ >      COMMAND2(ARGS \...)\ >      \...\ > endfunction() - 函数和宏的区别还在于,函数很难将计算结果传出来,使用宏就可以将一些值简单的传出来 - 例子\ > eg:\ > macro(macroTest)\ >      set(test1 \"aaa\")\ > endmacro()\ > ​\ > function(funTest)\ >      set(test2 \"bbb\")\ > endfunction()\ > ​\ > macroTest()\ > message(\"\${test1}\")\ > ​\ > funTest()\ > message(\"\${test2}\")\ > ​ - 运行上面这个代码,就会显示"aaa",因为函数里面的test1是局部的,出了这个函数就出了他的作用域 - 和其他文件的交互 - 在代码中使用CMake中定义的变量 - 命令:configure_file - 作用:让普通文件也能使用CMake中的变量。 - 语法\ > configure_file(\ \\ > \[COPYONLY\] \[ESCAPE_QUOTES\] \[[\@ONLY]{.underline}\]\ > \[NEWLINE_STYLE \[UNIX\|DOS\|WIN32\|LF\|CRLF\] \])​\ > 解释:拷贝一个 \(输入文件) 文件到 \ (输出文件),并且替换输入文件中被 \@VAR@ 或者 \${VAR} 引用的变量值。每一个变量将被替换成当前的变量值​ - 参数解析 - COPYONLY:只拷贝文件,不进行任何的变量替换。这个选项在指定了 NEWLINE_STYLE 选项时不能使用(无效)。 - ESCAPE_QUOTES:躲过任何的反斜杠(C风格)转义。\ > 注:躲避转义,比如你有个变量在CMake中是这样的 set(FOO_STRING \"\\\"foo\\\"\") 那么在没有 ESCAPE_QUOTES 选项的状态下,通过变量替换将变为 \"\"foo\"\",如果指定了 ESCAPE_QUOTES 选项,变量将不变。 - [\@ONLY]{.underline}:限制变量替换,让其只替换被 \@VAR@ 引用的变量(那么 \${VAR} 格式的变量将不会被替换)。这在配置 \${VAR} 语法的脚本时是非常有用的。 - NEWLINE_STYLE \