CMake Command Line Definitions Not Perpetuating To Toolchain File - c++

I have a cmake cross compiler toolchain file, abridged as:
set(CMAKE_SYSTEM_NAME Linux)
if( DEFINED TC_PATH )
message( STATUS " TC_PATH IS defined. ${TC_PATH}" )
else()
message( FATAL_ERROR " TC_PATH not defined." )
endif()
set(CMAKE_C_COMPILER ${TC_PATH}/usr/bin/i586-linux/i586-linux-gcc )
set(CMAKE_CXX_COMPILER ${TC_PATH}/usr/bin/i586-linux/i586-linux-g++ )
set(CMAKE_LINKER ${TC_PATH}/usr/bin/i586-linux/i586-linux-ld )
I call cmake, setting the TC_PATH as well as the toolchain file:
~/CMakeTest/output $ cmake -DTC_PATH:PATH=/opt/toolchain -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake ../
It appears cmake is invoking the toolchain file multiple times. On the first two time, the TC_PATH check succeeds, but later, after identifying the compilers, it throws an error:
-- TC_PATH IS defined. /opt/toolchain
-- TC_PATH IS defined. /opt/toolchain
-- The C compiler identification is GNU 4.9.1
-- The CXX compiler identification is GNU 4.9.1
-- Check for working C compiler: /opt/toolchain/usr/bin/i586-linux/i586-linux-gcc
CMake Error at /home/gnac/CMakeTest/toolchain.cmake:4 (message):
TC_PATH not defined.
Call Stack (most recent call first):
/home/gnac/CMakeTest/output/CMakeFiles/3.0.2/CMakeSystem.cmake:6 (include)
CMakeLists.txt:2 (project)
So, outside of setting a permanent environment variable in the shell, how I can set the TC_PATH variable via the command line so that it will be remain in context while executing the cmake generate command?

When compiling a test project, CMake does not pass variables to it by default.
There is CMAKE_TRY_COMPILE_PLATFORM_VARIABLES option for passing variables into the test project.
In order to fix your issue, this line should be put into the toolchain file:
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES TC_PATH)

Your toolchain needs to be self-sufficient. The step that fails is a try_compile() which is not getting your cached variables.
Your toolchain file does not look like you're cross-compiling (it does not have a CMAKE_SYSTEM_NAME) so you can do one of the following (besides setting the CC and CXX environment variables as you have mentioned):
It's sufficient to give the full path to your C and/or CXX compiler (depending on which languages you enabled), CMake will detect the rest of your GNU toolchain automatically
cmake -DCMAKE_C_COMPILER:PATH=/opt/toolchain/usr/bin/i586-linux/i586-linux-gcc
-DCMAKE_CXX_COMPILER:PATH=/opt/toolchain/usr/bin/i586-linux/i586-linux-g++ ...
Add the following to your toolchain to skip the compiler tests (because not all options may be passed to it or because your compiler/linker of choice will not produce a valid executable)
set(CMAKE_C_COMPILER_WORKS 1 CACHE INTERNAL "")
set(CMAKE_CXX_COMPILER_WORKS 1 CACHE INTERNAL "")
Or just use the CMakeForceCompiler macros, but the use is "Discouraged. Avoid using this module if possible."
Use configure_file() to put the path into your toolchain file (just make sure to do it before the project() call)
Prefer find_program() if the possible paths of your toolchain are known over setting it from the outside (see e.g. here)
References
CMake FAQ: How do I use a different compiler?
how to specify new gcc path for cmake
CMake Cross Compiling
cmake: problems specifying the compiler (2)
cmake cross-compile with specific linker doesn't pass arguments to armlink
CMake: In which Order are Files parsed (Cache, Toolchain, …)?

I am sorry that I have to revive this but I'm still wondering why I need to resort to hacks like this to propagate variables to the toolchain configuration file:
...
elseif(${OS_FSFW} STREQUAL linux AND TGT_BSP)
if(NOT SOME_VARIABLE_USED_BY_TOOLCHAINFILE)
set(ENV{SOME_VARIABLE_USED_BY_TOOLCHAINFILE} "$ENV{HOME}/raspberrypi/rootfs")
else()
set(ENV{SOME_VARIABLE_USED_BY_TOOLCHAINFILE} "${SOME_VARIABLE_USED_BY_TOOLCHAINFILE}")
endif()
set(CMAKE_TOOLCHAIN_FILE
${CMAKE_CURRENT_SOURCE_DIR}/buildsystem/cmake/CrossCompileConfig.cmake
)
endif()
I mean, it works, but why is the toolchain file unable to handle variables set by the upper CMakeList properly? CMake is even able to print out variables set that way, but doing things like checking their existence does not appear to work unless they are environmental variables.

Related

Issues when building C++ using CMake with Intel oneApi

I my library I use boost's float128 wrapper therefore changing the compiler is not an option.
Following Intel's developer guide I added find_package(IntelDPCPP REQUIRED) to my CMakeLists.txt and ran cmake -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icx -GNinja on the VS 2022 terminal. I get the following error message
Found package configuration file:
C:/Program Files (x86)/Intel/oneAPI/compiler/latest/windows/IntelDPCPP/IntelDPCPPConfig.cmake
but it set IntelDPCPP_FOUND to FALSE so package "IntelDPCPP" is considered
to be NOT FOUND. Reason given by package:
Unsupported compiler family and compiler icx!!
Anyone with a similar issue that can help out?
EDIT: as suggested by #Botje here the output information relevant to this case
IntelDPCPPConfig.cmake(84): string(COMPARE EQUAL ${CMAKE_CXX_COMPILER} nocmplr )
IntelDPCPPConfig.cmake(85): if(nocmplr)
IntelDPCPPConfig.cmake(93): if(NOT x${CMAKE_CXX_COMPILER_ID} STREQUAL xClang AND NOT x${CMAKE_CXX_COMPILER_ID} STREQUAL xIntelLLVM )
IntelDPCPPConfig.cmake(95): set(IntelDPCPP_FOUND False )
This is a known issue, it will be fixed in the OneAPI 2023.1 release.
You can try reversing the order of find_package and project or removing find_package(IntelDPCPP REQUIRED) in CMakeLists.txt. Because CMake identifies and sets up all the compiler-related variables when the project() is called.
Also, you can set the compiler option for the DPC++ compiler in CMakeLists.txt using the below command.
set(CMAKE_CXX_COMPILER dpcpp)

Parameterizing custom CMake toolchain [duplicate]

This question already has answers here:
Check CMake Cache Variable in Toolchain File
(2 answers)
Closed 1 year ago.
I'm writing CMake toolchain files for cross-compilation.
I've got several toolchains, that differ very slightly. I'd like to create a single file for cmake that describes all toolchains and have users to specify them from command line: cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/toolchain.cmake -DTOOLCHAIN_NAME=<name>
However, it seems that TOOLCHAIN_NAME sometimes becomes an empty string.
Here is the example of the toolchain file:
set(tools /opt/toolchains/Custom/toolchains/toolchain)
set(sdk /opt/toolchains/Custom/platforms/)
message(STATUS "toolchain_name ${TOOLCHAIN_NAME}")
if(NOT TOOLCHAIN_NAME)
message(SEND_ERROR "Please specify toolchain name in -DTOOLCHAIN_NAME parameter")
endif()
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR ARM)
# further processing
Then I call CMake:
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain.cmake -DTOOLCHAIN_NAME=proc-os-gnueabi-gcc_6_3
And get following output with errors:
-- toolchain name proc-os-gnueabi-gcc_6_3
-- toolchain name proc-os-gnueabi-gcc_6_3
-- The C compiler identification is GNU 6.3.1
-- The CXX compiler identification is GNU 6.3.1
-- Check for working C compiler: /opt/toolchains/.../bin/arm-os-gnueabi-gcc
CMake Error at /home/user/project/cmake/toolchain.cmake:59 (message):
Please specify toolchain name in -DTOOLCHAIN_NAME parameter
Call Stack (most recent call first):
/home/user/project/build/CMakeFiles/3.15.4/CMakeSystem.cmake:6 (include)
/home/user/project/build/CMakeFiles/CMakeTmp/CMakeLists.txt:2 (project)
CMake Error at /usr/local/share/cmake-3.15/Modules/CMakeTestCCompiler.cmake:44 (try_compile):
Failed to configure test project build system.
Call Stack (most recent call first):
CMakeLists.txt:15 (project)
-- Configuring incomplete, errors occurred!
See also "/home/user/project/build/CMakeFiles/CMakeOutput.log".
See also "/home/user/project/build/CMakeFiles/CMakeError.log".
CMakeOutput.log and CMakeError.log contain messages, saying that the compiler didn't find some libraries.
If I open generated file /home/user/project/build/CMakeFiles/3.15.4/CMakeSystem.cmake I can see the following
set(CMAKE_HOST_SYSTEM "Linux-4.2.0-27-generic")
set(CMAKE_HOST_SYSTEM_NAME "Linux")
set(CMAKE_HOST_SYSTEM_VERSION "4.2.0-27-generic")
set(CMAKE_HOST_SYSTEM_PROCESSOR "x86_64")
include("/home/user/project/cmake/toolchain.cmake")
set(CMAKE_SYSTEM "Generic")
set(CMAKE_SYSTEM_NAME "Generic")
set(CMAKE_SYSTEM_VERSION "")
set(CMAKE_SYSTEM_PROCESSOR "ARM")
set(CMAKE_CROSSCOMPILING "TRUE")
set(CMAKE_SYSTEM_LOADED 1)
According to CMake manual, its -D argument is used to specify cache entries.
I conclude from the output above that supplied toolchain file is processed by CMake at least 3 times. During first two times there were proper cache entries, and at the third run they were absent.
So, how can I avoid code duplication for toolchain files?
UPD. After reading this question I've tried several permutations of CMake command line parameters: specifying source path after toolchain but before options, specifying it after options, explicitly specifying source path and build path with -S and -B options. Nothing has helped.
I conclude from the output above that supplied toolchain file is processed by CMake at least 3 times. First two times there were proper cache entries, and at the third run they were absent.
Yes, this is very good observation. Technically, only the first toolchain invocation at the project() call is guarantee to see the CACHE entries (created with -D parameters to cmake or with set() commands prior to the project() call).
One approach for pass CACHE entries between the different toolchain invocations is storing them in the environment variables:
Example below uses MY_TOOLCHAIN_NAME environment variable as a storage for TOOLCHAIN_NAME CMake variable.
if(DEFINED ENV{MY_TOOLCHAIN_NAME})
# Environment variable is set.
if (TOOLCHAIN_NAME)
# CMake variable is set too.
# It is up to your which one to use.
# Uncomment line below for prefer environment variable to CMake one.
# set(TOOLCHAIN_NAME $ENV{MY_TOOLCHAIN_NAME})
else ()
# CMake variable is not set. Use environment one.
set(TOOLCHAIN_NAME $ENV{MY_TOOLCHAIN_NAME})
endif()
else()
# Environment variable is not set.
if (TOOLCHAIN_NAME)
# But CMake variable is set.
# Store it into the environment and use it.
set(ENV{MY_TOOLCHAIN_NAME} ${TOOLCHAIN_NAME})
else()
# Neither environment nor CMake variable is set.
message(SEND_ERROR "Please specify toolchain name in -DTOOLCHAIN_NAME parameter")
endif()
endif()
If you have several "tuning" variables, you may write a macro for all above steps and apply(call) this macro for every variable you need.

CMake won't build without set_source_files_properties with LANGUAGE CXX

I have a small library that I have made (mostly wrappers for a more obtuse library underneath) which I have been compiling and using no problem in a contained project. I am now using this library in another project and have attempted to change the CMakeLists.txt appropriately (see below).
cmake_minimum_required (VERSION 3.5)
set(project "foobar")
project(${project} LANGUAGES CXX)
set(${project}_VERSION_MAJOR 0)
set(${project}_VERSION_MINOR 1)
add_library(${project} SHARED
./driver/foo.h
./driver/foo.c
./bar.cpp
./bar.hpp)
set_source_files_properties(./driver/foo.c PROPERTIES LANGUAGE CXX)
target_compile_features(${project}
PUBLIC
cxx_std_11)
target_include_directories(${project} PUBLIC ./driver/ .)
set_target_properties(${project} PROPERTIES LINKER_LANGUAGE CXX)
add_executable(bno055-test
./testingProject.cpp
)
target_link_libraries(test ${project})
install(
TARGETS ${project}
RUNTIME DESTINATION bin)
The error I have encountered is with the line set_source_files_properties(./driver/foo.c PROPERTIES LANGUAGE CXX). When it comes to compiling this C file with the C++ flag above I get many warnings of the type clang-8: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]. This is expected, as I am using deprecated methods and should not be compiling this file using the C++ flag. However, whenever I remove this line, or alter it to specify C instead of C++, my entire project no longer builds and fails with error:
CMake Error: Error required internal CMake variable not set, cmake may not be built correctly.
Missing variable is:
CMAKE_C_COMPILE_OBJECT
-- Generating done
CMake Generate step failed. Build files cannot be regenerated correctly.
Makefile:283: recipe for target 'cmake_check_build_system' failed
make: *** [cmake_check_build_system] Error 1
"/usr/bin/make -j4 all" terminated with exit code 2. Build might be incomplete.
I'm not familiar enough with CMake to see why this line of code and error are related like this. Is there some alternative piece of code I should include to create the missing variable CMAKE_C_COMPILE_OBJECT?
I assume that when I initially created this project using Eclipse this line was automatically generated and I am unclear as to why it now creates a problem when I had been compiling these files with the same CMakeLists.txt before (without library linking).
This is with clang#8.0.0 on Ubuntu 16.04.
Any help would be much appreciated! Apologies if I have missed something simple!
Is there some alternative piece of code I should include to create the missing variable CMAKE_C_COMPILE_OBJECT?
No, as the CMake error states, this is an "internal CMake variable" which should be initialized by CMake. It is not something you should have to set yourself. It should be set indirectly when you call project(). However, you only tell CMake you are using C++ (with CXX option) in the project() command. If you want CMake to make use of C and C++, you need to add both:
project(${project} LANGUAGES C CXX)
Even better, CMake enables C and C++ by default, so you can simply do:
project(${project})

Which variable for compiler flags of CMake's ADD_LIBRARY function?

Does exist a variable which contains the compiler flags used in some call to CMake's ADD_LIBRARY function, for example the ones used when we add a module:
ADD_LIBRARY(mylib MODULE mysrc.cpp)
Or, is there a way of getting such flags?
Turning my comments into an answer
There is not a single CMake variable to get the all compiler flags. The problem is that the CMake generator will finally put together the compiler flags (from various CMake variables and properties incl. from depending targets). So you don't have all the flags during configuration step.
I see the following possible problem/solution pairs:
CMake is a cross-platform wrapper around your compiler (that's actually what the C stands for), so no need to extract the compiler flags into an external script
If you just want to add sort of a filter to what is called by CMake you can user set "launcher" variables/properties accordingly e.g. CMAKE_CXX_COMPILER_LAUNCHER or RULE_LAUNCH_LINK
If you want the compiler calls in a machine readable JSON format you could export those by setting CMAKE_EXPORT_COMPILE_COMMANDS
If you just want to see the compiler calls incl. all the flags you could set CMAKE_VERBOSE_MAKEFILE
If you really just need the compiler flags on the output and you don't want CMake to actually compile anything, you could - at least for CMake's Makefile generators - modify CMAKE_CXX_COMPILE_OBJECT and CMAKE_CXX_CREATE_SHARED_MODULE like this:
set(CMAKE_DEPFILE_FLAGS_CXX "")
set(
CMAKE_CXX_COMPILE_OBJECT
"<CMAKE_COMMAND> -E echo <FLAGS>"
)
set(
CMAKE_CXX_CREATE_SHARED_MODULE
"<CMAKE_COMMAND> -E echo <CMAKE_SHARED_MODULE_CXX_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS>"
)
file(WRITE mysrc.cpp "")
add_library(mylib MODULE mysrc.cpp)
References
Is Cmake set variable recursive?
What does the "c" in cmake stand for?
How to use CMAKE_EXPORT_COMPILE_COMMANDS?
Using CMake with GNU Make: How can I see the exact commands?
Retrieve all link flags in CMake

Using compiler prefix command(s) with CMake (distcc, ccache)

There are utilities which use an existing compiler by adding a command as a prefix (so instead of calling cc -c file.c you could call distcc cc -c file.c).
When using CMake the compiler command can be changed, however I ran into problems trying to use distcc, though this would likely apply to any command prefix to the compiler (ccache too).
CMake expects the compiler to be an absolute path,so setting CMAKE_C_COMPILER to /usr/bin/distcc /usr/bin/cc, gives an error:
/usr/bin/distcc /usr/bin/cc
is not a full path to an existing compiler tool.
Setting the compiler to /usr/bin/distcc andCMAKE_C_COMPILER_ARG1 or CMAKE_C_FLAGS to begin with /usr/bin/cc works in some cases, but fails with CHECK_C_SOURCE_COMPILES(checked if there was some way to support this, even prefixing CMAKE_REQUIRED_FLAGS didn't work).
The only way I found to do this is to wrap the commands in a shell script.
#!/bin/sh
exec /usr/bin/distcc /usr/bin/cc "$#"
While this works, It would be nice to be able to use compiler helpers with CMake, without having to go though shell scripts (giving some small overhead when the build system could just use a command prefix).
So my question is:
Can CMake use compiler prefix commands (such as distcc) directly?, without shell script wrappers?
Since CMake 3.4.0 there has been a CMAKE_<LANG>_COMPILER_LAUNCHER variable and corresponding target property <LANG>_COMPILER_LAUNCHER. So if your project is C-only you would do something like:
cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache /path/to/source
CCACHE_PREFIX=distcc make -j`distcc -j`
If you have a C++ project, use -DCMAKE_CXX_COMPILER_LAUNCHER=ccache.
Or, make your CMakeLists.txt smart and use ccache automatically if it can be found:
#-----------------------------------------------------------------------------
# Enable ccache if not already enabled by symlink masquerading and if no other
# CMake compiler launchers are already defined
#-----------------------------------------------------------------------------
find_program(CCACHE_EXECUTABLE ccache)
mark_as_advanced(CCACHE_EXECUTABLE)
if(CCACHE_EXECUTABLE)
foreach(LANG C CXX)
if(NOT DEFINED CMAKE_${LANG}_COMPILER_LAUNCHER AND NOT CMAKE_${LANG}_COMPILER MATCHES ".*/ccache")
message(STATUS "Enabling ccache for ${LANG}")
set(CMAKE_${LANG}_COMPILER_LAUNCHER ${CCACHE_EXECUTABLE} CACHE STRING "")
endif()
endforeach()
endif()
Just as a hint: never use <LANG>_COMPILER_LAUNCHER to cross compile. If <LANG>_COMPILER_LAUNCHER is used together with distcc the absolute compiler path is sent to distcc and the host is not using the cross comping toolchain!
Instead you should use the old school method, just overwrite the compiler path:
export PATH=/usr/lib/distcc:$PATH
It took me hours to find out...