CMake won't select the correct C++ compiler [duplicate] - c++

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.

Related

What cmake project command does?

I'm wondering how CMake finds the compiler you want to use.
I found that after I called project, the variable CMAKE_CXX_COMPILER_ID is automatically set to GNU. It's sure that when executing cmake command, I gave -DCMAKE_TOOLCHAIN_FILE flag to tell which toolchain file I wanted to use. And in the toolchain file, I specifies CMAKE_C_COMPILER and CMAKE_CXX_COMPILER as arm-none-eabi-gcc, g++.
However, just setting these variables doesn't mean telling CMake what compiler I'm using right?
According to https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html,
it seems that CMAKE_C_COMPILER or CMAKE_CXX_COMPILER is a so-called cmake-variables, which means CMake can parse the contents and do something based on them.
Does CMake read and understand CMAKE_C_COMPILER and CMAKE_CXX_COMPILER itself, and based on that, CMake automatically sets CMAKE_CXX_COMPILER_ID to GNU or something at the time when project is called?
When CMake first encounters a project() command, the compiler, linker and several properties based on those are "set in stone". Any adjustments before the first project() command can change the compiler cmake chooses, whether you set this via -D command line option, toolchain file or via set() commands in the CMakeLists.txt file. (I strongly recommend not using the last option though, since it limits reuseability.)
If no information was provided, cmake chooses the compiler itself; the exact logic depends on the platform though.
CMAKE_CXX_COMPILER_ID is one of the variables that are set based on the compiler chosen. Basically cmake performs some checks to determine the type of compiler used, the target platform, ect.

Setting Various compilers in CMake for creating a shared library

I am looking to set various compilers for different folders in my project, which should compile to a shared library.
The project structure is as follows -
/Cuda
a.cu
b.cu
c.cu
header.cuh
/SYCL
a.cpp
b.cpp
c.cpp
header.h
main.cpp
test.cpp
All the files under the Cuda folder must be compiled by nvcc, and the files under the SYCL folder by a specific compiler that is present at a path in the system. All the files outside these folders (namely main.cpp and test.cpp) are normal C++ code and use the headers present in these two folders and must be compiled with GCC.
How do I go about writing the CMake for such a project structure(which aims to be a shared lib).
Edit - The project needn't have only one dedicated CMake. My approach was as follows -
Each Folder(Cuda and SYCL) can have their dedicated CmakeLists.txt which would specify the compiler and the various flags to go with it.
A master CMake outside the folder can use the add_subdirectory command. And this is where I get stuck, I am not sure what to do next, how to link these two folders with the main and the test files.
CMake allows one compiler per language, so simply writing this is enough:
cmake_minimum_required(VERSION 3.20)
project(example LANGUAGES CXX CUDA)
add_subdirectory(Cuda)
add_subdirectory(SYCL)
You can separately set the C++ and CUDA compilers by setting CMAKE_CXX_COMPILER and CMAKE_CUDA_COMPILER at the configure command line.
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_COMPILER=g++ -DCMAKE_CUDA_COMPILER=nvcc
Also, I want to clear up this misconception:
Each Folder(Cuda and SYCL) can have their dedicated CmakeLists.txt which would specify the compiler and the various flags to go with it.
The CMakeLists.txt file should not attempt to specify the compiler. It's tricky to do correctly, can't always be done (especially in the add_subdirectory case) and unnecessarily restricts your ability to switch out the compiler. Maybe you have both GCC 10 and 11 installed and want to compare the two.
Similarly, you should not specify flags in the CMakeLists.txt file that aren't absolutely required to build, and you should always check the CMake documentation to see if the flags you're interested in have been abstracted for you. For instance, CMake has special handling of the C++ language standard (via target_compile_features) and CUDA separable compilation (via the CUDA_SEPARABLE_COMPILATION target property).
The best solution, as I have detailed here, is to set optional flags via the *_FLAGS* variables in a preset or toolchain.

How to partially disabling cmake C/C++ custom compiler checking

I am trying to do some crosscompilation using cmake. Some are easy with all the examples on Internet, I managed to crosscompile my library on Linux (x86 and ARM), Windows and Android. But now I would like to do it on a custom platform.
The process I need to achieve:
Sourcing my environment (this destroy all previous bash classic environment)
Compile with cmake
Execute what I want
But Cmake is testing for symbols in my custom C/C++ libraries which make my library unable to compile. The errors I have are that cmake some versions of GLIBCXX and CXXABI (no C issues) but not all of them.
Is there a way to make cmake ok with it ?
EDIT:
I tried using:
set(CMAKE_C_COMPILER_WORKS TRUE)
set(CMAKE_CXX_COMPILER_WORKS TRUE)
And with:
include(CMakeForceCompiler)
...
cmake_force_c_compiler(${ENV_PATH}/bin/${CC})
cmake_force_cxx_compiler(${ENV_PATH}/bin/${CXX})
But cmake is still checking for symbols.
Without having your environment nor the error message it's not easy to tell the actual root cause but here are two of the common causes and respective fixes:
If you don't have a complete toolchain file created for your custom environment - so CMake can't link a simple test program - you can try the relatively new (version 3.6) global CMake variable named CMAKE_TRY_COMPILE_TARGET_TYPE.
So just add the following:
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
Then CMake would just try to build a static library.
To have only the most common GCC compiler variables set and have only some basic checks, try:
SET(CMAKE_SYSTEM_NAME Generic)
See CMake Cross Compiling: Setting up the system and toolchain:
If your target is an embedded system without OS set CMAKE_SYSTEM_NAME to "Generic"
References
CMake AMRCC + custom linker
cmake cross-compile with specific linker doesn't pass arguments to armlink
Commandline:
cmake ... \
-DCMAKE_C_COMPILER_FORCED=TRUE \
-DCMAKE_CXX_COMPILER_FORCED=TRUE
or
CMakeLists.txt
...
set(CMAKE_C_COMPILER_FORCED TRUE)
set(CMAKE_CXX_COMPILER_FORCED TRUE)
...
Works for me.

cmake ExternalProject_Add and setting CMAKE_CXX_COMPILER with project() command

I'm trying to build a multi-project project with cmake, and have used the ExternalProject_Add() command. However, I can't get the CMAKE_CXX_COMPILER option to stick, which causes the following problem: During configure, the external project checks for several compiler capabilities, setting (or not setting, in this case) a necessary flag in a .h file. When I run make, a different compiler is used, and the values in the .h file are incorrect.
I've boiled it down to the following lines of code, which occur at the top of my CMakeLists.txt file for the external project (CGAL):
message("**** CXX Compiler is ${CMAKE_CXX_COMPILER}")
project(CGAL CXX C)
message("**** CXX Compiler is ${CMAKE_CXX_COMPILER}")
When I run this, the first message displays the correct value (the Frameworks compiler installed through XCode). However, the second message shows the wrong compiler (GNU version I've installed with MacPorts). I've looked up the project specifications, but they are quite sparse. They do note that, without including the project() command, an implicit one will be added with the CXX and C languages. Sure enough, when I remove the project() command, both messages report the wrong compiler.
Is there something I don't know about the project() command? Does it search for a CMakeLists.txt file somewhere in the filepath and load it by default? Is there a way I can overload this setting (something short of creating a temporary variable to hold the old value)?
FWIW, setting CMAKE_CACHE_ARGS doesn't work.

Setting default compiler in CMake

I'm using CMake version 2.8 on WinXP SP3. Whenever i run my CMakeLists script by default CMake use Visual Studio 10 compiler. I've tried to do:
SET( CMAKE_CXX_COMPILER "C:/MinGW/bin/g++" )
without success. How can i set MinGW as my default compiler so that i do not have to worry about setting compiler in the CMakeLists?
CMake 3.15 or later supports overriding the default generator by setting the environment variable CMAKE_GENERATOR.
E.g., using PowerShell, set the environment variable in the following way to make MinGW the default generator:
$Env:CMAKE_GENERATOR = 'MinGW Makefiles'
For older CMake versions (< 3.15), CMake uses the newest Visual Studio installation as default generator, unless the generator is explicitly specified upon invoking CMake. This behavior is hard coded and cannot be changed.
As a work-around you can use a batch wrapper script titled cmake.cmd with the following contents:
#cmake.exe -G "MinGW Makefiles" %*
The script should be placed in a directory on the system PATH and should take precedence over the CMake executable cmake.exe.
The script invokes cmake.exe with MinGW as a generator and forwards all other parameters to it.
You only have to set the toolchain/output format once, typically you'd do this upon running cmake for the first time:
cmake -G "MinGW Makefiles" .
Instead of the dot you can use your own parameters (if any) and/or the path to the source.
As an alternative, especially when you're new to CMake, use the GUI version under windows (run cmake-gui without parameters instead of cmake).
Once opened, set your paths and click on "Configure". If there's no compiler set, it will ask you to pick one (otherwise you have to clear the cache to make it reappear).
Updated configuration values will appear in red and it will also allow you to select files and paths using the common Windows dialog boxes.
Once configuration is complete and without errors you can hit "generate" to create your makefiles or project files. To update these later on, you can use cmake-gui again or just use the usual command line version cmake.
With CMake version 3.15 or later, you can set the CMAKE_GENERATOR environment variable to specify the default generator to be used on your system.