Native, built-in CMake Support in JUCE

Could you provide some more details about the issue you’re seeing? Specifically, it would be useful to know what your CMakeLists looks like. Are you passing the LAUNCH_STORYBOARD_FILE option to juce_add_gui_app, and/or the CUSTOM_XCASSETS_FOLDER option?

Hi @reuk. I will try to reproduce the problem in a small/simple project and then get back here. Anyways, I am not using the LAUNCH_STORYBOARD_FILE option, but I will try it now.

It would be great if juce cmake would be more friendly to lib using Juce in Plugin using juce and this lib

I’m having headache making it work. It either crashes or have weird non-virtual thunk to errors.
I made it work one time but then it fails again

I tried the suggestion in another thread

add_subdirectory(${PROJECT_SOURCE_DIR}/../JUCE ${CMAKE_CURRENT_BINARY_DIR}/JUCE)

target_link_libraries(myLib
    PRIVATE
        juce::juce_audio_basics juce::juce_audio_devices juce::juce_audio_formats
        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_video
    PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags)

target_compile_definitions(myLib
    PUBLIC
        JUCE_PLUGINHOST_VST=0
        JUCE_PLUGINHOST_AU=0
        DONT_SET_USING_JUCE_NAMESPACE=1
        JUCE_DISPLAY_SPLASH_SCREEN=0
        JUCE_REPORT_APP_USAGE=0
        JUCE_CHECK_MEMORY_LEAKS=0
        JUCE_QUICKTIME=0
        JUCE_USE_DIRECTWRITE=1
        JUCE_CATCH_UNHANDLED_EXCEPTIONS=0
        JUCE_COREGRAPHICS_DRAW_ASYNC=1
        JUCE_WIN_PER_MONITOR_DPI_AWARE=1
        JUCE_USE_FLAC=0
    INTERFACE
        $<TARGET_PROPERTY:myLib,COMPILE_DEFINITIONS>)

target_include_directories(myLib
    INTERFACE
        $<TARGET_PROPERTY:myLib,INCLUDE_DIRECTORIES>)

set_target_properties(myLibPROPERTIES
    POSITION_INDEPENDENT_CODE TRUE
    VISIBILITY_INLINES_HIDDEN TRUE
    C_VISIBILITY_PRESET hidden
    CXX_VISIBILITY_PRESET hidden)

where only this lib include Juce and link to it.
I still use juce_add_plugin in the main exe though

I don’t see anything obviously wrong with that. How are you adding the myLib target?
add_library(myLib STATIC) or similar?

My best guess is that some of your TUs are seeing JUCE headers with different sets of preprocessor definitions for some reason. Perhaps you could try building with cmake --build build-folder --target myplugin --verbose to check that your .cpp files and the JUCE modules are being built with the same set of preprocessor definitions. You should also check the verbose output to ensure that each module source file is only being built and linked into the plugin once.

yep I do add_library(myLib mySources.cpp …)
which is by default static

if I do this way, I got those errors:

Undefined symbol: non-virtual thunk to juce::Label::textEditorTextChanged(juce::TextEditor&)
Undefined symbol: non-virtual thunk to juce::Label::textEditorReturnKeyPressed(juce::TextEditor&)
Undefined symbol: non-virtual thunk to juce::Label::textEditorEscapeKeyPressed(juce::TextEditor&)
Undefined symbol: non-virtual thunk to juce::Label::componentMovedOrResized(juce::Component&, bool, bool)
Undefined symbol: non-virtual thunk to juce::Label::componentVisibilityChanged(juce::Component&)
Undefined symbol: non-virtual thunk to juce::Label::textEditorFocusLost(juce::TextEditor&)
Undefined symbol: non-virtual thunk to juce::Label::componentParentHierarchyChanged(juce::Component&)
Undefined symbol: non-virtual thunk to juce::Label::valueChanged(juce::Value&)

crashes happen in other cases.

The only thing that works right now is

target_link_libraries(myLib PUBLIC juce::juce_gui_basics)
with the definition mentioned above
and in my final app
target_link_libraries(${PROJECT_NAME} PUBLIC myLib juce::juce_audio_formats juce::juce_audio_utils juce::juce_audio_devices juce::juce_opengl juce::juce_cryptography)
with definition mentioned above

I see, you’re adding your own sources to the library too.

Can you try just putting the JUCE modules into the static lib, and putting any of your own sources into a separate target which depends on the JUCE lib?

same issue unfortunately

Undefined symbol: non-virtual thunk to juce::Label::textEditorTextChanged(juce::TextEditor&)
Undefined symbol: non-virtual thunk to juce::Label::textEditorReturnKeyPressed(juce::TextEditor&)
Undefined symbol: non-virtual thunk to juce::Label::textEditorEscapeKeyPressed(juce::TextEditor&)
Undefined symbol: non-virtual thunk to juce::Label::componentMovedOrResized(juce::Component&, bool, bool)
Undefined symbol: non-virtual thunk to juce::Label::componentVisibilityChanged(juce::Component&)
Undefined symbol: non-virtual thunk to juce::Label::textEditorFocusLost(juce::TextEditor&)
Undefined symbol: non-virtual thunk to juce::Label::componentParentHierarchyChanged(juce::Component&)
Undefined symbol: non-virtual thunk to juce::Label::valueChanged(juce::Value&)

Could it be related to the fact that the end targets (Standalone, Au, …) do not get the compile_definitions (JUCE_…) from the lib ? The SharedCode does

I tried adding those manually and it fixes the non virtual thunk.

Maybe related
MyProject_Standalone build the source file from the MyProject and link to it as well

Yes, as I said above:

These kinds of preprocessor definition issues are the main reason we recommend avoiding putting JUCE in a library target! It’s very easy for things to go wrong if some TUs see different preprocessor definitions.

That being said, I’m not sure why the preprocessor definitions wouldn’t be visible to the wrappers. This approach works for me, even when linking the JUCE staticlib PRIVATEly.

I just tried copying the source files from examples/CMake/AudioPlugin into a new directory, adding the following CMakeLists, and building with cmake . -B build -G Ninja; cmake --build build --verbose.

I’m able to open the resulting VST3 in the AudioPluginHost without issues, and the Standalone opens without problems. Could you try checking with the same setup? If the below works for you, then I think the issue is likely to be elsewhere in your project.

cmake_minimum_required(VERSION 3.18)

project(MYPROJ VERSION 0.0.1)

################################################################################

add_subdirectory(${PROJECT_SOURCE_DIR}/../juce-dev ${CMAKE_CURRENT_BINARY_DIR}/JUCE)

add_library(myLib STATIC)

target_link_libraries(myLib
    PRIVATE
        juce::juce_audio_basics juce::juce_audio_devices juce::juce_audio_formats
        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_video
    PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags)

target_compile_definitions(myLib
    PUBLIC
        JUCE_PLUGINHOST_VST=0
        JUCE_PLUGINHOST_AU=0
        DONT_SET_USING_JUCE_NAMESPACE=1
        JUCE_DISPLAY_SPLASH_SCREEN=0
        JUCE_REPORT_APP_USAGE=0
        JUCE_CHECK_MEMORY_LEAKS=0
        JUCE_QUICKTIME=0
        JUCE_USE_DIRECTWRITE=1
        JUCE_CATCH_UNHANDLED_EXCEPTIONS=0
        JUCE_COREGRAPHICS_DRAW_ASYNC=1
        JUCE_WIN_PER_MONITOR_DPI_AWARE=1
        JUCE_USE_FLAC=0
    INTERFACE
        $<TARGET_PROPERTY:myLib,COMPILE_DEFINITIONS>)

target_include_directories(myLib
    INTERFACE
        $<TARGET_PROPERTY:myLib,INCLUDE_DIRECTORIES>)

set_target_properties(myLib PROPERTIES
    POSITION_INDEPENDENT_CODE TRUE
    VISIBILITY_INLINES_HIDDEN TRUE
    C_VISIBILITY_PRESET hidden
    CXX_VISIBILITY_PRESET hidden)

target_compile_features(myLib PUBLIC cxx_std_11)

################################################################################

juce_add_plugin(demoPlugin
    FORMATS AU VST3 Standalone)

target_sources(demoPlugin PRIVATE PluginProcessor.cpp PluginEditor.cpp)

target_compile_definitions(demoPlugin PUBLIC
    JUCE_VST3_CAN_REPLACE_VST2=0)

target_link_libraries(demoPlugin
    PRIVATE myLib)

Humm

I was using

target_sources(${PROJECT_NAME} PUBLIC ${SOURCES})

target_link_libraries(${PROJECT_NAME} PUBLIC myLib)

When I was saying end targets (Standalone, Au, …) do not get the compile_definitions (JUCE_…) from the lib
I was talking of stuff like
JUCE_USE_DIRECTWRITE

Bu in theory as the end targets do not use specific juce stuff that shouldn’t be a problem
but in my case because of target_sources using PUBLIC, looks like it was included in those as well.
Maybe it’s related how you setup stuff with
juce_add_plugin

Are you saying it works when you do target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) instead?

I don’t think juce_add_plugin is doing anything magic here. It’s creating a static library target with the name passed to juce_add_plugin, and then creating separate targets for each plugin and linking the static lib to each plugin target.

The docs for target_sources say that sources with PUBLIC visibility will be added to both the SOURCES and INTERFACE_SOURCES properties, and that INTERFACE_SOURCES are used when building dependents (i.e. the plugin wrapper targets in this case).

I’ll check through the JUCE docs and make sure it’s clear that user sources should generally be added with target_sources(... PRIVATE)

not really.

My setup is a bit more complicated

cmake_minimum_required(VERSION 3.18)

project(MYPROJ VERSION 0.0.1)

################################################################################

add_subdirectory(${PROJECT_SOURCE_DIR}/../JUCE ${CMAKE_CURRENT_BINARY_DIR}/JUCE)

add_library(myLib_juce STATIC)

target_link_libraries(myLib_juce
    PRIVATE
        juce::juce_audio_basics juce::juce_audio_devices juce::juce_audio_formats
        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_video
    PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags)

target_compile_definitions(myLib_juce
    PUBLIC
        JUCE_PLUGINHOST_VST=0
        JUCE_PLUGINHOST_AU=0
        DONT_SET_USING_JUCE_NAMESPACE=1
        JUCE_DISPLAY_SPLASH_SCREEN=0
        JUCE_REPORT_APP_USAGE=0
        JUCE_CHECK_MEMORY_LEAKS=0
        JUCE_QUICKTIME=0
        JUCE_USE_DIRECTWRITE=1
        JUCE_CATCH_UNHANDLED_EXCEPTIONS=0
        JUCE_COREGRAPHICS_DRAW_ASYNC=1
        JUCE_WIN_PER_MONITOR_DPI_AWARE=1
        JUCE_USE_FLAC=0
    INTERFACE
        $<TARGET_PROPERTY:myLib_juce,COMPILE_DEFINITIONS>)

target_include_directories(myLib_juce
    INTERFACE
        $<TARGET_PROPERTY:myLib_juce,INCLUDE_DIRECTORIES>)

set_target_properties(myLib_juce PROPERTIES
    POSITION_INDEPENDENT_CODE TRUE
    VISIBILITY_INLINES_HIDDEN TRUE
    C_VISIBILITY_PRESET hidden
    CXX_VISIBILITY_PRESET hidden)

target_compile_features(myLib_juce PUBLIC cxx_std_11)

add_library(myLib CustomComponent.h CustomComponent.cpp)
target_link_libraries(myLib PUBLIC myLib_juce)

################################################################################

juce_add_plugin(demoPlugin
    FORMATS AU VST3 Standalone)

target_sources(demoPlugin PRIVATE PluginProcessor.cpp PluginEditor.cpp)

target_compile_definitions(demoPlugin PUBLIC
    JUCE_VST3_CAN_REPLACE_VST2=0)

target_link_libraries(demoPlugin
    PRIVATE myLib)

This will crashes. CustomComponent is just a dummy custom widget that inherit from juce::Component

If you want to give it a try.
Crashes at least on OSX.

Probably related to JUCE_CHECK_MEMORY_LEAKS not being defined on demoPlugin_Standalone

Looks like the issue here is that the wrappers do not take the flag from the main target unless they are on it explicitly and do not inherit from all the flag the original target has.

Yes, thanks for your patience with this issue. It looks like we’re being a bit conservative when selecting which flags get propagated through to the wrappers.

We can’t propagate all of the flags from the shared code target to the wrapper targets, as some of the JucePlugin_Build_* flags have different values depending on the target. Initially I was only passing the JUCE_MODULE_AVAILABLE_ definitions, but perhaps we can pass everything but the JucePlugin_Build definitions instead. I’ll test that out and update this thread once I’ve found a solution.

1 Like

I don’t mind spending time on this as long as I get something that works in the end :slight_smile:

We now propagate all flags other than the JucePlugin_Build_* flags and the JUCE_SHARED_CODE flag to the plugin wrappers:

I think this should fix the issue, let me know if not.

2 Likes

Hi! Any update on the static lib issue? I am completely stuck migrating my projects to JUCE 6 because of this, and have just wasted another night in trying to understand the CMake. I think by now I read the full source code of JUCEUtils.cmake, and can’t say I am happy about this. How are the compile defines read from the Defs.txt files created? There is a bit too much magic going on for my taste, but then again, this is a common problem with CMake.

Admittedly, I am at a loss about how to use the build system. I have a complex software build from 15+ static libs (which works because they configure JUCE all the same way).

My current hack is to define a “dummy plugin” which generates a JuceHeader.h with the configuration all the 15 static libs should use, and specify the path to the header as include file. But the compile fails because I do not have the compile switches that are in the Defs.txt file. How could I get those assigned to my targets? Is this a hidden CMake feature, or is there some helper function I have missed reading this file? Obviously, in a regular project all the switches are set, I suspect somewhere in _juce_configure_plugin_targets, but I cannot figure out how.

Thank you!