GitHub - szkkng/juzi: A Zig build API for JUCE

4 min read Original article ↗

juzi

Build JUCE projects using the Zig build system.

Limitations

  • Windows is not supported due to Zig issue.
  • Formats: VST3, AU, Standalone.

Requirements

  • Zig v0.15.2

Dependencies

  • JUCE v8.0.11 (automatically fetched by the Zig build system)

Usage

Initialize a Zig build project if you haven't already:

Download and add juzi as a dependency by running the following command in your project root:

zig fetch --save git+https://github.com/szkkng/juzi

Then, configure your build.zig to use juzi.
Here is an example configuration for an audio plugin project:

const std = @import("std");
const zon = @import("build.zig.zon");
// Import juzi build utilities.
const juzi = @import("juzi");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // Create project configuration.
    const config = juzi.ProjectConfig.create(b, .{
        .product_name = "JuziPlugin",
        .version = zon.version,
        .bundle_id = "com.example.juzi",
        .plugin_manufacturer_code = "Juzi",
        .plugin_code = "Juzi",
        .formats = &.{ .vst3, .au, .standalone },
    });

    // Create the module for the plugin's C++ source files.
    const module = b.createModule(.{ .target = target, .optimize = optimize });
    module.addCSourceFiles(.{
        .root = b.path("src"),
        .files = &.{
            "PluginEditor.cpp",
            "PluginProcessor.cpp",
        },
        .flags = &.{
            "--std=c++20",
            "-Wall",
            "-Wextra",
            "-Werror",
        },
    });

    // Initialize juzi setup using this module and the juzi dependency.
    const juzi_dep = b.dependency("juzi", .{});
    var juzi_setup = juzi.Setup.init(juzi_dep, module);

    // Configure JUCE-related preprocessor macros.
    juzi_setup.addJuceMacro("JUCE_VST3_CAN_REPLACE_VST2", "0");
    juzi_setup.addJuceMacro("JUCE_WEB_BROWSER", "0");
    juzi_setup.addJuceMacro("JUCE_USE_CURL", "0");

    // Configure embedded binary data here, similar to JUCE's add_binary_data.
    // juzi_setup.addBinaryData(.{
    //     .namespace = "JuziBinary",
    //     .header_name = "JuziBinary",
    //     .files = &.{ "res/juzi.wav", "res/juzi.icon" },
    // });

    // After configuring juzi, add plugin targets for the selected formats.
    const plugin = juzi_setup.addPlugin(.{
        .juce_modules = &.{juzi.modules.juce_audio_utils},
        .config = config,
    });

    // Add the collected install steps as dependencies of the top-level install step.
    var steps_it = plugin.install_steps.valueIterator();
    while (steps_it.next()) |step| {
        b.getInstallStep().dependOn(step.*);
    }
}

To build:

zig build -Doptimize=ReleaseFast

For audio plugin projects, you can build a specific format and install it:

zig build vst3 -Doptimize=ReleaseFast -p ~/Library/Audio/Plug-Ins/VST3

The vst3 step becomes available when you specify the format in your ProjectConfig, for example:

const config = juzi.ProjectConfig.create(b, .{
// ...
    .formats = &.{ .vst3 },
});

You can list all available steps by running:

ProjectConfig fields

These fields correspond to the arguments used in JUCE’s juce_add_<target> functions. Refer to the JUCE CMake API documentation for detailed behaviour of each option.

If you're using ZLS, your editor can show available values and jump to definitions, making it easy to inspect each field.

Note that some fields are not yet implemented.

juce_add_<target> argument ProjectConfig field
PRODUCT_NAME product_name
VERSION version
BUILD_VERSION build_version
BUNDLE_ID bundle_id
MICROPHONE_PERMISSION_ENABLED microphone_permission_enabled
MICROPHONE_PERMISSION_TEXT microphone_permission_text
CAMERA_PERMISSION_ENABLED camera_permission_enabled
CAMERA_PERMISSION_TEXT camera_permission_text
BLUETOOTH_PERMISSION_ENABLED bluetooth_permission_enabled
BLUETOOTH_PERMISSION_TEXT bluetooth_permission_text
LOCAL_NETWORK_PERMISSION_ENABLED local_network_permission_enabled
LOCAL_NETWORK_PERMISSION_TEXT local_network_permission_text
SEND_APPLE_EVENTS_PERMISSION_ENABLED send_apple_events_permission_enabled
SEND_APPLE_EVENTS_PERMISSION_TEXT send_apple_events_permission_text
FILE_SHARING_ENABLED (not implemented)
DOCUMENT_BROWSER_ENABLED (not implemented)
STATUS_BAR_HIDDEN (not implemented)
REQUIRES_FULL_SCREEN (not implemented)
BACKGROUND_AUDIO_ENABLED (not implemented)
BACKGROUND_BLE_ENABLED (not implemented)
APP_GROUPS_ENABLED (not implemented)
APP_GROUP_IDS (not implemented)
ICLOUD_PERMISSIONS_ENABLED (not implemented)
IPHONE_SCREEN_ORIENTATIONS (not implemented)
IPAD_SCREEN_ORIENTATIONS (not implemented)
LAUNCH_STORYBOARD_FILE (not implemented)
CUSTOM_XCASSETS_FOLDER (not implemented)
TARGETED_DEVICE_FAMILY (not implemented)
ICON_BIG (not implemented)
ICON_SMALL (not implemented)
COMPANY_COPYRIGHT company_copyright
COMPANY_NAME company_name
COMPANY_WEBSITE company_website
COMPANY_EMAIL company_email
DOCUMENT_EXTENSIONS document_extensions
NEEDS_CURL needs_curl
NEEDS_WEB_BROWSER needs_web_browser
NEEDS_WEBVIEW2 needs_webview2
NEEDS_STORE_KIT needs_store_kit
PUSH_NOTIFICATIONS_ENABLED (not implemented)
NETWORK_MULTICAST_ENABLED (not implemented)
HARDENED_RUNTIME_ENABLED (not implemented)
HARDENED_RUNTIME_OPTIONS (not implemented)
APP_SANDBOX_ENABLED (not implemented)
APP_SANDBOX_INHERIT (not implemented)
APP_SANDBOX_OPTIONS (not implemented)
APP_SANDBOX_FILE_ACCESS_HOME_RO (not implemented)
APP_SANDBOX_FILE_ACCESS_HOME_RW (not implemented)
APP_SANDBOX_FILE_ACCESS_ABS_RO (not implemented)
APP_SANDBOX_FILE_ACCESS_ABS_RW (not implemented)
APP_SANDBOX_EXCEPTION_IOKIT (not implemented)
PLIST_TO_MERGE plist_to_merge
FORMATS formats
PLUGIN_NAME plugin_name
PLUGIN_MANUFACTURER_CODE plugin_manufacturer_code
PLUGIN_CODE plugin_code
DESCRIPTION description
IS_SYNTH is_synth
NEEDS_MIDI_INPUT needs_midi_input
NEEDS_MIDI_OUTPUT needs_midi_output
IS_MIDI_EFFECT is_midi_effect
EDITOR_WANTS_KEYBOARD_FOCUS editor_wants_keyboard_focus
DISABLE_AAX_BYPASS (not implemented)
DISABLE_AAX_MULTI_MONO (not implemented)
AAX_IDENTIFIER (not implemented)
LV2URI (not implemented)
VST_NUM_MIDI_INS vst_num_midi_ins
VST_NUM_MIDI_OUTS vst_num_midi_outs
VST2_CATEGORY vst2_category
VST3_CATEGORIES vst3_categories
AU_MAIN_TYPE au_main_type
AU_EXPORT_PREFIX au_export_prefix
AU_SANDBOX_SAFE au_sandbox_safe
SUPPRESS_AU_PLIST_RESOURCE_USAGE suppress_au_plist_resource_usage
AAX_CATEGORY (not implemented)
PLUGINHOST_AU (not implemented)
USE_LEGACY_COMPATIBILITY_PLUGIN_CODE use_legacy_compatibility_plugin_code
COPY_PLUGIN_AFTER_BUILD (not implemented)
VST_COPY_DIR (not implemented)
VST3_COPY_DIR (not implemented)
AAX_COPY_DIR (not implemented)
AU_COPY_DIR (not implemented)
UNITY_COPY_DIR (not implemented)
IS_ARA_EFFECT (not implemented)
ARA_FACTORY_ID (not implemented)
ARA_DOCUMENT_ARCHIVE_ID (not implemented)
ARA_ANALYSIS_TYPES (not implemented)
ARA_TRANSFORMATION_FLAGS (not implemented)
VST3_AUTO_MANIFEST vst3_auto_manifest

Generating compile_commands.json

Zig doesn't currently support generating compile_commands.json.
A common solution is to use the-argus/zig-compile-commands.
See the example projects in this repo for how to use it with juzi.

Motivation

  • To better understand both the JUCE CMake build system and Zig's build system.
  • Just for fun.

License

MIT.
JUCE is licensed separately: https://github.com/juce-framework/JUCE