135

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?

2
  • 3
    First of all, CMAKE_CXX_COMPILER, the language is CXX, not CPP Commented Mar 21, 2022 at 7:52
  • See stackoverflow.com/questions/17986118/… for more details on @user7610's comment. Commented Aug 27, 2022 at 17:01

9 Answers 9

201

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

The reason this method should be avoided is because it hardcodes paths and compilers which is something that is usually configured in a package manager such as conan. Conan allows you to detect which compiler is preferable on a given platform and forwards this to cmake. You would make things harder for everyone if you were to hardcode it. That's why option 2 is the preferred way do do it.

Sign up to request clarification or add additional context in comments.

16 Comments

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
The CMake FAQ page has moved here. Please edit.
For "path/to/your/source" are you referring to the CMake source directory or the gcc/g++ compiler source directory?
|
60

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:

  1. 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.

  2. Set CC environment variable (CXX for C++ compiler). CMake checks this variable when selects a default compiler.

  3. (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.

3 Comments

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 Makefiles)
@Martin: You could use add_custom_command and/or add_custom_target. Not sure how this problem is related to the compiler's setting.
5

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 .

5 Comments

How does the developer knows it has to write cmake -DCMAKE_TOOLCHAIN_FILE=iar-toolchain.cmake .. In the traditional way with GNU Make, the developer sees a Makefile he hits make, that's simple. With CMake it looks a bit more complicated :(
You can make a wrapper script to call cmake. I have a script named build.sh at the root of all my projects
I get The CMAKE_FORCE_C_COMPILER macro is deprecated
@nowox It’s true that it’s deprecated. I’ve been seeing these warnings for a while now but when I tried to change it I got the error: "The C compiler XXX is not able to compile a simple test program". At the time I couldn't find a solution so I've been living with the warning. Thanks to you I just looked again and found a workaround, see my edit!
@nowox It's true that CMake takes a different approach than make but also consider that the toolchain is system-dependent, not project-dependent. Said differently, the toolchain specifies how, on the current system, you want the project built. It's not that different from overriding make variables like CC and CXX on the command line to use a different compiler than it would normally use, except even more powerful. You could also define a custom target in the CMakeLists.txt file that invokes CMake recursively if you want Make-like behavior.
5

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.

Comments

4

You can call cmake like this:

cmake -DCMAKE_C_COMPILER=iccarm ...

or

cmake -DCMAKE_CXX_COMPILER=...

Comments

1

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

2 Comments

Do I still need to set CMAKE_C_COMPILER or there is another way? If I do cmake . it still tries to use gcc
@nowox 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. here). Putting this into your CMakeLists.txt would stop your project from being cross-platform.
1

If you don't specify anything, CMake will try to detect a default compiler installed on your system. If you want to specify something else...

The following are not "right" answers. They're just some choices of methods that I think are appropriate for different use-cases.

  • If you're a user building one thing from source to use/install on your system, and you won't do it again for a while, specify CMAKE_<LANG>_COMPILER as a cache variable in the configure command via -D. <LANG> is a language placeholder. See the list of supported languages in the project command docs. You'd do this for every compiler you want to specify, and which is relevant to the languages used by the project(s) being built. Ex. cmake -S . -B build -DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_COMPILER=gcc.

  • If you're a user building several things from source to use/install on your system, and you want to use the same compiler(s) for them all, you can use recognized environment variables to configure them as "implicit" defaults at a system level. Ex. CC, and CXX. They're used to initialize the cache variables mentioned above.

  • If you're writing/using a toolchain file (typically for cross-compiling purposes), use the toolchain file to set CMAKE_<LANG>_COMPILER, and then use the toolchain file via --toolchain in the configure command.

  • If you're writing a project and need easy to reach, shareable or local buildsystem configurations specifying options that are more than just toolchain options (Ex. you use a bunch of compilers/toolchains, different build options, or do CI/CD stuff), I suggest using CMake Presets. This is not mutually exclusive from the mechanisms behind the above use-cases. In a configure preset, you can specify a toolchain file via the toolchainFile member, and for host-system use-cases, you can specify CMAKE_<LANG>_COMPILER in the cacheVariables member.

Comments

0

In Antwane's solution, we are to avoid using set(). A reason to avoid using set(CMAKE_CXX_COMPILER "/path/to/compiler"), etc. is that the output of cmake is misleading, while the results are correct.

Consider linux where the default compiler is gcc (GNU 10.2.1), if I have CMakeLists.txt containing:

set(CMAKE_C_COMPILER   "/path/to/llvm-16.0.0/bin/clang")
set(CMAKE_CXX_COMPILER "/path/to/llvm-16.0.0/bin/clang")

then with cmake 3.26.0, we get unexpected output:

cmake ../src
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
<snip>

but will use the specified clang compiler!

Removing the set() statements and using the other methods provides the expected result:

cmake -D CMAKE_C_COMPILER="/path/to/llvm-16.0.0/bin/clang" -D CMAKE_CXX_COMPILER="/path/to/llvm-16.0.0/bin/clang" ../src
-- The C compiler identification is Clang 16.0.0
-- The CXX compiler identification is Clang 16.0.0
<snip>

Comments

-2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.