• 8.cmake常用命令


    前面我们讲到了 cmake 常用的变量,相信“cmake 即编程”的感觉会越来越明显,无论如何,
    我们仍然可以看到 cmake 比 autotools 要简单很多。接下来我们就要集中的看一看
    cmake 所提供的常用指令。在前面的章节我们已经讨论了很多指令的用法,如
    PROJECT,ADD_EXECUTABLE,INSTALL,ADD_SUBDIRECTORY,SUBDIRS,INCLUDE
    _DIRECTORIES,LINK_DIRECTORIES,TARGET_LINK_LIBRARIES,SET 等。

    本节会引入更多的 cmake 指令,为了编写的方便,我们将按照 cmake man page 的顺序
    来介绍各种指令,不再推荐使用的指令将不再介绍,INSTALL 系列指令在安装部分已经做
    了非常详细的说明,本节也不在提及。

    一 基本命令

    1,ADD_DEFINITIONS
    向 C/C++编译器添加-D 定义,比如:
    ADD_DEFINITIONS(-DENABLE_DEBUG -DABC),参数之间用空格分割。
    如果你的代码中定义了#ifdef ENABLE_DEBUG #endif,这个代码块就会生效。
    如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量设置。
    我们定义CMakeList如下:

    cmake_minimum_required(VERSION 3.0.0)
    project(t6)
     
    set(SRC_LIST main.c)
    ADD_DEFINITIONS(-DENABLE_DEBUG)
    add_executable(hello ${SRC_LIST})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    main.c如下:

    #include 
    int main()
    {
        printf("main start!\n");
    #ifdef ENABLE_DEBUG
        printf("ENABLE_DEBUG is define!\n");
    #endif
        printf("main end!\n");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果如下: 从结果看出 ENABLE_DEBUG is define!打印出来

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t6/build# cmake ..
    -- The C compiler identification is GNU 9.4.0
    -- The CXX compiler identification is GNU 9.4.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /mnt/d/study/lwstudy/cmake/t6/build
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t6/build# make
    Scanning dependencies of target hello
    [ 50%] Building C object CMakeFiles/hello.dir/main.c.o
    [100%] Linking C executable hello
    [100%] Built target hello
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t6/build# ./hello
    main start!
    ENABLE_DEBUG is define!
    main end!
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t6/build#
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

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

    ADD_DEPENDENCIES(target-name depend-target1
                    depend-target2 ...)
    
    • 1
    • 2

    3,ADD_EXECUTABLE、ADD_LIBRARY、ADD_SUBDIRECTORY 前面已经介绍过了,这里不再罗唆。
    4,ADD_TEST 与 ENABLE_TESTING 指令。
    ENABLE_TESTING 指令用来控制 Makefile 是否构建 test 目标,涉及工程所有目录。语法很简单,没有任何参数,ENABLE_TESTING(),一般情况这个指令放在工程的主CMakeLists.txt 中.
    ADD_TEST 指令的语法是:

    ADD_TEST(testname Exename arg1 arg2 ...)
    
    • 1

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

    5,AUX_SOURCE_DIRECTORY
    基本语法是:

    AUX_SOURCE_DIRECTORY(dir VARIABLE)
    
    • 1

    作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来
    自动构建源文件列表。因为目前 cmake 还不能自动发现新添加的源文件。
    比如

    AUX_SOURCE_DIRECTORY(. SRC_LIST)
    ADD_EXECUTABLE(main ${SRC_LIST})
    
    • 1
    • 2

    6,CMAKE_MINIMUM_REQUIRED
    其语法为 CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])
    比如 CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR)
    如果 cmake 版本小与 2.5,则出现严重错误,整个过程中止。

    7,EXEC_PROGRAM
    在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行。具体语法为:

    EXEC_PROGRAM(Executable [directory in which to run]
                [ARGS ]
                [OUTPUT_VARIABLE ]
                [RETURN_VALUE ])
    
    • 1
    • 2
    • 3
    • 4

    用于在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值,可通过OUTPUT_VARIABLE 和 RETURN_VALUE 分别定义两个变量

    这个指令可以帮助你在 CMakeLists.txt 处理过程中支持任何命令,比如根据系统情况去修改代码文件等等。
    举个简单的例子,我们要在 src 目录执行 ls 命令,并把结果和返回值存下来。

    可以直接在 src/CMakeLists.txt 中添加:

    EXEC_PROGRAM(ls ${PROJECT_SOURCE_DIR} ARGS "*.c" OUTPUT_VARIABLE LS_OUTPUT RETURN_VALUE LS_RVALUE)
    MESSAGE(STATUS "LS_RVALUE: " ${LS_RVALUE})
    IF(NOT LS_RVALUE)
    MESSAGE(STATUS "ls result: " ${LS_OUTPUT})
    ENDIF(NOT LS_RVALUE)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在 cmake 生成 Makefile 的过程中,就会执行 ls 命令,如果返回 0,则说明成功执行,那么就输出 ls *.c 的结果。
    执行结果:

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t6/build# cmake ..
    PROJECT_BINARY_DIR==/mnt/d/study/lwstudy/cmake/t6/build
    -- LS_RVALUE: 0
    -- ls result: main.c
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /mnt/d/study/lwstudy/cmake/t6/build
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t6/build#
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    8,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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    9,INCLUDE 指令
    用来载入 CMakeLists.txt 文件,也用于载入预定义的 cmake 模块

    INCLUDE(file1 \[OPTIONAL])
    INCLUDE(module \[OPTIONAL])
    
    • 1
    • 2

    OPTIONAL 参数的作用是文件不存在也不会产生错误。
    你可以指定载入一个文件,如果定义的是一个模块,那么将在 CMAKE_MODULE_PATH 中搜索这个模块并载入。

    载入的内容将在处理到 INCLUDE 语句是直接执行。

    二,INSTALL 指令

    三,FIND_指令

    FIND_系列指令主要包含一下指令:

    VAR 变量代表找到的文件全路径,包含文件名

    FIND_FILE( name1 path1 path2 ...)
    
    • 1

    VAR 变量表示找到的库全路径,包含库文件名

    FIND_LIBRARY( name1 path1 path2 ...)
    
    • 1

    VAR 变量代表包含这个文件的路径。

    FIND_PATH( name1 path1 path2 ...)
    
    • 1

    VAR 变量代表包含这个程序的全路径。

    FIND_PROGRAM( name1 path1 path2 ...)
    
    
    
    FIND_PACKAGE( [major.minor] [QUIET] [NO_MODULE]
                [[REQUIRED|COMPONENTS] [componets...]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    用来调用预定义在 CMAKE_MODULE_PATH 下的 Find.cmake 模块,你也可以自己定义 Find模块,通过 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)
    
    • 1
    • 2
    • 3
    • 4

    四,控制指令

    1,IF 指令,基本语法为

    IF(expression)
        # THEN section.
        COMMAND1(ARGS ...)COMMAND2(ARGS ...)
        ...
    ELSE(expression)
        # ELSE section.
        COMMAND1(ARGS ...)
        COMMAND2(ARGS ...)
        ...
    ENDIF(expression)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    另外一个指令是 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    当给定的变量或者字符串能够匹配正则表达式 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    数字比较表达式

    IF(variable STRLESS string)
    IF(string STRLESS string)
    IF(variable STRGREATER string)
    IF(string STRGREATER string)
    IF(variable STREQUAL string)
    IF(string STREQUAL string)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    按照字母序的排列进行比较.

    IF(DEFINED variable),如果变量被定义,为真。
    
    • 1

    一个小例子,用来判断平台差异:

    IF(WIN32)
        MESSAGE(STATUS “This is windows.”)
        #作一些 Windows 相关的操作
    ELSE(WIN32)
        MESSAGE(STATUS “This is not windows”)
        #作一些非 Windows 相关的操作
    ENDIF(WIN32)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上述代码用来控制在不同的平台进行不同的控制,但是,阅读起来却并不是那么舒服,
    ELSE(WIN32)之类的语句很容易引起歧义。

    这就用到了我们在“常用变量”一节提到的 CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 开关。
    可以 SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)

    可以 SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
    这时候就可以写成:

    IF(WIN32)
    ELSE()
    ENDIF()
    
    • 1
    • 2
    • 3

    如果配合 ELSEIF 使用,可能的写法是这样

    IF(WIN32)
    #do something related to WIN32
    ELSEIF(UNIX)
    #do something related to UNIX
    ELSEIF(APPLE)
    #do something related to APPLE
    ENDIF(WIN32)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2,WHILE

    WHILE 指令的语法是:

    WHILE(condition)
        COMMAND1(ARGS ...)
        COMMAND2(ARGS ...)
        ...
    ENDWHILE(condition)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其真假判断条件可以参考 IF 指令。

    3,FOREACH

    FOREACH 指令的使用方法有三种形式:

    1,列表

    FOREACH(loop_var arg1 arg2 ...)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
    ENDFOREACH(loop_var)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    像我们前面使用的 AUX_SOURCE_DIRECTORY 的例子

    AUX_SOURCE_DIRECTORY(. SRC_LIST)
    FOREACH(F ${SRC_LIST})
    MESSAGE(${F})
    ENDFOREACH(F)
    
    • 1
    • 2
    • 3
    • 4

    2,范围

    FOREACH(loop_var RANGE total)
    ENDFOREACH(loop_var)
    
    • 1
    • 2

    从 0 到 total 以1为步进
    举例如下:

    FOREACH(VAR RANGE 10)
    MESSAGE(${VAR})
    ENDFOREACH(VAR)
    
    • 1
    • 2
    • 3

    最终得到的输出是:
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    3,范围和步进

    FOREACH(loop_var RANGE start stop [step])
    ENDFOREACH(loop_var)
    
    • 1
    • 2

    从 start 开始到 stop 结束,以 step 为步进,
    举例如下

    FOREACH(A RANGE 5 15 3)
    MESSAGE(${A})
    ENDFOREACH(A)
    
    • 1
    • 2
    • 3

    最终得到的结果是:
    5
    8
    11
    14
    这个指令需要注意的是,知道遇到 ENDFOREACH 指令,整个语句块才会得到真正的执行

    小结:

    本小节基本涵盖了常用的 cmake 指令,包括基本指令、查找指令、安装指令以及控制语句等,特别需要注意的是,在控制语句条件中使用变量,不能用${}引用,而是直接应用变量名。掌握了以上的各种控制指令,你应该完全可以通过 cmake 管理复杂的程序了,下一节,我们将介绍一个比较复杂的例子,通过他来演示本章的一些指令,并介绍模块的概念。

  • 相关阅读:
    话费充值接口文档
    (Java实现) HDOJ 1051 Wooden Sticks 贪心算法
    4.5每日一题(多元函数比较大小通过偏积分)
    Haproxy实现负载均衡
    volatile关键字是干什么的?他是怎样实现的?
    从零开始—仿牛客网讨论社区项目(一)
    蘑菇街API 获取商品详情 Onebound全球电商数据接口
    python:pyinstaller打包可执行文件
    Flutter的Constructors for public widgets should have a named ‘key‘ parameter警告
    【数组】最多能完成排序的块 数学
  • 原文地址:https://blog.csdn.net/qq_29407397/article/details/127685073