Search code examples
cmaketoolchainsdcc

Why is the toolchain file executed a few times in CMake?


In an attempt to create a cross-compilation CMake toolchain template with the SDCC compiler, I have come across a very weird issue.

As described in this link, if the toolchain.cmake file defines a CMAKE_SYSTEM_NAME, CMake will look for the file with the ${CMAKE_SYSTEM_NAME}.cmake under the Module/Platform directory. And this file should define platform-specific options. In my case, I am using it to find the sdcc compiler and setting some compiler flags.

This works just fine for me. Using cmake -DCMAKE_MODULE_PATH="${PATH_TO_MY_MODULES}" -DCMAKE_TOOLCHAIN_FILE="${PATH_TO_MY_TOOLCHAIN}" -DSDCC_SYSROOT="SOME_VALUE", CMake finds all the correct toolchain and platform files.

It seems like the toolchain and the platform file are executed (not sure if that's the correct term) a few times during the configuration process. In the first few times, the variable SDCC_SYSROOT I passed in the CMake command has the value SOME_VALUE as expected. However, the same variable SDCC_SYSROOT seems to lose the value in the last time these toolchain/platform files are executed. So they are empty. This causes my script to generate a fatal error.

toolchain.cmake has the following contents:

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")

the Module/Platform/SDCC_PIC_16F877A.cmake file has the contents:

# 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.")

and my CMakeLists.txt is the following:

cmake_minimum_required(VERSION 3.10)
project(PicExample)

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

add_executable(pic_example main.c)

what I invoke from my project/build directory and the error I get:

 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".

Why do the toolchain files get "executed" more than once by CMake and has no access to cache in the latest runs? I've been finding CMake documentation for cross-compilation very difficult, especially if you are working with a non-standard compiler.

I am aware that other people have had same issues before, but I am not simply asking for a simple hacky solution (setting environment variables). I actually want to know why this happens (which the previous answers don't tackle).


Solution

  • Tsyvarev answered the why the toolchain is used multiple times in CMake. TLDR; CMake needs it for multiple try_compile() calls it uses internally for error checking and other things.

    This works just fine for me. -DCMAKE_MODULE_PATH="${PATH_TO_MY_MODULES}" -DCMAKE_TOOLCHAIN_FILE="${PATH_TO_MY_TOOLCHAIN}" -DSDCC_SYSROOT="SOME_VALUE",

    To fix your problem here is what you need to do.

    Essentially you are passing an argument to your toolchain file. And this argument SDCC_SYSROOT essentially goes out of scope.

    To fix this problem here is what you need to do.

    # 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})
    

    If you want to see how many times your toolchain script gets executed try putting in a message() call in there for fun.

    And if you are really interested look inside your build folder and see what it is CMake is doing.

    If you are wondering how I know this information it's because I read the toolchain section in Craig Scott's CMake book "Professional CMake: A Practical Guide"

    Here is a link: https://crascit.com/professional-cmake/