I just had the same issue right now, but the other answer didn't help me. I'm also cross-compiling, and I need some utility programs to be compiled with GCC, but my core code to be compiled with avr-gcc.
Basically, if you have a CMakeLists.txt
, and you want all targets in this file to be compiled with another compiler, you can just set the variables by hand.
Define these macros somewhere:
macro(use_host_compiler)
if (${CURRENT_COMPILER} STREQUAL "NATIVE")
# Save current native flags
set(NATIVE_C_FLAGS ${CMAKE_C_FLAGS} CACHE STRING "GCC flags for the native compiler." FORCE)
# Change compiler
set(CMAKE_SYSTEM_NAME ${CMAKE_HOST_SYSTEM_NAME})
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR})
set(CMAKE_C_COMPILER ${HOST_C_COMPILER})
set(CMAKE_C_FLAGS ${HOST_C_FLAGS})
set(CURRENT_COMPILER "HOST" CACHE STRING "Which compiler we are using." FORCE)
endif()
endmacro()
macro(use_native_compiler)
if (CMAKE_CROSSCOMPILING AND ${CURRENT_COMPILER} STREQUAL "HOST")
# Save current host flags
set(HOST_C_FLAGS ${CMAKE_C_FLAGS} CACHE STRING "GCC flags for the host compiler." FORCE)
# Change compiler
set(CMAKE_SYSTEM_NAME ${NATIVE_SYSTEM_NAME})
set(CMAKE_SYSTEM_PROCESSOR ${NATIVE_SYSTEM_PROCESSOR})
set(CMAKE_C_COMPILER ${NATIVE_C_COMPILER})
set(CMAKE_C_FLAGS ${NATIVE_C_FLAGS})
set(CURRENT_COMPILER "NATIVE" CACHE STRING "Which compiler we are using." FORCE)
endif()
endmacro()
At the very beginning of your CMakeLists.txt script (or in a toolchain file), set the following variables according to what you need:
CURRENT_COMPILER
HOST_C_COMPILER
HOST_C_FLAGS
NATIVE_SYSTEM_NAME
NATIVE_C_COMPILER
NATIVE_C_FLAGS
The idea is that CMAKE_C_COMPILER
(and company) is a variable like any other, so setting it inside a certain scope will only leave it changed within that scope.
Caveat: Changing CMAKE_C_COMPILER
is a "hack" and isn't officially supported by CMake. That's because the CMake documentation on the compiler variable says "Once set, you can not change this variable". However, @ChrisB found out that, in practice and with the current versions of CMake (~3.25), this hack only works when all the targets in the same directory use the same compiler, at least when using CMake's Unix Makefiles
generator. This means, if you want to have targets that use different compilers, these targets need to be in different subdirectories, using add_subdirectory
. See @ChrisB's answer. I wouldn't be surprised if this hack works only with some CMake Generators and fails with others (say, works with Unix Makefiles
and Ninja
but not with Visual Studio 17 2022
or something like that).
Example usage:
# src/host/CMakeLists.txt
use_host_compiler()
add_executable(foo foo.c) # Compiled with your host (computer)'s compiler.
# src/native/CMakeLists.txt
use_native_compiler()
add_executable(bar bar.c) # Compiled with your native compiler (e.g. `avr-gcc`).
NOTE: Another approach, which requires more work but which is guaranteed to work well no matter what, and is good practice for projects of all sizes, is to call CMake multiple times on the project, once for each compiler, for example using a toolchain file to specify the compiler and flags for each build. That doesn't necessarily mean duplicating the CMake project and scripts. You can just ensure that your CMake build builds only the targets relevant for the current compiler.
For example, if you need to build a code generator that runs on the host platform (e.g. x64) that will generate code for an embedded platform (like was the case for me back then), then first invoke CMake with the host toolchain file (e.g. the x64 one), making sure that only the code generator gets built. Then, once that CMake build completes, make another CMake build in a new build directory with the embedded platform toolchain file (e.g. the one that uses avr-gcc), importing the code generator of the host platform that you just built. You can then write something like a shell script that makes both builds for you, unless you prefer simply writing the commands for building both of them in your project's README.md
.
ExternalProject
or to detect the toolchain and configure only the relevant artifacts, usingfind_package
andexport
to import things built by a different toolchain. CMake builds only support one compiler per language per configuration. Unless and until CMake's model changes, anything else is a brittle hack. – Aztec