CMake Plugins Compile Too Many Sources vs Projucer

Working on migrating a project from Projucer to CMake. Using JUCE 6.0.8 at the moment.

We have written our CMakeLists to use juce_add_plugin, like so:

juce_add_plugin(
  MyPlugin
  ....
  FORMATS Standalone VST VST3 AU AAX )

....

target_link_libraries( MyPlugin
  PUBLIC
     juce::juce_audio_basics
     juce::juce_audio_devices
     juce::juce_audio_formats
     juce::juce_audio_plugin_client
     juce::juce_audio_processors
     juce::juce_audio_utils
     juce::juce_core
     juce::juce_cryptography
     juce::juce_data_structures
     juce::juce_dsp
     juce::juce_events
     juce::juce_graphics
     juce::juce_gui_basics
     juce::juce_gui_extra
     juce::juce_opengl
     juce::juce_osc
     juce::juce_video
)

The thing we are noticing is that a lot of the dependencies for every type of plugin client are showing up in every plugin. This has led to an increase in build times, especially in CI where it seems every plugin is now building extra stuff, including plugin client formats we are not even targeting?

Here we have the target membership generated by Projucer, which only contains the client code for the specified target type
isolated-per-target

Here we have the sources being included by the CMake target:

Is there something I’m missing in controlling the way the sources get added to the auto generated plugin targets? Or is this a deficiency in the CMake API?

Thanks!

This behaviour is intentional. If you look at the contents of the wrapper files, you’ll see that they’re surrounded by guards like #if JucePlugin_Build_VST3. This means that when building a specific plugin format (e.g. VST3), the wrappers for the other formats will appear to be empty to the compiler. Other than preprocessing (which should be fast), these files should not contribute to the build time.

If you look at the build output in Xcode, it will show you how much time is spent building each TU. When I do that on my machine while building a VST3, all other wrapper files (AU, VST2, RTAS, AAX) each take 0.1s. Therefore, I think your build performance issues have a different cause.

You can also get more fine-grained build profiling information by adding -ftime-trace to your compiler flags, then viewing the generated json files in Chrome (type about:tracing into the address bar then drag the json file into the window).

2 Likes

OK thanks for the tip, I’m seeing the guards now, will look into the time tracing info. Thanks

Based on your recommendation @reuk did some profiling. The main thing I ended up noticing is that the CMake targets were all re-building things like juce_gui_basics etc. The reason for this was that I had put PUBLIC for the linkage of the JUCE libraries on the shared plugin library.

Changing this to PRIVATE brought the build times much closer inline with the Projucer. Now only the plugin_client related files are being compiled.

However there is still some discrepency in the way these are being handled. On the Projucer side, it seems to be compiling a smaller subset of the AU Wrapper code? Is this stuff just split up differently somehow?

Projucer:

  2701 ms: XXXXX - AU.build/Objects-normal/x86_64/include_juce_audio_plugin_client_AU_1.o
  1318 ms: XXXXX - AU.build/Objects-normal/x86_64/include_juce_audio_plugin_client_AU_2.o
   121 ms: XXXXX - AU.build/Objects-normal/x86_64/include_juce_audio_plugin_client_AU_1.o
    88 ms: XXXXX - AU.build/Objects-normal/x86_64/include_juce_audio_plugin_client_AU_2.o

CMake:

  7274 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_AU_1.o
  4165 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_AU_2.o
   317 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_AU_1.o
   237 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_AU_2.o
    81 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_RTAS_utils.o
    81 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_Standalone.o
    81 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_RTAS_4.o
    80 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_RTAS_3.o
    77 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_RTAS_2.o
    51 ms: XXXXX_AU.build/Objects-normal/x86_64/juce_audio_plugin_client_RTAS_1.o
1 Like

I don’t see this behaviour here. Building an AU, both CMake and Projucer projects take around 3.5 and 1.6 seconds to build the AU_1 and AU_2 files respectively. Building a VST3 target, the VST3 wrapper takes about 6 seconds in both CMake and Projucer projects. In a Standalone target, the Standalone wrapper takes about 3 seconds for both CMake and the Projucer.

Are you definitely building both projects in the same configuration (debug, release etc.)?

It doesn’t help to find the differences between cmake and Projucer, but I still wanted to mention that you are adding modules that you most likely don’t need in a plugin:

  • juce_audio_devices: a plugin doesn’t talk to devices
  • juce_plugin_client: does your plugin host other plugins?
  • juce_audio_formats: might be needed, if you read audio files
  • juce_video
1 Like

Like @daniel said for most plugins you just need to link juce_audio_utils.

Sometimes you may need others like juce_gui_extra, juce_dsp, etc but you want to make sure those are actual dependencies of your project before you bring them in.

Also regarding build times on Mac, Universal Binary builds are usually 2x the time of a regular build, so make sure that you disable those in local builds.

In CMake I do something like:

option(UniversalBinary "Build universal binary for mac" OFF)

if (UniversalBinary)
    set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE INTERNAL "")
endif ()

So I can pass the -DUniversalBinary=1 flag only when deploying a final build.