【问题标题】:Why is the toolchain file executed a few times in CMake?为什么工具链文件在 CMake 中执行了几次?
【发布时间】:2021-06-16 09:19:43
【问题描述】:

在尝试使用 SDCC 编译器创建交叉编译 CMake 工具链模板时,我遇到了一个非常奇怪的问题。

this link 中所述,如果toolchain.cmake 文件定义了CMAKE_SYSTEM_NAME,CMake 将在Module/Platform 目录下查找具有${CMAKE_SYSTEM_NAME}.cmake 的文件。这个文件应该定义特定于平台的选项。就我而言,我使用它来查找 sdcc 编译器并设置一些编译器标志。

这对我来说很好用。使用cmake -DCMAKE_MODULE_PATH="${PATH_TO_MY_MODULES}" -DCMAKE_TOOLCHAIN_FILE="${PATH_TO_MY_TOOLCHAIN}" -DSDCC_SYSROOT="SOME_VALUE",CMake 会找到所有正确的工具链和平台文件。

似乎工具链和平台文件在配置过程中执行(不确定这是否正确)几次。在最初的几次中,我在 CMake 命令中传递的变量SDCC_SYSROOT 的值符合预期的SOME_VALUE。但是,相同的变量SDCC_SYSROOT 似乎在最后一次执行这些工具链/平台文件时失去了价值。所以他们是空的。这会导致我的脚本生成致命错误。

toolchain.cmake有以下内容:

set(CMAKE_SYSTEM_NAME SDCC_PIC_16F877A)

# Finding resource settings
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

# Set default MCU family and model
if (NOT MICROCHIP_FAMILY)
  set(MICROCHIP_FAMILY "pic16")
endif()

if (MICROCHIP_MODEL STREQUAL "pic16")
  set(MICROCHIP_MODEL "16f877a")
endif()


# Need a better way to detect the supported models here
if (NOT MICROCHIP_FAMILY STREQUAL "pic16" AND NOT MICROCHIP_MODEL STREQUAL "16f877a")
  message(FATAL_ERROR "Settings not supported. Please drop a request.")
endif()

if (NOT SDCC_ROOT)
  message(FATA_ERROR "Need to provide the root (from toolchain.)")
endif()

# Cache those variables
set(SDCC_ROOT "${SDCC_ROOT}"
  CACHE INTERNAL "Root directory of SDCC installation")

set(MICROCHIP_FAMILY "${MICROCHIP_FAMILY}"
  CACHE INTERNAL "Family of the chip to compile for")

set(MICROCHIP_MODEL "${MICROCHIP_MODEL}"
  CACHE INTERNAL "Model of the chip to compile for")

Module/Platform/SDCC_PIC_16F877A.cmake 文件包含以下内容:

# Check if the shit exists
message("!!! The value of root is ${SDCC_ROOT}")
if (NOT SDCC_ROOT)
  message(FATAL_ERROR
    "SDCC_ROOT is not defined. Please set this variable e.g.\n"
    "cmake -DSDCC_ROOT=\"C:/Program Files/sdcc\"")
endif()

# Finding the compilers
find_program(CMAKE_C_COMPILER
  sdcc
  PATHS ${SDCC_ROOT}
  PATH_SUFFIXES "bin"
  DOC "path to the SDCC C compiler.")

而我的CMakeLists.txt 如下:

cmake_minimum_required(VERSION 3.10)
project(PicExample)

message("THE COMPILER IS ${CMAKE_C_COMPILER}")

add_executable(pic_example main.c)

我从project/build 目录调用的内容和我得到的错误:

 cmake -DCMAKE_MODULE_PATH:FILEPATH="/mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/Modules" -DCMAKE_TOOLCHAIN_FILE:FILEPATH="/mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/Modules/toolchain.cmake" -DSDCC_ROOT="testing/" ..
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
!!! The value of root is testing/
!!! The value of root is testing/
-- Check for working C compiler: /usr/bin/cc
FATA_ERRORNeed to provide the root (from toolchain.)
!!! The value of root is
CMake Error at /mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/Modules/Platform/SDCC_PIC_16F877A.cmake:4 (message):
  SDCC_ROOT is not defined.  Please set this variable e.g.

  cmake -DSDCC_ROOT="C:/Program Files/sdcc"
Call Stack (most recent call first):
  /usr/share/cmake-3.16/Modules/CMakeSystemSpecificInformation.cmake:26 (include)
  /mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/build/CMakeFiles/CMakeTmp/CMakeLists.txt:3 (project)


CMake Error at /usr/share/cmake-3.16/Modules/CMakeTestCCompiler.cmake:44 (try_compile):
  Failed to configure test project build system.
Call Stack (most recent call first):
  CMakeLists.txt:2 (project)


-- Configuring incomplete, errors occurred!
See also "/mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/build/CMakeFiles/CMakeOutput.log".

为什么工具链文件会被 CMake 多次“执行”,并且在最近的运行中无法访问缓存?我一直发现用于交叉编译的 CMake 文档非常困难,尤其是在您使用非标准编译器时。

我知道其他人之前也遇到过同样的问题,但我并不是简单地要求一个简单的 hacky 解决方案(设置环境变量)。我实际上想知道为什么会发生这种情况(以前的答案没有解决)。

【问题讨论】:

  • 但是.. cmake 有自己的 sdcc 支持。 github.com/Kitware/CMake/blob/master/Modules/Platform/…!!! The value of root is testing/ 输出什么?
  • @KamilCuk 该配置文件很好,除非您想为 PIC(微芯片)处理器进行编译。不幸的是,该平台不支持该架构所需的标志。主要问题是“gputils”中的 obj 输出扩展名不同,后者是使用 Sdcc 为 PIC 编译时使用的汇编程序。此外,输出来自平台 cmake 中的消息。
  • "在配置过程中,似乎工具链和平台文件被执行了几次(不确定这是否是正确的术语)。" - 是的,构建项目时会执行多次工具链文件。由于这个原因,它不能仅仅依赖于命令行中传递的 CACHE 变量。您可以将该变量存储在环境一中,以便工具链在下次调用时可以访问它。有关详细信息,请参阅重复的问题。
  • @Tsyvarev 我以前看过这些答案,但我仍然认为这不应该作为重复而关闭,因为虽然我确实有与其他两个问题相同的问题,但那里的答案确实如此不回答我的问题:“我有什么遗漏吗?为什么工具链文件会被 CMake 多次“执行”?”。另外,设置一个环境变量看起来很老套,可能不是现代 CMake 应该做的方式。通过重复这个问题,我们基本上阻止了具有专业知识的人实际回答和解释问题......
  • “虽然我确实和其他两个问题有同样的问题,但那里的答案并不能回答我的问题”。 - 请注意,Stack Overflow 期望该问题专门用于单个问题。根据标题 - “CMake 没有将参数传递给工具链平台文件” - 你的问题是将参数传递给工具链。而这个问题正是在其他问题中描述的。如果您想问为什么工具链被多次调用,那么将整个问题帖子专门用于这个问题。 (不要在一个问题帖子中提出多个问题)。

标签: cmake toolchain sdcc


【解决方案1】:

Tsyvarev 回答了为什么在 CMake 中多次使用工具链。 TLDR; CMake 需要它进行多次 try_compile() 调用,它在内部用于错误检查和其他事情。

这对我来说很好用。 -DCMAKE_MODULE_PATH="${PATH_TO_MY_MODULES}" -DCMAKE_TOOLCHAIN_FILE="${PATH_TO_MY_TOOLCHAIN}" -DSDCC_SYSROOT="SOME_VALUE",

在这里解决您的问题是您需要做的。

本质上,您正在将参数传递给您的工具链文件。而这个参数 SDCC_SYSROOT 基本上超出了范围。

要解决这个问题,你需要做的是。

# Use list(APPEND) rather than set() so that any variables added by CMake aren't lost!
#
# Here is the docs for this variable: 
# https://cmake.org/cmake/help/latest/variable/CMAKE_TRY_COMPILE_PLATFORM_VARIABLES.html
list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES ${SDCC_SYSROOT})

如果您想查看您的工具链脚本执行了多少次,请尝试在其中放入 message() 调用以获得乐趣。

如果您真的感兴趣,请查看您的构建文件夹,看看 CMake 正在做什么。

如果您想知道我是如何知道这些信息的,那是因为我阅读了 Craig Scott 的 CMake 书籍“Professional CMake: 实用指南”

这是一个链接:https://crascit.com/professional-cmake/

【讨论】:

  • 感谢您发布解决方案,这与 Tsyvarev 在此处stackoverflow.com/a/66706187/2430334 中的相同。但是,您确实发布了对 Craig Scott 的书的参考,这也非常有价值,感谢它!我其实早就想拿起那本书了,现在我有了另一个理由!
【解决方案2】:

为了确定编译器或库是否支持某些功能,CMake 使用try_compile 方法:在配置阶段,它创建单独的 CMake 项目并立即配置和构建它。因为它是一个单独的项目,所以它的配置和主项目的步骤相同,它也加载了工具链文件

try_compile 可以被(用户)项目用于检查库或编译器的功能。有许多 CMake 模块在其实现中使用 try_compile。例如。 CheckSymbolExists.

try_compile 也被 CMake 本身在平台文件中使用,当它对编译器执行基本检查时。在您的日志中,您可以找到以下行:

/usr/share/cmake-3.16/Modules/CMakeTestCCompiler.cmake:44 (try_compile) 处的 CMake 错误

除了try_compile,新的CMake项目是在ExternalProject_Add命令中创建的。该创建还伴随着对工具链文件的读取。 (更准确地说,新项目不是在处理ExternalProject_Add调用时而是在配置相应项目时创建的。这个配置是在主项目的构建阶段进行的。)

【讨论】:

  • 很好的解释。所以try_compile 将尝试查找支持的选项并使用工具链文件,所以这是一次执行。然后我猜,当 CMake 实际配置我的项目时,它会运行工具链/平台文件,所以这是 2 次执行。现在,从我的输出中,我可以看到工具链文件被执行了 3 次,那么最后一个在哪里(因为我不使用 ExternalProject_Add)?看来,在try_compile 运行之前,工具链/平台文件运行了两次。
  • 没关系,我意识到它可能会多次调用try_compile 命令!谢谢我的伙计。
  • 是的,关于工具链的第二次调用的起源是一个非常有趣的问题。我会猜测它具有try_compile 的性质,但很难猜测。实际上,您可以检查调用堆栈中记录的行。这可以给你一些提示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-31
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 2022-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多