Native, built-in CMake Support in JUCE

The latest juce6 branch from Github.
cmake -B cmake-build -DJUCE_BUILD_EXAMPLES=ON -DJUCE_BUILD_EXTRAS=ON
MacOs 10.14.6 with native AppleClang.

I’ve had some success (generated XCode project and compiled fine) with GUI version of CMake, by only selecting JUCE_BUILD_EXTRAS but not JUCE_BUILD_EXAMPLES, during the Configure process.

I can’t repro the issue with that commandline. Could you try removing the build directory (rm -r cmake-build) and running that command exactly as pasted above, and then paste the output?

So I tried to replicate what I did with gui app in command line, it’s indeed same result:

$rm -r cmake-build
$cmake -B cmake-build -DJUCE_BUILD_EXTRAS=ON
Succeeded.

$rm -r cmake-build
$cmake -B cmake-build -DJUCE_BUILD_EXAMPLES=ON
Error.

Could you please run git diff origin/juce6 -- examples/Audio/? (assuming that the remote to the JUCE repository is origin)
Maybe a PIP header was added/changed.

1 Like

Problem solved!
I was using downloaded zip from Github web, and CMake is somehow very sensitive with these ._ hidden files OSX littered during un-zip. Removing all of those inside examples/Audio and the error is gone, same with the following folders /BLOCKS /DSP etc. This is funny.

1 Like

A good reason not to use globbing in CMake code!

@reuk I guess this code should be made more robust:

1 Like

Will there be an export path from exisiting .jucer files to CMakeLists? From what I’ve seen they have mostly similar role in a project. It would be great if existing configurations could be shared in some way instead of manually crafting them again in CMake script.

I know no-Projucer-required seems to be the motto of this move, but I kind of wish Projucer could assist me setting up a CMake styled JUCE project for things like “how to tell juce_audio_device to use ASIO” or “how to request macOS microphone permission” that were immediate GUI options in Projucer.

Actually my brief experience of CMake-GUI is that, the things I’m going to use it for are pretty much already covered by Projucer, which makes me wonder if it’s a good idea to have a form of CMake-GUI built-inside Projucer, to have CMake export as seamless as exisiting IDE targets.

3 Likes

@hsean agreed.
Even though basic CMake functionality (adding sources, linking libraries, etc) is very well documented around the internet, plugin/audio/iOS apps related settings are quite hard to find, and it would be great having a few examples showing how to recreate many of these features the Projucer used to set up for us.

2 Likes

At the moment I’m not sure adding a new CMake exporter to the Projucer is a good idea. Currently, the Projucer is designed to act as the ‘single source of truth’ for JUCE project setup. Any changes to the build settings should happen inside the Projucer, so that the settings are propagated to all the exporters as appropriate. Users know that when making changes inside the Projucer, any changes to the generated project files (Xcode projects, VS projects, makefiles etc.) will be overwritten.

The new standalone CMake support behaves in exactly the same way: when using CMake, the project CMakeLists are now the ‘single source of truth’ for project configuration, and CMake generates Xcode/VS projects which may get overwritten when reconfiguring the project.

We expect that most users of the CMake support will use it for functionality that simply doesn’t (and can’t) exist in the Projucer: stuff like linking against other CMake targets or writing helper functions to configure multiple targets with minimal boilerplate. The CMakeLists must remain the ‘single source of truth’ if we want to allow this kind of usage.

Given that users must be free to modify the generated CMakeLists as they see fit, the Projucer could only be used to generate an initial project. Thereafter, users would still have to edit the CMakeLists directly if they wanted to make changes. This in turn would require users to be familiar with JUCE’s CMake API.

This being the case, I think that time spent adding a new CMake exporter to the Projucer would better be spent improving/extending the CMake API documentation (currently located in examples/CMake/readme.md). If you’re not sure how to set up a CMake project after reading this document and looking at the example projects, let us know here on the forum and we’ll try to improve the documentation.

2 Likes

Hi @reuk,

Point well taken, and I personally agree with the need to have a single source of truth so to speak.

It seems like some current Projucer users would benefit from being able to compare what they have in CMakeList files against what the Projucer does.

To (hopefully) say that a little more clearly by way of example, if someone were to change the default cpp version in the Projucer, what is the appropriate analog using CMake (without conflicts with what the JUCE team has put together in CMake).

Also, I have seen questions about Android - that user group may benefit even more from something like this. To be fair, I really have no idea; I have not (yet) dabbled in JUCE/Android projects.

I don’t know that a Projucer exporter is needed, but giving something to show analogs between the two methods may give more clarity to users, especially those who have not used CMake before.

2 Likes

First of all, thank you again for putting in the time in this very important project that is already significantly improving our build flexibility!

A little bug report:
Seems like if you have a plugin that’s also a host, the link settings aren’t set correctly.

For example, a VST3 plugin that is also a VST3 host produces:

/Users/eyalamir/Code/XferBase/cmake-build-debug/_deps/juce-src/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2936:2: error: unknown type name 'CFBundleRef'
 CFBundleRef globalBundleInstance = nullptr; 

And other similar errors. Same for AU.

Also: I know I mentioned this before, but the IDE-generated projects in Xcode/Visual Studio do not have the sources for the JUCE/custom modules included in them.

1 Like

Please move that documentation to a location where we can easily discover it. Maybe it’s just me, but I would not expect documentation to live under examples.

Thanks for the bug report! I’ll look into it.

The rationale for putting the docs where they are now is that I thought users would generally want to use them in conjunction with the CMake examples, especially when using the examples as a starting point for a new JUCE project.
I also can’t really think of a better home, but I’m open to suggestions. At the moment there’s a note in the top level readme directing users to the CMake API docs, so hopefully they’re fairly discoverable for new users.

In a top-level docs/documentation folder, or having a top-level cmake folder with all CMake-related stuff.

So far I found using global installation with find_package(...path) is the more sane approach to mimic Projucer workflow, although structured differently most of the API flags are relatable to Projucer options, and cross platform stuff can be done in one file through if(WIN32).

Now I can see why export from Projucer isn’t as direct as I thought might be, CMake has some quirky ordering rules, for example set(CMAKE_CXX_COMPILER "...") must be placed before project(... CXX) to take any effect, or set(CMAKE_GENERATOR "...") is ignored unless you do it in command line -DCMAKE_GENERATOR=..., I believe I’m only scratching the surface.

Thanks for the suggestions, we’ve added a new top-level docs folder which contains the old doxygen folder, along with docs for the CMake API and Module Format.

2 Likes

Sorry to jump into the middle of this thread, but I think I read it now 3 times and I am still at a loss on how to add libraries/modules of my own.

Background - I have been using Remymuller’s juce-cmake for now, and a simple example of my usage in action could be seen at https://github.com/christofmuc/BCR2000_Master.

What I do - and I feel this is not recommended practice - is to split up my code into static libraries that rely only on JuceHeaders, and compile one executable as a proper Juce application, linking in my libraries. I understood from above that this is a dangerous thing to do as the headers are not independent of each other, but ok, so far that worked.

Now, what would be the proper new Juce 6 way? I tried porting that simple app tonight, but didn’t get very far. I think we are lacking an example for a “user custom module” with Cmake, I only found plugin, console, and gui. The commands above with the traditional add_library() … add_link_libraries() for whatever reasons don’t work for me. in INTERFACE mode, it doesn’t find the headers (probably because I do add JUCE as a subdirectory from the top-level CMake), and in PRIVATE mode it does find them but also builds and links the juce.cpp files, which I do not want as it will lead to subsequent duplicate object errors as outlined above.

Normally, I’d just say add_target_include_directories(), but that is as we all know forbidden because then you are excluding the AppConfig.h and the Juce headers bark.

I feel I am completely on the wrong track here?

The short answer is that there’s not a good way to build JUCE as a staticlib using CMake at the moment. I am thinking about potential solutions to this problem, but it’s surprisingly tricky to get right. The JUCE 6 CMake support only allows generating JuceHeaders for targets created with a juce_add_* functions - this mirrors the behaviour of the Projucer, but may not be flexible enough to port a juce-cmake project without changes. At the moment, I’d recommend structuring user code as JUCE modules, or as CMake INTERFACE libraries. In a personal test project, my CMakeLists for one custom library looks like this:

add_library(bspe INTERFACE)

target_sources(bspe INTERFACE
    bspe_pluginprocessor.cpp        bspe_pluginprocessor.h
    ...) # etc.

target_link_libraries(bspe INTERFACE arbe juce::juce_audio_utils Boost::boost)

When structuring code this way, each of the bspe_* source files is able to include JUCE module headers as expected. I later link bspe privately against a target created with juce_add_plugin, which builds all of the necessary JUCE sources and bspe sources (once) into the final plugin.

That sounds about right, and about what I tried, with the exception that I did not add the “INTERFACE” to the add_library() call, which might explain why my target_link_libraries(… INTERFACE…) didn’t work. I’ll give that a try and if it doesn’t work report back!

I gave up building JUCE as a library early on, but the current sturcture works fairly well for me - build my code as libraries that just assume that the top level project is a juce project and will fulfill the linker demands. What I didn’t understand at all is how I could build my own code as proper JUCE modules.

Thanks for the quick reply, BTW!

Ok, I could spend some more time looking at this. In unsorted order of priority:

  • Porting an application from juce-cmake must be done in one go, as we have namespace clashes between that module and yours. I know it’s unauthorized third party, but maybe it would be good to talk and see how to fix that. For now, its either this or that.
  • Using the INTERFACE library, the libraries really disappear from view in Visual Studio and get sucked into the main project. Virtually you lose any structure of a big project. Sadly, CMake’s source_group() doesn’t seem to work with INTERFACE libraries. This is kind of a killer.
  • the juce_generate_juce_header() doesn’t work with the INTERFACE library obviously, but that could be fixed in an intermediate step of adding an own file called “JuceHeader.h” with the appropriate includes into each of the projects as a migration help.
  • I don’t know why, but for me the line target_link_libraries(… INTERFACE juce::juce_audio_utils) still adds the juce cpp files to the interface libraries, and I add with multiple copies of the juce cpp files in the final build, again with duplicate objects. But maybe I am still doing something wrong.

So, because of #2 I’d rather not go with INTERFACE libraries - is there any Cmake support for creating proper custom JUCE modules by myself? I have only seen inofficial ways which also did not integrate properly with CMake.