CMake:文件解析顺序(缓存,工具链等)是什么? [英] CMake: In which order are files parsed (cache, toolchain, etc.)?
问题描述
这似乎是一个琐碎的问题,因为CMake是一种脚本语言,通常的答案是:严格顺序执行。但是我遇到了几种情况,在什么情况下CMake解析特定文件的时间或顺序很重要。所以我想知道:
- 是否有可用的文档描述
文件(包括内部CMake文件)的排列顺序 - 文件顺序是否取决于CMake版本或某些CMake选项/设置/环境(包括)。选择的生成器或宿主
环境?
到目前为止,我遇到的情况是上述信息很重要: / p>
- 在识别出编译器之前已对工具链文件进行了解析,因此必须首先在工具链文件中/在工具链文件中填充某些CMake变量: CMake与特定链接器进行交叉编译不会将参数传递给armlink
- 该工具链文件已多次分析,因此例如工具链文件中的打印消息显示多次: CMake工具链包含多个文件
- 可以从主
CMakeLists.txt
文件已解析的范围之外的范围调用变量手表: 在CMake中执行命令或宏作为配置步骤完成之前的最后一步
也许您知道的更多。
要找到答案,我尝试了以下操作:我如下所示设置了一个简单的主CMakeLists.txt并运行 cmake --trace…
来分析分析顺序。
cmake_minimum_required(版本2.8)
include (BeforeProjectCmd.cmake)
项目(ParserTest CXX)
add_subdirectory(LibTarget1)
a dd_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
我得到了一个很长的跟踪,试图对其进行总结: / p>
#开始尝试读取
CMakeCache.txt
$ {CMAKE_BINARY_DIR} /CMakeCache.txt
PreLoad.cmake
$ {CMAKE_BINARY_DIR} /PreLoad.cmake
#尝试读取
End CMakeLists.txt(1):cmake_minimum_required(版本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 /模组/平台/Windows.cmake
││└─share / cmake-3.2 / Modules / Platform / WindowsPaths.cmake
│├share / cmake-3.2 / Modules / CMakeCXXInformation.cmake
│ ├┬分享/cmake-3.2/Modules/Compiler/MSVC-CXX.cmake
││├分享/cmake-3.2/Modules/Platform/Windows-MSVC-CXX.cmake
││├┬分享/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调试登录CMakeLists.txt:
b│使用值的UNKNOWN_READ_ACCESS访问了变量 CMAKE_BACKWARDS_COMPATIBILITY。
-配置完成的
-生成$ {CMAKE_BINARY_DIR}
-生成$ {CMAKE_BINARY_DIR} / LibTarget1
-生成$ {CMAKE_BINARY_DIR} / LibTarget2
-生成完成的
#写入
$ {CMAKE_BINARY_DIR} /CMakeCache.txt
因此,到目前为止,我看到了上面的输出得出以下结论(我希望这是正确的,并且有些通用):
- 启动配置时仅读取一次CMakeCache.txt文件,并在生成完成后写入。它只是保留全局变量缓存的状态。
-
project()
命令触发大多数CMake的检测魔术(包括从Toolchain.txt
文件读取)。 - 工具链文件被读取两次。一次在检测到make / compile系统之前,一次在随后生成的
CMakeSystem.cmake
内部。 -
variable_watch()
钩子可以随时触发,因此未定义最佳执行命令的作用域。
目前尚无关于CMake特定内部运作的官方文档,因此,请在下面找到我到目前为止所学到的CMake的摘要...
要解析的文件取决于
- 主机和目标操作系统
- 目标编译器
- 您的主机环境(变量,注册表,已安装的软件)
- 您项目的CMake脚本文件,其中可能包括
- 您的工具链文件
- 您选择的编程语言
- 任何外部项目/库/文件/脚本
这些参数有很多可能的组合,但是大多数时候CMake都会自动为您检测正确的设置,而您无需理会它的完成方式。好消息是-当您需要知道时-它遵循某些固有模式。
有趣的是,它仅在一定程度上取决于 CMake生成器您正在选择。
初始步骤:编译器检测和验证
这主要从 project()
命令。以 CXX
语言为例,用于编译器检测的主要文件是(另请参阅问题的跟踪输出中的根文件):
-
share / cmake-xy / Modules / CMakeDetermineCXXCompiler.cmake
这基本上是尝试确定编译器可执行文件的位置,并调用它来获取更具体的编译器ID。
此外,例如定义基于主机环境和目标操作系统的源/输出文件扩展名。
-
share / cmake-xy / Modules / CMakeCXXCompiler.cmake.in
这是用于将编译器检测结果存储在<$ c $中的模板c> $ {CMAKE_BINARY_DIR} / $ {CMAKE_FILES_DIRECTORY} /xyz/CMakeCXXCompiler.cmake 。
主要这些变量是:
CMAKE_CXX_COMPILER
,CMAKE_CXX_SOURCE_FILE_EXTENSIONS
,CMAKE_CXX_IGNORE_EXTENSIONS
和CMAKE_CXX_COMPILER_ENV_VAR
-
share / cmake-xy / Modules / CMakeCXXInformation.cmake
此文件设置编译器的基本标志。这也是编译器,宿主和目标对设置的影响最大的地方,例如:
include(Platform / $ {CMAKE_SYSTEM_NAME}-$ {CMAKE_CXX_COMPILER_ID} -CXX-$ {CMAKE_SYSTEM_PROCESSOR}可选)
include(平台/ $ {CMAKE_SYSTEM_NAME}-$ {CMAKE_CXX_COMPILER_ID} -CXX可选)
include {平台}-$ {CMAKE_BASE_NAME}可选)
include(Platform / $ {CMAKE_SYSTEM_NAME}可选)
-
share / cmake-xy / Modules / CMakeTestCXXCompiler.cmake
这确实测试了所有内容,例如通过在简单生成的CMake项目中实际调用编译器来确定编译器功能。
这些步骤的结果存储在缓存的变量中,并且这些文件是特殊的,在这种情况下,它们受到保护变量,例如 CMAKE_CXX_COMPILER_LOADED
, CMAKE_CXX_INFORMATION_LOADED
或 CMAKE_CXX_COMPILER_WORKS
不再与每个连续的CMake配置步骤一起运行。
项目配置文件:修改默认值
有几种方法可以更改CMake默认值,而无需实际触摸项目的 CMakeLists.txt
文件。
-
-C< initial-cache>
命令行选项
如果要提供一些预设值(可以正常使用y通过
-D ...
选项给出),一次又一次地通过多个项目。就像您计算机上的某些图书馆搜索路径或公司中使用的某些预设一样。 -
CMakeCache.txt
通过例如cmake-gui
cmake-gui
可让您手动修改项目的选项(编辑所有非内部变量最终生成构建环境之前,请在CMakeCache.txt
中进行操作。 -
主要用于交叉编译,但更笼统地可以描述为使用的每个编译器工具链的预设值。
-
PreLoad.cmake
与初始缓存选项大致相同(请参见上文) ),但不是通过命令行选项给出的。它必须与您项目的
CMakeLists.txt
位于同一目录。
注意:它支持所有CMake脚本命令,例如
if()
调用,但PreLoad.cmake
具有
- 自变量范围(这里所有未缓存的内容在主
CMakeLists.txt
中不可见) - 限制是什么已经知道(它先于其他所有元素运行,因此大多数情况下,您可以检查
CMAKE_GENERATOR
)
- 自变量范围(这里所有未缓存的内容在主
-
CMAKE_USER_MAKE_RULES_OVERRIDE
,CMAKE_USER_MAKE_RULES_OVERRIDE_< LANG>
这允许在CMake自动检测后修改非缓存的默认值
示例:通过
.c
文件
扩展有效的CXX源文件扩展名MakeRulesOverwrite.cmake
列表(APPEND CMAKE_CXX_SOURCE_FILE_EXTENSIONS c)
然后您可以使用类似
$ b $的名称调用cmake
b
> cmake -D CMAKE_USER_MAKE_RULES_OVERRIDE:PATH = .. \MakeRulesOverwrite.cmake ..
-
>
CMAKE_PROJECT_ParserTest_INCLUDE
这是为了在您的
project()<之后直接将自定义代码注入到项目构建中,而无需修改其源代码
命令已处理(并检测到构建环境)。
Toolchain.cmake:已解析多次
A 工具链文件在确定系统,编译器等时被多次读取。
重要的是:
-
每个
try_compile()
调用。而且由于try编译必须产生有效的可执行文件,因此您可能需要-如果您是交叉编译-到
-
CMAKE_TRY_COMPILE_TARGET_TYPE
到STATIC_LIBRARY
(CMake 3.6或更高版本) - 检查
IN_TRY_COMPILE
全局属性以添加其他选项
-
-
如果更改工具链文件,CMake将重新触发编译器检测(如上面的跟踪所示)。
CMake重新配置:一切都来自缓存
最后但并非最不重要的一点是,重要的是要知道上面的跟踪仅显示初始步骤。所有连续的项目配置几乎都将使用缓存变量中的所有内容,因此在重新配置运行中将读取更少的文件。
参考
This seems as a trivial question, since CMake is a script language the general answer is: strictly sequential. But I came across several cases where it was important when or in which order CMake is parsing certain files. So I wonder:
- Is there a documentation available that describes the order in which files (incl. internal CMake files) are parsed?
- Is the file order depending on the CMake version or some CMake options/settings/environment incl. the chosen generator or host environment?
The cases I came across so far, where the above information was important:
- The toolchain file is parsed before the compiler is identified, so you have to populate certain CMake variables in the cache first/in the toolchain file: CMake cross-compile with specific linker doesn't pass arguments to armlink
- The toolchain file is parsed multiple times, therefore e.g. printing messages from the toolchain file show multiple times: CMake toolchain includes multiple files
- Variable watch can be called from a scope outside your main
CMakeLists.txt
file has been parsed: Execute command or macro in CMake as the last step before the 'configure' step finishes
Maybe you know even more.
To find an answer, I have tried the following: I have setup a simple main CMakeLists.txt as shown below and run cmake --trace …
to analyze the parsing order.
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)
When I then run e.g. cmake --debug-output --trace -G"Visual Studio 12 2013" -DCMAKE_TOOLCHAIN_FILE:FILE_PATH=Toolchain.txt
I got a long trace that I tried to summarize:
# 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
So seeing the above output I came - so far - to following conclusion (which I hope are true and somewhat generic):
- The CMakeCache.txt file is only read once when configuration is started and written after the generation is finished. It just persists the state of the "global variables" cache.
- The
project()
command trigger most of CMake's detection magic (including reading from theToolchain.txt
file). - The toolchain file is read twice. Once before the make/compile system is detected and once inside the then generated
CMakeSystem.cmake
. - The
variable_watch()
hook can trigger anytime, so the scope in which the optimal "command to execute" is called is undefined.
There's no official documentation about this particular inner workings of CMake, so please find below a summary of what I've learned about CMake so far ...
What files are parsed depends on the
- The host and target operating system
- The target compiler
- Your host computer's environment (variables, registry, installed software)
- Your project's CMake script files, which could include
- Your toolchain file
- Your selected programming languages
- Any external projects/libraries/files/scripts
There are a lot of possible combinations of those parameters, but most of the time CMake does all the magic of automatically detecting the correct settings for you and you don't need to bother how it's done. The good news is - when you need to know - it follows certain intrinsic patterns.
Interesting is that it only marginally depends on the CMake generator you are selecting.
Initial Step: Compiler Detection and Verification
This mainly starts with the project()
command. Taking CXX
language as an example, the main files for compiler detection are (see also the root files in the question's trace output):
share/cmake-x.y/Modules/CMakeDetermineCXXCompiler.cmake
This basically tries to determine the compiler executable's location and does call it to get a more specific compiler id.
Furthermore it e.g. defines source/output file extensions based on the host computer environment and target operating system.
share/cmake-x.y/Modules/CMakeCXXCompiler.cmake.in
This is the template to store the result of the compiler detection in
${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/x.y.z/CMakeCXXCompiler.cmake
.Mainly those variables are:
CMAKE_CXX_COMPILER
,CMAKE_CXX_SOURCE_FILE_EXTENSIONS
,CMAKE_CXX_IGNORE_EXTENSIONS
andCMAKE_CXX_COMPILER_ENV_VAR
share/cmake-x.y/Modules/CMakeCXXInformation.cmake
This file sets the basic flags for the compiler. It's also where the compiler, host and target does have the most influence on the setup with calls like this:
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
This does test everything and e.g. determine compiler features by actually calling the compiler in a simple generated CMake projects.
The results of those steps are stored in cached variables and those files are special in such case the they are safeguarded by variables like CMAKE_CXX_COMPILER_LOADED
, CMAKE_CXX_INFORMATION_LOADED
or CMAKE_CXX_COMPILER_WORKS
to not run with each consecutive CMake configuration step again.
Project Configuration Files: Modify the Defaults
There are several ways you could change a CMake default values without actually having to touch your project's CMakeLists.txt
files.
-C <initial-cache>
command line optionThis can be used if you want to give some preset values (you would normally give via
-D ...
option) trough several projects over and over again. Like some library search paths on your computer or some presets used in your company.CMakeCache.txt
through e.g.cmake-gui
cmake-gui
lets you manually modify your project's options (editing all non-internal variables inCMakeCache.txt
) before you finally generate the build environment.-
Mainly used for cross-compiling, but it can more generally describes as preset values per compiler toolchain used.
PreLoad.cmake
More or less the same as the "initial cache" option (see above), but it's not given through a command line option. It has just to be in the same directory as your project's
CMakeLists.txt
.Note: It supports all CMake script commands like
if()
calls, butPreLoad.cmake
has its- own variable scope (everything non-cached here is not visible in your main
CMakeLists.txt
) - limitations what is already known (it runs before everything else, so mostly you can check against
CMAKE_GENERATOR
)
- own variable scope (everything non-cached here is not visible in your main
CMAKE_USER_MAKE_RULES_OVERRIDE
,CMAKE_USER_MAKE_RULES_OVERRIDE_<LANG>
This allows to modify non-cached default values after the automatic detection by CMake.
Example: Extending the valid CXX source file extensions by
.c
filesMakeRulesOverwrite.cmake
list(APPEND CMAKE_CXX_SOURCE_FILE_EXTENSIONS c)
Then you can call
cmake
with something like> cmake -D CMAKE_USER_MAKE_RULES_OVERRIDE:PATH=..\MakeRulesOverwrite.cmake ..
CMAKE_PROJECT_ParserTest_INCLUDE
This is meant to "inject custom code into project builds without modifying their source" directly after your
project()
command was processed (and the build environment was detected).
Toolchain.cmake: Parsed Multiple Times
A toolchain file is read multiple time while determining the system, compiler, etc.
Important to know is:
It's read with each
try_compile()
call. And since try compile must produce a valid executable, you may need - if you are e.g. cross-compiling - toCMAKE_TRY_COMPILE_TARGET_TYPE
toSTATIC_LIBRARY
(CMake version 3.6 or above)- Check
IN_TRY_COMPILE
global property to add additional options
If you change your toolchain file, CMake will re-trigger the compiler detection (as in the trace above). Which profoundly helps to play with your compiler settings.
CMake Re-Configurations: Everything comes from the Cache
Last but not least, it's important to know that the trace above only shows the initial step. All consecutive project configurations will take almost everything from cached variables and therefore will read much less files in the re-configuration runs.
References
- The Architecture of Open Source Applications: CMake
- Generic rule from makefile to CMake
- CMake error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found
这篇关于CMake:文件解析顺序(缓存,工具链等)是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!