CMake学习总结

本文介绍CMake的基础知识,包括核心功能、主要特点、常用指令及变量等,帮助读者快速掌握CMake的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CMake学习总结

Cmake主要功能:

  • 根据编译器产生相应编译器的源码编译环境
  • 根据相应编译器编译程序
  • 编译执行程序可安装到任何一指定路径

Cmake主要特点

  • 开放源代码,使用类似BSD许可发布。
  • 跨平台,并可生成native编译配置文件,在Linux/Unix平台,生成makefile,在苹果平台,可以生成xcode,在Windows平台,可以生成MSVC平台,可以生成MSVC的工程文件。
  • 能够管理大型项目。
  • 简化编译构建过程和编译过程。Cmake的工具链非常简单:cmake+make
  • 高效率,主要因为Cmake的工具链中没有libtool。
  • 可扩展,可以为cmake编写特定功能的模块,扩充cmake功能。

Cmake常用技巧

  • 需要看make构建的详细过程,可以使用make VERBOSE=1或者VERBOSE=1 make来进行构建。
  • Cmake的构建文件为CMakeLists.txt。

CMake基本指令

  • PROJECT指令

    PROJECT指令语法为:PROJECT(projectname [CXX][C][Java]。
    主要是指定工程名称和工程支持的语言,默认支持所有语言。
    该指令隐式定义了两个cmake变量:_BINARY_DIR和_SOURCE_DIR,若采用内部编译,则这两个变量都是工程所在路径。同时Cmake系统也预定义了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR变量。
    为统一起见,建议以后直接使用PROJECT_BINARY_DIR,PROJECT_SOURCE_DIR,即使修改了工程名称,也不会影响这两个变量。如果使用了_SOURCE_DIR,修改工程名称后,需要同时修改这些变量。

  • SET指令

    SET指令语法为:SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE])。
    SET指令用来显示定义变量。

  • MESSAGE指令

    MESSAGE指令的语法为:MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display” …)。
    这个指令用于向终端输出用户定义的信息,包含了三种类型:SEND_ERROR,产生错误,生成过程被跳过。SATUS,输出前缀为——的信息。FATAL_ERROR,立即终止所有camke过程。

  • ADD_EXECUTABLE指令

    ADD_EXECUTABLE(exe$(SRC_LIST))定义这个工程会生成一个文件名为exe的可执行文件,相关源文件是SRC_LIST中定义的源文件列表。

  • ADD_SUBDIRECTORY指令

    ADD_SUBDIRECTORY指令语法为:ADD_SUBDIEECTORY(source_dir [binary_dir][EXCLUDE_FROM_ALL])。
    这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。
    EXCLUDE_FROM_ALL参数的含义是将这个目录从编译过程排除,如工程example,可能就需要工程构建完成后,再进入example目录单独进行构建。(也可以通过以来解决此类问题)

  • SUBDIRS指令

    SUBDIRS指令用法为:SUBDIRS(dir1 dir2 …),此指令不推荐使用。
    它可以一次添加多个子目录,并且,即使外部编译,子目录体系仍然会被保存。

  • ADD_LIBARAY指令

    ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXECLUDE_FROM_ALL],其中,SHARED:动态库,STATIC:静态库,MODULE为在使用dyld的系统有效,如果不支持dyld,则被当作SHARED对待。EXCLUDE_FROM_ALL参数的意思是这个库不会默认构建,除非有其他组件依赖或者手工构建。

  • SET_TARGET_PROPERTIES指令

    SET_TARGET_PROPERTIES基本语法:SET_TARGET_PROPERTIES(target1 target2 … PROPRERTIES prop1 value1 prop2 values …)
    这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和API版本。
    SET_TARGET_PROOERTIES(hello PROPERTIES VERSION 1.2 SVOERSION 1)VERSION指代动态库版本,SOVERSION指代API版本。

  • INCLUDE_DIRECTORIES指令

    INCLUDE_DIRECTORIES指令语法为:INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)
    该指令用于向工程中添加多个特定的头文件搜索路径,路径指令用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面。
    可以通过两种方式进行控制搜索路径添加的方式:
    1)CMAKE_INCLUDE_DIRECTIONS_BEFORE,通过SET这个cmake变量为on,可以将添加的头文件的搜索路径放在已有路径的前面。
    2)通过AFTER或者BEFORE参数,也可以控制追加还是置前。

  • TARGET_LINK_LIBRARIES指令

    TARGET_LINK_LIBRARIES语法为:TARGET_LINK_LIBRARIES(target library1<debug | optimized> library2 ...)
    这个指令可以用来为target添加需要链接的共享库。
    
  • 特殊环境变量

    特殊环境变量CMAKE_INCLUDE_PATH和CMAKE_LIBRARY_PATH。这两个变量是环境变量但不是cmake变量。这两个变量主要用来解决autotools工程中-extra-include-dir等参数的支持。即如果头文件没有放在常规路径(/usr/include,/usr/local/include等),则可以通过这些变量进行弥补。也可以使用export CMAKE_INCLUDE_PATH=/usr/include/…

  • FIND_PATH用来指定路径中搜索文件名。如果不使用FIND_PATH,CMAKE_INCLUDE_PATH变量的设置是没有作用。

Cmake基本语法规则

  • 变量使用${}方式取值,但是在IF控制语句中是直接使用变量名
  • 指令(参数1 参数2 …)参数使用括弧括起,参数之间使用空格或分号分开。
  • 指令是大小写无关的,参数和变量是大小写相关的,推荐全部使用大写指令。
  • 可以使用make clean对构建结果进行清理。

Cmake内部编译和外部编译

  • Cmake内部编译,它会生成一些无法自动删除的中间文件,故建议使用外部编译。
  • 外部编译的好处为,对原有工程没有任何影响,所有动作全部发生在编译目录。外部编译的过程如下:
    • 首先,先清除目录中原工程文件和CMakeLists.txt之外的所有中间文件,最关键的是CMakeCache.txt。
    • 构建build目录
    • 进入build目录,运行cmake <工程的全部路径>,查看build目录,就能够生成编译需要的Makefile和其他中间文件。
      -运行make构建工程,就会在当前目录(build目录)中获得目标文件。

程序安装方法

  • 从代码编译后直接make install安装。
  • 打包时指定目录安装。安装指令具体如下:

    INSTALL指令用法为:
    INSTALL指令用于定义安装规则,安装的内容包括目标二进制、动态库、静态库以及文件、目录、脚本等。
    INSTALL指令包含了各种安装类型。
    INSTALL(TARGETS targets ... [[ARCHIVE][LIBRARY][RUNTIME][DESTINATION <dir>][PERMISSIONS permissions...][CONFIGURATIONS][Debug][Release]...]][COMPONENT <component>][OPTIONAL]]
    目标类型有三种:ARCHIVE特指静态库,LIBARAY特指动态库,RUNTIME:特指可执行目标二进制。DESTINATION:安装路径。
    

Cmake常用变量

  • CMAKE_BINARY_DIR PROJECT_BINARY_DIR _BINARY_DIR:这三个变量指代的内容是一致的,如果是in source编译,指的就是工程顶层目录,如果时out-of-source编译,指的是工程编译发生的目录。
  • CMAKE_SOURCE_DIR PROJECT_SOURCE_DIR _SOURCE_DIR:这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。
  • CMAKE_CURRENT_SOURCE_DIR:指的是当前处理的CMakeLists.txt所在的路径。
  • CMAKE_CURRENT_BINARY_DIR:如果是in-source编译,它跟CMAKE_CURRENT_SOURCE_DIR一致,如果是our-of-source编译,它指的的target编译目录。使用ADD_SUBDIRECTORY(src bin)可以改变这个变量值。使用SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它只是修改了最终目标文件存放的路径。
  • CMAKE_CURRENT_LIST_FILE:输出调用这个变量的CMakeLists.txt的完整路径。
  • CMAKE_CURRENT_LIST_LINE:输出这个变量所在的行
  • CMAKE_MODULE_PATH:这个变量用来定义自己的cmake模块所在路径。如果工程比较复杂,可能需自己编写cmake模块,这些cmake模块随工程发布,为让cmake在处理CMakeLists.txt时,需要通过SET指令,将自己的cmake模块路径设置一下。
  • EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH分别用来重新定义最终结果存放目录。
  • PROJECT_NAME:返回通过PROJECT指令定义的项目名称。

Cmake调用环境变量方式

  • 使用$ENV{NAME}指令就可以调用系统的环境变量。
  • 设置环境变量的方式:SET(ENV{变量名} 值)

Cmake系统变量信息

  • CMAKE_MAJOR_VERSION,CMAKE主版本号
  • CMAKE_MINOR_VERSION,CMAKE次版本号
  • CMAKE_PATCH_VERSION,CMAKE补丁等级
  • CMAKE_SYSTEM,系统名称
  • CMAKE_SYSTEM_NAME:不包含版本的系统名
  • CMAKE_SYSTEM_VERSION:系统版本
  • CMAKE_SYSTEM_PROCESSOR:处理器名称
  • UNIX,在所有的类UNIX平台为TRUE,包括OS X和cygwin
  • WIN32,在所有的win32平台为TRUE,包括cygwin

Cmake主要开关选项

  • CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS,用来控制IF ELSE语句的书写方式
  • BULID_SHARED_LIBS,用来控制默认的库编译方式,如果不进行设置,使用ADD_LIBRARY并没有设置库类型的情况下,默认编译生成的库都是静态库。如果SET(BUILD_SHARED_LIB\ ON)后,默认生成为动态库。
  • CMAKE_C_FLAGS设置C编译选项,也可以通过指令ADD_DEFINITIONS()添加。
  • CMKAE_CXX_FLAGS,设置C++编译选项,可以通过指令ADD_DEFINITIONS()添加。

Cmake常用指令

  • ADD_DEFINITIONS向C/C++编译器添加-D定义,比如:ADD_DEFINITIONS(-DENABLE_DEBUG_DABC),参数之间用空格分割。

  • ADD_DEPENDENCIES定义target依赖的其他target,确保在编译target之前,其他target已经被构建。

  • ADD_EXECUTABLE、ADD_LIBRARY、ADD_SUBDIRECTORY。

  • ADD_TEST与ENABLE_TESTING指令。ENABLE_TESTING指令迎来控制Makefile是否构建test目标,涉及工程所有目录。语法很简单,没有任何参数,一般情况下,这个指令放在工程的主CMakeList.txt中。

  • ADD_TEST指令的语法:ADD_TEST(testname Exename arg1 arg2 …)testname是自定义的test名称,Exename可以是构建的目标文件也可以是外部脚本等等。后面连接传递给可执行文件的参数。如果没有在任何同一个CMakeLists.txt中打开ENABLE_TESTING()指令,任何ADD_TEST都是无效的。

  • AUX_SOURCE_DIRECTORY基本语法是:AUX_SOURCE_DIRECTORY(dir VAEIABLE)作用是发现一个目录下所有源代码文件并将列表存储在一个变量中,这个指令被用来自动构建源文件列表。目前,cmake还不能自动发现新添加的源文件。

  • CMAKE_MINIMUM_REQUIRED语法为CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])如果Cmake版本低于某一个值,则出现严重错误,整个过程终止。

  • EXEC_PROGRAM:在CMakeLists.txt处理过程中执行命令,并不会在生成Makefile中执行。具体语法为:EXEC_PROGRAME(Executable [directory in which to run][ARGS][OUTPUT_VARIABLE][RETURN_VALUE])用于在指定的目录运行某个程序,通过ARGS添加参数,如果要获取输出和返回值,可通过OUTPUT_VARIABLE和RETURN_VALUE分别定义两个变量,这个指令可以在CMakeLists.txt处理过程中支持任何命令,如根据系统情况去修改代码文件等。

  • FILE指令:文件操作指令,基本语法为:

    • FILE(WRITE filename “message to write” …)
    • FILE(APPEND filename “message to write” …)
    • FILE (READ filename variable)
    • FILE(GLOB variable [RELATIVE path][globbing expression]…)
    • FILE(REMOVE [directory]…)
    • FILE(REMOVE_RECURESE [directory]…)
    • FILE(MAKE_DIRECTORY [directory]…)
    • FILE(RELATIVE_PATH variable directory file)
    • FILE(TO_CMAKE_PATH path result)
    • FILE(TO_NATIVE_PATH path result)
  • INCLUDE指令,用来载入CMakeLists.txt文件,也用于载入预定义的cmake模块。INCLUDE(file1 [OPTIONAL]);INCLUDE(module [OPTIONAL])。OPTIONAL参数的作用是文件不存在也不会产生错误。可以指定载入一个文件,如果定义的是一个模块,那么将在CMAKE_MODULE_PATH中搜索这个模块并载入。载入的内容将在处理到INCLUDE语句时直接执行。

  • FIND_指令,主要包括以下:

    • FIND_FILE(name1 path2 path2 …)VAR变量代表找到的文件全路径,包含文件名
    • FIND_LIBRARY(name1 path1 path2 …)VAR变量表示找到的库全路径,包括库文件名
    • FIND_PATH(name1 path1 path2 …)VAR变量代表包含这个文件的路径。
    • FIND_PROGRAM(name1 path1 path2 …)VAR变量代表包含这个程序的全路径。

控制指令

  • IF指令,基本语法如下:

    IF(expression)# THEN section.COMMOND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
    ELSE(expression)# ELSE section.COMMAND1(ARGS ...)COMMAND2(ARGS ...)...
    ENDIF(expression)
    

    另一个指令是ELSEIF,总体把握一个原则,范式出现IF的地方,一定要有ENDIF,出现ELSEIF地方,ENDIF是可选的。表达式的使用方法如下:

    IF(var),如果变量不是空,0,N,NO,OFF,FALSE,NOTFOUND或_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时为真。
    
  • WHILE指令语法如下:

    WHILE(condition)COMMAND1(ARGS ...)COMMAND2(ARGS ...)...ENDWHILE(condition)其真假判断条件可以参考IF指令。
    
  • FOREACH指令语法如下:

    FOREACH(loop_var arg1 arg2 ...)COMMAND1(ARGS ...)COMMOND2(ARGS ...)...ENDFOREACH(loop_var)
    FOREACH(loop_var RANGE total)ENDFOREACH(loop_var)从0到total以1位步进。
    FOREACH(loop_var RANGE start stop [step])ENDFOREACH(loop_var),从start开始到stop结束,以step为步进。
    FOREACH指令遇到ENDFOREACH指令,整个语句块才会得到真正的执行。
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值