CMake -DCMAKE_CXX_COMPILER works but SET(CMAKE_CXX_COMPILER . ) is ignored?
Been messing around trying to set the CPP compiler to g++ using the following line in my toolchain file.
# specify the cross compiler SET(CMAKE_CXX_COMPILER:FILEPATH /opt/qnx641/host/linux/x86/usr/bin/arm-unknown-nto-qnx6.4.0-g++)
SET(CMAKE_C_COMPILER:FILEPATH /junk/gcc)
Okay, overkill, but this way I’m definitely sure CMake isn’t doing any funny inferences from the path and command structure I was passing in. I’d gone down this route by looking at the CMake Cross Compiling page where it talks about toolchain files. So read around a bit and decided to do it from the command line as this QA thread showed that the command line -D would override cached vars. This worked! So my question is, does anyone know why when I do not use the -D command line option how CMake finds my cross-compiler path and exename but ignores the SET(CMAKE_CXX_COMPILER. line in my toolchain. I have, before each build deleted the build directory and the CMakeCache.txt file. For completeness the command line I’m using is
rm CMakeCache.txt; rm -fr build; cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=./build -DMAKE_TOOLCHAIN_FILE=/path/to/toolchain-file.cmake -C CMakeCrossCompile-TryRunResult-qnx-armle.cmake .
The file CMakeCrossCompile-TryRunResult-qnx-armle.cmake does not contain CMAKE_CXX_COMPILER . Any clue to what’s giong on much appreciated! I also used the -LA command line option to see the cached variables being used and its definitely not getting the right value from the SET command when the command linne -D. is not present. EDIT: I found the following CMake Support Request that recommends using environment variables, found via this SO thread. It seems that using the variables in the ToolChain file might be too late in the process? Strange, because it shows this method in the guide linked to above. Can any confirm/deny this? Thanks.
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")
8 Answers 8
To select a specific compiler, you have several solutions, as explained 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 don’t understand why the third method should be avoided. With the first method, the user has to know the compiler he wants to use which shouldn’t be the case because that project only builds with the specified compiler. With the second target the user has to write a very long line to configure the project. I rather prefer to put the command of method 2 in a bash file. This is very confusing to me.
3d method has disadvantage that it hardcodes not only a compiler, but also a path to it. Or required from a user to have compiler under PATH . In any case user should be aware which compiler is used.
you can pass the name of the compiler, without setting the full path to it. In that case, it is searched in the PATH env variable
For «path/to/your/source» are you referring to the CMake source directory or the gcc/g++ compiler source directory?
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= 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.
- 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.
Good explanation for the question. BTW, I think this reveals an issue with the CMake language. You have to know how their algorithm works behind commands, i.e., it is does not follow a declarative rather than a procedural model.
What if I have to declare a complete custom command with all options? (Yes, this is the territory where make is easier. But CLion doesn’t like Makefile s)
@Martin: You could use add_custom_command and/or add_custom_target. Not sure how this problem is related to the compiler’s setting.
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 .