AUv3: Customize Bundle ID

This is following a chat with @reuk on the discord channel:

With our product, we’d like to distribute multiple AUv3 plugins with a single app purchase.

This is supported by Apple, and commercial plugins already do that. For example:

Reports three different .appex files: A synth, a MIDI FX an an audio FX.

For this to work, the bundle ID that Apple requires has to start with the standalone app id, and then end with a single extension, that needs to be unique.

For example, with Mela the bundle IDs are:

com.nikolozi.Mela3.InstrumentExtension
com.nikolozi.Mela3.EffectExtension
com.nikolozi.Mela3.MIDIExtension

ATM, this is isn’t possible with JUCE/CMake, as JUCE hard codes the extension. It would be great to add another property to juce_add_plugin that will allow customizing the extension.

I did it with my own (very trivial) fork of JUCE where I added it to juceutils.cmake, as well as JUCEAide.

Is it possible to add that feature to the main branch? I’m happy to send a PR as well if you feel this would make things easier, but the changes are really trivial.

2 Likes

Great work - thanks for taking the time to inform those of us who are also working on AUV3 builds. I’ll be very interested in any other gotcha’s you run into - I am preparing something similar for our (Austrian Audio) free plugins, too …

For an AUv3 plugin, it looks like the BUNDLE_ID set in the CMake file is only used to set the CFBundleIdentifier key in the appex bundle’s plist. Given that keys in the PLIST_TO_MERGE take precedence over keys specified in other ways, I believe that it’s possible to set a different ID by adding something like this to the juce_add_plugin call:

    PLIST_TO_MERGE [[<plist>
        <dict>
            <key>CFBundleIdentifier</key>
            <string>com.company.application.extension</string>
        </dict>
    </plist>]]

Note that this will set the same ID on the AUv3 and its standalone, so the generated AUv3 will need to be copied into a different Standalone with a compatible ID in order for the appex to be discovered and loaded.

Is this a viable solution for your use-case?

Thanks @reuk, but No. We need a single standalone with multiple AUv3 components similar to Mela 3.

Changing the CMake was only half the process indeed: I needed to tweak JUCEAide to also write the plist file correctly for each AUv3 component. Will a full PR help you see the full changes required?

Are you trying to do this with a single juce_add_plugin call?

I’d imagined that you’d call juce_add_plugin once for each AUv3, and optionally once more for the Standalone, and then copy the AUv3s all into the same Standalone as a post-build step.

Is the problem that you are building each of the inner plugins in other formats too, and that you need the bundle ID for the AUv3 version to differ from the bundleID for the VST3 version (for example)?

No, I’m totally fine with multiple calls! (see below)

Yes, but Apple has a strict requirement for the Bundle ID. It has to start with the one from the standalone, and then only differ in the extension with no additional dots, as seen in the Mela IDs, otherwise this will fail.

Our AUv3 deployment is not related to the VST3, AAX ones, etc.

So after forking JUCE CMake + JUCEAide, I’m able to do:

#Instrument plugin: has the standalone:
    juce_add_plugin(BeatScholar-MAS
            ${BeatScholarCommonDefs}
            ${MasCommonDef}
            FORMATS Standalone AUv3
            PLUGIN_CODE BTSM
            PRODUCT_NAME "Beat Scholar-MAS")

#MIDI FX plugin: no standalone, has a custom extension:
    juce_add_plugin(BeatScholar-MAS-MIDI
            ${BeatScholarMidiDefs}
            ${MasCommonDef}
            FORMATS AUv3
            PLUGIN_CODE BTam
            BUNDLE_ID com.Modalics.BeatScholar-MAS
            AUv3_BUNDLE_EXT MIDI
            PRODUCT_NAME "Beat Scholar MIDI-MAS")

   #add the midi component as a dependency of the main plugin
    add_dependencies(BeatScholar-MAS_Standalone
            BeatScholar-MAS_AUv3
            BeatScholar-MAS-MIDI_AUv3)

    set_target_properties(BeatScholar-MAS-MIDI PROPERTIES
            XCODE_ATTRIBUTE_SKIP_INSTALL "YES")

    set_target_properties(BeatScholar-MAS-MIDI_AUv3 PROPERTIES
            XCODE_ATTRIBUTE_SKIP_INSTALL "YES")

    copy_full_dir(BeatScholar-MAS_Standalone
            "$<TARGET_BUNDLE_DIR:BeatScholar-MAS-MIDI_AUv3>"
            "$<TARGET_BUNDLE_CONTENT_DIR:BeatScholar-MAS_Standalone>/PlugIns")

#(copy_full_dir is similar to juce_copy_dir but also follows symlinks).

After doing the above, I see a single standalone with two .appex files, one is:

com.modalics.beatscholar.auv3

And the other is:

com.modalics.beatscholar.midi

Both can be archived just fine and pass the app store validation.

Unless I’m missing something, I think this can be achieved without forking JUCE. I can successfully archive the AudioPluginExample with two inner AUv3s after building it like this:

juce_add_plugin(AudioPluginExample
    FORMATS Standalone AUv3
    PLUGIN_CODE m1n3
    BUNDLE_ID "com.reuk.mycoolplugin"
    PRODUCT_NAME "StandaloneAndAUv3")

set(auv3_identifier "com.reuk.mycoolplugin.justAUv3")

juce_add_plugin(AudioPluginExampleTwo
    FORMATS AUv3
    PLUGIN_CODE m1n1
    PRODUCT_NAME "JustAUv3"
    PLIST_TO_MERGE "<plist><dict>
        <key>CFBundleIdentifier</key>
        <string>${auv3_identifier}</string>
    </dict></plist>")

add_dependencies(AudioPluginExample_Standalone AudioPluginExampleTwo_AUv3)

set_target_properties(AudioPluginExampleTwo AudioPluginExampleTwo_AUv3
    PROPERTIES XCODE_ATTRIBUTE_SKIP_INSTALL "YES")

set_target_properties(AudioPluginExampleTwo_AUv3
    PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${auv3_identifier}")

copy_full_dir(AudioPluginExample_Standalone
    "$<TARGET_BUNDLE_DIR:AudioPluginExampleTwo_AUv3>"
    "$<TARGET_BUNDLE_CONTENT_DIR:AudioPluginExample_Standalone>/PlugIns")
3 Likes

That looks good! I was able to remove the fork.
It’s possible you might want to document it somewhere, as it isn’t very clear one will be able to do that.

Thanks @reuk!

Slightly off topic: Does anyone know of a way using CMake to archive + deploy the app through the command line without going through the xcode UI?

It would be great to have more cmake template in the examples/cmake folder. It would help a lot of people to start with it and show trickier use case like app with resource and other stuff like this Auv3 stuff.

1 Like

@reuk , while I think it works for now, looks like xcode also throws a warning during archiving:

/Users/eyalamir/Code/Modalics/build/Modalics.xcodeproj 
User-supplied CFBundleIdentifier value 'com.Modalics.BeatScholar-MAS.MIDIFX' 
in the Info.plist must be the same 
as the PRODUCT_BUNDLE_IDENTIFIER build setting value 
'com.Modalics.BeatScholar-MAS-MIDI.BeatScholar-MAS-MIDIAUv3'.

Edit: Never mind, fixed with an explicit bundle ID set:

    set_target_properties(BeatScholar-MAS-MIDI_AUv3 PROPERTIES
            XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER
            com.Modalics.BeatScholar-MAS.MIDIFX)

So many details to tune!

1 Like

I’m following this with great interest as I’m also bundling AUV3 at the moment – so if you get the chance to write those details up a little more formally, I’d be happy to provide feedback. :stuck_out_tongue: