Wondering what the Pied Piper image is doing here? Well, just imagine the Pied Piper as CMake and the C++ based projects as Children.
The parents among us would relate to the amount of nurturing and care required to bring up a child. Similar is the difficulty in getting a C++ project cross-compiled. A nanny would make things simpler. Analogously we have CMake to make things simpler in the cross-compilation world.
Cross Compile? Care to Explain More?
Sure. Think of a well-known application like Microsoft Paint. Before it becomes an executable, it exists as a set of source code files. Compiling the source code and linking object codes to a single executable is the job of a compiler. In a normal scenario:
The source code of Microsoft Paint exists as a set of (no doubt C++) files that get compiled within Microsoft windows using (no doubt Microsoft Visual Studio) compiler that begets Microsoft Paint. Now take the cross compile scenario:
So cross compilation happens when the source code compiler is targeting a different operating system than the one it is currently hosted in. A bit clearer should this picture make:
It should now be obvious where the term cross compilation comes from… Cross platform software development is not easy as each operating system has its idiosyncrasies. Source code written for windows cannot normally be compiled for Linux and vice versa. This is where frameworks like Qt and standards like POSIX come into play.
Can I Not Live With a Simple Compilation?
Not always. Take for example an app that a developer is planning for Android. He would use the computational power of his development machine (Windows, Linux, or Mac) to cross compile the app for Android. The same would be the situation if the developer is targeting a platform like Raspberry Pi. It is much easier to set up the cross-compilation environment on a powerful development machine and copy the binaries to Raspberry Pi than spending too much time running the compilation on such a low power machine.
Another use case for cross compilation is the complexity of getting dependencies to build using the target machine’s native compiler. For example, take an open-source project like Chromium (base for both Microsoft Edge browser and Google Chrome browser) that is supported on multiple platforms. Because of the multiple dependencies, it is not advisable to attempt the build of Chromium natively. Rather, docker images are setup that brings up a Linux container that hosts GNU GCC to cross compile the source.
I Have Never Attempted a Cross Compilation. Can You Please Give an Example?
Windows 10 desktop operating system comes in two different flavors:
- Windows 10 for Intel CPUs (Both 32 Bit and 64 Bit versions)
- Windows 10 for ARM CPUs (Both ARM32 and ARM64 bit versions)
Programs compiled for Intel CPUs are not compatible with ARM CPUs and vice-versa. Microsoft Visual Studio comes with the following command tools some of which are used for cross compilation:
- VS2015 x64 ARM Cross Tools Command
- VS2015 x64 Native Tools Command
- VS2015 x64 x86 Cross Tools Command
- VS2015 x86 ARM Cross Tools Command
- VS2015 x86 Native Tools Command
- VS2015 x86 x64 Cross Tools Command
We are interested in the Cross Tools compiler. Let us try to create a simple “Hello, World!” C++ program:
int main(int argc, char** argv)
std::cout << "Hello, World!" << std::endl;
On an x64 Native command line, compiling the above program gives:
On an x64 ARM cross tools command prompt, compiling the same program gives:
Pay special attention to the /machine:arm output. The compilation is successful and an executable is generated, but it is not for the current machine as can be seen by running the executable:
Congratulations! You have successfully cross compiled the simplest C++ program for a different machine.
Thanks! So Why CMake Cross Compile?
Compiling for different CPUs supported by the same operating system is the simplest form of cross compilation. What if you wanted to be in Linux and wants to generate an executable that supports Windows? Better still, you want to use, if possible, the same codebase to generate both the Linux and Windows executables.
Let us try this out. I have a Windows 10 machine that runs Windows Subsystem for Linux where I have installed Ubuntu. A simple
sudo apt-get install mingw-w64
will install the mingw cross compiler toolchain for windows. We create CMakeLists.txt file like so:
# set minimum cmake version
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# project name and language
project(HelloWorld LANGUAGES CXX)
# define executable and its source file
Let us create a toolchain file for CMake that tells some information about the cross-compilation toolchain. Like so:
# the name of the target operating system
# which compilers to use
# adjust the default behavior of the find commands:
# search headers and libraries in the target environment
# search programs in the host environment
Save this file as cross-compilation.cmake and keep it in the same folder as CMakeLists.txt (given above) and Main.cpp. Create a new folder called build and change to that directory. Now issue the commands:
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-compilation.cmake
cmake --build .
This should create a HelloWorld.exe as specified in the CMakeLists.txt in the build/bin folder. What have we achieved? We have created an executable for Windows entirely working under Linux. Different toolchain files and compilers should allow the same CMakeLists.txt target to be created for multiple platforms.
Cross Platform Software Development Using CMake
CMake is a great tool for cross platform software development. It uses a set of utilities called a toolchain to drive the build. There are two main scenarios of using CMake for builds:
- Normal builds where CMake is responsible for choosing the toolchain
- Cross platform builds where the user specifies a toolchain file
The simple example above shows how we can create a toolchain file and tell CMake to use that toolchain file to drive the build. In real-world cross platform software development, the stakeholders would carefully select frameworks that aid such a development – for example Qt.
By the way, If you’d like to read more about CMake please visit our blog post about CMake vs Make.
Did the pied piper’s story have a happy ending? Some versions of the legend say piper took the children to a beautiful land. CMake as a choice for cross platform software development should also result in such a happy ending. The End ?