Adding support to new compiler vendor in CMake (cross-compiling) - cmake

I want to cross compile an embedded application using Tasking TriCore toolchain and CMake (3.16 is ok).
I started with a complete toolchain file, where all paths to binaries and rules were specified, but then I read this thread and used the Compiler/IAR* scripts as an example (https://github.com/Kitware/CMake/blob/master/Modules/Compiler) to end up with a cleaner way.
I know that by default in C/CXX projects, CMake tries to compile a simple test program CMakeCCompilerId.c to detect the compiler id vendor and version. With IAR-DetermineCompiler.cmake I created my own Tasking-DetermineCompiler.cmake which would used symbols defined by the ctc compiler. But even if this file is executed by CMake (syntax errors in there are detected) it does not seem to have any impact on the generated test source file.
This means that the default mechanism for compiler identification does not work for my compiler and I get the message "The C compiler identification is unknown". On my toolchain file I need to force the identification:
set(CMAKE_SYSTEM_NAME Generic)
# will load Platform/Generic.cmake under CMAKE_MODULE_PATH
# Normalize, convert Windows backslashes to forward slashes or CMake will crash
get_filename_component(TASKING_ROOT_PATH "$ENV{TASKING_TRICORE_PATH}" ABSOLUTE)
# Specify the compiler
# will load Platform/Generic-ctc.cmake CMAKE_MODULE_PATH
foreach (_prefix C CXX)
if ("${CMAKE_${_prefix}_COMPILER}" STREQUAL "")
set(CMAKE_${_prefix}_COMPILER "${TASKING_ROOT_PATH}/bin/cctc.exe")
endif()
# This is used only if we skip auto compiler identification
set(CMAKE_${_prefix}_COMPILER_ID "Tasking")
set(CMAKE_${_prefix}_COMPILER_VERSION "6.3.1r1")
# Skip compiler ID identification: use "Tasking"
set(CMAKE_${_prefix}_COMPILER_ID_RUN TRUE)
set(CMAKE_${_prefix}_COMPILER_FORCED TRUE)
endforeach()
# will load Compiler/Tasking.cmake under CMAKE_MODULE_PATH
# and Compiler/Tasking-FindBinUtils.cmake as well
So far, this is working but I would like to understand whether it is possible to use the default CMake way of identifying the compiler id and version. I could not find how to add other vendors in the documentation!
Many thanks

Related

CMake automatically adds -MD -MT and -MF options [duplicate]

I would like to use the IAR compiler. I noticed CMake has already have a bunch of files about this compiler:
https://github.com/jevinskie/cmake/blob/master/Modules/Compiler/IAR.cmake
From what I read the common solution is to specify manually ALL the toolchain in my CMakeLists.txt:
set(CMAKE_C_COMPILER iccarm)
set(CMAKE_CPP_COMPILER iccarm)
How CMake can link these definitions with `Modules/Compiler/IAR.cmake"?
I thought I would just have to do
include("Modules/Compiler/IAR.cmake")
What is the correct way to specify my IAR compiler?
When I do
cmake .
It still tries to use gcc instead of my IAR compiler. Why?
To select a specific compiler, you have several solutions, as exaplained in CMake wiki:
Method 1: use environment variables
For C and C++, set the CC and CXX environment variables. This method is not guaranteed to work for all generators. (Specifically, if you are trying to set Xcode's GCC_VERSION, this method confuses Xcode.)
For example:
CC=gcc-4.2 CXX=/usr/bin/g++-4.2 cmake -G "Your Generator" path/to/your/source
Method 2: use cmake -D
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path on the command-line using cmake -D.
For example:
cmake -G "Your Generator" -D CMAKE_C_COMPILER=gcc-4.2 -D CMAKE_CXX_COMPILER=g++-4.2 path/to/your/source
Method 3 (avoid): use set()
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path in a list file using set(). This must be done before any language is set (ie: before any project() or enable_language() command).
For example:
set(CMAKE_C_COMPILER "gcc-4.2")
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.2")
project("YourProjectName")
The wiki doesn't provide reason why 3rd method should be avoided...
I see more and more people who set CMAKE_C_COMPILER and other compiler-related variables in the CMakeLists.txt after the project call and wonder why this approach breaks sometimes.
What happens actually
When CMake executes the project() call, it looks for a default compiler executable and determines the way for use it: default compiler flags, default linker flags, compile features, etc.
And CMake stores path to that default compiler executable in the CMAKE_C_COMPILER variable.
When one sets CMAKE_C_COMPILER variable after the project() call, this only changes the compiler executable: default flags, features all remains set for the default compiler.
AS RESULT: When the project is built, a build system calls the project-specified compiler executable but with parameters suitable for the default compiler.
As one could guess, this approach would work only when one replaces a default compiler with a highly compatible one. E.g. replacement of gcc with clang could work sometimes.
This approach will never work for replacement of cl compiler (used in Visual Studio) with gcc one. Nor this will work when replacing a native compiler with a cross-compiler.
What to do
Never set a compiler in CMakeLists.txt.
If you want, e.g., to use clang instead of defaulted gcc, then either:
Pass -DCMAKE_C_COMPILER=<compiler> to cmake when configure the project. That way CMake will use this compiler instead of default one and on the project() call it will adjust all flags for the specified compiler.
Set CC environment variable (CXX for C++ compiler). CMake checks this variable when selects a default compiler.
(Only in rare cases) Set CMAKE_C_COMPILER variable before the project() call. This approach is similar to the first one, but makes the project less flexible.
If the ways above do not work
If on setting CMAKE_C_COMPILER in the command line CMake errors that a compiler cannot "compile a simple project", then something wrong in your environment.. or you specify a compiler incompatible for chosen generator or platform.
Examples:
Visual Studio generators work with cl compiler but cannot work with gcc.
A MinGW compiler usually requires MinGW Makefiles generator.
Incompatible generator cannot be fixed in CMakeLists.txt. One need to pass the proper -G option to the cmake executable (or select the proper generator in CMake GUI).
Cross-compiling
Cross-compiling usually requires setting CMAKE_SYSTEM_NAME variable, and this setting should normally be done in the toolchain file. That toolchain file is also responsible for set a compiler.
Setting CMAKE_SYSTEM_NAME in the CMakeLists.txt is almost always an error.
You need to create a toolchain file, and use the CmakeForceCompiler module.
Here is an example toolchain file for bare-metal ARM development with IAR:
include(CMakeForceCompiler)
set(CMAKE_SYSTEM_NAME Generic) # Or name of your OS if you have one
set(CMAKE_SYSTEM_PROCESSOR arm) # Or whatever
set(CMAKE_CROSSCOMPILING 1)
set(CMAKE_C_COMPILER iccarm) # Change the arm suffix if appropriate
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # Required to make the previous line work for a target that requires a custom linker file
The last line is necessary because CMake will try to compile a test program with the compiler to make sure it works and to get some version information from preprocessor defines. Without this line, CMake will use add_executable() for the test program, and you will get the error "The C compiler "XXX" is not able to compile a simple test program." This is because the test program fails to link, as it doesn't have your custom linker file (I'm assuming bare-metal development since this is what IAR is usually used for). This line tells CMake to use add_library() instead, which makes the test succeed without the linker file. Source of this workaround: this CMake mailing list post.
Then, assuming that your toolchain file is named iar-toolchain.cmake, invoke CMake like this:
cmake -DCMAKE_TOOLCHAIN_FILE=iar-toolchain.cmake .
You can call cmake like this:
cmake -DCMAKE_C_COMPILER=iccarm ...
or
cmake -DCMAKE_CXX_COMPILER=...
If you don't want to use your PC's standard compiler, you have to give CMake the path to the compiler. You do this via environment variables, a toolchain file or direct definitions in the CMake command line (see e.g. CMake Error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found).
Putting the compiler's name/path into your CMakeLists.txt would stop your project from being cross-platform.
CMake does check for the compiler ids by compiling special C/C++ files. So no need to manually include from Module/Compiler or Module/Platform.
This will be automatically done by CMake based on its compiler and platform checks.
References
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
CMake GitLab Commit: Add support files for C, C++ and ASM for the IAR toolchain.
IAR Systems recently published a basic CMake tutorial with examples under their GitHub profile.
I like the the idea of a generic toolchain file which works seamlessly for both Windows and Linux compilers using find_program().
The following snippet will be used for when using C and can be used similarly for CXX:
# IAR C Compiler
find_program(CMAKE_C_COMPILER
NAMES icc${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{ProgramFiles}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
For ASM, I initially got puzzled with the NAMES but then I realized that the toolchain file was made that way for working with old Assemblers shipped with XLINK:
find_program(CMAKE_ASM_COMPILER
NAMES iasm${CMAKE_SYSTEM_PROCESSOR} a${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{PROGRAMFILES}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
Also, take a look at the full toolchain file. It will work automatically for "Arm" when the tools are installed on their default locations, otherwise it is just about updating the TOOLKIT variable and the compilers for all the supported languages should adjust automatically.
If your wanting to specify a compiler in cmake then just do ...
cmake_minimum_required(VERSION 3.22)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
Options 1 is only used if you want to specify what compiler you want to use as default for everything that you might compile on your computer. And I don't even think it would work on windows.
Option 2 would be used if you only want to use a different temporarily.
Option 3 is used if that's the compiler that should be used for that particular project. Also option 3 would be the most cross compatible.

build cmake subproject with differents toolchain [duplicate]

I have embedded project using cross compiler. I would like to introduce Google test, compiled with native GCC compiler. Additionally build some unit test targets with CTC compiler.
Briefly:
I have 3 different targets and compile them with 3 different compilers. How to express it in CMakeLists.txt? I Tried SET_TARGET_PROPERTIES;
but it seems impossible to set CXX variable with this command!
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.
Example usage:
use_host_compiler()
add_executable(foo foo.c) # Compiled with your host (computer)'s compiler.
use_native_compiler()
add_executable(bar bar.c) # Compiled with your native compiler (e.g. `avr-gcc`).
There is no proper way to change compiler for individual target.
According to cmake manual "Once set, you can not change this variable". This is about CMAKE_<LANG>_COMPILER.
The solution suggested by AnthonyD973 does not seem to work, which is sad of course. The ability to use several compilers in a project without custom_command things is very useful.
One solution (that I haven't tried yet) is to use
set_target_properties(your_target CXX_COMPILER_LAUNCHER foo_wrapper)
Then make foo_wrapper a script that just drops the first argument (which will be the default compiler, e.g. c++) and then calls the compiler you want.
There's also CXX_LINKER_LAUNCHER and the same for C_....
CMake is a make file generator. It generates a file that you can then use to build. If you want to more than one target platform, you need to run CMake multiple times with different generators.
So what you want to do is not possible in CMake, but with CMake: You can create a shell script that invokes CMake multiple times.

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

How to specify a compiler in CMake?

I would like to use the IAR compiler. I noticed CMake has already have a bunch of files about this compiler:
https://github.com/jevinskie/cmake/blob/master/Modules/Compiler/IAR.cmake
From what I read the common solution is to specify manually ALL the toolchain in my CMakeLists.txt:
set(CMAKE_C_COMPILER iccarm)
set(CMAKE_CPP_COMPILER iccarm)
How CMake can link these definitions with `Modules/Compiler/IAR.cmake"?
I thought I would just have to do
include("Modules/Compiler/IAR.cmake")
What is the correct way to specify my IAR compiler?
When I do
cmake .
It still tries to use gcc instead of my IAR compiler. Why?
To select a specific compiler, you have several solutions, as exaplained in CMake wiki:
Method 1: use environment variables
For C and C++, set the CC and CXX environment variables. This method is not guaranteed to work for all generators. (Specifically, if you are trying to set Xcode's GCC_VERSION, this method confuses Xcode.)
For example:
CC=gcc-4.2 CXX=/usr/bin/g++-4.2 cmake -G "Your Generator" path/to/your/source
Method 2: use cmake -D
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path on the command-line using cmake -D.
For example:
cmake -G "Your Generator" -D CMAKE_C_COMPILER=gcc-4.2 -D CMAKE_CXX_COMPILER=g++-4.2 path/to/your/source
Method 3 (avoid): use set()
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path in a list file using set(). This must be done before any language is set (ie: before any project() or enable_language() command).
For example:
set(CMAKE_C_COMPILER "gcc-4.2")
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.2")
project("YourProjectName")
The wiki doesn't provide reason why 3rd method should be avoided...
I see more and more people who set CMAKE_C_COMPILER and other compiler-related variables in the CMakeLists.txt after the project call and wonder why this approach breaks sometimes.
What happens actually
When CMake executes the project() call, it looks for a default compiler executable and determines the way for use it: default compiler flags, default linker flags, compile features, etc.
And CMake stores path to that default compiler executable in the CMAKE_C_COMPILER variable.
When one sets CMAKE_C_COMPILER variable after the project() call, this only changes the compiler executable: default flags, features all remains set for the default compiler.
AS RESULT: When the project is built, a build system calls the project-specified compiler executable but with parameters suitable for the default compiler.
As one could guess, this approach would work only when one replaces a default compiler with a highly compatible one. E.g. replacement of gcc with clang could work sometimes.
This approach will never work for replacement of cl compiler (used in Visual Studio) with gcc one. Nor this will work when replacing a native compiler with a cross-compiler.
What to do
Never set a compiler in CMakeLists.txt.
If you want, e.g., to use clang instead of defaulted gcc, then either:
Pass -DCMAKE_C_COMPILER=<compiler> to cmake when configure the project. That way CMake will use this compiler instead of default one and on the project() call it will adjust all flags for the specified compiler.
Set CC environment variable (CXX for C++ compiler). CMake checks this variable when selects a default compiler.
(Only in rare cases) Set CMAKE_C_COMPILER variable before the project() call. This approach is similar to the first one, but makes the project less flexible.
If the ways above do not work
If on setting CMAKE_C_COMPILER in the command line CMake errors that a compiler cannot "compile a simple project", then something wrong in your environment.. or you specify a compiler incompatible for chosen generator or platform.
Examples:
Visual Studio generators work with cl compiler but cannot work with gcc.
A MinGW compiler usually requires MinGW Makefiles generator.
Incompatible generator cannot be fixed in CMakeLists.txt. One need to pass the proper -G option to the cmake executable (or select the proper generator in CMake GUI).
Cross-compiling
Cross-compiling usually requires setting CMAKE_SYSTEM_NAME variable, and this setting should normally be done in the toolchain file. That toolchain file is also responsible for set a compiler.
Setting CMAKE_SYSTEM_NAME in the CMakeLists.txt is almost always an error.
You need to create a toolchain file, and use the CmakeForceCompiler module.
Here is an example toolchain file for bare-metal ARM development with IAR:
include(CMakeForceCompiler)
set(CMAKE_SYSTEM_NAME Generic) # Or name of your OS if you have one
set(CMAKE_SYSTEM_PROCESSOR arm) # Or whatever
set(CMAKE_CROSSCOMPILING 1)
set(CMAKE_C_COMPILER iccarm) # Change the arm suffix if appropriate
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # Required to make the previous line work for a target that requires a custom linker file
The last line is necessary because CMake will try to compile a test program with the compiler to make sure it works and to get some version information from preprocessor defines. Without this line, CMake will use add_executable() for the test program, and you will get the error "The C compiler "XXX" is not able to compile a simple test program." This is because the test program fails to link, as it doesn't have your custom linker file (I'm assuming bare-metal development since this is what IAR is usually used for). This line tells CMake to use add_library() instead, which makes the test succeed without the linker file. Source of this workaround: this CMake mailing list post.
Then, assuming that your toolchain file is named iar-toolchain.cmake, invoke CMake like this:
cmake -DCMAKE_TOOLCHAIN_FILE=iar-toolchain.cmake .
You can call cmake like this:
cmake -DCMAKE_C_COMPILER=iccarm ...
or
cmake -DCMAKE_CXX_COMPILER=...
If you don't want to use your PC's standard compiler, you have to give CMake the path to the compiler. You do this via environment variables, a toolchain file or direct definitions in the CMake command line (see e.g. CMake Error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found).
Putting the compiler's name/path into your CMakeLists.txt would stop your project from being cross-platform.
CMake does check for the compiler ids by compiling special C/C++ files. So no need to manually include from Module/Compiler or Module/Platform.
This will be automatically done by CMake based on its compiler and platform checks.
References
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
CMake GitLab Commit: Add support files for C, C++ and ASM for the IAR toolchain.
IAR Systems recently published a basic CMake tutorial with examples under their GitHub profile.
I like the the idea of a generic toolchain file which works seamlessly for both Windows and Linux compilers using find_program().
The following snippet will be used for when using C and can be used similarly for CXX:
# IAR C Compiler
find_program(CMAKE_C_COMPILER
NAMES icc${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{ProgramFiles}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
For ASM, I initially got puzzled with the NAMES but then I realized that the toolchain file was made that way for working with old Assemblers shipped with XLINK:
find_program(CMAKE_ASM_COMPILER
NAMES iasm${CMAKE_SYSTEM_PROCESSOR} a${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{PROGRAMFILES}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
Also, take a look at the full toolchain file. It will work automatically for "Arm" when the tools are installed on their default locations, otherwise it is just about updating the TOOLKIT variable and the compilers for all the supported languages should adjust automatically.
If your wanting to specify a compiler in cmake then just do ...
cmake_minimum_required(VERSION 3.22)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
Options 1 is only used if you want to specify what compiler you want to use as default for everything that you might compile on your computer. And I don't even think it would work on windows.
Option 2 would be used if you only want to use a different temporarily.
Option 3 is used if that's the compiler that should be used for that particular project. Also option 3 would be the most cross compatible.

CMake: how to change compiler for individual target

I have embedded project using cross compiler. I would like to introduce Google test, compiled with native GCC compiler. Additionally build some unit test targets with CTC compiler.
Briefly:
I have 3 different targets and compile them with 3 different compilers. How to express it in CMakeLists.txt? I Tried SET_TARGET_PROPERTIES;
but it seems impossible to set CXX variable with this command!
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.
Example usage:
use_host_compiler()
add_executable(foo foo.c) # Compiled with your host (computer)'s compiler.
use_native_compiler()
add_executable(bar bar.c) # Compiled with your native compiler (e.g. `avr-gcc`).
There is no proper way to change compiler for individual target.
According to cmake manual "Once set, you can not change this variable". This is about CMAKE_<LANG>_COMPILER.
The solution suggested by AnthonyD973 does not seem to work, which is sad of course. The ability to use several compilers in a project without custom_command things is very useful.
One solution (that I haven't tried yet) is to use
set_target_properties(your_target CXX_COMPILER_LAUNCHER foo_wrapper)
Then make foo_wrapper a script that just drops the first argument (which will be the default compiler, e.g. c++) and then calls the compiler you want.
There's also CXX_LINKER_LAUNCHER and the same for C_....
CMake is a make file generator. It generates a file that you can then use to build. If you want to more than one target platform, you need to run CMake multiple times with different generators.
So what you want to do is not possible in CMake, but with CMake: You can create a shell script that invokes CMake multiple times.