Using CMake as build tool

The most common way of building audio plugins is probably Projucer. It is quite easy to setup a project using the tool, however when integrating the JUCE framework with other ones, such as PyTorch using libtorch. With libtorch, you can use machine learning inside of your audio plugin, however this is topic of another wiki entry.

Here, CMake comes into action. It allows the developer to include other libraries and frameworks (such as PyTorch) to be included into a project. It also allows the developer to export the project into more formats than Projucer provides, thus making the development more accessible for everybody.

It requires a bit more of knowledge to use CMake as a build tool due to the complexity of it. Also it doesn't have a "fancy" gui, but rather has none at all. Everything is done via the cli (Command Line Interface), however some IDE's provide a wrapper around those functions.

Guide

The following guide showcases the user on how to set up a JUCE starter project using CMake in favour of Projucer.

Downloading JUCE

JUCE is hosted on github and thus can be cloned. We will need to do that because there is a CMake Example inside of the repository. Please note that the CMake Example directory is called AudioPlugin. This name can be quite confusing and thus the name "CMake Example" will be continued to be used. First off, go to the JUCE github repository and clone it into a directory of your choice. This usually can be done via the following command:

git clone https://github.com/juce-framework/JUCE.git

Moving the CMake Example

To find the CMake Example, go into the examples/CMake/AudioPlugin/ folder. Copy it into a place where you find it again, for example to $HOME/projects/. After this step is done, you now should have the CMake Example (the AudioPlugin folder) inside of your $HOME/projects folder. If the copied folder is some other, this is fine too. Just make sure that the CMake Example isn't in the JUCE's repository anymore.

Add JUCE as a submodule

In this guide, instead of a global JUCE path, a per-repository JUCE instance is used. This should be possible to change, however i never did that and thus i cannot provide any assistance or guidance with that.

In simpler words: Projucer requires the developer to set a global JUCE path. A path to where the JUCE framework is located on the system. This also means, that every project will just use this global JUCE instance. When using CMake, this is usually not done in the global space. Every project has it's own JUCE instance.

This requires a bit more space on the computer, however it is quite handy for per-project tweaks, that might interfere with other projects. Also it makes sure, that the JUCE version will be exactly the one, that you used while developing. This means that if you use some feature that was removed in a newer version of JUCE, you will still be able to compile just fine, because it automatically doesn't use the latest version, but rather the version that you used while developing.

Here Git Submodules come quite handy. It allows the developer to add a repository into another repository. However instead of taking space on a git storage server, such as github or sourcehut, it just holds a reference to the repository. This makes the repository significantly smaller in size on the source forge.

To add JUCE as a submodule, first initialize a new git repository inside of the CMake Example:

git init

Next, add the JUCE repository as a submodule. This will create a new subfolder inside of the repository, that is called JUCE. In there, all the files from the JUCE repository can be found, however git is not treating as a regular subfolder, but rather as a reference to the repository provided. In our case, the JUCE repository:

git submodule add https://github.com/juce-framework/JUCE.git

Path to JUCE

CMake doesn't automatically detect the JUCE instance. The developer needs to define where the instance is located. To define this, edit the CMakeLists.txt with your editor of choice. Since JUCE is no enemy to CMake, easy access is already provided and the developer just needs to uncomment the following line:

add_subdirectory(JUCE)

Building

After all those steps, the CMake is finally able to detect JUCE inside of the CMake Example. The only thing left to do is to build. For this, two commands are needed. One to generate the build files and one to build the project. Please note, that both of these steps can take a substantial amount of time. Don't stress out if you have to wait a bit.

Generating the build files will create a folder called build. This will optimize the code for your hardware and environment. (Linux, MacOS, Windows):

cmake -B build .

After generating the build files, the next and final step is to build the application. This will compile the application using the files inside of the build directory. The binaries will be created in build/projectname_artefacts:

cmake --build build

Whats next?

In a project, some JUCE modules might want to be added. Assets, such as fonts or images can be required. Or maybe a new file with a own processor needs to be included into the project. This is all not done automatically, just as in Projucer, and you have to define this all in the CMakeLists.txt.