【问题标题】:CMake: In which order are files parsed (cache, toolchain, etc.)?CMake:文件解析的顺序(缓存、工具链等)?
【发布时间】:2015-08-10 18:55:26
【问题描述】:

这似乎是一个微不足道的问题,因为 CMake 是一种脚本语言,所以一般的答案是:严格顺序。但是我遇到了几个案例,其中 CMake 解析某些文件的时间或顺序很重要。所以我想知道:

  1. 是否有可用的文档来描述其中的顺序 文件(包括内部 CMake 文件)被解析?
  2. 文件顺序是否取决于 CMake 版本或某些 CMake 选项/设置/环境,包括。选择的生成器或主机 环境?

到目前为止我遇到的案例,其中上述信息很重要:

也许你知道得更多。

为了找到答案,我尝试了以下方法:我设置了一个简单的主 CMakeLists.txt,如下所示并运行 cmake --trace … 来分析解析顺序。

cmake_minimum_required(VERSION 2.8)

include(BeforeProjectCmd.cmake)

project(ParserTest CXX)

add_subdirectory(LibTarget1)
add_subdirectory(LibTarget2)

add_executable(ExeTarget Test.cpp)

variable_watch(CMAKE_BACKWARDS_COMPATIBILITY)

当我然后运行时,例如cmake --debug-output --trace -G"Visual Studio 12 2013" -DCMAKE_TOOLCHAIN_FILE:FILE_PATH=Toolchain.txt我有一个很长的痕迹,我试图总结:

# Begin try to read
CMakeCache.txt
${CMAKE_BINARY_DIR}/CMakeCache.txt
PreLoad.cmake
${CMAKE_BINARY_DIR}/PreLoad.cmake
# End try to read

┌ CMakeLists.txt(1):  cmake_minimum_required(VERSION 2.8 )
│ CMakeLists.txt(3):  include(BeforeProjectCmd.cmake )
│
├─ BeforeProjectCmd.cmake
│
│ CMakeLists.txt(5):  project(ParserTest CXX )
├┬ share/cmake-3.2/Modules/CMakeDetermineSystem.cmake
││
│└─ Toolchain.txt
│
├┬ ${CMAKE_PLATFORM_INFO_DIR}/CMakeSystem.cmake
││
│└─ Toolchain.txt
│
├─ share/cmake-3.2/Modules/CMakeSystemSpecificInitialize.cmake
├┬ share/cmake-3.2/Modules/CMakeDetermineCXXCompiler.cmake
│├┬ share/cmake-3.2/Modules/CMakeDetermineCompiler.cmake
││├ share/cmake-3.2/Modules/Platform/Windows-CXX.cmake
…
││├ share/cmake-3.2/Modules/CMakeDetermineCompilerId.cmake
││├─ share/cmake-3.2/Modules/CMakeCompilerIdDetection.cmake
…
││├ share/cmake-3.2/Modules/Compiler/MSVC-DetermineCompiler.cmake
…
│├ ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/3.2.2/CMakeCXXCompiler.cmake
│├ share/cmake-3.2/Modules/CMakeSystemSpecificInformation.cmake
│├┬ share/cmake-3.2/Modules/CMakeGenericSystem.cmake
││├ share/cmake-3.2/Modules/Platform/Windows.cmake
││└─ share/cmake-3.2/Modules/Platform/WindowsPaths.cmake
│├ share/cmake-3.2/Modules/CMakeCXXInformation.cmake
│├┬ share/cmake-3.2/Modules/Compiler/MSVC-CXX.cmake
││├ share/cmake-3.2/Modules/Platform/Windows-MSVC-CXX.cmake
││├┬ share/cmake-3.2/Modules/Platform/Windows-MSVC.cmake
│││└─ share/cmake-3.2/Modules/CMakeRCInformation.cmake
││└ share/cmake-3.2/Modules/CMakeCommonLanguageInclude.cmake
│├ share/cmake-3.2/Modules/CMakeTestCXXCompiler.cmake
│├┬ share/cmake-3.2/Modules/CMakeTestCompilerCommon.cmake
││├ share/cmake-3.2/Modules/CMakeDetermineCompilerABI.cmake
││├ share/cmake-3.2/Modules/CMakeDetermineCompileFeatures.cmake
││├ share/cmake-3.2/Modules/Internal/FeatureTesting.cmake
││└ share/cmake-3.2/Modules/Compiler/MSVC-CXX-FeatureTests.cmake
│└ ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/3.2.2/CMakeCXXCompiler.cmake
│
│ CMakeLists.txt(7):  add_subdirectory(LibTarget1 )
│
├─ LibTarget1/CMakeLists.txt
│
│ CMakeLists.txt(8):  add_subdirectory(LibTarget2 )
│
├─ LibTarget2/CMakeLists.txt
│
│ CMakeLists.txt(10):  add_executable(ExeTarget Test.cpp )
│ CMakeLists.txt(12):  variable_watch(CMAKE_BACKWARDS_COMPATIBILITY )
│
│  CMake Debug Log in CMakeLists.txt:
│  Variable "CMAKE_BACKWARDS_COMPATIBILITY" was accessed using UNKNOWN_READ_ACCESS with value "".

-- Configuring done
-- Generating ${CMAKE_BINARY_DIR}
-- Generating ${CMAKE_BINARY_DIR}/LibTarget1
-- Generating ${CMAKE_BINARY_DIR}/LibTarget2
-- Generating done

# Writes
${CMAKE_BINARY_DIR}/CMakeCache.txt

所以看到上面的输出,到目前为止,我得出了以下结论(我希望这是真实的并且有点笼统):

  1. CMakeCache.txt文件只在配置启动时读取一次,在生成完成后写入。它只是保持“全局变量”缓存的状态。
  2. project() 命令触发了 CMake 的大部分检测魔法(包括从 Toolchain.txt 文件中读取)。
  3. 工具链文件被读取两次。在检测到 make/compile 系统之前一次,然后在生成的 CMakeSystem.cmake 内部一次。
  4. variable_watch() 挂钩可以随时触发,因此未定义最佳“执行命令”的调用范围。

【问题讨论】:

  • 也许您应该与一些 CMake 核心开发人员分享您的问题。使用 CMake3.0,他们开始大量改进他们的文档,所以这对他们来说可能很有趣。 cmake-developers 邮件列表非常活跃
  • 我认为this 可能会对您有所帮助。
  • @thiagowfx 感谢您的链接。它还帮助我理解了 CMake 背后的解析概念的根源和部分内容(另请参阅here),但我必须承认我曾经是并且我仍在寻找更详细的东西。而且我认为-在我获得了更多见解之后,例如here 因为我已经发布了这个问题 - 当我有时间编译必要的信息时,我会尝试回答我自己的问题。
  • @Florian:是的,这个问题非常好。有一个完整的答案会很棒。因此,如果您认为自己已经了解了足够的信息,请随时准备好发布答案。
  • @thiagowfx 好的。我已经添加了到目前为止我遇到的内容。

标签: cmake


【解决方案1】:

没有关于 CMake 的这种特定内部工作的官方文档,所以请在下面找到我迄今为止对 CMake 的了解的摘要......

解析什么文件取决于

  1. 主机和目标操作系统
  2. 目标编译器
  3. 您的主机环境(变量、注册表、安装的软件)
  4. 您项目的 CMake 脚本文件,其中可能包括
    1. 您的工具链文件
    2. 您选择的编程语言
    3. 任何外部项目/库/文件/脚本

这些参数有很多可能的组合,但大多数情况下,CMake 会自动为您检测正确的设置,您无需担心它是如何完成的。好消息是 - 当您需要知道时 - 它遵循某些内在模式。

有趣的是,它仅在一定程度上取决于您选择的CMake generator。

初始步骤:编译器检测和验证

这主要从project() 命令开始。以CXX语言为例,编译器检测的主要文件是(另见问题跟踪输出中的根文件):

  • share/cmake-x.y/Modules/CMakeDetermineCXXCompiler.cmake

    这基本上试图确定编译器可执行文件的位置,并调用它来获取更具体的编译器 ID。

    此外,例如根据主机环境和目标操作系统定义源/输出文件扩展名。

  • share/cmake-x.y/Modules/CMakeCXXCompiler.cmake.in

    这是将编译器检测结果存储在${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/x.y.z/CMakeCXXCompiler.cmake 中的模板。

    主要是那些变量是:CMAKE_CXX_COMPILER、CMAKE_CXX_SOURCE_FILE_EXTENSIONS、CMAKE_CXX_IGNORE_EXTENSIONS和CMAKE_CXX_COMPILER_ENV_VAR

  • share/cmake-x.y/Modules/CMakeCXXInformation.cmake

    此文件设置编译器的基本标志。这也是编译器、主机和目标对设置影响最大的地方,调用如下:

    include(Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_CXX_COMPILER_ID}-CXX-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL)
    include(Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_CXX_COMPILER_ID}-CXX OPTIONAL)
    include(Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_BASE_NAME} OPTIONAL)
    include(Platform/${CMAKE_SYSTEM_NAME} OPTIONAL)        
    
  • share/cmake-x.y/Modules/CMakeTestCXXCompiler.cmake

    这确实测试了一切,例如通过在简单生成的 CMake 项目中实际调用编译器来确定编译器功能。

这些步骤的结果存储在缓存的变量中,这些文件在这种情况下是特殊的,它们由CMAKE_CXX_COMPILER_LOADED、CMAKE_CXX_INFORMATION_LOADED 或CMAKE_CXX_COMPILER_WORKS 等变量保护,不会在每个连续的 CMake 配置步骤中再次运行。

项目配置文件:修改默认值

您可以通过多种方式更改 CMake 默认值,而无需实际修改项目的 CMakeLists.txt 文件。

  • -C <initial-cache> 命令行选项

    如果您想通过多个项目一遍又一遍地给出一些预设值(通常通过-D ... 选项给出),则可以使用此选项。比如你电脑上的一些库搜索路径或者你公司使用的一些预设。

  • CMakeCache.txt 通过例如cmake-gui

    cmake-gui 允许您在最终生成构建环境之前手动修改项目的选项(编辑 CMakeCache.txt 中的所有非内部变量)。

  • CMAKE_TOOLCHAIN_FILE

    主要用于cross-compiling,但它可以更一般地描述为每个使用的编译器工具链的预设值。

  • PreLoad.cmake

    与“初始缓存”选项(见上文)大致相同,但它不是通过命令行选项给出的。它必须与项目的CMakeLists.txt 位于同一目录中。

    注意:它支持所有 CMake 脚本命令,如 if() 调用,但 PreLoad.cmake 有其

    • 自己的变量范围(此处所有未缓存的内容在您的主 CMakeLists.txt 中不可见)
    • 已知的限制(它先于其他一切运行,所以大多数情况下您可以检查 CMAKE_GENERATOR)
  • CMAKE_USER_MAKE_RULES_OVERRIDE, CMAKE_USER_MAKE_RULES_OVERRIDE_<LANG>

    这允许在 CMake 自动检测后修改非缓存的默认值。

    Example:通过.c文件扩展有效的CXX源文件扩展名

    MakeRulesOverwrite.cmake

    list(APPEND CMAKE_CXX_SOURCE_FILE_EXTENSIONS c)
    

    然后你可以打电话给cmake 类似

    > cmake -D CMAKE_USER_MAKE_RULES_OVERRIDE:PATH=..\MakeRulesOverwrite.cmake ..
    
  • CMAKE_PROJECT_ParserTest_INCLUDE

    这意味着在处理完您的project() 命令(并检测到构建环境)之后,直接“将自定义代码注入项目构建而不修改其源代码”。

Toolchain.cmake:多次解析

在确定系统、编译器等时多次读取toolchain file。

重要的是要知道:

  • 每个try_compile() 调用都会读取它。而且由于 try compile 必须产生一个有效的可执行文件,你可能需要 - 如果你是例如交叉编译 - 到

  • 如果您更改工具链文件,CMake 将重新触发编译器检测(如上面的跟踪)。这对您的编译器设置有很大帮助。

CMake 重新配置:一切都来自缓存

最后但并非最不重要的一点是,要知道上面的跟踪仅显示了初始步骤,这一点很重要。所有连续的项目配置都会从缓存变量中获取几乎所有内容,因此在重新配置运行时读取的文件会少得多。

参考文献

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-31
    • 2019-01-28
    • 1970-01-01
    • 2022-06-22
    • 2017-06-17
    • 1970-01-01
    • 2013-11-20
    • 1970-01-01
    相关资源
    最近更新 更多