Skip to content

Building an application with CMake

Everything needed to start an application on shuffle_engine: a generator that creates a project from scratch, three ways to bring the framework in, and two CMake functions.

cmake -D NAME=MyShow -D DIR=path/to/MyShow -P path/to/shuffle_engine/cmake/ShuffleEngineNewProject.cmake
cd path/to/MyShow
cmake --preset default # or: cmake --preset windows
cmake --build --preset default

Options:

Variable Values Default
NAME a C++/CMake identifier required
DIR a new or empty folder required
BACKEND core (plain C++20), juce (JUCE 9 app), tracktion (JUCE 9 + Tracktion Engine) core
ENGINE_DIR a local shuffle_engine checkout to use instead of downloading empty
ENGINE_REF git tag, branch or commit to download main

The generated project contains CMakeLists.txt, CMakePresets.json, src/main.cpp (a small working demo), README.md and .gitignore. These files are CC0: they belong to you. The framework itself stays under its own licence (Apache-2.0).

After cmake --install, the same generator is available inside the installed package: <prefix>/lib/cmake/ShuffleEngine/ShuffleEngineNewProject.cmake.

The generated CMakeLists.txt tries them in this order.

flowchart LR
    a{"SHUFFLE_ENGINE_DIR set?"} -- yes --> s["add_subdirectory<br/>(local checkout)"]
    a -- no --> f{"find_package<br/>(ShuffleEngine)?"}
    f -- found --> p["installed package"]
    f -- not found --> g["FetchContent<br/>from GitHub at SHUFFLE_ENGINE_REF"]
  1. Local checkout: add_subdirectory (<path> shuffle_engine). Best while developing the framework and the app together.
  2. Installed package: build shuffle_engine, then cmake --install <build> --prefix <dir>; in the app, find_package (ShuffleEngine CONFIG REQUIRED [COMPONENTS timecode ...]) with CMAKE_PREFIX_PATH=<dir>.
  3. Download: FetchContent_Declare (shuffle_engine GIT_REPOSITORY ... GIT_TAG ...) then FetchContent_MakeAvailable (shuffle_engine).

Used as a subproject, shuffle_engine builds only its libraries: tests, examples, install rules and the adapters that download libraries (codecs, plugin hosting, libremidi) are switched off unless it is the top-level project.

A project that already has JUCE adds shuffle_engine after it. When juce::juce_core exists at that point, JUCE already gives the platform, the sockets and the audio devices, so the three JUCE-free adapters that are otherwise on by default, platform, net_sockets and miniaudio, default to OFF: nothing is downloaded or built for them. To have one anyway (to move a port away from JUCE, for example), turn it on before adding the engine:

add_subdirectory (JUCE) # the project's own JUCE 9
set (SHUFFLE_ENGINE_WITH_MINIAUDIO ON CACHE BOOL "") # only if you want it
add_subdirectory (../shuffle_engine shuffle_engine)

SHUFFLE_ENGINE_WITH_NET_SOCKETS also builds net_websocket (the UI bridge over WebSocket and HTTP): a JUCE app that serves its web interface to browsers turns it on. Projects without JUCE, satellites included, keep all three on as before. shuffle_engine_find_juce() (called by shuffle_juce and by shuffle_engine_add_app with a JUCE backend) uses the project’s JUCE and checks its version once per configure, however many modules call it: anything before JUCE 9 stops configure with the version found and how to fix it.

shuffle_engine_add_app (MyShow
BACKEND tracktion # core | juce | tracktion (default: core)
GUI # windowed app; juce/tracktion only
SOURCES src/main.cpp src/MainWindow.cpp
USES timecode # shuffle_engine modules
PRODUCT_NAME "My Show" # optional
COMPANY_NAME "My Band" # optional
VERSION 1.0.0) # optional, defaults to the project version
  • core: a plain C++20 executable. No JUCE, no Tracktion: builds anywhere a C++20 compiler does.
  • juce: a JUCE 9 console app, or a GUI app with GUI. JUCE is found through JUCE_HOME (CMake or environment variable) or downloaded at the pinned version.
  • tracktion: like juce, plus Tracktion Engine through TRACKTION_HOME or a download at the pinned commit.

Backend apps get sensible defaults (no web browser, no curl, no splash screen, no modal loops). Add further JUCE modules with target_link_libraries.

BACKEND juce and BACKEND tracktion give the application JUCE (and Tracktion Engine); shuffle_engine itself builds no JUCE code. The framework’s JUCE and Tracktion adapter modules (juce_platform, juce_midi, juce_audio, juce_plugins, juce_webui, te_devices, te_engine) come from the shuffle_juce satellite (satellites.md). Add it after shuffle_engine, then name its modules in USES like any other:

add_subdirectory (../shuffle_engine shuffle_engine) # or FetchContent / find_package
add_subdirectory (../shuffle_juce shuffle_juce) # or FetchContent_Declare (shuffle_juce GIT_REPOSITORY https://github.com/ShuffleFramework/shuffle_juce.git ...)
shuffle_engine_add_app (MyShow
BACKEND juce
SOURCES src/main.cpp
USES setlist engine juce_platform juce_audio)

shuffle_juce finds the shuffle_engine already added by the parent project, so both use the same build. An app that USES juce_webui is built with the JUCE web browser component (WebKitGTK on Linux, WebView2 on Windows when shuffle_engine_find_webview2 finds its SDK). The AI task modules (ai_tasks, ai_audiocpp) come from shuffle_ai the same way.

add_executable (Tool tool.cpp)
shuffle_engine_use (Tool timecode)

Links modules to a target you created yourself. An unknown module name stops the configure step with the list of where modules live.

cmake/ShuffleEngineApp.cmake and cmake/ShuffleEngineDeps.cmake are public: they are installed with the package and available after add_subdirectory, FetchContent or find_package. Besides the two functions above they offer what satellites and larger builds need:

Function What it does
shuffle_engine_apply_warnings (<target>) the framework’s warning flags (/W4 /permissive- on MSVC, -Wall -Wextra -Wpedantic -Wshadow -Wconversion elsewhere), then shuffle_engine_apply_rt_checks
shuffle_engine_apply_rt_checks (<target>) with SHUFFLE_ENGINE_RT_CHECKS=ON and Clang 20 or later (not clang-cl), makes the compiler’s checks of SHUFFLE_RT functions errors; nothing otherwise
shuffle_engine_find_juce() the project’s own JUCE when juce::juce_core already exists, otherwise JUCE 9 from JUCE_HOME (CMake or environment variable) or a download at the pinned version; either way a version before 9 stops configure with the version found and the fix
shuffle_engine_find_tracktion() Tracktion Engine from TRACKTION_HOME or a download at the pinned commit
shuffle_engine_find_webview2 (<out>) TRUE in <out> on Windows when the WebView2 SDK package is where JUCE looks for it
shuffle_engine_fetch_catch2() Catch2 at the pinned version, with its CMake scripts on the caller’s module path (include (Catch), catch_discover_tests)
shuffle_engine_fetch_codecs(), _fetch_miniaudio(), _fetch_plugin_sdks(), _fetch_libremidi() the third-party libraries of the adapters, pinned and checked against their SHA-256; the engine calls them when the matching SHUFFLE_ENGINE_WITH_* option is on

cmake/ShuffleEngine.cmake (shuffle_engine_add_core_library, shuffle_engine_add_core_tests) builds the framework itself and is not public (decision 0020).

Whichever way the engine is brought in (add_subdirectory, FetchContent or find_package), these are set as cache entries, so the whole build sees them:

Variable Example
SHUFFLE_ENGINE_VERSION 0.1.0
SHUFFLE_ENGINE_VERSION_MAJOR, _MINOR, _PATCH 0, 1, 0
SHUFFLE_ENGINE_VERSION_PRERELEASE empty on a release, dev, rc.1
SHUFFLE_ENGINE_VERSION_STRING 0.1.0, 0.2.0-dev
SHUFFLE_ENGINE_API_LEVEL 1; grows with every minor or major version

find_package (ShuffleEngine 0.1 CONFIG REQUIRED) also checks the version (the same minor version before 1.0), and C++ code reads it from shuffle/base/Version.h.

The options an application or a satellite usually sets on shuffle_engine:

Option Default What it does
SHUFFLE_ENGINE_BUILD_TESTS ON at top level builds the test runners
SHUFFLE_ENGINE_BUILD_EXAMPLES ON at top level builds and runs the examples
SHUFFLE_ENGINE_BUILD_CONTRACTS as SHUFFLE_ENGINE_BUILD_TESTS builds the shuffle::<core>_contracts targets (the cores’ contract suites, header-only Catch2) without the engine’s tests, for adapters tested in another repository, such as a satellite (ports.md)
SHUFFLE_ENGINE_INSTALL ON at top level generates install rules (find_package support)
SHUFFLE_ENGINE_WITH_PLATFORM, _WITH_NET_SOCKETS, _WITH_MINIAUDIO ON, except inside a project that already has JUCE (above); _WITH_NET_SOCKETS OFF on WebAssembly build the platform, sockets and audio device adapters that need no JUCE (choose backends)
SHUFFLE_ENGINE_WITH_LIBREMIDI, _WITH_CODECS, _WITH_PLUGIN_HOST ON at top level (codecs not on WebAssembly, plugin hosting on desktops only) build the adapters that download a library

shuffle_engine_fetch_catch2() (in cmake/ShuffleEngineDeps.cmake, public) brings Catch2 at the pinned version and puts its CMake scripts on the caller’s module path, so a satellite’s test runner can include (Catch) and call catch_discover_tests.

Dependency Version Override
JUCE 9.0.3 -DJUCE_HOME=<checkout> or environment JUCE_HOME
Tracktion Engine develop at 5ee811e -DTRACKTION_HOME=<checkout> or environment TRACKTION_HOME

The pins live in cmake/ShuffleEngineDeps.cmake and are found with shuffle_engine_find_juce and shuffle_engine_find_tracktion; shuffle_juce uses the same pins, so an application and the adapters always agree on the JUCE and Tracktion versions.

Three tests (ctest -L packaging) check this page end to end with the build’s own compiler and flags:

  • packaging:new-project builds a project generated from scratch against the checkout and runs it;
  • packaging:subproject configures two projects that add the checkout after a stand-in JUCE: JUCE 8 is refused with its version, JUCE 9 is accepted and the JUCE-free adapters are not added;
  • packaging:install runs cmake --install into an empty prefix, then configures cmake/install-test, an application that sees only that prefix: find_package (ShuffleEngine <version> CONFIG REQUIRED COMPONENTS <every built module>), the version variables, every helper function above, every example of examples/ built with shuffle_engine_add_app against the imported targets, and shuffle_engine_fetch_catch2 with include (Catch); it then runs the version check and an example.

The juce and tracktion backends have been built and run with JUCE 9.0.3 and Tracktion 5ee811e.