Overview
This document describes how you can build libcinder and your application using cmake. This is Cinder's officially supported build system on most POSIX platforms, though in general you can use cinder's cmake system on any platform. See the section on platform-specific notes for details.
Table of Contents
- Building libcinder with CMake
- Building your application with CMake
- CinderBlocks and CMake
- Using CLion to build libcinder and your application
- Structure of Cinder's CMake Files
- CMake Best Practices
- Platform Specific Notes
Building libcinder with CMake
To build libcinder from the command line, first make sure you have CMake version 2.8 or later installed. Then the process is similar to most other cmake projects you may have used, for example you can do the following from within the main cinder repo path:
mkdir build
cd build
cmake ..
make -j4
Upon completion, this will generate a static libcinder binary within the $(CINDER_PATH)/lib
folder for your application to link against. By default you'll be building for Debug configuration, to build release you change the above cmake
command to:
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j4
The runtime output directory for different configurations will automatically end up within different folders (e.g. lib/macosx/Debug/libcinder.a
and lib/macosx/Release/libcinder.a
), so multiple configurations can live side by side.
Command-line Options
There are a few project configuration settings you can change by either editing the CMakeCache.txt
file (or using some GUI to do this), or from the command line with the -D
option. All settings related to cinder are prefixed with CINDER_
. Some of the more common settings are listed below, take a look in the CMakeCache.txt
file for a full and updated list.
CINDER_VERBOSE
: prints out verbose information from within Cinder's CMake scripts during configuration.
CINDER_TARGET
: Sets the target to compile to. This defaults to one appropriate for the current operating system, but in some cases, for example Android, you must set it manually (i.e. cmake -DCINDER_TARGET=android ..
).
CINDER_TARGET_GL
: Sets the target OpenGL version. Usually defaults to ogl
(desktop modern OpenGL), but other valid options are es2
, es3
, and es31
. These are useful when you are building for something like the Raspberry Pi.
CINDER_HEADLESS_GL
: Configure Cinder with support for headless rendering. This allows for offscreen (desktop modern OpenGL) rendering on systems without a display or even a GPU attached. Available options are egl
for hardware accelerated rendering through EGL
and the EGL_EXT_platform_device
extension when available ( currently only NVIDIA ) or osmesa
for software rendering through Mesa and OpenSWR. When building for osmesa
you can additionally set OSMESA_ROOT
to specify the root directory where Mesa is installed ( useful in the case that Mesa is installed locally and not systemwide ). Note that headless mode is supported only on Linux.
CINDER_BUILD_TESTS
: Builds the unit tests (You can then run the tests from the command line with make test
/ make check
).
CINDER_BUILD_SAMPLE
: Specify the name of a single sample to build, after libcinder successfully builds. It will end up in the current build folder. For example, adding the option -DCINDER_BUILD_SAMPLE=BasicApp
will also build the BasicApp sample and place it at build/Debug/BasicApp/BasicApp.app
.
CINDER_BUILD_ALL_SAMPLES
: Specifying True
here will tell CMake to try to build all of the samples within cinder's samples directory. Be prepared to wait a while.
CINDER_DISABLE_ANTTWEAKBAR
: Build Cinder without GUI support. Can be used for reducing buid time when GUI support through AntTweakBar is not a requirement.
CINDER_DISABLE_AUDIO
: Build Cinder without audio support. Can be used for reducing build time when there is no requirement for audio features.
CINDER_DISABLE_VIDEO
: Build Cinder without video support. Can be used for reducing build time when there is no requirement for video features.
Building your application with CMake
Often the best thing to do when adding CMake support to your own project is to look at one of the samples as a starting point. You'll find the CMakeLists.txt
file will be at proj/cmake/CMakeListst.txt
. You can still build it from the base directory of your project if you choose, with something like:
cd my_project
mkdir build
cd build
cmake ../proj/cmake
make -j4
To simplify configuring your project and the many platform-specific particulars, we use a utility function called ci_make_app()
, which takes a number of arguments to build your application. For example, here is the CMakeLists.txt file used for building the _opengl/ObjLoader
sample:
cmake_minimum_required( VERSION 2.8 FATAL_ERROR )
set( CMAKE_VERBOSE_MAKEFILE ON )
project( opengl-ObjLoader )
get_filename_component( CINDER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../../.." ABSOLUTE )
get_filename_component( APP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../" ABSOLUTE )
include( "${CINDER_PATH}/proj/cmake/modules/cinderMakeApp.cmake" )
ci_make_app(
APP_NAME "ObjLoader"
CINDER_PATH ${CINDER_PATH}
SOURCES ${APP_PATH}/src/ObjLoaderApp.cpp
INCLUDES ${APP_PATH}/include
RESOURCES ${APP_PATH}/../../data/models/8lbs.obj
)
It is quite brief, but you can see that we've specified a number of common things necessary when building a custom application, such as source files and include paths.
Arguments to ci_make_app()
include:
APP_NAME
: Defines the name of your application. Defaults to the current ${PROJECT_NAME}
.
CINDER_PATH
: Relative path to cinder's root directory, used to find the cinder
module via CMake's find_package()
system. Required.
SOURCES
: list of source files. Required.
INCLUDES
: list of include directories
ASSETS_PATH
: path to your assets folder (see explanation of assets here. This will automatically be found if your project folder structure follows the standard pattern. Note that by default, this gets symlinked next to your built application, however specifying -DCINDER_COPY_ASSETS=On
at the command line will cause the assets to be copied over instead.
RESOURCES
: list of resources, which are handled in a platform specific manner (see explanation of resources here.
BLOCKS
: list of CinderBlocks that your application depends on. These can be specified by either a relative or absolute path, or by providing the name of one of the blocks that ships with cinder (e.g. lives in the $(CINDER_PATH)/blocks
folder).
LIBRARIES
: list of lib files to be linked (via target_link_libraries()
).
Printing verbose information when building your application
ci_make_app()
also respects the CINDER_VERBOSE
variable, so if that is on then you will see much more configuration information.
CinderBlocks and CMake
Using CinderBlocks
As mentioned above, you can provide a list of CinderBlocks to ci_make_app()
BLOCKS
argument, which will then attempt to first find the block by either relative or absolute path, and then within Cinder's blocks
folder (by its name alone).
Writing CMake support for a CinderBlock
The basic pattern we use for CinderBlock support is CMake's packages system, specific the Package Configuration File
variant. For a concrete example, take a look at the OSC block's OSCConfig.cmake file.
First and foremost, ci_make_app()
finds a file by looking for it at a specific location, within the block's proj/cmake/
folder, and you must use the block's name as the prefix to *Config.cmake
. Then, we define that it is a library with the add_library( OSC ${source_files} )
command, along with any other dependencies on the block.
In the case of a header-only CinderBlock, there won't be a target, but you can still specify the include and library dependencies with BLOCK_NAME_INCLUDE_DIRS
and BLOCK_NAME_LIBRARIES
. Also note that, as in the case of the TUIO block, a block configuration can specify that it depends on another block with CMake's add_dependencies()
.
Using CLion to build libcinder and your application
Aside from the command line, you can also use JetBrains CLion to build and work on both libcinder and your application. CLion
is a sophisticated C++ IDE with many nice syntax features around context-aware searching and refactoring, so it makes for a nice environment to code on your Cinder tasks.
To build libcinder with CLion, open the main Cinder path within the IDE, wait for the initial symbol indexing to finish, and select Run -> Build
. This will by default build the Debug configuration; to change that to Release, you can modify the CMakeCache.txt file (along with any other Cinder cache options) by navigating to the CMake panel at the bottom and finding the button on the left that says 'Open CMakeCache File', and changing the CMAKE_BUILD_TYPE
setting to Release
there.
Note that by default, CLion will output build files into a folder called cmake-build-debug
. If you want to change this to the usual build
, which will automatically get ignored by Cinder's .gitignore
file, then navigate to Preferences -> Build, Execution, Deployment
, and change the field called 'Generation path:' to 'build'.
Running a Cinder sample directly from Cinder's main project.
There are a couple CMakeCache.txt options that will allow you to conveniently build and run a sample that ships with Cinder alongside building the main library. First, you can set the option CINDER_BUILD_SAMPLE
to any of the sample names (ex. CINDER_BUILD_SAMPLE=_opengl/Cube
), and then you will be able to run that sample by selecting it in the the Select/Run Configuration
drop-down in the top right. Alternatively, you could set CINDER_BUILD_ALL_SAMPLES=On
, which will load the configurations for all Cinder samples, and you can then run any one of them from within the IDE.
Structure of Cinder's CMake Files
This section provides an overview of how CMake build support is structured within the Cinder repo.
Cinder's CMake scripts live in the common folder proj/cmake. The root CMakeLists.txt
file is largely there so that it is easy to find, and so that our build configuration plays nicely with CMake-based IDEs like CLion.
configure.cmake
One of the first things that we do is to include()
the common configure.cmake
file, which is shared by Cinder, libraries that depend on Cinder, and applications. It defines some global variables such as:
CINDER_TARGET
, if it hasn't been defined by the user. See above section on Command-line Options for details.- A platform-specific variable: one of
CINDER_LINUX
,CINDER_MAC
,CINDER_MSW
,CINDER_COCOA_TOUCH
orCINDER_ANDROID
. CINDER_TARGET_GL
, if it hasn't been defined by the user. See above section on Command-line Options for details.- Defines some useful OpenGL variables such as
CINDER_GL_CORE
,CINDER_GL_ES
, etc. (seeconfigure.cmake
for the full list). - Sets the
CMAKE_BUILD_TYPE
toDebug
if the user hasn't specified one. - Provides some shared cache options like
CINDER_VERBOSE
. - Sets a couple variables,
CINDER_TARGET_SUBFOLDER
andCINDER_LIB_DIRECTORY
, which define where the output binaries will be generated for each target.
In short, configure.cmake
is meant to provide a a place where common variables can be declared based on the same commands for any cinder-based project, including cinder itself.
Configuring libcinder
The next thing we do is configure the platform independent aspects of the cinder
target. These steps live in libcinder_configure.cmake and libcinder_source_files.cmake. The platform-specific configuration is broken out into separate sources (ex. platform_linux.cmake to ease extending the build system to new platforms. This way you can see source files that only pertain to a specific platform by viewing its specific platform_*.cmake
file.
Finally, we create the actual cinder
target within libcinder_target.cmake, which among other things contains the commands to add_libary( cinder ... )
. Also importantly, this file is responsible for exporting dependency information and creating a cinderConfig.cmake
file that will later be used with find_package()
by libraries and applications that depend on Cinder.
CMake Best Practices
Below are some guidelines for how we've been working on the CMake build setup within Cinder, which are useful to read over if you plan on making contributions and want to streamline your pull requests.
Assign compiler configurations to targets
We try to 'leak' as little compiler configuration as possible. This means using things methods like, for example, target_include_directories( target ... )
instead of the older include_directories( ... )
and then relying on CMake's package system to apply the settings based on dependencies. This pertains to both developing on CMake for libcinder and CinderBlocks.
Keep the code style consistent
As with all community projects, it makes things easier to keep a consistent style. CMake might be a funky language, but we've more or less developed our own readable way to write in it, which is based on the spacing described in Cinder's styleguide as much as possible.
Platform Specific Notes
Microsoft Windows
As of this writing, we do not support building from CMake-generated Makefiles on Windows, simply because the GCC toolchain is too difficult to set up there with a compiler that supports the features we need (C++11). The best approach there is to use the provided Visual Studio project files, as explained here. You can however generate new Visual Studio project files from CMake, if that suits your needs.