Issues archiving iOS AUv3 plugin with CMake

Hi all,

I’ve been working lately to port one of my plugins to iOS AUv3, and I’ve been been using the CMake build system as I find that I much prefer it to the Projucer. Everything has been working fine with building the plugin and testing on my device locally, but I’ve been trying to upload builds to TestFlight so I can share it with others, and I’ve run into some issues.

Basically, I can run the “Archive” command (either from Xcode, or the command line), and a .xcarchive file will be generated, but when I try to upload it to TestFlight, I get an email saying:

ITMS-90018: This bundle is invalid - The file extension must be .zip.

All my internet searching about this error has led me to issues with “CocoaPods” that I don’t really understand. As a workaround, I’ve made a Projucer configuration for the plugin as well, and when I archive the plugin from there, everything works correctly. However, I would much prefer to use CMake.

One potential cause of the issue is that the generated archive does not contain the actual AUv3 plugin, just a symlink to it. When I look through the contents of the archive, I see:

MyPlugin.xcarchive
|-- dSYM
|-- Info.plist
|-- Products
    |-- Applications
        |-- MyPlugin.app
             |-- Info.plist
             |-- Other content...
             |-- Plugins
                  |-- MyPlugin.appex

When I generate the archive from the Projucer project, the .appex file actually contains the AUv3 plugin, but when I generate from the CMake project, it only contains the symlink.

Anyway, I’m not sure if this issue has something to do with the JUCE AUv3 configuration in CMake, or if it’s something wrong with my CMake setup. Any assistance would be appreciated! Maybe @reuk or @McMartin has some insight?

Here’s all the relevant parts of my CMake configuration:


CMakeLists.txt:

juce_add_plugin(MyPlugin
    COMPANY_NAME yourcompany
    PLUGIN_MANUFACTURER_CODE XXX
    PLUGIN_CODE xxx
    FORMATS AU VST3 Standalone AUv3
    ProductName "MyPlugin"
    ICON_BIG res/logo.png
    MICROPHONE_PERMISSION_ENABLED TRUE # otherwise Standalone will crash on iOS
)

add_dependencies(SharedCode MyPlugin)

juce_generate_juce_header(MyPlugin)

target_sources(MyPlugin PRIVATE
    MyPlugin.h
    MyPlugin.cpp
)

juce_add_binary_data(BinaryData SOURCES
    res/gui.xml
)

# Need to build BinaryData with -fPIC flag on Linux
set_target_properties(BinaryData PROPERTIES
    POSITION_INDEPENDENT_CODE TRUE)

target_link_libraries(MyPlugin PRIVATE
    juce::juce_audio_utils
    juce::juce_audio_plugin_client
    foleys_gui_magic
    chowdsp_utils
    BinaryData
    SharedCode
)

if(IOS)
    # properties needed for iOS archiving, see (https://github.com/juce-framework/JUCE/blob/master/docs/CMake%20API.md#archiving-for-ios)
    message(STATUS "Setting iOS-specific properties...")
    set_target_properties(MyPlugin PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "./")
    set_target_properties(BinaryData PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "./")
    set_target_properties(SharedCode PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "./")
    set_target_properties(MyPlugin_Standalone PROPERTIES
        XCODE_ATTRIBUTE_INSTALL_PATH "$(LOCAL_APPS_DIR)"
        XCODE_ATTRIBUTE_SKIP_INSTALL "NO")
endif()

Command used for CMake configuration:

cmake -Bbuild-ios -GXcode -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=11.4 \
    -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM="$TEAM_ID" \
    -DCMAKE_XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY="1,2" \
    -DCMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE="NO" # for some reason linking during archiving fails without this set to "NO"

Then for archiving, I either use Xcode → Product → Archive, or:

xcodebuild -project build-ios/MyPlugin.xcodeproj \
  -scheme MyPlugin_Standalone archive -configuration Release \
  -sdk iphoneos -jobs 9 | xcpretty

I was able to get CMake to archive a version of the AUv3SynthPlugin demo which appears to have the correct structure (i.e. the .appex is an actual bundle rather than a symlink) by adjusting your snippet a little. The important bit is that I’m setting the INSTALL_PATH of the AUv3 to match the ‘PlugIns’ folder of the app bundle. You could make this nicer by querying the OUTPUT_NAME property of the standalone binary, rather than hardcoding the app name.

I haven’t tried uploading this build to Apple so I’m not sure that it will solve all the problems. Please let me know if this does work for you, so that I can update the documentation in the repo.

if(IOS)
    foreach(target IN ITEMS AUv3SynthPlugin AUv3SynthPlugin_Standalone AUv3SynthPlugin_AUv3)
        set_target_properties(${target}
            PROPERTIES
                RUNTIME_OUTPUT_DIRECTORY "./"
                ARCHIVE_OUTPUT_DIRECTORY "./"
                LIBRARY_OUTPUT_DIRECTORY "./"
                XCODE_ATTRIBUTE_INSTALL_PATH "$(LOCAL_APPS_DIR)"
                XCODE_ATTRIBUTE_SKIP_INSTALL "NO")
    endforeach()

    set_target_properties(AUv3SynthPlugin_AUv3
        PROPERTIES
            XCODE_ATTRIBUTE_INSTALL_PATH "$(LOCAL_APPS_DIR)/AUv3SynthPlugin.app/PlugIns")
endif()

@reuk, yes editing the CMake configuration as you showed worked correctly for me. I was able to run the upload step without any failures, and then test on my device via TestFlight. Thanks so much for the assistance!

1 Like

The CMake docs are now updated on develop.

It seems from my experience and the topic here like the iOS configuration has a few ‘gotchas’ like the archiving directory that’s different for apps/AUv3. How come those aren’t handled by juce_add_xxx?

If those really should be optional, how about a config target like juce_recommended_ios_settings that we could link against?

Good idea. I think I didn’t add this initially as it seemed like the issue mainly affected non-JUCE staticlibs (which JUCE obviously can’t automatically configure). We can definitely automatically set these properties on JUCE targets, though.

I’ve added that change here:

1 Like

Thank you so much for fixing that!