Native, built-in CMake Support in JUCE

updating did fix COMPANY_COPYRIGHT, COMPANY_WEBSITE
and COPY_PLUGIN_AFTER_BUILD

Thanks !

1 Like

What about adding
killall -9 AudioComponentRegistrar
in the AudioUnit copy phase ?

Otherwise you cannot test your AU straight away.

Maybe doing it only in the case of the .component not existing in the folder previous to the copy.

Moreover in the plist type is wrong, it uses kAudioUnitType_Effect instead of aufx
Otherwise it’s not detected in auval
At least this is what should be done according to https://github.com/WeAreROLI/JUCE/tree/juce6/examples/CMake
so either update the doc (and mentioning we should use single quote) or it should be converted by the cmake

1 Like

By the way, what is the rationale when using juce in utility library as well as in the main project ?
Something like that ?

if(NOT TARGET juce::juce_audio_basics)
    find_package(JUCE CONFIG REQUIRED)
ENDIF()

target_link_libraries(myLib PRIVATE juce::juce_audio_basics)

Could the lib could access to JuceHeader.h as well ?
if yes what is the way to add it.
${juce_header} ?

Thanks !

I’m not sure exactly what you’re trying to achieve, so I might not be able to give a useful answer (sorry!). If you have a target main which depends on util which depends on juce::juce_audio_basics, something like this might work:

# always require JUCE for the util library
find_package(JUCE CONFIG REQUIRED)

add_library(util)
# Allows util to include headers from juce_audio_basics
# Note that we *don't* want to build juce_audio_basics directly into our
# util library (so no PRIVATE or PUBLIC linking here), because otherwise
# we might end up with duplicate symbols (ODR violations) if we link against
# other targets that also happened to contain compiled juce modules. 
target_link_libraries(util INTERFACE juce::juce_audio_basics)

juce_add_gui_app(main)
# On executable targets we *should* link privately
target_link_libraries(main PRIVATE util)

How come CMake or some other standard build system wasn’t available already as opposed to creating Projucer as the default?

The thing is that you want the fact that util lib uses Juce to be an internal matter to the lib itself so you want those detail to be in its CMakelist.txt of util not in the main project one.
So something like that ?

if(NOT TARGET juce::juce_audio_basics)
    find_package(JUCE CONFIG REQUIRED)
ENDIF()

target_link_libraries(myLib INTERFACE juce::juce_audio_basics)

If it’s definitely an implementation detail, then you should use PRIVATE linking as in your original question, but be careful not to link myLib into a target which includes any of the same JUCE modules as myLib, or you’ll run into ODR issues.

The check for the existence of juce_audio_basics seems a bit suspect. I think it should be safe to just call find_package(JUCE... unconditionally in every place that it’s needed, and this will convey intent better and keep the code cleaner.

You can generate a juce header for myLib simply by calling juce_generate_juce_header(myLib).

If I don’t do the conditional check I have

CMake Error at /Users/otristan/Desktop/testJuce/JuceBuild/lib/cmake/JUCE-6.0.0/JUCEConfig.cmake:57 (add_executable):
  add_executable cannot create imported target "juce::juceaide" because
  another target with the same name already exists.

With stuff like boost I indeed do not have this issue.

Ah thanks, I’ll see if I can fix this on our end.

1 Like

This commit should allow multiple calls to find_package(JUCE):
https://github.com/juce-framework/JUCE/commit/1dd3b6a711d0d54bfb5bd99d176cbbf1103e8254

The AudioUnit plist issue is fixed by this commit:

Thanks for all your feedback so far, please let us know if you run into any other issues.

Cool !

please add
include_guard(GLOBAL)
at the top level CMakeLists.txt of Juce so if I include juce using
add_subdirectory(${PROJECT_SOURCE_DIR}/../JUCE ${CMAKE_CURRENT_BINARY_DIR}/JUCE)
it works as well in the case of sub library depending on Juce as well using the same method.

Thanks !

I’m not sure adding the same directory multiple times is something we want to encourage. Could you provide an example use-case? Perhaps there’s an alternative way of structuring the project that would sidestep this requirement altogether.

I did a quick search through some other popular libraries with CMake builds and couldn’t see this being done anywhere, which suggests this isn’t a common requirement.

Let’s say you have a utility lib that depends on Juce for some file reading.

For convenience use, you don’t want to use the find_package which requires you to install JUCE in your system but uses the add_subdirectory(JUCE) approach that you offer in your example.
You have Juce dir in your projects root folder like some sort of SDK shared across your whole projects.

Now you have your main project which uses Juce for its UI and depends on this utility lib
You want to use there as well the add_subdirectory approach.

This approach is quite convenient as you only need to do a git pull to be up to date. No need to build and install the CMake manually.
Clearly we update JUCE more than boost for example.

This is not a common requirement because most lib will only offer the find_package approach.

and voila.

Is there a reason you can’t just depend on the util library pulling in JUCE? Why do you need to add it from the top level CMakeLists too?

Because technically if I separate my project the right way, I have no idea or shouldn’t have to care that the util library is actually using Juce to do some of its stuff. Moreover, if at some point I change the implementation of my util lib to use boost filesystem/iostream for example I don’t want to change the cmake of my main program. make sense ?

The main thing that’s putting me off is that, if we add include_guard and remove it at some point in the future (because we discover some unintended consequence of the include guard), the removal will be a breaking change. However, if we leave it out, we can add it at any point without breaking peoples’ projects.

Hiding the JUCE dependency sounds nice in theory, but that’s only going to work if util builds as a shared library with hidden symbol visibility, so that none of the contained JUCE symbols are visible to main. I think in practice, if util is a static library you need to know that it uses JUCE because otherwise you might end up (for example) building juce_core into both util and main. On some platforms this would result in “duplicate symbol” errors at link time, but on other platforms the duplicates will get silently discarded and the linker will just choose one set of symbols to use. If you’re lucky, this will result in weird runtime crashes when types with unexpected layouts are passed to some function (imagine util was built with JUCE_STRING_UTF_TYPE=8 but main was built with JUCE_STRING_UTF_TYPE=32… strings are going to end up broken in either util or main depending on which symbols the linker decides to keep). If you’re unlucky, everything will appear to work for a while, but then break when util changes to a different JUCE version than main.

At the moment this kind of project organisation just feels a bit too error-prone to explicitly endorse with an include_guard.

1 Like

Your call.
if you only use CMake and your util lib is added through add_subdirectory then you don’t get the duplicated symbol issue FWIW as CMake handle this nicely.

That would be true if JUCE was a normal staticlib target, but it isn’t. JUCE modules are separately compiled into each target that publicly/privately links against them, which means that if util and main both privately link juce_core, main will end up failing to link, or with ODR violations.

For example, in this CMakeLists, main will fail to link with duplicate symbol errors:

add_subdirectory(JUCE)

add_library(util OBJECT lib.cpp)
target_link_libraries(util PRIVATE juce::juce_core)
target_compile_definitions(util PUBLIC JUCE_STANDALONE_APPLICATION=1)

add_executable(main main.cpp)
target_link_libraries(main PRIVATE util juce::juce_core)

Results in:

duplicate symbol 'juce::String::formattedRaw(char const*, ...)' in:
    Modules/CMakeFiles/util.dir/__/SDKs/JUCE/modules/juce_core/juce_core.mm.o
    Modules/CMakeFiles/main.dir/__/SDKs/JUCE/modules/juce_core/juce_core.mm.o
duplicate symbol 'juce::String::createHex(unsigned long long)' in:
    Modules/CMakeFiles/util.dir/__/SDKs/JUCE/modules/juce_core/juce_core.mm.o
    Modules/CMakeFiles/main.dir/__/SDKs/JUCE/modules/juce_core/juce_core.mm.o
duplicate symbol 'juce::operator<<(juce::String&, unsigned long long)' in:
    Modules/CMakeFiles/util.dir/__/SDKs/JUCE/modules/juce_core/juce_core.mm.o
    Modules/CMakeFiles/main.dir/__/SDKs/JUCE/modules/juce_core/juce_core.mm.o
etc. etc.

Note that I’m using an OBJECT library rather than a STATIC library, just to force the linker to include all object files in main. The only difference with a STATIC library would be that the linker may discard/ignore objects which it considers to be duplicates from the static library. This would still be an ODR error, even though the linker wouldn’t warn us!

1 Like

Fair enough.

In that case, it’s best to do that in my own code

if (NOT TARGET juce::juce_audio_basics)
    add_subdirectory(${PROJECT_SOURCE_DIR}/../JUCE ${CMAKE_CURRENT_BINARY_DIR}/JUCE)
endif()
target_link_libraries(util PRIVATE juce::juce_audio_basics)

Probably for the same reason that JUCE has its own variants of std::unique_ptr and std::string - at the time there wasn’t any standard build system. The world’s changing and it’s good that JUCE adapts to it.

5 Likes