Native, built-in CMake Support in JUCE

This is unfortunately not supported by CMake… CMake only supports the following IDEs: Visual Studio, Green Hills, and Xcode. See cmake-generators(7) — CMake 3.28.0-rc5 Documentation for more details.

However, this doesn’t meant that Android support with CMake is not possible. It is just a lot more work :sweat_smile:

1 Like

Although it’s quite easy to build a C++ shared library for Android using CMake, building a full Android app is quite a bit more involved, as it requires some interop with Java, and the Java stuff is built with Gradle. So, to get a working Android project you need a working CMake build, and some slightly edited Java files, and a Gradle file to build the Java code and pass the right options to the CMake build. Gradle expects to invoke the CMake build itself, so if we wanted to have CMake run the show we’d end up with a chain where

  1. CMake generates a Gradle file, configures some Java sources, and calls Gradle
  2. Gradle builds the Java sources, and reinvokes CMake
  3. CMake builds a shared library containing JUCE

Finding a way to do this that isn’t horrendously confusing and error-prone is very tricky!

That makes sense. Actually all I’m interested in is building a shared library for inclusion in an Android project.

In that case you may be able to build something suitable with the current CMake support. Assuming you have Android Studio installed, you can configure CMake like this:

cmake -DCMAKE_BUILD_TYPE=Release \
  "-DCMAKE_TOOLCHAIN_FILE=/Users/me/Library/Android/sdk/ndk/20.0.5594570/build/cmake/android.toolchain.cmake" \
  -DANDROID_STL=c++_static \
  -DANDROID_NATIVE_API_LEVEL=23"
1 Like

Thanks!
The regex replace trick does exactly what I want. I didn’t think that this would work since the properties are set on files that are “different” from the INTERFACE_SOURCES , but I guess I still don’t fully get the spirit of CMake.

Such a no-generator-expression option sure would be hacky.
Would it be possible to use the non-installed Juce without add_subdirectory? I.e. add a function that generates JUCE.cmake and JUCEUtils.cmake that reference the sources so that this could be used exactly like the installed Juce:

include("path/to/checked/out/juce/cmake/generate_package.cmake")
juce_generate_package("${CMAKE_CURRENT_BINARY_DIR}/juce")

set(JUCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/juce")
find_package(JUCE CONFIG REQUIRED)

I don’t know how well this plays with the generated juceaide binary and how hard it is to force a regeneration when Juce is updated. Anyhow, I’m fine for now with the current solution.

I’ve never used the Projucer and I’m relatively new to Juce. A while back I used hand rolled CMake for a fixed set of modules to build a static library according to the Projucer generated makefiles. So I could just use different flags for that library.

@reuk @TobiasLudwig one can use string(GENEX_STRIP) to remove generator expressions (see https://cmake.org/cmake/help/latest/command/string.html#gene-strip).

1 Like

I think there is a way to get CMake to treat the build directory as an install directory, but that would be awkward to use because you’d still have to get CMake to run the build somehow. At that point, you may as well go a step further and install JUCE locally to your build tree, something like this:

include(FetchContent)

# If JUCE is a submodule you could use SOURCE_DIR here instead of the GIT_* args
FetchContent_Declare(juce
    GIT_REPOSITORY "https://github.com/juce-framework/juce"
    GIT_SHALLOW TRUE
    GIT_PROGRESS TRUE
    GIT_TAG "origin/juce6")

# This is our local install dir for JUCE
set(install_dir "${CMAKE_CURRENT_BINARY_DIR}/install")

# Check whether we already set up JUCE during a previous configure
FetchContent_GetProperties(juce)

if(NOT juce_POPULATED)
    # Clone repo if necessary, record that JUCE has been populated
    FetchContent_Populate(juce)
    # Configure JUCE
    execute_process(
        COMMAND "${CMAKE_COMMAND}" -S "${juce_SOURCE_DIR}" -B "${juce_BINARY_DIR}" "-DCMAKE_INSTALL_PREFIX=${install_dir}")
    # Install JUCE locally to the build tree
    execute_process(
        COMMAND "${CMAKE_COMMAND}" --build "${juce_BINARY_DIR}" --target install)
endif()

# Tell CMake where to find our local JUCE install
set(JUCE_ROOT "${install_dir}")
find_package(JUCE CONFIG REQUIRED)
1 Like

@reuk this is awesome!

I adopted your code into a rudimentary “FindJUCE-git” module you could add to your repo, and later just call:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Cmake")
find_package(JUCE-GIT REQUIRED)

project(JUCECMakeRepo)

add_subdirectory(MyPlugin)

example here (just grab repo and build, no need to set any flags):

1 Like

@McMartin I think string(GENEX_STRIP) doesn’t work in this cause, because I need to retain the BUILD_INTERFACE part of the generator expression, which is the path to the modules directory.

There are some things I don’t like about actually installing Juce to the build tree:

  • The unnecessary copy of all the files (This is probably neglectable compared to the time consumed for building juceaide)
  • I want to compile the Juce code with a high warning level because I want to identify and possibly fix potential issues. If I would install Juce, my changes would be made to the copy and not the checked out code.

Thanks for the detailed FetchContent example. I didn’t know of this module and this will come in handy somewhere else :slight_smile:

Makes sense, I’ll take a look at adding the necessary exports so that the JUCE build tree can be treated as an install tree. That should simplify the FetchContent stuff a little bit too.

I’ve put together a commit that allows the build tree to be used as a pseudo-install-tree. Usage looks like this:

include(FetchContent)

# You could also use `GIT_*` args to clone directly from github
FetchContent_Declare(juce SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/JUCE")

# Check whether we already set up JUCE during a previous configure
FetchContent_GetProperties(juce)

if(NOT juce_POPULATED)
    # Clone repo if necessary, record that JUCE has been populated
    FetchContent_Populate(juce)
    # Configure JUCE
    execute_process(COMMAND "${CMAKE_COMMAND}" -S "${juce_SOURCE_DIR}" -B "${juce_BINARY_DIR}")
    # Build JUCE
    execute_process(COMMAND "${CMAKE_COMMAND}" --build "${juce_BINARY_DIR}")
endif()

# Import JUCE targets directly from the build tree
include("${juce_BINARY_DIR}/JUCEExportConfig.cmake")

# juce:: targets are now available, as if `find_package(JUCE)` had been called
3 Likes

Hi @reuk, excellent work!

I was just giving CMake + CLion a spin on a project that uses a personal juce module, but ran into missing symbols linker errors. The custom module links to a third-party static lib passed in the “OSXLibs” module declaration metadata, but that doesn’t seem to be handled in JUCEUtils.cmake. Is that omitted on purpose? Am I missing something?

(I got it working by adding the path to the lib through my project’s target_link_libraries in CMakeList.txt, but most likely not the right approach)

Thanks for trying JUCE 6! This was just an oversight on my part, and there should be a fix up in the next day or so.

Nice one! Thanks much.

Modules now understand OSXLibraries, iOSLibraries, and windowsLibraries:

2 Likes

One addition that i would really fancy is a way to name plugin development builds differently (including AU Plugin Codes) so we can use stable versions of our own plugins to make music.

2 Likes

What is the recommended way to turn off the splash screen, if you’re using JUCE6 under GPL for internal evaluation now and buy a license later?

If your JUCE license allows you to disable the splash screen, you may add JUCE_DISPLAY_SPLASH_SCREEN=0 in your target’s compile definitions. This will turn off the splash screen for that target.

2 Likes

I’m new to CMake, the -DJUCE_BUILD_EXAMPLES=ON option from the document seems to cause the below error with CMake 3.17.2:

CMake Error at extras/Build/CMake/JUCEUtils.cmake:1958 (message):
PIP headers must declare a name field
Call Stack (most recent call first):
examples/CMakeLists.txt:26 (juce_add_pip)
examples/Audio/CMakeLists.txt:17 (_juce_add_pips)

– Configuring incomplete, errors occurred!

That’s strange, I just tried building with CMake 3.17.2 and the latest juce6 branch on macOS 10.15.4 and Xcode 11.4.1 and configuration succeeded with no issues. Can you give any more details about what caused the issue? Some info that could be useful:

  • The JUCE commit
  • The CMake command you tried
  • The platform you’re using to build (OS, compiler)