following situation:
At the moment, I have a C++/CUDA project and I use make to produce two different executables: The first one is only a CPU version, which ignores the CUDA parts, and the second one is the GPU version.
I can run make to build both versions, make cpu to only build the CPU version and make gpu to only build the GPU version.
For the CPU version, the Intel compiler is used , and for the GPU version, nvcc with g++ is used instead.
Why I consider using cmake:
Now I would also like to be able to build the project under Windows with nmake. Therefore, I thought that cmake was the appropriate solution to generate a make or nmake file based on the platform.
Problem:
However, it seems to be difficult to specify a compiler based on a target (Using CMake with multiple compilers for the same language).
Question:
Which is the best way to achieve the behavior as described above, building different executables for different architectures with different compilers out of the same code base as well for Windows as for Linux, by using cmake?
In your situation you can just use cmake's CUDA support: FindCUDA.
Your cmake lists would then look like:
find_package(CUDA)
add_executable(cpu ${SRC_LIST})
CUDA_ADD_EXECUTABLE(gpu ${SRC_LIST})
Related
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).
I would like to offer a way that always builds my target as a 32-bit or always as 64-bit executable executable with cmake independent of the host system (Adding the "-m32" or "-m64" flag for gcc, not sure yet what to do for other compilers).
I can think of three ways to do this, which one should I use?
an option (-DUSE32bit=true)
a tool chain (-DCMAKE_TOOLCHAIN_FILE=64bit.toolchain)
build types (-DCMAKE_BUILD_TYPE=release32)
In my case the forced 32-bit build will be the default and should be easy to use. A forced 64-bit build is the also useful for some cases and should not be too difficult. Using the bit width of the host system rarely makes sense for my case and I don't want to support it.
I found a related question here (The proper way of forcing a 32-bit compile using CMake) but the answers mostly discuss how it can be done at all, not how best to make it configurable.
For Visual Studio and per https://cmake.org/cmake/help/latest/variable/CMAKE_GENERATOR_PLATFORM.html
For Visual Studio Generators with VS 2005 and above this specifies the target architecture.
cmake . -DCMAKE_GENERATOR_PLATFORM=x64
TL;DR
Use toolchain
In depth
an option (-DUSE32bit=true)
This is not scalable I guess. So what if you want to build N projects? You have to add N options.
build types (-DCMAKE_BUILD_TYPE=release32)
This may work well. But in my opinion you're mixing unrelated stuff. Also I'm sure you have to adapt find_package behaviour by setting some *_ROOT CMake variables. It's not possible to do it with CMAKE_BUILD_TYPE (at least, again, in a scalable fashion).
a tool chain (-DCMAKE_TOOLCHAIN_FILE=64bit.toolchain)
The best variant. If you want to build two projects - just use same toolchain:
cmake -Hproj-1 -B_builds/proj-1 -DCMAKE_TOOLCHAIN_FILE=/.../64bit.toolchain
cmake -Hproj-2 -B_builds/proj-2 -DCMAKE_TOOLCHAIN_FILE=/.../64bit.toolchain
If you want to build your 3rd party ExternalProject_Add with 64 bit architecture - just pass toolchain to CMAKE_ARGS:
ExternalProject_Add(
...
CMAKE_ARGS ... -DCMAKE_TOOLCHAIN_FILE=/.../64bit.toolchain
...
)
Want to adapt find_package - just add any CMake variables to toolchain file.
thanks for all your inputs, but on my side i've finally chosen the -m32 hack with cmake
# cmake windows 32 bits mode bug: 32 bits link mode must be explicit (otherwise cmake will always guess a 64 bits target)
# 1- run "vcbarsall.bat x86" to setup Visual Studio build profile
# 2- "set cflags=-m32" and "set cxxflags=-m32"
# 3- let the cmake plugin "automatically" guess the target platform
Personally I wanted to switch to 32/64-bit projects within same solution for Visual studio / cmake configuration. I've figured out that this can be done using
set_target_properties(${project} PROPERTIES LINK_FLAGS ${PROJ_LINK_FLAGS})
where PROJ_LINK_FLAGS can be either /MACHINE:X86 or /MACHINE:X64 depending on compilation.
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.
We have some applications written in C/C++ and makefiles for the same. Currently, we are using the GNU make system on windows (cygwin based). The makefiles were written long back considering only Windows OS in mind. Now we are going to revamp everything.
I am unaware of the factors to be considered while writing the makefiles so as to make them cross platform compatible. I looked at some Sources on the Internet, but they were unsatisfactory. Can someone please list out the issues to be considered while writing the makefiles so as to make them compatible across various platforms.
PS: I have seen this link, but i think it isn't what i want.
Makefiles and cross platform development
You can use cmake - it's a cross platform tool which generates makefiles or projects files with respect to your platform. So instead of writing Makefile you write CMakeLists.txt, then you run cmake and it will generate Makefiles. When you want to compile your program on another platform you just ru-run cmake with different target project system.
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).