Using CMake flow to build libraries, Android apps

I just started to use the new JUCE 6 distribution.
Reading the CMake documentation, I did not see any instructions or example on:

  • Building Android application (I saw examples for iOS application)
  • Building libraries (static/dynamic)

Can someone point me to how I can do that?

At the moment, the CMake support focuses on building plugins, gui apps, and console apps, using the JUCE ‘build everything directly into the final binary’ model. Unfortunately we don’t have android or library examples, yet.

There are good reasons in both cases:

  • The Android/Gradle build system expects to ‘own’ the build, and will itself invoke CMake. That means it’s not really possible to write a CMake-only Android build - Gradle is required too. There’s also a lot of stuff that the Projucer normally does (like modifying and copying boilerplate Java files into the Gradle project) that we can’t easily recreate from a CMake build, because that stuff needs to happen before Gradle is invoked. It’s a bit of a circular problem, with no clear solution at the moment. That being said, it should be possible to create a new ‘native’ project which includes a JUCE-dependent shared library in its CMake portion. This leads onto JUCE-dependent libraries…
  • Due to the way JUCE modules work, it’s important that every module sees exactly the same set of JUCE_MODULE_AVAILABLE_* flags during the build, which in turn means that the final set of JUCE modules that end up in the build must be known ahead of time. When building JUCE into a library, it’s very easy to run into situations where a JUCE module gets included twice in the build, sometimes with different flags, which is likely to introduce subtle bugs. We don’t really want to encourage this build model, because it’s too easy to get it wrong.

With that out of the way, it should be possible to build a JUCE staticlib using a setup a bit like this:

add_library(juce_plugin_modules STATIC)

target_link_libraries(juce_plugin_modules
    PRIVATE
        juce::juce_audio_utils
        juce::juce_dsp
    PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags)

target_compile_definitions(juce_plugin_modules
    PUBLIC
        JUCE_WEB_BROWSER=0
        JUCE_USE_CURL=0
    INTERFACE
        $<TARGET_PROPERTY:juce_plugin_modules,COMPILE_DEFINITIONS>)

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

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

This will build the audio_utils and dsp modules (along with all their dependencies) into a staticlib, which exports the recommended warning/config/lto flags in its interface. Note that if you choose to use an approach like this, the staticlib should be the one and only source of JUCE modules in the project. That means, all juce-dependent targets in the project should link against the same juce_plugin_modules target, and they should avoid linking any additional JUCE modules directly.

1 Like

Thanks for the detailed reply.

Regarding libraries: I would like to build libraries of my own code (not juce modules). My applications will use these libraries.
The projucer has static library project that could do that. How can this be replicated using the new CMke flow?

To create a custom library containing your own code, you can use the add_library function, which is a CMake built-in. A simple library might look like this:

# Tell CMake to add a static library target
add_library(custom_lib STATIC)
# Add sources to the target
target_sources(custom_lib PRIVATE
    src_0.cpp    src_0.hpp
    src_1.cpp    src_1.hpp) # etc.
# Tell CMake where our library's headers are located
target_include_directories(custom_lib PUBLIC
    "${CMAKE_CURRENT_SOURCE_DIR}")

If you plan to use the library in a plugin, you may also need to set the following properties on the library:

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

Thanks.
Last question: when building the GUI example using CMake, I notice that I see the JUCE splash screen on startup, even though I have an indie license.
How do I should set the project so this splash screen will not be shown?

If your license terms allow, you may add JUCE_DISPLAY_SPLASH_SCREEN=0 to your target_compile_definitions to hide the splashscreen.

Is there an example CMakeFile example to that can be used to build and Audio Application?
I tried starting from the GUI app, but I get many errors along the way…

There isn’t, but it shouldn’t be all too different to the gui app example. The AudioPluginHost also has a CMake build, so you could check that out to see how it works.

What kinds of errors are you seeing?

Sorry it took a bit of time to get back to you.
I was able to create an audio application using CMake. It took a bit.

I’m building now quite a large application, and it makes sense to ma to partition it to several static libraries, test console apps, and the final application (this is the method I have been using a lot to develop large scale programs using other frameworks, like Qt.

I believe it would be very useful if the JUCE CMake could have additional commands to add static or dynamic library targets. Just using add_library() command, requires to add a lot of other CMake directives to allow it to build correctly and link agains the other applications.

Just a suggestion…

I agree it would be great to simplify the flow of static library creation with Cmake.

I do have to say, @YairAtRoli, that developing using JUCE modules can achieve a similar flow to what you’re after - this is exactly how we’re structuring things and have quite a lot of modules as well as apps/plugins/tests that call functions from those shared libs.

Indeed, using JUCE modules (or user modules) can achieve the same thing. However, for my application, I’m using proprietary (US Patented) algorithm we have developed. I assume that at a certain point, I’ll have to use some third party developers/company to work on the UI (as I’m not the greatest UI designer in the world ;-).
Having all the IP code as library, can enable that in a secure way (as I can not expose the source code).

So, what I’d do in your case is to build libraries that link with your juce-based code but don’t export any of the JUCE functions.

@reuk correct me if I’m wrong, but I think if you’re only exporting your own interface (and not JUCE) via DLL/static libs you shouldn’t have any problems, similar to how multiple DLL/VST files can live in a host environment while each one links to a different implementation of JUCE.

Am I right to assume that?

Yes, this is exactly what I want to do.
My libraries might use JUCE API, but the JUCE modules/API should only link to the final application.

That’s not what I meant - what you’re suggesting will not work (unless you use JUCE modules/interface libraries and expose your code).

What I think you should do is link your closed source part privately with JUCE, not exposing to the caller your internal library use.

Instead, only export your own functions like process() or calculateAlgorithm(). I believe that way you can accomplish what you wanted and hide code from your users (which could also link with a different version of JUCE if they want to).

1 Like

Can you suggest a CMake file that will do that (assuming my source files are: my_class1.h, my class1.cpp, my_class2.h, my_class2.cpp)?

I believe that this would be true for a dynamic library built with hidden symbol visibility. I think it’s untrue for static libraries. Static libraries only support visibility in the plain C++ sense (internal/external linkage), and the bulk of JUCE’s functions have external linkage. Statically linking two copies of JUCE into the same binary will result in ODR violations for this reason.

1 Like