[RFC] Cross-platform CMake support

Hi all,

After months of hard work by multiple people, we’ve come to a decent approach at incorporating CMake as a build tool in a manner we hope to be extendable. It is currently the only supported build tool on Linux (and related devices like raspberry pi or TK1), and it comes as an addition to Xcode on Mac OS X (the old, hand-tuned xcode projects remain). Moving over the current android cmake files to the new cross-platform approach is on the way, so in the meantime things haven’t been touched there. This work was merged into the android_linux branch with this PR.

Now, we’d like to ask for community feedback for those who are interested, as we move closer towards the 0.9.1 milestone. To partake in this, please work from the android_linux branch. Testing samples, adding cmake to existing projects, or just general feedback are all welcome. Feel free to PR fixes or improvements.

To build cinder with cmake, you can either use the command line, or Jetbrains CLion, which has been maturing quite a bit in the past few months. For command line, the process is similar to most other cmake projects you might have used, for example from the main cinder repo path

mkdir build
cd build
cmake ..
make -j4

As with all cmake projects, you only need to perform the cmake command once, afterwards calling make is enough. There are a few options in the cmake cache that you can define define from the command line as well.

//Build a specific sample by specifying its path relative to the
// samples directory (ex. '_opengl/Cube').
CINDER_BUILD_SAMPLE:STRING=Extrude

//Build all samples.
CINDER_BUILD_SAMPLES:BOOL=

//Target platform to build for.
CINDER_TARGET:STRING=

//Target GL for the system. Valid options : ogl, es2, es3, es31,
// es32, es2-rpi
CINDER_TARGET_GL:STRING=

//Print verbose build configuration messages. 
CINDER_VERBOSE:BOOL=1

In the above, I’ve also set -DCINDER_BUILD_SAMPLE=Extrude, which tells cmake to also build the Extrude sample with libcinder as a dependency of it (this is great when you are working on cinder core code, and want to see the results in your application in one build step).

Note that you can also build the samples directly:

cd samples/Extrude
mkdir build
cd build
cmake ../proj/cmake
make -j4

Building libcinder with CLion is as easy as opening up the main repo folder within the CLion IDE, selecting the cinder configuration in the top-right, and building. You can also build samples from this project, or you can open up the individual sample’s proj/cmake folder in CLion and build it that way.

Let us know what you find. Personally, I find myself using CLion more and more on Mac OS X as the debugger works better and the editor has more features. At the same time, we’ve tried to keep the cmake setup as ‘traditional’ as possible, so that it is familiar to those coming from other projects that use this build tool. I’d also like to take this moment to say thanks to @petros who has worked on this the entire way through, as well as @chaoticbob who wrote the original cmake files (and of course the majority of the android and linux ports that will be making their way into 0.9.1, hopefully soon!).

cheers,
Rich

6 Likes

This is great! Thanks to all who was involved.

Do you have any ideas how cinder blocks would be supported with the cmake system? It would be nice to be able to simply include a cmake file from the block directory or something similar.

-Gabor

Great work! I am very enthusiastic about CMake support in Cinder as I use it for most of my other projects. One thing I noticed on the old cmake branch is that some build artefacts are left in the source directory (e.g. some library files), even when performing an out-of-source build. I was taking a look at this, and hopefully I can contribute something to the android-linux branch soon.

Kind regards,

Dave

@gabor_papp

Hey Gabor,

The idea of blocks providing a .cmake or .config file for including them is definitely the ideal option but it requires some effort / cmake knowledge from the block maintainers part which I am not sure that everyone would like to invest in. I suppose that for the blocks that ship with Cinder this could be much easier to establish but I m not sure how feasible this would be for third party blocks. AFAIK this is still an open question and there is no general consensus yet on what would be the best way to go forward with this but lets keep the discussion open and see where we can get. CMake really shines in projects with a lot of dependencies and it’s actually not that hard to even manually incorporate external dependencies as long as you have a basic understanding of the general process. Its all about finding a balance between automation and features that already exist in the tool itself in my opinion.

@DaveEdmunds

Hi Dave,

really nice to see that more people are interested in these developments. I am not entirely sure that I understand what you mean by build artifacts though – Would you like to elaborate a bit more on this ? Maybe this is something that was there in the very early stages of the cmake branch and I am not aware of but currently ( speaking only for out-of-source builds ) there shouldn’t be any kind of ‘bleeding’ of your build tree to your source tree i.e these are two totally separate things and there should be no build residuals into your source tree. It would be really nice to know if you still find something strange about it.

Taking this chance I would also like to thank @rich.e and @chaoticbob for all the work they have put in the last months on the CMake and Android/Linux developments. It feels really good to have a CMake workflow and be able to run Cinder on Linux.

Cheers,
Petros

Thanks guys, this is great news!!
I can’t wait until I get a chance to take a look, very excited to no longer need to port XCode/Appcode projects to visual studio.

Are there any plans to add Tinderbox support down the line? This could eliminate the need for block .cmake/.config files.

Hi @petros,

I just cloned the latest Cinder repo, switched to the android_linux branch and built with CMake on Mac OS X 10.9.5 using the following commands:

mkdir Cinder_build
cmake ../Cinder
make -j4

This creates the folder

Cinder_build/lib/macosx/Debug

which contains only

cinderTargets.cmake

along with a few other CMake files. The actual library files are created in the source tree, at Cinder/lib/macosx.

I believe this can be fixed by changing lines 5 and 6 in proj/cmake/libcinder_target.cmake from

set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CINDER_PATH}/${CINDER_LIB_DIRECTORY} )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CINDER_PATH}/${CINDER_LIB_DIRECTORY} )

to

set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CINDER_LIB_DIRECTORY} )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CINDER_LIB_DIRECTORY} )

There are probably several other paths that need to be changed to make sure there are no build products created in the source tree, which I will take a look at when I get a chance.

Regards,

Dave

Hi Dave,

the in-source vs out-of-source build concept of CMake exists for not polluting your source code directory with files generated during the build phase and for having the flexibility to have separate directories for different build configurations ( Debug vs Release ). Here is the official CMake FAQ answer on this specific topic.

The Cinder/lib/* directories are not part of the source code tree and they only serve as output directories for the final result of the build phase i.e the only files that go there is libcinder.a and the export file for finding cinderTargets.cmake which contains all of Cinder’s dependencies so that applications can link with Cinder without having to respecify dependencies that were already specified while building Cinder.

The current configuration also takes care of different directories for for different build configurations and on Linux it even supports multiple GL targets in parallel ( es2 vs es3 vs ogl etc. ).

So for example on ARM Linux you could have an output directory like Cinder/lib/linux/arm7l/es2/Debug and in parallel with this, if ogl is supported like in the case of TK1 for example, another one like Cinder/lib/linux/arm7l/ogl/Debug and be able to switch on the fly between them.

There are no other build products created or outputted in any other directory besides the one that you created in your initial step ( in your case it would be Cinder_build ). This is also a design decision in order to keep in line with how Cinder has traditionally treated build products in the various platforms which is by outputting the final result of the build phase under Cinder/lib/ .

I hope this clears things out a bit.

Best,
Petros

Great job guys! This is really excellent work!

I noticed that building Cinder in release mode on OS X with CMake adds the following compiler flags:

-DFT2_BUILD_LIBRARY -DFT_DEBUG_LEVEL_TRACE -std=c++11 -O3 -DNDEBUG -Wfatal-errors

while the previous fullbuild script added many other warning and optimization flags:

-fmessage-length=120 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -fcolor-diagnostics -std=c++11 -stdlib=libc++ -Wno-trigraphs -fpascal-strings -O3 -fno-common -Wno-missing-field-initializers -Wno-missing-prototypes -Wunreachable-code -Wno-non-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wshorten-64-to-32 -Wno-newline-eof -Wno-c++11-extensions -DNDEBUG=1 -fasm-blocks -ffast-math -fstrict-aliasing -Wdeprecated-declarations -Winvalid-offsetof -mmacosx-version-min=10.8 -g -fvisibility-inlines-hidden -Wno-sign-conversion -MMD -MT dependencies -MF --serialize-diagnostics

Are they left out on purpose?

Thanks for the feedback guys,

re blocks: I’m beginning to work on adding cmake support for some of the blocks we ship, like box2d and cairo for starters (and I know @petros has done some good work here in his own repo). We’re still figuring the process out, but personally I’m going to try to go with making the block as a cmake ‘package’ just like libcinder, and relying on the mutual configure.cmake to pull in variables that are shared across cinder and applications alike.

I think one question worth addressing is whether we want a CMakeLists.txt file to live at the base path of each cinderblock, or if it is better to live in the proj/cmake folder (keeping in mind issue #1307).

rasberry pi: One thing that has come up is that the old cibuild had a nice convenience functionality where you could pass -rasbpi as a flag and it would set all the right variables to build on that platform. @petros do you think we could do the same using straight cmake, possible by defining a new CINDER_TARGET? I don’t use this platform so help here would be great.

@gabor_papp re compiler flags on OS X: Those are mostly defined by Xcode, I think we can increase the warnings but it would be nice to leave them the same across all platforms, or at least posix.

cheers,
Rich

I’ve just put up a PR for our proposed method of cinderblock support: https://github.com/cinder/Cinder/pull/1511

Basically, the ci_make_app() macro now supports a BLOCKS argument, which can be either a relative path or the name of the cinderblock within cinder’s blocks folder. For example:

ci_make_app(
	SOURCES     ${SRC_FILES}
	CINDER_PATH ${CINDER_PATH}
	BLOCKS      Box2D
)

The cinderblock itself is expected to create a config cmake file used by the find_package() command, located at:

${BLOCK_NAME}/proj/cmake/${BLOCK_NAME}Config.cmake

As an example, here’s the OSCConfig.cmake file.

Please let us know what you think, and if you try it out with any of your own blocks then please give us any feedback that you think of. Sorry for the lack of documentation, that is still to come…

cheers,
Rich

1 Like

Great! Thanks for your work on this.

As I see the block has to be in cinder/blocks or in the cmake file folder at the moment. If I submodule cinder and all the necessary blocks for a project the latter can work only, but it is not very flexible. I found a workaround, although it might be intentional. Specifying the block relative to the cmake folder works fine:

ci_make_app(
	SOURCES     ${SRC_FILES}
	CINDER_PATH ${CINDER_PATH}
	BLOCKS      ../blocks/Cinder-ImGui
)

Blocks are first searched for relative to the CMakeLists.txt file specifically for this use case, so that they don’t need to live in cinder’s blocks folder. The relevant code for that starts here. We could also search for the path as absolute, if this would be helpful (or expected).

I’m having problems adding a header only block with CMake. It seems that cinderMakeApp.cmake would like to link to a library and I’m not sure how to prevent this. Is there a way to do this?
I’m trying with Simon’s SimplexNoise.
This is the proj/cmake/SimplexNoiseConfig.cmake file:

get_filename_component( SIMPLEX_NOISE_PATH "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE )

set( SIMPLEX_NOISE_INCLUDES
    ${SIMPLEX_NOISE_PATH}/include
)
    
include_directories( ${SIMPLEX_NOISE_INCLUDES} )

Are you meaning that since Simon’s SimplexNoise is header only, so there is no library to link to? We haven’t actually thought this path out from a cinderblocks perspective, which currently all create a target that gets added to your main app here.

At first thought, using a cinderblock for this doesn’t seem necessary because all you need to do is add an INCLUDE path when calling ci_make_app(), however perhaps there is a more unified solution that we haven’t thought of yet.

Yes, there’s no library. I added the include path as you suggest, but it would be nicer to be able to use the BLOCKS setting.

And what about Cinder-OpenCV, which is header only, but links to a lot of OpenCV libraries? Do we have to link them together to a big static libCinder-OpenCV.a to be able to use it instead of adding them separately?

Hi,

an idea of how we could handle this in a unified way would be to have BLOCK_NAME_INCLUDE_DIRS, BLOCK_NAME_LIBRARIES variables defined optionally from the block’s CMake configuration file in case there is no need for a target.

So in the case of OpenCV for example you could have OPENCV_INCLUDE_DIRS and OPENCV_LIBRARIES and then inside ci_make_app we could first check for the existence of a block target and if not present fallback to the above two variables which would be added manually then with target_include_directories and target_link_libraries. This would also be in-line with the way that the FindLib mechanism of CMake works ( well, more-or-less lets say… )

Also just a note that on Linux I would just directly link with OpenCV like so https://github.com/PetrosKataras/Cinder-OpenCV3/blob/linux-project-files/samples/ocvWarp/linux/CMakeLists.txt#L21-L38

Cheers,
Petros

Hi Petros,

This BLOCK_NAME_* idea sounds nice. Thanks for the OpenCV link sample.

Hey @gabor_papp, we’ve been working on support for blocks with no target, which ended up in pr #1560. I don’t have a header-only cinder block in front of me though, would you mind testing?

Also thanks for your PR for custom icons on OS X, hoping to get that in there soon.

Cheers,
Rich