[RFC] Cross-platform CMake support

Hi,

Thanks for this. I tested #1560 with the SimplexNoise block and it works fine.

-Gabor

Hi,

I am excited to see that there has been some significant work done towards implementing CMake into the build workflow of Cinder. I have some previous experience with porting OpenFrameworks to CMake. Both on the lib side (static/shared) and on the examples side as I do not use any IDEs and like using CMake for project configuration.

So I am new to Cinder and I would like to give it a try under OSX. Thanks for the hard work.

Cheers,
Milen

First Impression:

libcinder.a and libcinder_d seems to be building fine with XCode under OSX. The examples are running. The only issue is that boost is missing in the original project structure and every project would fail at the #include <boost/filesystem.hpp> directive including the libcider. Once that problem is fixed, everything builds and runs.

With CMake, obviously the boost issue is there, however once that is fixed libcinder.a builds fine.

TODO:

  1. Try building -DEBUG to get the libcinder_d.a with CMake.
  2. Try building one of the examples with CMake.

Thanks.

boost headers are part of a submodule, make sure you do a git submodule update --init after any time you update your cinder repo.

Thanks rich.e,

That solved the boost issues. I would like to ask couple of questions:

  1. Right now, I am running CMake from within cinder-dev-0.9.1/build by running cmake .. which seems to be working so far. Is this the intended approach?

  2. To build an example, from within the build folder cinder-dev-0.9.1/build I would be running cmake .. -DCINDER_BUILD_SAMPLE="BezierPathIteration" which also seems to be working but partially. The sample application would be placed under cinder-dev-0.9.1/build/Debug however non of the assets would be placed under the package Resources folder. So the application would crash. Only after any dependent content such as svn assets or images are manually copied, the application will function as intended. Maybe because the COPY functionality has not been implemented yet?

  3. What would be the ideal workflow to build a cinder project outside of the samples folder. Do you guys have a solution to that, or do you expect the user to come up with their own CMake scripts?

Letā€™s say, I have a project that is a clone of the the BasicApp sample but is inside myDevFolder/cinder_projects/ci_MyCinderApp folder. Should I be writing my own CMakeLists.txt or is there a way to invoke your CMake approach from with in my project?

Thanks.

Hey @symbolix,

We have tried to keep Cinderā€™s CMake configuration aligned as much as possible with the way you would use any other CMake-based project. This means that for 1) this would be the ā€˜defaultā€™ way for building Cinder but there should be nothing that stops you to build Cinder from another directory. If you try a different dir structure and you hit any issues drop a line here so that we can have a look on it.

Your observations about 2) are correct. My personal opinion on that would be to have a custom post-build command that copies the assets to the binary directory from the source directory through add_custom_command.

Regarding 3) we donā€™t have any template generator system at the moment in place so for using a custom dir or even for creating a new app under the samples folder you would need to copy one of the existing CMakeLists.txt files and modify it for your particular needs. This should be quite straightforward since the only thing that would need to be changed besides things like project name etc. would be the CINDER_PATH variable which helps locate the exported config file of Cinder.

HTH,
Petros

  1. Is indeed a problem that weā€™ve been trying to sort out. It came about when I addressed #1505, so that samples build into whatever folder youā€™re currently in (such as build/Debug/SampleApp or whatever), as this is the typical place where out-of-source cmake builds place the resulting binary. But yep, no assets can be found there because they live relative to the application directory. So lets discuss how to solve this. :slight_smile:

The thing I donā€™t like about copying the assets folder to the build directory is that it will hinder the ability to update assets during runtime, whether with a filewatcher or just reloading your application. Either a) youā€™d mistakenly be modifying the assets that live in the sampleā€™s directory, or b) youā€™d be modifying the assets in the build directory that arenā€™t versioned and will be overwritten during your next build.

What do people think about sym-linking the assets folder into the runtime output directory?

This isnā€™t just an issue for cinderā€™s samples and tests, by the way. Iā€™m seeing the same thing in a personal project setup, which AFAICT is the recommended way to organize a project with many dependencies and executable targets. Basically this is the structure:

[root]
CMakeLists.txt # <- carries global flags and brings in all targets from sub-projects, including libcinder
app/ # main application
-- assets/
-- proj/cmake/CMakeLists.txt
-- src/
test/ # test suite for app's various components
-- assets/
-- proj/cmake/CMakeListst.txt
-- src/
common/ # shared library used by both app and test
-- proj/cmake/CMakeLists.txt
src/  # common source files
cinder/ # where cinder lives, as a dependency of common, test, and app
blocks/ # any cinderblocks live in here
-- OSC 
-- Cinder-View/
-- -- assets/

In the layout above, both the app and test targets have separate assets folders, and they live relative to their project folder. So if all build products end up somewhere in [root]/build, they wonā€™t be automatically found.

Itā€™s also a problem if you want to run a target from a sub-project that needs to find its own assets, such as Cinder-View in this case. Its test suite is looking for some button images that live in blocks/Cinder-View/assets/images/, but the build product ends up in the root build dir.

Hi rich.e,

I can look into that. There are quite a few options that CMake provides. Just let me get up to speed with some of the aspects you guys have been working on :slight_smile:

I have just managed to compile an independent cinder project, outside the cinder project structure using bits and pieces from your CMake scripts. I will create a case-study repo in a second. It will be messy but, for me, it should server as a good starting point, something I know that works.

Hello again,

This here is the initial repo:

Please let me know if there are any issues related to this. I have put this together as a case study to help myself understand the process of building a cinder application outside the cinderā€™s own project structure.

I have added verbose messages etc. only to get a better understanding of what is happening. The idea of building libcinder and preparing it to become an imported target is cool.

My initial question is, how can one make the build process of libcinder a dependency for the ci_BezierPathIteration application. Meaning, starting the build process for both libcinder (as a dependency) and as ci_BezierPathIteration from within the ci_BezierPathIteration/build folder.

Maybe something like this?

ExternalProject_Add(
        ci_static
        SOURCE_DIR ${CINDER_PATH}
        INSTALL_DIR ${cinder_install_dir}
        CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
        CMAKE_CACHE_ARGS -DCINDER_VERBOSE=On -DCMAKE_BUILD_TYPE=Release
)

In that way, the build process of libcinder can be invoked within the project itself. Is this possible or is it a good idea?

Thanks.

Iā€™m trying to implement some post build action like supporting make run and post build copy.

This is what I managed to do for make run. It should use CMAKE_RUNTIME_OUTPUT_DIRECTORY instead of the CMAKE_BUILD_TYPE hack, but I could not access that variable.

add_custom_target( run
    COMMAND open ${CMAKE_BUILD_TYPE}/${APP_NAME}.app
    DEPENDS ${CMAKE_BUILD_TYPE}/${APP_NAME}.app/Contents/MacOS/${APP_NAME}
    WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
)

I also have to define CMAKE_BUILD_TYPE in my CMake file to make this work.

It is also often necessary to copy a shared library from a block next to the application executable. It would be more elegant to do this from the block config cmake than hacking it into the application cmake. This is what I have for a block with Leap Motion dynamic libraries in the app cmake. Iā€™m not sure if it is possible to put this in the block cmake.

if( CINDER_MAC )
    add_custom_command( TARGET ${APP_NAME} POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy
            ${APP_DIR}/blocks/Cinder-Leap/lib/macosx/libLeap.dylib
            ${CMAKE_BUILD_TYPE}/${APP_NAME}.app/Contents/MacOS/
    )
elseif( CINDER_LINUX )
  ...
elseif( CINDER_MSW )
  ...
endif()

Do you have any suggestions to make these solutions simpler, more elegant? Or is it not possible to do this with the current cinder cmake system?

Hi Gabor,

just looking into thisā€¦

For the first issue I think we could get away by making the CMAKE_RUNTIME_OUTPUT_DIRECTORY visible in the parent scope. Something like :

set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} PARENT_SCOPE ) at the end of cinderMakeApp.cmake should do the trick for this issue.

For your second issue I m not entirely sure yet what would be the best way to handle it ā€“ Right now personally I could imagine that the BLOCKNAME_LIBRARIES var that we introduced for non-target blocks could be of use and help here i.e we could check for this var even with blocks that define targets and if set copy these next to the binary.

Any other ideas are of course more than welcome!

Hey Rich,

I havenā€™t encountered a symlink approach for handling assets on a CMake project before but this should not be something that stops us from trying it out and check how it works! From the top of my head I donā€™t see any obvious downsides with this approach and what you mention about copying assets definitely holds true i.e you have to be really aware of which directory you are actually watching/modifying etc.

Cheers,
Petros

Hi Petros,

Thanks for the suggestions. It would be nice to have CMAKE_RUNTIME_OUTPUT_DIRECTORY available in parent scope if you think it is a valid use case. Would it make sense to do the same with ARG_APP_NAME as well? CMAKE_RUNTIME_OUTPUT_DIRECTORY only contains the directory, but the executable is placed in ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${ARG_APP_NAME}.app/Contents/MacOS/ on OS X. The easiest path for the dynamic libraries is next to the executable, but from the block config file this path is not possible to guess I think. Additionally, if the app name was available it would be possible to use it as a target in add_custom_command in post build actions.

-Gabor

A warm thank you to all the team!
Videodrƶmm now runs on Linux!!!
I had to put some #if (defined( CINDER_MSW) ) || (defined( CINDER_MAC )) to compile it and avoid some segfaults, but this is very promising.
Here is my CMakeLists.txt file if you need, Iā€™m glad to have imgui, warping, osc blocks compiling fine.

3 Likes

Hi @rich.e and everyone. Thanks very much for bringing CMake into the mix. Iā€™m pretty sure that this is the primary reason Iā€™ve been able to get Cinder running on Linux now and itā€™s much appreciated.

I noticed that CLion support is made explicitly. As this is a RFC thread, Iā€™d like to ask if juCi++ could possibly be supported as well? It also uses CMake as itā€™s build system and hopefully could be integrated into your system as well.

Apologies for the silence, been working on totally unrelated thingsā€¦

Re @petros exporting CMAKE_RUNTIME_OUTPUT_DIRECTORY to parent scope - Iā€™m slightly hesitant here, as subsequent targets are going to pick this build variable up. What about defining a new variable in the parent scope, ex. CINDER_RUNTIME_OUTPUT_DIRECTORY?

Re @gabor_papp copying dylib: yes perhaps if it is a dylib passed into cinderMakeApp(), we should copy it to the appropriate place for the current platform. Although Iā€™m not sure how to distinguish when we need to copy a library to be local to the app executable and when the app should be using a shared lib at some global location (ex /usr/local/lib). Open to thoughts here, but if we canā€™t then perhaps cinderblocks can define a custom property like CINDER_APP_COPY_LIBS or something.

I think this weekend I will go ahead with trying out symlinking the assets folder. This will change the build folder layout slightly as on some platforms (linux) we need one extra folder with the appā€™s name, but I donā€™t think it will have any side effects.

@hurpyderpy do we need to make changes to support juCi++? For CLion, it was necessary to make the cinder assets system work.

juCi++ expects a CMakeLists.txt file to be in the base folder of a project. Is it possible to accommodate this via the CMake

add_subdirectory()

command? I suppose this would mean 'add_subdirectory()'ing the other folders needed in this base CMakeLists.txt file, and CMake will find them properly thereafter.

With this addition, Juci could then pick up the other CMakeLists.txt files automatically and it seems like this is a more general approach and would accommodate more development environments both now and in the future.

edit: Clarification

juCi++ expects a CMakeLists.txt file to be in the base folder of a project.Is it possible to accommodate this via the CMake add_subdirectory() command?

Thereā€™s a CMakeLists.txt file at the root of the repo that builds libcinder and optionally the samples. Does this work in juCi++?

After discussing, we decided for the samples themselves that they wouldnā€™t contain a CMakeLists.txt file in the their base folder (instead it is in proj/cmake/CMakeLists.txt) as weā€™re moving all build files into the proj folder to reduce clutter. This isnā€™t a problem in CLion at least, as you can tell it where the base directory of your project is. Thereā€™s no feature like this in your IDE?

Edit: I also saw that juC++ is planning to ditch cmake support in favor of Bazel (yet another build language from google)ā€¦

TBH, I donā€™t know enough about Cinder and building with it to be certain. But just now I was able to remove the previous ā€˜buildā€™ dir inside the Cinder checkout dir, fire up Juci in the Cinder dir, and run a project build on that folder. As far as I can tell, it worked correctly. I included a capp of my desktop for you from after the fact, and if all the output obj files and the build output info text looks right to you, then it seems safe to assume the answer is yes.

Not that Iā€™m able to discern, no. However I wouldnā€™t be surprised to learn that Iā€™m simply unaware of it.

Haha you know Juci better than I do! :smiley: I was unaware of that. To me, itā€™s a great editor with itā€™s auto-complete and syntax error analysis and Iā€™ve quickly become rather attached to it (even though itā€™s still very early in itā€™s development cycle).