Native, built-in CMake Support in JUCE

No Projucer Required!

Over the past few months, we’ve been working to add standalone, idiomatic CMake support to JUCE. This idea has been around for a while (see this forum post from 2017), and was a popular suggestion in the 2019 JUCE User Survey, especially amongst our Pro users. This new native CMake support will effectively deprecate the beta CLion exporter in the Projucer.

Building with CMake can provide a few advantages over the current Projucer-based approach, including:

  • wider IDE/tooling integration, as most tools now have some kind of CMake support,
  • more flexible options for modularising code, beyond the JUCE module format,
  • simpler CI configuration, as the Projucer no longer needs to be built at the beginning of each pipeline,
  • easier integration with other projects that already build with CMake, and
  • allowing distribution of JUCE through CMake-based package managers, like Vcpkg and Hunter, making it faster to get started on new projects. (Coming soon; we’ll author packages once we’re happy that the CMake API is stable after a bit of real-world usage. Please don’t submit packages on our behalf!)

The predominant design goal of JUCE’s CMake support is to allow users to write build configs that looks like ‘normal’ CMake. Modules, executables, plugins, and binary data are just normal CMake targets, and including JUCE in a project is as simple as find_package(juce CONFIG REQUIRED) or add_subdirectory(JUCE). All of the example projects in the JUCE repo have been given new CMakeLists that you can browse to see how the support looks in practice. In order to support a completely Projucerless workflow, we’ve also added some CMake-based project templates to the repo at examples/CMake that you can copy, modify, and build immediately.

One side-effect of trying to make our CMake support idiomatic is that we require some very recent CMake features in order to seamlessly handle the complexities of a JUCE project. At the moment we require version 3.12 for apps, and 3.15 for plugins. If your system package manager doesn’t include a sufficiently recent CMake, you should be able to get the most recent version from the official downloads page.

Adding a whole new build system to JUCE gave us a chance to reevaluate the structure of a JUCE project; in particular, the role of the AppConfig.h and JuceHeader.h. The AppConfig.h was originally designed to hold global compile definitions for a project, but CMake allows us to specify compile definitions on a target very easily, even on a per-configuration basis. This build-system-level support obviates the need for a dedicated AppConfig.h, so this file is not generated when using CMake-based builds. Similarly, the role of the JuceHeader.h is to include the AppConfig.h before all of a target’s module headers, but this becomes less useful in the absence of the AppConfig. For this reason, we’ve made the JuceHeader ‘opt-in’, and it can be enabled using juce_generate_juce_header(<target>). Finally, it should go without saying that all intermediate build files have been moved to the CMake build tree, so there’s no more need for the JuceLibraryCode folder in the source tree.

CMake support is not limited to juce modules; you can also build your personal modules with CMake by registering them with juce_add_modules. We’ve also added a helper to generate a static library containing binary assets, juce_add_binary_data. One request from the community was to make it easier to package binary assets alongside code in a custom module. Using this new CMake support, it should be possible to add a binary data target with juce_add_binary_data(module_data SOURCES foo.txt bar.wav ...), and to introduce a dependency between the module and its binary data using target_link_libraries(my_module INTERFACE module_data).

Replicating all of the Projucer’s functionality is a massive undertaking, and we wanted to share our work with you now to check that we’re heading in the right direction. For this reason, the initial release doesn’t include full support for Android targets. We may look at adding better mobile support in the future, depending on the priorities of our CMake users. In the meantime, the Projucer isn’t going anywhere, and we will continue to support mobile projects in that way.

This being a very early release, it’s likely that our documentation might lack a little detail, and there may be certain use-cases that we didn’t manage to test thoroughly. If you find something missing that you’d like to see, please let us know and we’ll try to add it. Also, if you find any neat tricks that you can accomplish with CMake, please don’t hesitate to post them on the forum so that the community can benefit.

We hope you enjoy using this feature, and look forward to seeing what you create with it!

22 Likes

Will the Projucer have a CMake exporter? To ease the transition for those intending to evaluate it?

For the moment, our focus is on supporting CMake as a completely ‘standalone’ build solution, so our focus will be on improving the CMake API itself, along with its documentation. A new Projucer exporter is not planned currently, but we’ll consider it if such a feature becomes a frequent request.

Even without such an exporter, I expect the transition to be fairly painless. All of the CMakeLists on the juce6 branch were handwritten, and they should all be self-explanatory. We’ve also provided some example/template projects in examples/CMake, and there’s a comprehensive API guide in examples/CMake/readme.md.

Looks very similar to https://github.com/remymuller/juce-cmake

I suppose juce_add_plugin create one target per plugin format
and you can address those like that
myFunc(${PROJECT_NAME}AU)
Or are there cleaner macros ?

There’s no macro at the moment, but we could definitely make juce_add_plugin report the names of any targets it added.

At the moment, the target names are constructed by appending an underscore and the target kind to the base target name which was originally passed to juce_add_plugin. So, juce_add_plugin(AudioPluginDemo FORMATS AU VST VST3 Standalone) will create targets named AudioPluginDemo_AU, AudioPluginDemo_VST etc., along with a utility target AudioPluginDemo_All which will build all of the plugin formats in one go.

I don’t mind the _Format as long as the doc states the convention used.

Cool stuff in any case. Congrats !
I like PLIST_TO_MERGE

Haven’t check yet, but I would suggest PLUGIN_MANUFACTURER_CODE and PLUGIN_CODE
check for a valid one at generation time if you haven’t done it yet

This is great news. Thanks for the comprehensive information.

Which version/branch includes/will include this feature?

You can test it out right now on the juce6 branch (currently under GPL v3 exclusively). Once JUCE 6 is released officially, in a few months, cmake support will make its way to the develop and master branches, and will be available under open-source/educational/commercial licenses, in a similar way to the JUCE 5 licensing scheme. There are more details about the release plan and licensing options here.

I’ve added a property on plugin shared-code targets, which will allow you to query the list of plugins that will be built using the shared-code target. Usage looks like this:

juce_add_plugin(myplugin FORMATS AU VST3 Standalone ...)

# Find which targets were added
get_target_property(active_plugin_targets myplugin JUCE_ACTIVE_PLUGIN_TARGETS)

# Do some work with each final plugin
foreach(target IN ITEMS myplugin ${active_plugin_targets})
    # Set some property on each plugin target that we can build on this platform
    set_target_properties(${target} PROPERTIES MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>)
endforeach()

Should be up on the juce6 branch shortly.

1 Like

Hi, I don’t think I need to move to Cmake, or really want to learn another build system as Projucer does everything I want - is Projucer still going to be developed/enhanced?

2 Likes

Looks like I can change JUCE_INSTALL_DESTINATION
but it’s only for the cmake. Includes (the whole juce at first glance) are still copied in /usr/local/include
Would really like to try it but not mess my /usr/local :slight_smile:

Am I missing something ?

Thanks !

By the way, looks like JuceHeader.h and PkgInfo are added to examples projects although those file don’t exists.

To install to a custom location, you can set a custom prefix path when configuring, e.g.

# from the JUCE directory
cmake -B cmake-build-install -DCMAKE_INSTALL_PREFIX=/my/custom/path 
cmake --build cmake-build-install --target install

Then, to find this copy of juce using find_package(JUCE CONFIG REQUIRED) you’d build your project like so:

# from your project directory
cmake -B cmake-build -DCMAKE_PREFIX_PATH=/my/custom/path
cmake --build cmake-build

For the example projects, the JuceHeader.h and PkgInfo files should be generated at build time. That is, they won’t be immediately available, but if you run the actual build once, the files should be generated and the build should succeed.
If this doesn’t work, let me know I’ll try to work out what’s going wrong.

1 Like

Yes, we’re enhancing the Projucer as part of the JUCE 6 release too, and we’ll be previewing that soon. The Projucer isn’t going anywhere, but the current CMake exporter will likely be superseded by the new standalone CMake functionality.

3 Likes

First of all this is amazing, thank you @reuk and the rest of the team for making this happen!

I added a basic “0 configuration time” repo to show one way of structuring a repository with many apps/plugins using the new JUCE 6 Cmake features:

2 Likes

10 min to port a project from juce-cmake to juce own new cmake.
Yeahhh !

4 Likes

The JUCE team would like to thank @McMartin for his help reviewing this substantial chunk of work :slight_smile:

7 Likes

COPY_PLUGIN_AFTER_BUILD is supposed to work ?

I’m having a cmake error as soon as I declare COMPANY_COPYRIGHT or COMPANY_WEBSITE
in a juce_add_plugin

CMake Error at /Users/otristan/Desktop/testJuce/JuceBuild/lib/cmake/JUCE-6.0.0/JUCEUtils.cmake:1485 (if):
if given arguments:

    "STREQUAL" "existing_property-NOTFOUND"

  Unknown arguments specified
Call Stack (most recent call first):
  /Users/otristan/Desktop/testJuce/JuceBuild/lib/cmake/JUCE-6.0.0/JUCEUtils.cmake:1741 (_juce_set_property_if_not_set)
  /Users/otristan/Desktop/testJuce/JuceBuild/lib/cmake/JUCE-6.0.0/JUCEUtils.cmake:1780 (_juce_initialise_target)
  CMakeLists.txt:55 (juce_add_plugin)

other properties works fine.

Thanks !

This is great news for a current project where CLion is our main IDE.

1 Like

I think this issue is fixed by a commit I pushed yesterday evening. Could you check that your juce6 branch is pointing at 28ef311 and try again? You might need to remove your build folder and reconfigure from scratch.

1 Like