• cmake详细教程(三)


    一、参考资料

    cmake详细教程(经验版)

    二、CMake常用指令

    add_compile_options()

    功能:添加编译参数

    语法:add_compile_options(编译参数)

    # 添加编译参数 -wall -std=c++11
    add_compile_options(-wall -std=c++11 -o2)
    
    • 1
    • 2

    add_definitions()

    功能:向C/C++编译器添加编译选项 -D

    语法:add_definitions(-DENABLE_DEBUG -DABC),参数之间用空格分隔。

    #ifdef ENABLE_DEBUG 
    ...
    #endif
    
    • 1
    • 2
    • 3

    add_dependencies()

    CMAKE之add_dependencies使用

    问题引入

    在项目中通常会遇见这样的情况:(例如一个项目中有:main,libhello.a, libworld.a),当项目过小的时候,编译顺序是*.a,然后是main,但是当一个项目的文件过于庞大,就会导致编译的顺序不会按照主CMAKE的 add_subdirectory 引入的先后顺序,为了解决这一问题,就需要使用 add_dependencies 进行依赖指定

    简单示例

    项目结构
    ├── CMakeLists.txt          // 下面用主CMAKE表示
    ├── hello
    │   ├── CMakeLists.txt		// 下面用HELLOCMAKE表示
    │   ├── hello.c
    │   └── hello.h
    ├── main
    │   ├── CMakeLists.txt		// 下面用MAINCMAKE表示
    │   └── main.c
    └── world
        ├── CMakeLists.txt		// 下面用WORLDCMAKE表示
        ├── world.c
        └── world.h
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    HELLOCMAKE的内容
    cmake_minimum_required(VERSION 3.5.1)
    
    set(CMAKE_C_STANDARD 99)
    
    # 生成hello静态链接库
    add_library(hello STATIC world.c hello.h)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    MAINCMAKE的内容
    cmake_minimum_required(VERSION 3.5.1)
    project(CmakeDemo C)
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY /home/lib)
    set(CMAKE_C_STANDARD 99)
    
    # 生成CmakeDemo可执行文件
    add_executable(CmakeDemo main.c)
    link_directories(/home/lib)
    
    target_link_libraries(
        CmakeDemo
        hello
        world
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    WORLDCMAKE的内容
    cmake_minimum_required(VERSION 3.5.1)
    set(CMAKE_C_STANDARD 99)
    
    # 生成world静态链接库
    add_library(world STATIC world.c world.h)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    主CMAKE的内容
    cmake_minimum_required(VERSION 3.5)
    
    add_subdirectory(main)
    add_subdirectory(hello)
    add_subdirectory(world)
    
    add_dependencies(CmakeDemo hello world)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    注意
    • 该示例中,只有在 主CMAKE 中使用到 add_dependencies()
    • add_dependencies() 中所填写的名称应该是其他 CMAKE 生成的名称。
    • 该示例中,如果写成 add_dependencies(CmakeDemo libhello.a libworld.a) ,则会报错。
    总结

    当构建一个项目时,由于依赖关系的存在,所以被依赖的项目总是最先构建,这样可避免出现"找不到库的错误"。

    add_executable()

    功能:生成可执行文件

    语法:add_executable(exe文件名 source1 source2 .. sourceN)

    # 编译main.cpp生成可执行文件main
    add_executable(main main.cpp)
    
    • 1
    • 2

    add_library()

    功能:生成库文件,.so动态库、.a静态库、.o中间产物。

    语法:add_library( [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 .. sourceN)

    参数解释

    • ,表示库文件的名字,该库文件会根据命令里列出的源文件来创建。
    • STATICSHAREDMODULE 的作用是指定生成的库文件的类型。STATIC 库是目标文件的归档文件,在链接其它目标的时候使用。
    • SHARED库会被动态链接(动态链接库),在运行时会被加载。
    • MODULE库是一种不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数。
    # 生成 libmindsporelite.so 动态链接库
    add_library(mindsporelite SHARED main.cc)
    
    # 生成 libmindsporelite.a 静态链接库
    add_library(mindsporelite STATIC main.cc)
    
    # 生成 libmindsporelite.o 未被链接的中间产物
    # 中间文件不能作为库或者单独执行
    add_library(mindsporelite OBJECT main.cc)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    aux_source_directory()

    功能:查找指定目录下的所有源文件,并将结果存进指定变量名。

    语法:aux_source_directory(文件夹路径 变量名)

    # 遍历当前目录下的所有源代码文件,并将其添加到变量DIR_SRCS中
    aux_source_directory(. DIR_SRCS)
    
    # 编译SRC变量所代表的源代码文件,生成main可执行文件
    add_executable(main ${SRC})
    
    • 1
    • 2
    • 3
    • 4
    • 5

    add_subdirectory()

    功能:向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。

    语法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

    顶层CMakeLists.txt 通过 add_subdirectory(dir),指明本项目包含的子目录 ,子目录下的 CMakeLists.txt 文件和源代码也会被处理,通过多层的CmakeLists,一一构建各层的库和依赖。binary_dir 指定的目录存放输出文件,如果没有指定则使用source_dir

    # 添加src子目录,src中需要有一个CMakeLists.txt
    add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src)
    
    • 1
    • 2

    cmake_minimum_required()

    功能:指定CMake的最小版本要求。

    语法:cmake_minimum_required(VERSION 版本号 [FATAL_ERROR])

    # CMake最小版本要求为2.8.3
    cmake_minimum_required(VERSION 2.8.3)
    
    • 1
    • 2

    find_指令

    FIND_FILE( name1 path1 path2 …)

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

    FIND_LIBRARY( name1 path1 path2 …)

    功能:VAR变量表示找到的库文件的全路径,包含库文件。

    FIND_PATH( name1 path1 path2 …)

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

    FIND_PROGRAM( name1 path1 path2 …)

    功能:VAR变量代表包含这个工程的全路径。

    find_package()

    Cmake命令之find_package介绍

    功能:本地导入外部库。用于查找包(通常是使用三方库),并返回关于包的细节(使用包所依赖的头文件、库文件、编译选项、链接选项等)。

    语法:FIND_PACKAGE( [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [componets …]])

    参数解释

    • REQUIRED 表示该库是必须的,如果找不到会报错;
    • COMPONENTS 用于检测该库的对应组件是否存在,如果不存在则认为找到的库不满足条件;
    # find a boost install with the libraries filesystem and system
    # 使用库文件系统和系统查找boost install
    find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)
    
    #这是第三方库,而不是自己生成的静态动态库
    # check if boost was found
    if(Boost_FOUND)
        message ("boost found")
    else()
        message (FATAL_ERROR "Cannot find Boost")
    endif()
    
    # Add an executable
    add_executable(third_party_include main.cpp)
    
    # link against the boost libraries
    target_link_libraries( third_party_include
        PRIVATE
            Boost::filesystem
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    上面的指令,使用 find_package 指令来在本地搜索对应的第三方库,Boost 代表需要查询的库名称;1.46.1 代表需要库的最低版本。

    FetchContent

    功能:外部导入外部库。FetchContent 是 3.11.0 版本开始提供的功能,只需要一个 URL 或者 Git 仓库即可引入一个库。

    使用方法

    1. include(FetchContent) :表示引入 FetchContent。
    2. FetchContent_Declare(第三方库) :获取第三方库,可以是一个 URL 或者一个 Git 仓库。
    3. FetchContent_MakeAvailable(第三方库) :将这个第三方库引入项目。
    4. target_link_libraries(主项目 PRIVATE 子模块::子模块) :链接这个第三方库。
    cmake_minimum_required(VERSION 3.14)
    project(my_project)
    
    # GoogleTest requires at least C++11
    set(CMAKE_CXX_STANDARD 11)
    
    include(FetchContent)
    FetchContent_Declare(
      googletest
      URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
    )
    # For Windows: Prevent overriding the parent project's compiler/linker settings
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(googletest)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    FILE()

    CMake 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
    • 12
    • 13

    include()

    Load and run CMake code from a file or module.

    功能:用来载入 CMakeLists.txt 文件,也用于载入预定义的cmake模块。

    语法:include( [OPTIONAL] [RESULT_VARIABLE ] [NO_POLICY_SCOPE])

    如果指定文件,则直接处理。如果指定module,则寻找 module.cmake 文件,首先在${CMAKE_MODULE_PATH} 中寻找,然后在CMake的module目录中查找。

    include_directories()

    功能:向工程添加多个特定的 头文件搜索路径,相当于指定g++编译器的 -I 参数。

    语法:include_directories([AFTER|BEFORE][SYSTEM] dir1 dir2 ...)

    include_directories(header-dir) 是一个全局包含,向下传递。也就是说,如果某个目录的 CMakeLists.txt 中使用了该指令,其下所有的子目录默认也包含了header-dir 目录。

    # 包含所有子目录
    aux_source_directory()
    
    # 包含指定目录
    include_directories()
    
    # 将/usr/include/myincludefolder 和 ./include 添加到头文件搜索路径
    include_directories(/usr/include/myincludefolder ./include)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    install()

    项目工程结构

    .
    ├── CMakeLists.txt
    ├── config.h.in
    ├── License.txt
    ├── main.cc
    └── math
        ├── CMakeLists.txt
        ├── MathFunctions.cc
        └── MathFunctions.h
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    math 目录下的 CMakeLists.txt

    # 查找当前目录下的所有源文件,并将名称保存到 DIR_LIB_SRCS 变量
    aux_source_directory(. DIR_LIB_SRCS)
    
    # 指定生成 MathFunctions 链接库
    add_library (MathFunctions ${
      DIR_LIB_SRCS})
    
    # 指定 MathFunctions 库的安装路径
    # 将静态库 MathFunctions 安装到 `/usr/local/bin` 目录下
    install(TARGETS MathFunctions DESTINATION bin)
    
    # 将头文件 MathFunctions.h 安装到 `/usr/local/include` 目录下
    install(FILES MathFunctions.h DESTINATION include)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    根目录的 CMakelists.txt

    # 指定安装路径
    # 将可执行程序 Demo 安装到了 `/usr/local/bin` 目录下
    install(TARGETS Demo DESTINATION bin)
    
    # 将 config.h 安装到 `/usr/local/include` 目录下
    install(FILES "${PROJECT_BINARY_DIR}/config.h" DESTINATION include)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    [root@root]$ sudo make install
    [ 50%] Built target MathFunctions
    [100%] Built target Demo
    Install the project...
    -- Install configuration: ""
    -- Installing: /usr/local/bin/Demo
    -- Installing: /usr/local/include/config.h
    -- Installing: /usr/local/bin/libMathFunctions.a
    -- Up-to-date: /usr/local/include/MathFunctions.h
    [ehome@xman Demo5]$ ls /usr/local/bin
    Demo  libMathFunctions.a
    [ehome@xman Demo5]$ ls /usr/local/include
    config.h  MathFunctions.h
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    /usr/local/ 是默认安装的根目录,可以通过修改 CMAKE_INSTALL_PREFIX 变量的值来指定这些文件拷贝到哪个根目录。

    LIST()

    cmake命令之list

    link_directories()(不推荐)

    功能:指定要链接的库文件的搜索路径,相当于指定g++编译器的 -L 参数。

    语法:link_directories(dir1 dir2 ...)

    link_directories(/home/lib)
    target_link_libraries(
        CmakeDemo
        hello
        world
    )
    
    # 将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径
    link_directories(/usr/lib/mylibfolder ./lib)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注意:该指令有时候不一定需要。因为 find_packagefind_library 指令可以得到库文件的绝对路径。然而,自己写的动态库文件放在自己新建的目录下时,可以用该指令指定该目录的路径,以便工程能够找到

    link_libraries()

    功能:通过 add_executable()add_library() 指令生成目标文件,link_libraries 将库链接到目标文件中。
    语法:link_libraries([item1 [item2 [...]]] [[debug|optimized|general] ] ...)

    cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
     
    include_directories("/opt/MATLAB/R2012a/extern/include")
     
    #directly link to the libraries.
    LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
    LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
     
    #equals to below
    #LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
     
    add_executable(myProject main.cpp)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    cmake_minimum_required(VERSION 3.10)
    project(TestSharedLib)
    
    # C++11 编译
    set(CMAKE_CXX_STANDARD 11)
    
    # 头文件路径
    set(INC_DIR /home/yoyo/MyDocuments/C++Projects/MySharedLib)
    # 库文件路径
    set(LIB_DIR /home/yoyo/MyDocuments/C++Projects/MySharedLib/build)
    
    include_directories(${INC_DIR})
    link_directories(${LIB_DIR})
    link_libraries(MySharedLib)
    
    add_executable(TestSharedLib main.cpp)
    
    # 链接 MySharedLib 库
    target_link_libraries(TestSharedLib MySharedLib)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    MESSAGE()

    功能:向终端输出用户定义的信息。

    用法:MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message")

    #产生错误,生成过程被跳过。
    MESSAGE(SEND_ERROR "message")
    
    #输出前缀为--d的信息。
    MESSAGE(STATUS "message")
    
    #立即终止所有的cmake过程
    MESSAGE(FATAL_ERROR "message")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    # 终端执行export
    export ASCEND_AICPU_PATH=/usr/local/Ascend/ascend-toolkit/latest
    
    # CMakeLists.txt文件打印
    message(STATUS "ASCEND_AICPU_PATH=${ASCEND_AICPU_PATH}")
    
    # 终端打印输出
    -- ASCEND_AICPU_PATH=/usr/local/Ascend/ascend-toolkit/latest
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    option()

    Options that can be configured through environment variables or manually.(option通过配置环境变量或者手动设置)

    对于跨平台的项目,我们往往要进行交叉编译,针对不同环境的代码和功能可能有所不同,我们往往通过option确定编译选项,不同平台编译的代码用宏进行隔离,同时编译宏也可以决定某部分代码是否编译,是否包含某个功能,对一些还不稳定的特性进行隔离

    比如,在mindsporelilte中,通过设置MSLITE_ENABLE_SHARING_MEM_WITH_OPENGL来确定是否编译OpenGL相关代码:

    option(MSLITE_ENABLE_SHARING_MEM_WITH_OPENGL "enable sharing memory with OpenGL" on)
    
    if(DEFINED ENV{MSLITE_ENABLE_SHARING_MEM_WITH_OPENGL})
        set(MSLITE_ENABLE_SHARING_MEM_WITH_OPENGL $ENV{MSLITE_ENABLE_SHARING_MEM_WITH_OPENGL})
    endif()
    
    if(MSLITE_ENABLE_SHARING_MEM_WITH_OPENGL)
        add_definitions(-DENABLE_OPENGL_TEXTURE)
    endif()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在相关cpp文件中用宏ENABLE_OPENGL_TEXTURE隔离相关代码:

    #ifdef ENABLE_OPENGL_TEXTURE
      if (!lite::opencl::LoadOpenGLLibrary(&gl_handle_)) {
        MS_LOG(ERROR) << "Load OpenGL symbols failed!";
        return RET_ERROR;
      }
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    project()

    功能:定义工程名称,并可指定工程支持的语言。

    语法:project(工程名称 [CXX] [C] [java])

    # 指定工程名及版本号
    project(HELLOWORLD VERSION 1.0)
    
    • 1
    • 2

    set()

    功能:显式的定义变量。

    语法:set(变量名 [变量值] [CACHE TYPE DOCSTRING [FORCE]])

    # 定义SRC变量,其值为main.cpp hello.cpp
    set(SRC sayhello.cpp hello.cpp)
    
    # 指定C++标准
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    
    # 设置生成可执行文件的路径
    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    
    # 设置生成链接库的路径
    SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    SET_TARGET_PROPERTIES()

    功能:同时构建 libhello.solibhello.a 两个库。

    # 动态库
    SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_PUTPUT 1)
    # 静态库
    SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
    
    
    # 增加动态库的版本号
    # VERSION指代动态库版本,SOVERSION指代API版本。
    SET_TARGET_PROPERTIES(hello PROPERTIES VERION 1.2 SOVERSION 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    target_include_directories()

    功能:为编译目标文件 指定头文件

    语法:target_include_directories( [SYSTEM] [AFTER|BEFORE] [items1...] [ [items2...] ...])

    必须是通过 add_executable()add_library() 创建,且不能是ALIAS目标

    target_link_libraries()

    功能:将目标文件与库文件进行链接,相当于指定g++编译器 -l 参数。

    target_link_libraries( ... ... ...)
    target_link_libraries(
                           ...
                         [ ...]...)
    
    • 1
    • 2
    • 3
    • 4

    参数解释

    • ,是指通过 add_executable()add_library() 指令生成已经创建的目标文件,并且不能是 ALIAS 目标。
    • [item],表示库文件没有后缀的名字。
    • PUBLIC,在public后面的库会被Link到target中,并且里面的符号也会被导出,提供给第三方使用。
    • PRIVATE,在private后面的库仅被link到target中,并且终结掉,第三方不能感知你调了什么库。
    • INTERFACE,在interface后面引入的库不会被链接到target中,只会导出符号。

    target_link_libraries() 往往和 add_subdirectory() 一起使用,在编译好一个可执行文件或库时,如果它依赖其他库,可以使用 target_link_libraries() 链接其他库。target_link_libraries 会在目标程序中生成rpath 。示例如下:

    # 生成动态库
    add_library(mindsporelite SHARED $)
    # 生成静态库
    add_library(mindsporelite-static STATIC $)
    # 生成可执行文件
    add_executable(benchmark main.cc)
    
    # 动态链接,将 mindsporelite动态库文件链接到 benchmark可执行文件 
    target_link_libraries(benchmark mindsporelite)
    # 静态链接,将 mindsporelite-static静态库文件链接到 benchmark可执行文件 
    target_link_libraries(benchmark mindsporelite-static)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    link_librariestarget_link_libraries 的区别

    • link_libraries用在 add_executable 之前,target_link_libraries 用在 add_executable 之后。
    • link_libraries用来链接静态库,target_link_libraries用来链接导入库,即按照 header file + .lib + .dll 方式隐式调用动态库的 .lib 库。

    控制指令

    IF指令

    总体把握一个原则,凡是出现 IF 的地方一定要有对应的ENDIF,出现 ELSEIF 的地方,ENDIF 是可选的。

    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
    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 时为真。比如:
    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)#如果变量被定义,为真。
    
    # 一个小例子,用来判断平台差异:
    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
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

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

    SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
    
    • 1
    IF(WIN32)
    ELSE()
    ENDIF()
    
    # 如果配合 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
    • 8
    • 9
    • 10
    • 11
    • 12

    WHILE指令

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

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

    FOREACH指令

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

    列表形式

    FOREACH(loop_var arg1 arg2 ...)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
    ENDFOREACH(loop_var)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    AUX_SOURCE_DIRECTORY(. SRC_LIST)
    FOREACH(F ${SRC_LIST})
    MESSAGE(${F})
    ENDFOREACH(F)
    
    • 1
    • 2
    • 3
    • 4

    范围形式

    FOREACH(loop_var RANGE total)
    ENDFOREACH(loop_var)
    #从 0 到 total 以1为步进举例如下:
    FOREACH(VAR RANGE 10)
      MESSAGE(${VAR})
    ENDFOREACH(VAR)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    范围和步长

    # 从 start 开始到 stop 结束,以 step 为步长
    FOREACH(loop_var RANGE start stop [step])
    ENDFOREACH(loop_var)
    
    • 1
    • 2
    • 3
    FOREACH(A RANGE 5 15 3)
    MESSAGE(${A})
    ENDFOREACH(A)
    
    • 1
    • 2
    • 3

    最终得到的结果是:
    5
    8
    11
    14

    三、CMake预定义变量

    CMake中常用的预定义变量

    BUILD_SHARED_LIBS

    默认的库编译方式(shared or static),默认为static,一般在ADD_LIBRARY时直接指定编译库的类型。

    CMAKE_C_FLAGS

    功能:gcc编译选项。

    SET(CMAKE_C_FLAGS_PUBLIC "-mcpu=cortex-a7 -mfloat-abi=softfp -mfpu=neon-vfpv4 -ffunction-sections -mno-unaligned-access -fno-aggressive-loop-optimizations -mapcs-frame -rdynamic")
    SET(CMAKE_C_FLAGS_DEBUG "-Wall -ggdb3 -DNM_DEBUG ${CMAKE_C_FLAGS_PUBLIC}")
    SET(CMAKE_C_FLAGS_RELEASE  "-Wall -O3  ${CMAKE_C_FLAGS_PUBLIC}")
    
    • 1
    • 2
    • 3

    CMAKE_CXX_FLAGS

    功能:g++编译选项。

    # 在CMAKE_CXX_FLAGS编译选项后追加-std=c++11
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
    
    • 1
    • 2
    • CMAKE_CXX_FLAGS_DEBUG: 设置编译类型为Debug时的编译选项;
    • CMAKE_CXX_FLAGS_RELEASE: 设置编译类型为Release时的编译选项;

    CMAKE_CXX_COMPILER

    设置C++编译器。

    # 设置C++编译器为g++
    set(CMAKE_CXX_COMPILER "g++")
    # 设置标准库版本为c++17 并开启警告
    set(CMAKE_CXX_FLAGS "-std=c++17 -Wall")
    # 设置Debug模式下,不开启优化,开启调试,生成更详细的gdb调试信息
    set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -ggdb")
    # 设置Release模式下,开启最高级优化
    set(CMAKE_CXX_FLAGS_RELEASE "-O3")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    CMAKE_BUILD_TYPE

    功能:编译类型(Debug, Release)。

    # 设定编译类型为debug,因为在调试时需要选择debug
    set(CMAKE_BUILD_TYPE Debug)
    # 设定编译类型为release,因为在发布时需要选择release
    set(CMAKE_BUILD_TYPE release)
    
    • 1
    • 2
    • 3
    • 4

    CMAKE_CURRENT_SOURCE_DIR

    当前CMakeLists.txt文件所在目录。

    CMAKE_CURRENT_BINARY_DIR

    编译目录,可使用ADD_SUBDIRECTORY来修改此变量。

    # 添加cmake执行子目录
    ADD_SUBDIRECTORY(example)
    
    • 1
    • 2

    EXECUTABLE_OUTPUT_PATH

    功能:可执行文件输出的存放路径。

    # 设置可执行文件的输出路径为 build/bin
    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    
    • 1
    • 2

    LIBRARY_OUTPUT_PATH

    功能:库文件输出的存放路径。

    set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    
    • 1

    PROJECT_NAME

    功能:指定项目工程的名称。

    project(Demo)
    
    • 1

    PROJECT_SOURCE_DIR

    功能:项目工程的根目录,例如:/PATH/TO/CMakeDemo

    PROJECT_BINARY_DIR

    功能:执行cmake命令的编译路径,一般是在build目录,例如:即/PATH/TO/CMakeDemo/build

    TOP_DIR

    功能:项目工程的的根目录。

    include(${TOP_DIR}/cmake/utils.cmake)
    
    • 1

    系统信息变量

    root@root:~$ cmake --version
    cmake version 3.11.2
    
    • 1
    • 2
    • CMAKE_MAJOR_VERSION: cmake的主版本号cmake version 3.11.2中的3
    • CMAKE_MINOR_VERSION: cmake的次版本号cmake version 3.11.2中的11
    • CMAKE_PATCH_VERSION: cmake的补丁等级cmake version 3.11.2中的2
    • CAMKE_SYSTEM。系统名称比如 LInux-2.6.22
    • CAMKE_SYSTEM_NAME,不包含版本的系统名,比如 linux
    • CMAKE_SYSTEM_VERSION,系统版本,比如 2.6.22
    • CMAKE_SYSTEM_PROCESSOR,处理器的名称,比如 i686
  • 相关阅读:
    基于Python的Django出租车大数据分析系统实现
    python学习之【包和内置模块】
    Ali MaxCompute SDK
    Windows 桌面窗口管理器
    你所不知道的实用类
    金仓数据库KingbaseES运维工具参考手册(4. 问题诊断辅助工具)
    07--nginx负载均衡实战
    Groovy单元测试
    统信UOS桌面操作系统安装教程
    【MySQL】并发事务产生的问题及事务隔离级别
  • 原文地址:https://blog.csdn.net/m0_37605642/article/details/128055068