Claude-skill-registry 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.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/cross-platform-builds" ~/.claude/skills/majiayu000-claude-skill-registry-cross-platform-builds && rm -rf "$T"
skills/data/cross-platform-builds/SKILL.mdCross-Platform Builds for JUCE Plugins
Comprehensive guide to building JUCE audio plugins across macOS, Windows, and Linux with proper configuration, code signing, and continuous integration.
Overview
JUCE audio plugins must be built for multiple platforms and plugin formats:
- macOS: VST3, AU (Audio Unit), AAX
- Windows: VST3, AAX
- Linux: VST3
Each platform has specific requirements for build tools, code signing, and packaging. This skill covers:
- CMake configuration for all platforms and formats
- Platform-specific build instructions
- Code signing and notarization
- Continuous integration setup
- Reproducible builds
1. CMake Configuration
Root CMakeLists.txt Structure
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()
Key Configuration Options
Plugin Codes
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 Identifiers
BUNDLE_ID com.yourcompany.myplugin # Reverse domain notation
Must be unique and consistent across versions for AU validation.
Plugin Characteristics
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
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 Types
AU_MAIN_TYPE kAudioUnitType_Effect # Effect AU_MAIN_TYPE kAudioUnitType_MusicDevice # Instrument AU_MAIN_TYPE kAudioUnitType_MIDIProcessor # MIDI effect
2. macOS Builds
Prerequisites
-
Xcode (latest version recommended)
xcode-select --install -
CMake (3.22+)
brew install cmake -
Developer ID Certificate (for distribution)
- Enroll in Apple Developer Program ($99/year)
- Create "Developer ID Application" certificate in Xcode
Building
# 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
Universal Binaries (Apple Silicon + Intel)
# 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
Code Signing
Manual Signing
# 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
Entitlements File (Resources/Entitlements.plist)
<?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>
Automated Signing in CMake
# 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()
Notarization
Required for macOS 10.15+ (Catalina and later).
Setup
- Create app-specific password at appleid.apple.com
- Store credentials in keychain:
xcrun notarytool store-credentials "notary-profile" \ --apple-id "developer@example.com" \ --team-id "TEAM_ID" \ --password "xxxx-xxxx-xxxx-xxxx"
Notarize Plugin
# 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
Troubleshooting Notarization
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:
- Missing entitlements: Add to Entitlements.plist
- Unsigned nested binaries: Sign all frameworks before parent bundle
- Invalid bundle structure: Verify with
pkgutil --check-signature
AU Validation
# Validate AU (required for App Store distribution) auval -v aufx Plug Manu # Output should end with "PASSED"
Fix common AU validation errors:
- "Could not open component": Check bundle ID and AU type
- "Plugin crash": Debug in Xcode, check for exceptions in initialization
- "Latency reporting": Implement
correctlygetTailLengthSeconds()
3. Windows Builds
Prerequisites
-
Visual Studio 2022 (Community, Professional, or Enterprise)
- Install "Desktop development with C++" workload
- Includes Windows 10 SDK
-
CMake (3.22+)
# Via Chocolatey choco install cmake # Or download from cmake.org -
Code Signing Certificate (optional, for distribution)
- EV or standard code signing certificate
- From vendors: DigiCert, Sectigo, GlobalSign
Building
# 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
MSVC Runtime Linking
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")
Code Signing
Manual Signing with signtool
# 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
Automated Signing in CMake
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()
Visual Studio Configuration
Optimization Settings
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()
Suppress Warnings
target_compile_definitions(MyPlugin PRIVATE _CRT_SECURE_NO_WARNINGS # Disable CRT security warnings NOMINMAX # Prevent min/max macros )
4. Linux Builds
Prerequisites
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
Building
# 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
Packaging
Create .tar.gz
tar -czf MyPlugin-1.0.0-Linux-x86_64.tar.gz \ -C build-linux/MyPlugin_artefacts/Release/VST3 \ MyPlugin.vst3
Create .deb Package
# Install packaging tools sudo apt-get install checkinstall # Create .deb sudo checkinstall \ --pkgname=myplugin \ --pkgversion=1.0.0 \ --pkgrelease=1 \ --pkggroup=sound \ --maintainer="you@example.com" \ cmake --install build-linux
5. AAX Format (Pro Tools)
Prerequisites
-
AAX SDK (requires iLok account)
- Sign up at developer.avid.com
- Download AAX SDK
- Extract to
SDKs/AAX/
-
PACE Licensing (for distribution)
- Create account at paceap.com
- Use PACE Eden for signing (replaces codesign for AAX)
CMake Configuration
# 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)
Building AAX
# 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 Signing with PACE Eden
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.
6. Continuous Integration
GitHub Actions Workflow
.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
Secrets for Code Signing
Store signing credentials in GitHub Secrets:
- Go to repository Settings → Secrets → Actions
- Add secrets:
: Base64-encoded .p12 fileMACOS_CERTIFICATE_BASE64
: Certificate passwordMACOS_CERTIFICATE_PASSWORD
: Apple ID for notarizationAPPLE_ID
: Developer team IDAPPLE_TEAM_ID
: App-specific passwordAPPLE_APP_PASSWORD
: Base64-encoded .pfx fileWINDOWS_CERTIFICATE_BASE64
: Certificate passwordWINDOWS_CERTIFICATE_PASSWORD
Automated Code Signing in CI
macOS:
- 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
7. Reproducible Builds
Deterministic Builds
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 )
Build Verification
Generate checksums for reproducibility:
# macOS/Linux shasum -a 256 MyPlugin.vst3 > checksums.txt # Windows certutil -hashfile MyPlugin.vst3 SHA256 >> checksums.txt
8. Troubleshooting
Common Build Errors
"JUCE modules not found"
Solution: git submodule update --init --recursive
"Symbol not found" (macOS)
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>
"Unresolved external symbol" (Windows)
Solution: - Ensure all .cpp files are in CMakeLists.txt - Check library linking order - Verify static/dynamic runtime consistency (/MT vs /MD)
"Undefined reference" (Linux)
Solution: - Install missing libraries (libasound2-dev, etc.) - Add libraries to target_link_libraries() - Check pkg-config: pkg-config --libs alsa
Plugin Doesn't Load in DAW
macOS:
- Check signing:
codesign --verify --deep --strict MyPlugin.vst3 - Verify notarization:
spctl -a -vvv -t install MyPlugin.vst3 - Check Gatekeeper:
(remove quarantine if needed)xattr -l MyPlugin.vst3 - AU validation:
auval -v aufx Plug Manu
Windows:
- Check dependencies: Use Dependency Walker
- Verify signature:
signtool verify /pa MyPlugin.vst3 - Check registry (for VST3):
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\VST3
Linux:
- Check shared library dependencies:
ldd MyPlugin.vst3 - Verify VST3 path:
or~/.vst3//usr/lib/vst3/ - Check permissions:
chmod 755 MyPlugin.vst3
9. Best Practices
Version Management
project(MyPlugin VERSION 1.2.3) # Access in code target_compile_definitions(MyPlugin PRIVATE PLUGIN_VERSION="${CMAKE_PROJECT_VERSION}" )
Conditional Compilation
#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
Minimize Plugin Size
- Strip symbols in Release builds
- Enable LTO (link-time optimization)
- Remove unused JUCE modules
- Compress resources (images, fonts)
Cross-Platform File Paths
// 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" ❌
Summary
Key Takeaways:
- Use CMake for cross-platform builds - single configuration for all platforms
- Code signing is essential for distribution (macOS requires notarization)
- Test on all platforms - behavior can differ (especially AU vs VST3)
- Automate in CI/CD - GitHub Actions, GitLab CI, or Jenkins
- Reproducible builds - pin dependency versions, document toolchain
Platform Checklist:
- macOS: Universal binary (arm64 + x86_64)
- macOS: Code signed with Developer ID
- macOS: Notarized (10.15+ requirement)
- macOS: AU validation passes (
)auval - Windows: Code signed (recommended)
- Windows: Static runtime linked (/MT)
- Linux: Dependencies documented
- All: Tested in major DAWs on each platform
Related Resources:
command - Automated release workflow/release-build- BUILD_GUIDE.md - Detailed build procedures
- RELEASE_CHECKLIST.md - Pre-release validation steps
- @build-engineer - CI/CD and build automation expert