【问题标题】:Using a CMake cache variable "before" it is defined使用“之前”定义的 CMake 缓存变量
【发布时间】:2017-11-14 16:41:28
【问题描述】:

几乎可以从任何地方设置 CMake 缓存变量(参见此处@Florian 的What's the CMake syntax to set and use variables?)。我假设设置值在任何地方都可见,甚至对于之前解析的 CMake 列表也是如此,但事实并非如此。

用例

  • 模块 A 使用 ${CMAKE_MYDEF}
  • 模块 B 设置缓存变量CMAKE_MYDEF
  • add_subdirectory(A)add_subdirectory(B) 之前被调用。

显示行为的简短示例

cmake_minimum_required(VERSION 3.7)
project(test)
add_executable(EXEC test.cpp)
target_compile_definitions(EXEC PRIVATE MYDEF=${CMAKE_MYDEF})
set(CMAKE_MYDEF "MyValue" CACHE STRING "")

问题

  • 无论我添加模块 A 和模块 B 的顺序如何,如何确保 CMAKE_MYDEF 具有所需的值?
  • 是否有任何方法可以确保 CMake 配置步骤重新运行两次,或者(如果适用)只要缓存变量发生更改? (这可能不是一个干净的解决方案,但由于我使用的是遗留代码,因此并非所有事情都可以完美地完成。)
  • 是否有缓存变量的替代方法来获得相同的结果,而无需手动重新运行 CMake 配置?
  • 是否可以在生成阶段设置编译器定义(即当所有 CMake 缓存变量都已知并设置时)?使用某种生成器表达式?

编辑:简短示例解决方案

按照@Florian 的回答,这里是显示解决方案的改编示例:

cmake_minimum_required(VERSION 3.7)
project(test)
add_executable(EXEC test.cpp)
target_link_libraries(EXEC MyOtherLib)

add_library(MyOtherLib INTERFACE)
set(CMAKE_MYDEF "MyValue" CACHE STRING "")
target_compile_definitions(MyOtherLib INTERFACE MYDEF=${CMAKE_MYDEF})

【问题讨论】:

    标签: cmake


    【解决方案1】:

    是的,我完全同意@Tsyvarev's answer CMake 的解析器按顺序工作。所以变量——即使是缓存的——或生成器表达式——不能读取变量——在这里都不好。

    我只是想根据 A 和 B 之间的依赖关系添加使用目标和目录属性的可能性:

    1. A 依赖于B 时,例如

      target_link_libraries(A PUBLIC B)
      

      然后一个简单的

      target_compile_definitions(B PUBLIC MYDEF=SOME_DEF)
      

      会将必要的定义传播到A

    2. B 依赖于A 并且A 已知时

      target_link_libraries(B PUBLIC A)
      target_compile_definitions(A PUBLIC MYDEF=SOME_OTHER_DEF)
      
    3. 如果您正在使用子目录,我建议您将定义放在全局的根 CMakeLists.txt 中:

      add_definitions(-DMYDEF=GLOBAL_DEF)
      
    4. 最后是带有子目录的完整变体,让B 决定做什么:

      CMakeLists.txt

      cmake_minimum_required(VERSION 3.7)
      
      project(test)
      
      add_subdirectory(A)
      add_subdirectory(B)
      

      A\CMakeLists.txt

      file(WRITE a.cpp [=[
      #include <iostream>
      
      #ifndef MYDEF
      #define MYDEF "Hello from A!"
      #endif
      
      void a()
      {
          std::cout << MYDEF << std::endl;
      }
      ]=])
      
      add_library(A a.cpp)
      

      B\CMakeLists.txt

      file(WRITE b.cpp [=[
      
      void a();
      
      void main()
      {
          a();
      }
      ]=])
      
      add_executable(B b.cpp)
      target_link_libraries(B A)
      
      if (TARGET "A")
          target_compile_definitions(A PRIVATE MYDEF="Hello from B!")
      else()
          set_property(
              DIRECTORY ".." 
              APPEND PROPERTY 
                  COMPILE_DEFINITIONS "MYDEF=\"Hello from Global!\""
          )
      endif()
      

    参考

    【讨论】:

    • 我不知何故错过了 CMake 用于编译器定义的传递依赖功能,尽管我目前完全在这个主题内......感谢您的回答,我将添加改编后的示例作为单独的答案来说明我最终将如何使用它。
    【解决方案2】:

    CMake按顺序处理脚本,从顶级 CMakeLists.txt 开始并逐行执行。

    因此,如果在分配之前读取变量,您将一无所获。在这种情况下,CACHE 变量的唯一特定之处是该变量可能在之前的 cmake 调用中被分配。


    需要在分配变量之前使用变量,这通常表明糟糕的设计。在许多情况下(即使是遗留代码),设计可以被优雅地修复。

    强制 CMake 重新配置项目可以完成,例如touching current script:

    要强制重新配置,可以“cmake -E touch” CMAKE_CURRENT_LIST_FILE,不知何故在目标构建期间 或类似的。

    【讨论】:

    • 感谢您的回答,Tsyvatev。你的回答也很有用w.r.t。多次运行,但对于我的用例,@Florian 关于传递编译器定义的答案更合适,因此接受他的。
    • @RolandSarrazin:当我写下我的答案时,您的问题错过了“是否可以在生成阶段设置编译器定义”的部分内容,因此非常笼统。在您未来的问题中具体:不仅要问“您想要什么一般功能”,还要说明您想要它的特定目的 .一些一般性问题没有简单的答案,但基于该问题的具体问题可以很好地解决。例如。 CMake 脚本不像通用编程语言那样工作得很好,但它是为构建事物而设计的,并且在该领域工作得相对较好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-07
    • 2015-04-06
    • 1970-01-01
    相关资源
    最近更新 更多