0

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

Mat Gomes
  • 435
  • 1
  • 8
  • 22
  • But.. cmake has sdcc support on it's own. https://github.com/Kitware/CMake/blob/master/Modules/Platform/Generic-SDCC-C.cmake . What does output `!!! The value of root is testing/`? – KamilCuk Mar 18 '21 at 22:47
  • @KamilCuk that profile is fine unless you want to compile for the PIC (microchip) processors. Unfortunately that platform doesn't support the flags needed for that architecture. The main issue is the obj output extension that is different in "gputils", the assembler used when compiling for PIC with Sdcc. Also, the output comes from the message in the platform cmake. – Mat Gomes Mar 18 '21 at 23:00
  • "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." - Yes, a toolchain file is executed several times when build the project. For that reasons, it cannot rely just on the CACHE variable passed in the command line. Your may store that variable in the environment one, so it will accessible to the toolchain upon next invocations. See duplicate questions for more details. – Tsyvarev Mar 18 '21 at 23:18
  • @Tsyvarev I had seen those answers before, but still I don't think this should be closed as duplicate, because although I do have the same problem as the other two quesitons, the answers in there do not answer my questions: "Is there something I am missing? Why do the toolchain files get "executed" more than once by CMake?". Plus, setting an environment variable seems very hacky and probably not the way it's supposed to be done in modern CMake. By duplicating this question we essentially block people with expertise from actually answering and explaining the problem... – Mat Gomes Mar 19 '21 at 08:17
  • "although I do have the same problem as the other two quesitons, the answers in there do not answer my questions". - Please note, that Stack Overflow expects the question to be devoted for the **single problem**. And according to the title - "CMake not passing arguments to toolchain platform file" - your problem is passing arguments to the toolchain. And exactly this problem is described in the other questions. If you want to ask why toolchain is called multiple times, then make the whole your question post devoted for this problem. (Do not ask multiple questions in a single question post). – Tsyvarev Mar 19 '21 at 08:33
  • "... seems very hacky and probably not the way it's supposed to be done in modern CMake" - If you have the same problem as in the other question, but *feel* that provided answers are not the only solutions for the problem, then set a [bounty](https://stackoverflow.com/help/bounty) for that other question. This will attract others to (re)-visit a question and think about the problem more thoroughly, so you have a chance to get better answer. This is how Stack Overflow works. – Tsyvarev Mar 19 '21 at 08:40
  • @Tsyvarev Agreed, perhaps I should have written a better title: the question as opposed to the problem. I have fixed it and now it only focuses on the "why" I care about. Apologies. – Mat Gomes Mar 19 '21 at 08:49
  • @Tsyvarev in terms of the solution, I was not aware of bounties. I will set up a bounty for one of the similar questions, hopefully, a better solution will come along. Just wish I had more reputation to attract more people :D – Mat Gomes Mar 19 '21 at 08:50
  • Ok, the question is reopened now. Since you don't ask (here) about the passing parameters to the toolchain, you probably want the final words of your question "... and, if someone has the CMake expertise, what is the proper modern CMake way of doing what I want (accessing cache in the toolchain)" to be removed too. – Tsyvarev Mar 19 '21 at 09:11
  • @Tsyvarev brilliant, removed the text. – Mat Gomes Mar 19 '21 at 11:05

2 Answers2

5

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/

  • Thanks for posting a solution, it's the same as Tsyvarev's in here https://stackoverflow.com/a/66706187/2430334 . However, you did post the reference to Craig Scott's book and that is very valuable as well, appreciate it! I've actually been meaning to pick that book up for a while, now I have another reason to! – Mat Gomes Mar 19 '21 at 18:20
  • I believe that list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES ${SDCC_SYSROOT}) should be list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES SDCC_SYSROOT), that is SDCC_SYSROOT shouldn't be evaluated. – Dustin Simpson Dec 31 '22 at 20:31
3

For determine, whether some feature is supported by the compiler or by some library, CMake uses try_compile approach: during the configuration phase, it creates separate CMake project and immediately configures and builds it. Because it is a separate project, its configuration has the same steps as the main project and it loads the toolchain file too.

try_compile could be used by the (user) project for check features of the library or of the compiler. There are many CMake modules which use try_compile in their implementation. E.g. CheckSymbolExists.

try_compile is also used by CMake itself, in platform files, when it perform basics checks for the compiler. In your log you could find the line:

CMake Error at /usr/share/cmake-3.16/Modules/CMakeTestCCompiler.cmake:44 (try_compile)

Aside from try_compile, the new CMake project is created in ExternalProject_Add command. That creation is also accompanied by the reading of the toolchain file. (More correctly, the new project is created not when ExternalProject_Add invocation is processed but when corresponding project is configured. This configuration is performed on the build stage of the main project.)

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • good explanation. So `try_compile` will attempt to find supported options and will use the toolchain file, so that's one execution. Then I guess, when CMake is actually configuring my project, it will run the toolchain/platform files, so thats 2 executions. Now, from my output, I can see the toolchain files being executed 3 times, so where's the last one (as I don't use `ExternalProject_Add`)? It seems that, before `try_compile` runs, the toolchain/platform files run twice. – Mat Gomes Mar 19 '21 at 14:27
  • Nevermind, I realised that it probably calls the `try_compile` command a few times! Thanks my dude. – Mat Gomes Mar 19 '21 at 14:33
  • Yes, it is quite interesting question about the origin of the **second invocation** of the toolchain. I would *guess* that it has `try_compile` nature, but it is hard guess. Actually, you may examine lines which are noted in the call stack. That could give you some hint. – Tsyvarev Mar 19 '21 at 14:42