xiaohongshu
xiaohongshu
cross-platform-builds
Comprehensive guide to building JUCE plugins for macOS, Windows, and Linux with CMake, code signing, notarization, and CI/CD. Use when configuring builds, setting up CI/CD pipelines, troubleshooting cross-platform compilation, implementing code signing, or creating installers for multiple platforms.
Full skill instructions
Comprehensive guide to building JUCE audio plugins across macOS, Windows, and Linux with proper configuration, code signing, and continuous integration.
JUCE audio plugins must be built for multiple platforms and plugin formats:
Each platform has specific requirements for build tools, code signing, and packaging. This skill covers:
cmake_minimum_required(VERSION 3.22)
project(MyPlugin VERSION 1.0.0)
# C++17 minimum for JUCE 7+
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Export compile_commands.json for IDEs
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Add JUCE
add_subdirectory(JUCE)
# Plugin formats to build
set(PLUGIN_FORMATS VST3 AU Standalone)
# Add AAX if PACE SDK is available
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/SDKs/AAX")
list(APPEND PLUGIN_FORMATS AAX)
juce_set_aax_sdk_path("${CMAKE_CURRENT_SOURCE_DIR}/SDKs/AAX")
endif()
# Define the plugin
juce_add_plugin(MyPlugin
COMPANY_NAME "YourCompany"
PLUGIN_MANUFACTURER_CODE Manu # 4-character code
PLUGIN_CODE Plug # 4-character code (unique!)
FORMATS ${PLUGIN_FORMATS}
PRODUCT_NAME "MyPlugin"
# Bundle IDs
BUNDLE_ID com.yourcompany.myplugin
# Plugin characteristics
IS_SYNTH FALSE
NEEDS_MIDI_INPUT FALSE
NEEDS_MIDI_OUTPUT FALSE
IS_MIDI_EFFECT FALSE
EDITOR_WANTS_KEYBOARD_FOCUS FALSE
# Copy plugin to system folder after build
COPY_PLUGIN_AFTER_BUILD TRUE
# VST3 category
VST3_CATEGORIES Fx
# AU type (aufx = effect, aumu = instrument)
AU_MAIN_TYPE kAudioUnitType_Effect
)
# Source files
target_sources(MyPlugin PRIVATE
Source/PluginProcessor.cpp
Source/PluginEditor.cpp
Source/DSP/Filter.cpp
Source/DSP/Modulation.cpp
)
# Public compile definitions
target_compile_definitions(MyPlugin PUBLIC
JUCE_WEB_BROWSER=0
JUCE_USE_CURL=0
JUCE_VST3_CAN_REPLACE_VST2=0
JUCE_DISPLAY_SPLASH_SCREEN=0 # Commercial license only!
)
# Link JUCE modules
target_link_libraries(MyPlugin PRIVATE
juce::juce_audio_utils
juce::juce_dsp
juce::juce_recommended_config_flags
juce::juce_recommended_lto_flags
juce::juce_recommended_warning_flags
)
# Platform-specific settings
if(APPLE)
# macOS deployment target
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum macOS version")
# Universal binary (Apple Silicon + Intel)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "macOS architectures")
# Hardened runtime for notarization
target_compile_options(MyPlugin PUBLIC
-Wall -Wextra -Wpedantic
)
elseif(WIN32)
# Static runtime for standalone distribution
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# Windows-specific definitions
target_compile_definitions(MyPlugin PRIVATE
_CRT_SECURE_NO_WARNINGS
)
elseif(UNIX)
# Linux-specific flags
target_compile_options(MyPlugin PRIVATE
-Wall -Wextra
)
# Link against ALSA, JACK, etc.
find_package(PkgConfig REQUIRED)
pkg_check_modules(ALSA REQUIRED alsa)
target_link_libraries(MyPlugin PRIVATE ${ALSA_LIBRARIES})
endif()
# Tests (optional)
option(BUILD_TESTS "Build unit tests" ON)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(Tests)
endif()
PLUGIN_MANUFACTURER_CODE Manu # Your unique 4-character manufacturer ID
PLUGIN_CODE Plug # Unique 4-character plugin ID
Important: Register manufacturer code at Steinberg to avoid conflicts.
BUNDLE_ID com.yourcompany.myplugin # Reverse domain notation
Must be unique and consistent across versions for AU validation.
IS_SYNTH TRUE # Instrument vs effect
NEEDS_MIDI_INPUT TRUE # Accept MIDI input
NEEDS_MIDI_OUTPUT FALSE # Send MIDI output
IS_MIDI_EFFECT FALSE # MIDI-only processing (no audio)
VST3_CATEGORIES Fx # Effect
VST3_CATEGORIES Instrument # Instrument
VST3_CATEGORIES Fx Dynamics # Multiple categories
Available categories: Fx, Instrument, Analyzer, Delay, Distortion, Dynamics, EQ, Filter, Mastering, Modulation, Restoration, Reverb, Spatial, Synth, Tools
AU_MAIN_TYPE kAudioUnitType_Effect # Effect
AU_MAIN_TYPE kAudioUnitType_MusicDevice # Instrument
AU_MAIN_TYPE kAudioUnitType_MIDIProcessor # MIDI effect
Xcode (latest version recommended)
xcode-select --install
CMake (3.22+)
brew install cmake
Developer ID Certificate (for distribution)
# Configure
cmake -B build-mac -G Xcode \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
# Build all formats
cmake --build build-mac --config Release --parallel
# Or build with Xcode
open build-mac/MyPlugin.xcodeproj
# In CMakeLists.txt
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
Or at build time:
cmake -B build-mac -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
Verify architectures:
lipo -info build-mac/MyPlugin_artefacts/Release/VST3/MyPlugin.vst3/Contents/MacOS/MyPlugin
# Output: Architectures in the fat file: MyPlugin are: x86_64 arm64
# Sign VST3
codesign --force \
--sign "Developer ID Application: Your Name (TEAM_ID)" \
--options runtime \
--entitlements Resources/Entitlements.plist \
--timestamp \
--deep \
MyPlugin.vst3
# Sign AU
codesign --force \
--sign "Developer ID Application: Your Name (TEAM_ID)" \
--options runtime \
--timestamp \
--deep \
MyPlugin.component
# Verify signature
codesign --verify --deep --strict --verbose=2 MyPlugin.vst3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Allow JIT for DSP optimization -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<!-- Allow loading unsigned plugins (for VST3 presets, etc.) -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- For networked plugins (optional) -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
# Add to CMakeLists.txt
if(APPLE AND CMAKE_BUILD_TYPE STREQUAL "Release")
set(CODESIGN_IDENTITY "Developer ID Application: Your Name")
add_custom_command(TARGET MyPlugin POST_BUILD
COMMAND codesign --force
--sign "${CODESIGN_IDENTITY}"
--options runtime
--entitlements "${CMAKE_SOURCE_DIR}/Resources/Entitlements.plist"
--timestamp
$<TARGET_BUNDLE_DIR:MyPlugin>
COMMENT "Code signing ${TARGET}"
)
endif()
Required for macOS 10.15+ (Catalina and later).
xcrun notarytool store-credentials "notary-profile" \
--apple-id "[email protected]" \
--team-id "TEAM_ID" \
--password "xxxx-xxxx-xxxx-xxxx"
# 1. Create ZIP for notarization
ditto -c -k --keepParent MyPlugin.vst3 MyPlugin-vst3.zip
# 2. Submit to notary service
xcrun notarytool submit MyPlugin-vst3.zip \
--keychain-profile "notary-profile" \
--wait
# 3. If successful, staple the ticket
xcrun stapler staple MyPlugin.vst3
# 4. Verify
spctl -a -vvv -t install MyPlugin.vst3
xcrun stapler validate MyPlugin.vst3
Check submission status:
xcrun notarytool info <submission-id> --keychain-profile "notary-profile"
View detailed log:
xcrun notarytool log <submission-id> --keychain-profile "notary-profile"
Common issues:
pkgutil --check-signature# Validate AU (required for App Store distribution)
auval -v aufx Plug Manu
# Output should end with "PASSED"
Fix common AU validation errors:
getTailLengthSeconds() correctlyVisual Studio 2022 (Community, Professional, or Enterprise)
CMake (3.22+)
# Via Chocolatey
choco install cmake
# Or download from cmake.org
Code Signing Certificate (optional, for distribution)
# Configure for Visual Studio 2022
cmake -B build-win -G "Visual Studio 17 2022" -A x64
# Build Release
cmake --build build-win --config Release --parallel
# Or open in Visual Studio
start build-win/MyPlugin.sln
Static Runtime (recommended for plugins):
# Statically link MSVC runtime (no DLL dependencies)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
Dynamic Runtime (smaller binary, requires MSVC redistributable):
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
# Sign with PFX file
signtool sign /f certificate.pfx /p <password> `
/tr http://timestamp.digicert.com `
/td sha256 /fd sha256 `
MyPlugin.vst3
# Sign with certificate store
signtool sign /n "Your Company Name" `
/tr http://timestamp.digicert.com `
/td sha256 /fd sha256 `
MyPlugin.vst3
# Verify signature
signtool verify /pa /v MyPlugin.vst3
if(WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Release")
find_program(SIGNTOOL_EXECUTABLE signtool
PATHS "C:/Program Files (x86)/Windows Kits/10/bin/*/x64"
)
if(SIGNTOOL_EXECUTABLE)
add_custom_command(TARGET MyPlugin POST_BUILD
COMMAND ${SIGNTOOL_EXECUTABLE} sign
/f "${CMAKE_SOURCE_DIR}/certificate.pfx"
/p "$ENV{CERT_PASSWORD}"
/tr http://timestamp.digicert.com
/td sha256 /fd sha256
$<TARGET_FILE:MyPlugin>
COMMENT "Code signing ${TARGET}"
)
endif()
endif()
if(MSVC)
# Enable whole program optimization (Release)
target_compile_options(MyPlugin PRIVATE
$<$<CONFIG:Release>:/GL> # Whole program optimization
/MP # Multi-processor compilation
)
target_link_options(MyPlugin PRIVATE
$<$<CONFIG:Release>:/LTCG> # Link-time code generation
)
endif()
target_compile_definitions(MyPlugin PRIVATE
_CRT_SECURE_NO_WARNINGS # Disable CRT security warnings
NOMINMAX # Prevent min/max macros
)
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y \
build-essential \
cmake \
libasound2-dev \
libjack-jackd2-dev \
libfreetype6-dev \
libx11-dev \
libxrandr-dev \
libxinerama-dev \
libxcursor-dev \
libgl1-mesa-dev \
libcurl4-openssl-dev
Fedora/RHEL:
sudo dnf install -y \
gcc-c++ \
cmake \
alsa-lib-devel \
jack-audio-connection-kit-devel \
freetype-devel \
libX11-devel \
libXrandr-devel \
libXinerama-devel \
libXcursor-devel \
mesa-libGL-devel \
libcurl-devel
# Configure
cmake -B build-linux -DCMAKE_BUILD_TYPE=Release
# Build
cmake --build build-linux --config Release --parallel
# Install to system (optional)
sudo cmake --install build-linux
tar -czf MyPlugin-1.0.0-Linux-x86_64.tar.gz \
-C build-linux/MyPlugin_artefacts/Release/VST3 \
MyPlugin.vst3
# Install packaging tools
sudo apt-get install checkinstall
# Create .deb
sudo checkinstall \
--pkgname=myplugin \
--pkgversion=1.0.0 \
--pkgrelease=1 \
--pkggroup=sound \
--maintainer="[email protected]" \
cmake --install build-linux
AAX SDK (requires iLok account)
SDKs/AAX/PACE Licensing (for distribution)
# Set AAX SDK path
juce_set_aax_sdk_path("${CMAKE_CURRENT_SOURCE_DIR}/SDKs/AAX")
# Add AAX to plugin formats
set(PLUGIN_FORMATS VST3 AU AAX Standalone)
# macOS
cmake -B build-mac -DAAX_SDK_PATH=SDKs/AAX
cmake --build build-mac --config Release
# Windows
cmake -B build-win -DAAX_SDK_PATH=SDKs/AAX
cmake --build build-win --config Release
AAX plugins must be signed with PACE Eden (not regular codesign).
# Sign AAX (macOS/Windows)
wraptool sign \
--account <your-pace-account> \
--password <password> \
--signid <signid> \
--in MyPlugin.aaxplugin \
--out MyPlugin-signed.aaxplugin
# Verify
wraptool verify --verbose MyPlugin-signed.aaxplugin
Note: Keep AAX signing credentials secure. Never commit to version control.
.github/workflows/build.yml:
name: Build Plugin
on: [push, pull_request]
jobs:
build:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: macOS
os: macos-latest
cmake_args: -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
- name: Windows
os: windows-latest
cmake_args: -G "Visual Studio 17 2022" -A x64
- name: Linux
os: ubuntu-latest
cmake_args: ""
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev libjack-jackd2-dev \
libfreetype6-dev libx11-dev libxrandr-dev libxinerama-dev \
libxcursor-dev libgl1-mesa-dev
- name: Configure
run: cmake -B build ${{ matrix.cmake_args }} -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --config Release --parallel
- name: Test
run: ctest --test-dir build -C Release --output-on-failure
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.name }}
path: |
build/*_artefacts/Release/VST3/*.vst3
build/*_artefacts/Release/AU/*.component
Store signing credentials in GitHub Secrets:
MACOS_CERTIFICATE_BASE64: Base64-encoded .p12 fileMACOS_CERTIFICATE_PASSWORD: Certificate passwordAPPLE_ID: Apple ID for notarizationAPPLE_TEAM_ID: Developer team IDAPPLE_APP_PASSWORD: App-specific passwordWINDOWS_CERTIFICATE_BASE64: Base64-encoded .pfx fileWINDOWS_CERTIFICATE_PASSWORD: Certificate passwordmacOS:
- name: Import Certificate
env:
CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE_BASE64 }}
CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
run: |
echo $CERTIFICATE_BASE64 | base64 --decode > certificate.p12
security create-keychain -p temp build.keychain
security import certificate.p12 -k build.keychain -P $CERTIFICATE_PASSWORD -T /usr/bin/codesign
security set-keychain-settings -lut 21600 build.keychain
security unlock-keychain -p temp build.keychain
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k temp build.keychain
- name: Sign and Notarize
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
run: |
codesign --force --sign "Developer ID Application" --options runtime MyPlugin.vst3
xcrun notarytool submit MyPlugin.vst3.zip --apple-id $APPLE_ID --team-id $APPLE_TEAM_ID --password $APPLE_APP_PASSWORD --wait
xcrun stapler staple MyPlugin.vst3
Windows:
- name: Import Certificate
env:
CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: |
[System.Convert]::FromBase64String($env:CERTIFICATE_BASE64) | Set-Content -Path certificate.pfx -Encoding Byte
certutil -importpfx -p $env:CERTIFICATE_PASSWORD certificate.pfx
- name: Sign Binary
run: |
signtool sign /f certificate.pfx /p $env:CERTIFICATE_PASSWORD /tr http://timestamp.digicert.com /td sha256 /fd sha256 MyPlugin.vst3
Ensure builds are reproducible across machines:
Pin JUCE version (use git submodule or specific release)
git submodule add https://github.com/juce-framework/JUCE.git
cd JUCE && git checkout 7.0.9
Lock dependency versions (CMake FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0 # Specific version
)
Document toolchain versions (README.md)
Build Requirements:
- CMake 3.22+
- JUCE 7.0.9
- macOS: Xcode 14.3+
- Windows: Visual Studio 2022
- Linux: GCC 11+ or Clang 14+
Disable timestamp embedding
# Remove __DATE__ and __TIME__ macros
target_compile_definitions(MyPlugin PRIVATE
NO_BUILD_TIMESTAMP=1
)
Generate checksums for reproducibility:
# macOS/Linux
shasum -a 256 MyPlugin.vst3 > checksums.txt
# Windows
certutil -hashfile MyPlugin.vst3 SHA256 >> checksums.txt
Solution:
git submodule update --init --recursive
Solution:
- Check deployment target matches minimum system requirement
- Verify all symbols are available in target SDK
- Use `nm` to inspect missing symbols:
nm -gU MyPlugin.vst3/Contents/MacOS/MyPlugin | grep <symbol>
Solution:
- Ensure all .cpp files are in CMakeLists.txt
- Check library linking order
- Verify static/dynamic runtime consistency (/MT vs /MD)
Solution:
- Install missing libraries (libasound2-dev, etc.)
- Add libraries to target_link_libraries()
- Check pkg-config: pkg-config --libs alsa
macOS:
codesign --verify --deep --strict MyPlugin.vst3spctl -a -vvv -t install MyPlugin.vst3xattr -l MyPlugin.vst3 (remove quarantine if needed)auval -v aufx Plug ManuWindows:
signtool verify /pa MyPlugin.vst3Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\VST3Linux:
ldd MyPlugin.vst3~/.vst3/ or /usr/lib/vst3/chmod 755 MyPlugin.vst3project(MyPlugin VERSION 1.2.3)
# Access in code
target_compile_definitions(MyPlugin PRIVATE
PLUGIN_VERSION="${CMAKE_PROJECT_VERSION}"
)
#if JUCE_MAC
// macOS-specific code
#elif JUCE_WINDOWS
// Windows-specific code
#elif JUCE_LINUX
// Linux-specific code
#endif
#if JUCE_DEBUG
// Debug-only code
#endif
// Use JUCE File class for portability
juce::File presetFolder = juce::File::getSpecialLocation(
juce::File::userApplicationDataDirectory
).getChildFile("MyPlugin").getChildFile("Presets");
// Not hardcoded paths like:
// "C:\\Users\\...\\Presets" ❌
Key Takeaways:
Platform Checklist:
auval)Related Resources:
/release-build command - Automated release workflowxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience