美文网首页
CMake学习小结--变量

CMake学习小结--变量

作者: brownfeng | 来源:发表于2020-02-11 19:48 被阅读0次

    CMake学习小结

    参考资料

    CGold的Cmake教程 -- 这个教程很新:

    https://cgold.readthedocs.io/en/latest/overview.html

    CMakeLists中的变量

    常规变量

    CMakeLists.txt中的常规变量有作用域的, 变量分以下两种变量:

    1. 常规变量set(abc "123")
    2. 缓存变量set(abc "123" CACHE STRING ""), 在CMakeLists.txt构造以后,会在CMakeCache.txt中有所有缓存变量的记录.

    常规变量有如下特点:

    1. 常规变量每次重新创建, 没有缓存
    2. 常规变量使用set(abc "123")的方式创建
    3. 其他变量要引用这个创建的变量,需要用解引用方法${}, 例如:set(bcd "${abc}xyz"), 此时bcd的值是123xyz
    4. 常规变量受作用域影响, 作用域的变化会产生父作用域和子作用域的关系, 子作用域中会继承所有的父作用的常规变量
      1. functionadd_subdirectory 内部会创建新的作用域!!!新作用域内会继承父作用域的变量, 改变子作用域中的变量的值,不会影响父作用域中变量的值
      2. includemacro 并不会创建新的作用域!!! 因此, 在这内容中,修改变量会影响变量的值
      3. 如果在子作用域中, 如果想修改父作用域中的变量的值, 需要使用PARENT_SCOPE关键字 -- set(abc "786" PARENT_SCOPE). 注意这里不会改变子作用域中的abc的值, 只会修改父作用域中abc的值
    5. 常规变量使用unset(abc)来重置某个变量在当前作用域的值
      变量以外,我们在命令行中使用的-D参数也是声明缓存变量的方式.

    变量的引用于解引用${...}:

    cmake_minimum_required(VERSION 2.8)
    project(foo NONE)
    
    set(a "xyz")
    
    set(b "${a}_321")
    set(${a}_1 "456")
    set(variable_${a} "${a} + ${b} + 155")
    
    message("b: '${b}'")
    message("xyz_1: '${xyz_1}'")
    message("variable_xyz: '${variable_xyz}'")
    
    [usage-of-variables]> rm -rf _builds
    [usage-of-variables]> cmake -Hdereference -B_builds
    b: 'xyz_321'
    xyz_1: '456'
    variable_xyz: 'xyz + xyz_321 + 155'
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /.../usage-of-variables/_builds
    

    嵌套使用${...}:

    cmake_minimum_required(VERSION 2.8)
    project(foo)
    
    foreach(lang C CXX)
      message("Compiler for language ${lang}: ${CMAKE_${lang}_COMPILER}")
      foreach(build_type DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
        message("Flags for language ${lang} + build type ${build_type}: ${CMAKE_${lang}_FLAGS_${build_type}}")
      endforeach()
    endforeach()
    
    Compiler for language C: /usr/bin/cc
    Flags for language C + build type DEBUG: -g
    Flags for language C + build type RELEASE: -O3 -DNDEBUG
    Flags for language C + build type RELWITHDEBINFO: -O2 -g -DNDEBUG
    Flags for language C + build type MINSIZEREL: -Os -DNDEBUG
    Compiler for language CXX: /usr/bin/c++
    Flags for language CXX + build type DEBUG: -g
    Flags for language CXX + build type RELEASE: -O3 -DNDEBUG
    Flags for language CXX + build type RELWITHDEBINFO: -O2 -g -DNDEBUG
    Flags for language CXX + build type MINSIZEREL: -Os -DNDEBUG
    

    其他的例如 list 列表,常规使用比较少, 可以自己已查资料

    需要小结的是:

    1. All variables have a string type
    2. List is nothing but string, elements of list separated by ;
    3. The way how variables are interpreted depends on the command
    4. Do not give same names for cache and regular variables
    5. add_subdirectory and function create new scope
    6. include and macro work in the current scope

    缓存变量(全局变量)

    缓存变量由如下特点:

    1. 缓存变量是全局作用域, 变量的值会从CMakeCache.txt中获取,缓存变量在写入CMakeCache.txt以后, 每次都从这个缓存文件中读取该值, 而不会重新调用set(... CACHE ...)!!!(Cache变量只会初始化一次,在CMakeCache.txt中存储以后, 多次调用set(... CACHE ...)不起作用!!!)
    2. 缓存变量的常规初始化的方式是set(abc "789" CACHE STRING "")
    3. 当常规变量和缓存变量重名时, 如果缓存变量声明在后面, 会覆盖常规变量
    4. 除了使用set(... CACHE ...)可以声明缓存, 也可以在命令行使用-Dxxx=xxx修改缓存变量的值!!!
    5. 如果某个Cache变量已经存在于CMakeCache.txt中, 代码中的set(... CACHE ...)将不会对这个值有影响, 如果我们在代码中需要强制修改Cache变量的值, 需要使用FORCE字段 --- set(abc "123" CACHE STRING "" FORCE)

    最佳实践, 对于全局Cache变量太多,我们最好将他们放在一个单独的文件, 然后在调用CMake .. -C cache.cmake方法将这个文件加载进去.

    # cache.cmake
    
    set(A "123" CACHE STRING "")
    set(B "456" CACHE STRING "")
    set(C "789" CACHE STRING "")
    
    # CMakeLists.txt
    
    cmake_minimum_required(VERSION 2.8)
    project(foo NONE)
    
    message("A: ${A}")
    message("B: ${B}")
    message("C: ${C}")
    
    [usage-of-variables]> rm -rf _builds
    [usage-of-variables]> cmake -C initial-cache/cache.cmake -Hinitial-cache -B_builds
    loading initial cache file initial-cache/cache.cmake
    A: 123
    B: 456
    C: 789
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /.../usage-of-variables/_builds
    

    option() 命令创建的是一个缓存变量

    最佳实践, 由于Cache变量的全局性特点, 用于做开关, 使用option:

    # top-level CMakeLists.txt
    
    cmake_minimum_required(VERSION 2.8)
    project(zoo)
    
    add_subdirectory(boo)
    add_subdirectory(foo)
    
    # foo/CMakeLists.txt
    
    cmake_minimum_required(VERSION 2.8)
    project(foo)
    
    option(FOO_FEATURE_1 "Enable feature 1" OFF)
    option(FOO_FEATURE_2 "Enable feature 2" OFF)
    
    # boo/CMakeLists.txt
    
    cmake_minimum_required(VERSION 2.8)
    project(boo)
    
    option(BOO_FEATURE_1 "Enable feature 1" ON)
    option(BOO_FEATURE_2 "Enable feature 2" ON)
    

    总结:

    1. Use cache to set global variables
    2. Cache variables fits perfectly for expressing customized options: default value and respect user’s value
    3. Type of cache variable helps CMake-GUI users
    4. Prefixes should be used to avoid clashing because of the global nature of cache variables

    环境变量 Environment variables

    环境变量可以使用$ENV{...}访问:

    cmake_minimum_required(VERSION 2.8)
    project(foo NONE)
    
    message("Environment variable USERNAME: $ENV{USERNAME}")
    
    [usage-of-variables]> rm -rf _builds
    [usage-of-variables]> echo $USERNAME
    ruslo
    [usage-of-variables]> export USERNAME
    [usage-of-variables]> cmake -Hread-env -B_builds
    Environment variable USERNAME: ruslo
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /.../usage-of-variables/_builds
    

    可以使用set(ENV{...})来设置环境变量:

    cmake_minimum_required(VERSION 2.8)
    project(foo NONE)
    
    set(ENV{USERNAME} "Jane Doe")
    message("Environment variable USERNAME: $ENV{USERNAME}")
    
    [usage-of-variables]> rm -rf _builds
    [usage-of-variables]> echo $USERNAME
    ruslo
    [usage-of-variables]> export USERNAME
    [usage-of-variables]> cmake -Hset-env -B_builds
    Environment variable USERNAME: Jane Doe
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /.../usage-of-variables/_builds
    

    总结:

    1. CMake can set, unset and read environment variables
    2. Check carefully configure-build steps where you set environment variables
    3. Child processes will inherit environment variables of parent
    4. Do not make your CMake code depends on environment variable if that variable may change

    CMake的代码执行的过程

    源码树(source-tree):

    CMakeLists.txt一般会通过add_subdirectory方法给source-tree添加节点, 而CMAKE_CURRENT_SOURCE_DIR变量就是当前处理节点的全路径, 而整个source-tree的根节点则是CMAKE_SOURCE_DIR.

    二进制树(binary-tree):

    在调用CMake命令编译以后,生成的文件夹是一个编译结果二进制树. 其中,CMAKE_BINARY_DIR是二进制树的树根, CMAKE_CURRENT_BINARY_DIR是当前处理的二进制树的节点.

    CMakeLists.txt的编写过程中, 我们可以用CMake module结合include()命令来重用代码. 常见的module是*.cmake

    不要用include命令加载Find*.cmake模块!!!它有专用命令find_package

    如果我们有一个专门存放的cmake module的文件夹, 我们可以将它添加到CMAKE_MODULE_PATH变量中, 最好使用list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules"):

    # Top level CMakeLists.txt
    
    cmake_minimum_required(VERSION 2.8)
    project(foo NONE)
    
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
    
    include(MyModule)
    
    # modules/MyModule.cmake
    
    message("Hello from MyModule!")
    

    除此之外, 一下是一些常用的变量:

    1. CMAKE_CURRENT_LIST_DIRCMAKE_CURRENT_LIST_FILE

    可以用下面一个实例展示:

    # Top-level CMakeLists.txt
    
    cmake_minimum_required(VERSION 2.8)
    project(foo NONE)
    
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
    
    include(mymodule)
    
    # cmake/mymodule.cmake
    
    message("Full path to module: ${CMAKE_CURRENT_LIST_FILE}")
    message("Module located in directory: ${CMAKE_CURRENT_LIST_DIR}")
    
    [cmake-sources]> rm -rf _builds
    [cmake-sources]> cmake -Hpath-to-module -B_builds
    Full path to module: /.../cmake-sources/path-to-module/cmake/mymodule.cmake
    Module located in directory: /.../cmake-sources/path-to-module/cmake
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /.../cmake-sources/_builds
    

    下面一个图表现几个常见的变量的关系:

    关系
    1. CMAKE_SOURCE_DIR/CMAKE_BINARY_DIR these variables point to the root of the source/binary trees. If your project will be added to another project as a subproject by add_subdirectory, the locations like ${CMAKE_SOURCE_DIR}/my-resource.txt will point to <top-level>/my-resource.txtinstead of <my-project>/my-resource.txt
    2. PROJECT_SOURCE_DIR/PROJECT_BINARY_DIR these variables are better then previous but still have kind of a global nature. You should change all paths related to PROJECT_SOURCE_DIR if you decide to move declaration of your project or decide to detach some part of the code and add new project command in the middle of the source tree. Consider using extra variable with clean separate purpose for such job set(FOO_MY_RESOURCES "${CMAKE_CURRENT_LIST_DIR}/resources") instead of referring to ${PROJECT_SOURCE_DIR}/resources.
    3. CMAKE_CURRENT_SOURCE_DIR this is a directory with CMakeLists.txt. If you’re using this variable internally you can substitute is with CMAKE_CURRENT_LIST_DIR. In case you’re creating module for external usage consider moving all functionality to function.

    add_executable

    cmake_minimum_required(VERSION 2.8)
    project(foo)
    
    add_executable(foo main.cpp)
    

    注意这里定义的 foo是一个Target, 名称是全局范围的变量, 并且唯一的!!!

    相关文章

      网友评论

          本文标题:CMake学习小结--变量

          本文链接:https://www.haomeiwen.com/subject/lwqffhtx.html