Using CMake with multiple compilers for the same language - c++

It seems like CMake is fairly entrenched in its view that there should be one, and only one, CMAKE_CXX_COMPILER for all C++ source files. I can't find a way to override this on a per-target basis. This makes a mix of host-and-cross compiling in a single CMakeLists.txt very difficult with the built-in CMake facilities.
So, my question is: what's the best way to use multiple compilers for the same language (i.e. C++)?

It's impossible to do this with CMake.
CMake only keeps one set of compiler properties which is shared by all targets in a CMakeLists.txt file. If you want to use two compilers, you need to run CMake twice. This is even true for e.g. building 32bit and 64bit binaries from the same compiler toolchain.
The quick-and-dirty way around this is using custom commands. But then you end up with what are basically glorified shell-scripts, which is probably not what you want.
The clean solution is: Don't put them in the same CMakeLists.txt! You can't link between different architectures anyway, so there is no need for them to be in the same file. You may reduce redundancies by refactoring common parts of the CMake scripts into separate files and include() them.
The main disadvantage here is that you lose the ability to build with a single command, but you can solve that by writing a wrapper in your favorite scripting language that takes care of calling the different CMake-makefiles.

You might want to look at ExternalProject:
http://www.kitware.com/media/html/BuildingExternalProjectsWithCMake2.8.html

Not impossible as the top answer suggests. I have the same problem as OP. I have some sources for cross compiling for a raspberry pi pico, and then some unit tests that I am running on my host system.
To make this work, I'm using the very shameful "set" to override the compiler in the CMakeLists.txt for my test folder. Works great.
if(DEFINED ENV{HOST_CXX_COMPILER})
set(CMAKE_CXX_COMPILER $ENV{HOST_CXX_COMPILER})
else()
set(CMAKE_CXX_COMPILER "g++")
endif()
set(CMAKE_CXX_FLAGS "")
The cmake devs/community seems very against using set to change the compiler since for some reason. They assume that you need to use one compiler for the entire project which is an incorrect assumption for embedded systems projects.
My solution above works, and fits the philosophy I think. Users can still change their chosen compiler via environment variables, if it's not set then I do assume g++. set only changes variables for the current scope, so this doesn't affect the rest of the project.

To extend #Bill Hoffman's answer:
Build your project as a super-build, by using some kind of template like the one here https://github.com/Sarcasm/cmake-superbuild
which will configure both the dependencies and your project as an ExternalProject (standalone cmake configure/build/install environment).

Related

AHow to use different compilers for projects with CMake? [duplicate]

It seems like CMake is fairly entrenched in its view that there should be one, and only one, CMAKE_CXX_COMPILER for all C++ source files. I can't find a way to override this on a per-target basis. This makes a mix of host-and-cross compiling in a single CMakeLists.txt very difficult with the built-in CMake facilities.
So, my question is: what's the best way to use multiple compilers for the same language (i.e. C++)?
It's impossible to do this with CMake.
CMake only keeps one set of compiler properties which is shared by all targets in a CMakeLists.txt file. If you want to use two compilers, you need to run CMake twice. This is even true for e.g. building 32bit and 64bit binaries from the same compiler toolchain.
The quick-and-dirty way around this is using custom commands. But then you end up with what are basically glorified shell-scripts, which is probably not what you want.
The clean solution is: Don't put them in the same CMakeLists.txt! You can't link between different architectures anyway, so there is no need for them to be in the same file. You may reduce redundancies by refactoring common parts of the CMake scripts into separate files and include() them.
The main disadvantage here is that you lose the ability to build with a single command, but you can solve that by writing a wrapper in your favorite scripting language that takes care of calling the different CMake-makefiles.
You might want to look at ExternalProject:
http://www.kitware.com/media/html/BuildingExternalProjectsWithCMake2.8.html
Not impossible as the top answer suggests. I have the same problem as OP. I have some sources for cross compiling for a raspberry pi pico, and then some unit tests that I am running on my host system.
To make this work, I'm using the very shameful "set" to override the compiler in the CMakeLists.txt for my test folder. Works great.
if(DEFINED ENV{HOST_CXX_COMPILER})
set(CMAKE_CXX_COMPILER $ENV{HOST_CXX_COMPILER})
else()
set(CMAKE_CXX_COMPILER "g++")
endif()
set(CMAKE_CXX_FLAGS "")
The cmake devs/community seems very against using set to change the compiler since for some reason. They assume that you need to use one compiler for the entire project which is an incorrect assumption for embedded systems projects.
My solution above works, and fits the philosophy I think. Users can still change their chosen compiler via environment variables, if it's not set then I do assume g++. set only changes variables for the current scope, so this doesn't affect the rest of the project.
To extend #Bill Hoffman's answer:
Build your project as a super-build, by using some kind of template like the one here https://github.com/Sarcasm/cmake-superbuild
which will configure both the dependencies and your project as an ExternalProject (standalone cmake configure/build/install environment).

How should SSE flags be added with modern CMake?

I'm trying to follow the guidelines for modern cmake >= 3.0 at least, where I don't assume anything about the compiler and use target based commands.
See: https://www.youtube.com/watch?v=bsXLMQ6WgIk
or search "effective cmake".
So, how then, when my target depends on SSE 4.1, should I be declaring this information?
I was actually expecting FindSSE to include an interface target with no source but carries it's own appropriate linker flags. But it only states if SSE versions are present on the host.
This varies by platform, so like:
/arch:sse4.1 vs -msse4.1
So am I missing something internal to cmake that handles this setting for different compilers or do I just need to check the platform and set target_compile_options accordingly?
Based on lubgr's reply, I devised this:
add_library(sse4_1 INTERFACE IMPORTED)
if(MSVC)
target_compile_options(sse4_1 INTERFACE /arch:SSE4.1)
else()
target_compile_options(sse4_1 INTERFACE -msse4.1)
endif()
For global flags, that are used by all targets in the project it is perfectly fine to still use the non-target based approach, eg. by calling add_compile_options.
For maintainability reasons, you should do this in one place only, preferably close to the root CMakeLists.txt.
This saves you from having to repeat the same target dependency everywhere throughout the project. Note though that this relies somewhat on programmer discipline: As soon as you have singular targets that need different flags or you start introducing multiple places where the global flags are being set, this can quickly turn into a maintenance nightmare.

How do I use CMAKE for a custom compiler and custom OS?

I want to use CMAKE for a project that is very big, written in C++. But this project is not using a "modern compiler" nor a popular compiler, and is for a unique OS that, for practical purposes, let say I made it so I have access to almost all of its source code (but I can't modify it, I only have modify access to my project). Lets call this OS, OSreally.
My project can be compiled Windows and can run in Windows and OSreally.
What I need to do to implement CMAKE for this project? So I can get all of the CMAKE "benefits".
In a sense, your question isn't terribly hard to answer. CMake is extremely extensible, and almost everything can be changed. For example, to use your custom compiler, you could SET(CMAKE_CXX_COMPILER /path/to/compiler) in your top level build file. However, even though a few more hacks like this will get you on your way to compiling code for your strange OS, there may also be issues with various system functions and the standard library.
If no one has implemented the standard library on your OS, you may need to spend some time building it and possibly modifying it to fit with whatever is different on the system. The same goes for basically any library you would want to import. Regardless, if you have a conforming C++ compiler for the system, the proper path would be to simply set the above variable in your CMakeLists.txt and fix any problems that arise as you encounter them.
The steps I need to do to use CMake with a custom compiler and custom OS is create a new CMake Toolchain configuration file. We can treat our custom OS as a embedded system.
After configuring all the options in the Toolchain file, CMake should work with a custom compiler/OS.

CMake 3.8+: Setting different compiler flags for projects that include both .cpp (C++) and .cu (CUDA) files

I have a CMake project which includes a single target that includes both C++(.cpp) and CUDA C++(.cu) files. However I have some questions which I failed to address.
At the top of my CMakeLists.txt I have:
project(my-project CUDA CXX)
For the ones confused: Yes, CMake 3.8 makes CUDA C++ an intrinsically supported language. So there is no longer need to use things like cuda_add_executable() and sorcery like that. Everything works just fine by using standard and native CMake commands. Also, apparently you can set more than one languages in CMake's project() command. See: https://devblogs.nvidia.com/parallelforall/building-cuda-applications-cmake/
My problem now is that I want to set different compiler flags for the different compilers in a CMake way (i.e., with the target_compile_options() command). The first solution I can think of is:
add_compile_options(my_target
PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-Wsign-conversion>
$<$<COMPILE_LANGUAGE:CUDA>:-arch=compute_30>
)
However, I find this code a bit ugly. The other option I can think of would be to create two separate targets, one for the .cpp files and one for the .cu files. Then I will be able to set the compiler options separately for the GCC and NVCC targets.
Both of the above solutions seem to work, but I am trying to figure out which is the better way. Any suggestions? Pros and cons?
I am also having trouble with the first solution when I want to include multiple compiler flags. What is the proper way of splitting them? Just use space between the different flags? And what if I want to span the expression on multiple lines? If I try to do so in any way, CMake gives me syntax errors.
Finally, I want to avoid manually altering CMAKE_CXX_FLAGS and CMAKE_CUDA_FLAGS.
Thank you in advance
I agree with #OutOfBound in comments, the simplest solution is to split your code in sub project(s). This way you can control each project flags independently, and even build multiple .cu files with different flags (if needed). If this becomes more complex, you might also want to build kernels with nvcc and load them with cuModuleLoad*().

What is the difference between using a Makefile and CMake to compile the code?

I code in C/C++ and use a (GNU) Makefile to compile the code. I can do the same with CMake and get a Makefile. However, what is the difference between using a Makefile and CMake to compile the code?
Make (or rather a Makefile) is a buildsystem - it drives the compiler and other build tools to build your code.
CMake is a generator of buildsystems. It can produce Makefiles, it can produce Ninja build files, it can produce KDEvelop or Xcode projects, it can produce Visual Studio solutions. From the same starting point, the same CMakeLists.txt file. So if you have a platform-independent project, CMake is a way to make it buildsystem-independent as well.
If you have Windows developers used to Visual Studio and Unix developers who swear by GNU Make, CMake is (one of) the way(s) to go.
I would always recommend using CMake (or another buildsystem generator, but CMake is my personal preference) if you intend your project to be multi-platform or widely usable. CMake itself also provides some nice features like dependency detection, library interface management, or integration with CTest, CDash and CPack.
Using a buildsystem generator makes your project more future-proof. Even if you're GNU-Make-only now, what if you later decide to expand to other platforms (be it Windows or something embedded), or just want to use an IDE?
The statement about CMake being a "build generator" is a common misconception.
It's not technically wrong; it just describes HOW it works, but not WHAT it does.
In the context of the question, they do the same thing: take a bunch of C/C++ files and turn them into a binary.
So, what is the real difference?
CMake is much more high-level. It's tailored to compile C++, for which you write much less build code, but can be also used for general purpose build. make has some built-in C/C++ rules as well, but they are useless at best.
CMake does a two-step build: it generates a low-level build script in ninja or make or many other generators, and then you run it. All the shell script pieces that are normally piled into Makefile are only executed at the generation stage. Thus, CMake build can be orders of magnitude faster.
The grammar of CMake is much easier to support for external tools than make's.
Once make builds an artifact, it forgets how it was built. What sources it was built from, what compiler flags? CMake tracks it, make leaves it up to you. If one of library sources was removed since the previous version of Makefile, make won't rebuild it.
Modern CMake (starting with version 3.something) works in terms of dependencies between "targets". A target is still a single output file, but it can have transitive ("public"/"interface" in CMake terms) dependencies.
These transitive dependencies can be exposed to or hidden from the dependent packages. CMake will manage directories for you. With make, you're stuck on a file-by-file and manage-directories-by-hand level.
You could code up something in make using intermediate files to cover the last two gaps, but you're on your own. make does contain a Turing complete language (even two, sometimes three counting Guile); the first two are horrible and the Guile is practically never used.
To be honest, this is what CMake and make have in common -- their languages are pretty horrible. Here's what comes to mind:
They have no user-defined types;
CMake has three data types: string, list, and a target with properties. make has one: string;
you normally pass arguments to functions by setting global variables.
This is partially dealt with in modern CMake - you can set a target's properties: set_property(TARGET helloworld APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}");
referring to an undefined variable is silently ignored by default;
As mentioned in the other answers CMake can generate other project files. It refers to these projects as generators.
This lets users write/describe their build using a domain specific language, and use the generator to compile the project. It often results in simpler/better code than writing to these project files directly.
A big advantage is users can use the tool that they are the most comfortable with (Makefiles, Visual Studio, XCode, Ninja, etc). This is nice but arguable introduces complexity. Why not just use Ninja?
The answer is history. (As is the norm in C/C++)
Build systems like Visual Studio have tools that will only accept those project files.
For example Microsoft has a feature called "Static Driver Verifier". A tool to analyze the code of kernel mode windows drivers. However, this tool only works on Visual Studio projects since it works alongside msbuild.
msbuild /t:sdv /p:Inputs="Parameters" ProjectFile /p:Configuration=configuration /p:Platform=platform
If your build system can't generate Visual Studio project files, then you can't use the tool. This can be a very big deal for some projects/companies.