CMake:parsing文件的哪个Order(Cache,Toolchain,…)?

这似乎是一个微不足道的问题,因为CMake是一种脚本语言,一般的答案是:严格的顺序。 但是我遇到过几种情况,CMake在parsing某些文件的时候或者以何种顺序重要。 所以我想知道:

  1. 是否有可用的文档描述文件(包括内部CMake文件)的parsing顺序?
  2. 文件顺序取决于CMake版本或一些CMake选项/设置/环境。 select的发生器或主机环境?

我遇到的情况到目前为止,上面的信息是重要的:

  • 在编译器被识别之前,工具链文件被parsing,所以你必须首先在工具链文件中/在工具链文件中填充某些CMakevariables: 具有特定链接器的cmake交叉编译不会将parameter passing给armlink
  • 工具链文件被多次parsing,因此例如从工具链文件中显示多次打印消息: cmake toolchain包含多个文件
  • 可以从主CMakeLists.txt文件之外的范围调用variables监视器已parsing: 在“configuration”步骤完成之前的最后一步,执行CMake中的命令或macros

也许你知道更多。

要find答案,我已经尝试了以下内容:我已经安装了一个简单的主CMakeList.txt,如下所示,并运行cmake --trace …来分析parsing顺序。

 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文件只有在configuration开始后才读取一次,并在生成完成后写入。 它只是坚持“全局variables”caching的状态。
  2. project()命令触发了大部分CMake的检测魔法(包括从Toolchain.txt文件中读取)。
  3. 工具链文件被读取两次。 一旦检测到make / compile系统,并且一旦在生成的CMakeSystem.cmake
  4. variable_watch()钩子可以随时触发,因此调用optinal“执行命令”的作用域是未定义的。

关于CMake的这个特定的内部工作没有官方文档,所以请在下面总结一下我所了解到的关于CMake的内容。

什么文件被parsing取决于

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

这些参数有很多可能的组合,但是大部分时间CMake都会自动检测正确的设置,并且不需要麻烦它是如何完成的。 好消息是,当你需要知道的时候,它会遵循一些固有的模式。

有趣的是,它只是勉强取决于你正在select的CMake发生器 。

初始步骤:编译器检测和validation

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

  • share/cmake-xy/Modules/CMakeDetermineCXXCompiler.cmake

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

    此外,它还定义了基于主机环境和目标操作系统的源/输出文件扩展名。

  • share/cmake-xy/Modules/CMakeCXXCompiler.cmake.in

    这是将编译器检测结果存储在${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/xyz/CMakeCXXCompiler.cmake

    主要是这些variables是: CMAKE_CXX_COMPILERCMAKE_CXX_SOURCE_FILE_EXTENSIONSCMAKE_CXX_IGNORE_EXTENSIONSCMAKE_CXX_COMPILER_ENV_VAR

  • share/cmake-xy/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-xy/Modules/CMakeTestCXXCompiler.cmake

    这确实testing了一切,例如通过在一个简单的生成的CMake项目中调用编译器来确定编译器function。

这些步骤的结果存储在cachingvariables中,这些文件在这种情况下是特殊的,它们由像CMAKE_CXX_COMPILER_LOADEDCMAKE_CXX_INFORMATION_LOADEDCMAKE_CXX_COMPILER_WORKS这样的variables进行保护,以便不再在每个连续的CMakeconfiguration步骤中运行。

项目configuration文件:修改默认值

有几种方法可以改变一个CMake默认值,而不需要实际触及你项目的CMakeLists.txt文件。

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

    如果你想给出一些预置值(你通常会通过-D ...选项)一次又一次地通过几个项目,这可以使用。 就像计算机上的某些库searchpath或公司中使用的某些预设一样。

  • CMakeCache.txt通过例如cmake-gui

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

  • CMAKE_TOOLCHAIN_FILE

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

  • PreLoad.cmake

    或多或less与“初始caching”选项相同(请参阅上文),但不是通过命令行选项给出的。 它只是在你的项目的CMakeLists.txt相同的目录。

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

    • 自己的variables范围(这里的所有非caching在主CMakeLists.txt是不可见的)
    • 限制什么是已知的(它在一切之前运行,所以主要是你可以检查CMAKE_GENERATOR
  • CMAKE_USER_MAKE_RULES_OVERRIDECMAKE_USER_MAKE_RULES_OVERRIDE_<LANG>

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

    示例 :通过.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()命令(并检测到构build环境project()后直接“将自定义代码注入到项目构build中而不修改其源代码”。

Toolchain.cmake:parsing多次

在确定系统,编译器等时多次读取工具链文件 。

重要的是要知道的是:

  • 它是每个try_compile()调用读取的。 而且由于尝试编译必须产生一个有效的可执行文件,所以你可能需要 – 如果你正在交叉编译 –

    • CMAKE_TRY_COMPILE_TARGET_TYPESTATIC_LIBRARY (CMake版本3.6或以上)
    • 检查IN_TRY_COMPILE全局属性以添加其他选项
  • 如果你改变你的工具链文件,CMake会重新触发编译器检测(如上面的跟踪)。 这对你的编译器设置有很大的帮助。

CMake重新configuration:一切都来自caching

最后但并非最不重要,重要的是要知道上面的跟踪只显示了第一步。 所有连续的项目configuration将从caching的variables中获取几乎所有内容,因此在重新configuration运行时将读取less得多的文件。

参考

  • 开源应用程序体系结构:CMake
  • 从makefile到cmake的一般规则
  • CMakeLists.txt CMake错误:30(项目):找不到CMAKE_C_COMPILER