CMake for Multi-Platform Builds
I want to develop an engine that will run on Windows, OS X, and Linux. This means having a build system that will build binaries for all those platforms also.
Cross-platform development of this sort means dealing with platform-specific libraries and toolchain. It requires branching within build scripts to deal with things like linking libraries with different names and extensions, paths for toolchain, and different compiler options.
Variables
I mentioned briefly in the previous post how you can set variables using set
:
set(NAME value)
But these variables have scopes. You cannot use them until after the first set
command. Normally they also cannot be modified by CMakeLists.txt
in a sub-directory. This is the first obstacle we must overcome in order to have newly loaded CMakeLists.txt
change behavior of our build script. This default scoping behavior isn't obvious unless you are looking for it, but is easily accomplished:
set(NAME "old value")
add_subdirectory(A)
message("VALUE: ${NAME}")
in CMakeLists.txt
that lives in A
:
set(NAME "new value" PARENT_SCOPE)
Passing In Variable Values
To pass in values when you run cmake
from command line:
cmake ../src/ -DMY_VARIABLE="Value to pass in"
This is useful for setting values for variables that controls branching within your build scripts.
Environment Variables
Access environment variables using $ENV
. For example, the PATH
variable that determines the search order for binaries to execute:
$ENV{PATH}
Built-In Variables
Often there are other built-in variables specific to your operating system and compiler, some of which are listed here. Examples include APPLE
, UNIX
, WIN32
, and CMAKE_COMPILER_IS_GNUCXX
.
The variable CMAKE_BUILD_TYPE
deserves some note, it determines the type of build and compiler flags. For example, a release build with no debugging symbols:
set(CMAKE_BUILD_TYPE "Release")
With clang
or gcc
this often means -O3
optimization. To build binaries for use with gdb
or other debugger:
set(CMAKE_BUILD_TYPE "Debug")
By setting the build type, it affects the variables that store the flags used for compiling and linking. For release builds, they are: CMAKE_C_FLAGS_RELEASE
and CMAKE_CXX_FLAGS_RELEASE
. For debug builds they are: CMAKE_C_FLAGS_DEBUG
and CMAKE_CXX_FLAGS_DEBUG
. It may be surprising to find out that the compiler flags you set have no effect, and this is where you should investigate.
Branching
All these variables allows you to evaluate conditions and branch:
if( ... AND ... )
elseif( ... OR ... )
elseif( EXISTS ... )
elseif( DEFINED ... )
else()
endif()
Often you will be comparing strings:
if( $ENV{MACHINE} MATCHES "x86" )
endif()
The above block is executed if x86
appears anywhere in the value stored in $ENV{MACHINE}
.
The ability to conditionally execute different parts of your build script means we can now use a single set of CMakeLists.txt
, maybe in different sub-directories, to build and package binaries for different platforms.
Next up, let's talk about CMake support for macros, linking, and other useful techniques.