From a8c0ec0e77d581566d9dd81e7cef088c852f656e Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 20 Aug 2026 22:40:30 +0300 Subject: [PATCH] Add optional XCFramework distribution tooling ## Summary Adds an isolated, opt-in build path for producing: - `AudioKitEX.xcframework` - `CAudioKitEX.xcframework` The generated frameworks support iOS devices, iOS Simulator, and native macOS. Swift Package Manager remains the default integration path, and this change does not add or publish prebuilt binaries. ## Motivation Binary packaging for AudioKitEX must coordinate the Swift AudioKitEX module, the C and C++ CAudioKitEX module, and a compatible AudioKit binary. An archive can succeed while still containing missing public headers or symbols, incompatible deployment targets or architectures, unstable module metadata, mismatched dSYMs, or linkage failures that only appear in a consumer. Without a shared recipe, every downstream binary user must independently maintain and validate this integration. ## Dependency contract AudioKit is consumed as an explicit external XCFramework input instead of being copied into or bundled with the AudioKitEX output. Before archiving, the builder validates: - The required AudioKit release - Deployment-target compatibility - Selected macOS architectures - Binary checksums This keeps the AudioKit and AudioKitEX repositories and artifacts independent. ## Included tooling - Framework targets for the existing `Sources/AudioKitEX` and `Sources/CAudioKitEX` trees - Public CAudioKitEX header packaging - A command-line builder and Finder-friendly `build.command` wrapper - Configurable deployment targets - Optional Intel Mac support - Stable Swift interfaces and matching dSYMs - Exported-symbol checks and build provenance - Atomic replacement of both XCFrameworks ## Safety and validation The builder: - Requires a clean checkout based on an AudioKitEX release tag - Validates architectures, deployment targets, dynamic linkage, exported C symbols, module interfaces, headers, and dSYM UUIDs - Verifies linkage between AudioKitEX, CAudioKitEX, and AudioKit - Runs a clean binary-only smoke consumer that imports AudioKit and AudioKitEX and creates a `DryWetMixer` for iOS Simulator and every selected macOS architecture ## Project impact - No changes to AudioKitEX production sources or public Swift API - No changes to `Package.swift` behavior - No changes to the existing SwiftPM workflow or default CI path - All tooling is isolated under `XCFramework` and is fully opt-in - Binary hosting and release policy remain downstream decisions This is the companion build path to the AudioKit XCFramework tooling while keeping both distributions independent. --- .../project.pbxproj | 517 ++++++++++++ .../xcschemes/AudioKitEXFramework.xcscheme | 58 ++ XCFramework/README.md | 72 ++ XCFramework/Support/CAudioKitEX.h | 7 + XCFramework/build-xcframework.sh | 739 ++++++++++++++++++ XCFramework/build.command | 50 ++ 6 files changed, 1443 insertions(+) create mode 100644 XCFramework/AudioKitEXFramework.xcodeproj/project.pbxproj create mode 100644 XCFramework/AudioKitEXFramework.xcodeproj/xcshareddata/xcschemes/AudioKitEXFramework.xcscheme create mode 100644 XCFramework/README.md create mode 100644 XCFramework/Support/CAudioKitEX.h create mode 100755 XCFramework/build-xcframework.sh create mode 100755 XCFramework/build.command diff --git a/XCFramework/AudioKitEXFramework.xcodeproj/project.pbxproj b/XCFramework/AudioKitEXFramework.xcodeproj/project.pbxproj new file mode 100644 index 0000000..392d53c --- /dev/null +++ b/XCFramework/AudioKitEXFramework.xcodeproj/project.pbxproj @@ -0,0 +1,517 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 100; + objects = { + +/* Begin PBXBuildFile section */ + B11000000000000000000001 /* AtomicDataPtr.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000001 /* AtomicDataPtr.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000002 /* BufferedAudioBus.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000002 /* BufferedAudioBus.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000003 /* CAudioKit.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000003 /* CAudioKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000004 /* CAudioKitEX.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000004 /* CAudioKitEX.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000005 /* DSPBase.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000005 /* DSPBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000006 /* DebugDSP.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000006 /* DebugDSP.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000007 /* ExceptionCatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000007 /* ExceptionCatcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000008 /* Interop.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000008 /* Interop.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000009 /* ParameterAutomation.h in Headers */ = {isa = PBXBuildFile; fileRef = B12000000000000000000009 /* ParameterAutomation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B1100000000000000000000A /* ParameterRamper.h in Headers */ = {isa = PBXBuildFile; fileRef = B1200000000000000000000A /* ParameterRamper.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B1100000000000000000000B /* RenderMeasurer.h in Headers */ = {isa = PBXBuildFile; fileRef = B1200000000000000000000B /* RenderMeasurer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B1100000000000000000000C /* SequencerEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = B1200000000000000000000C /* SequencerEngine.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B11000000000000000000020 /* CAudioKitEX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B12000000000000000000021 /* CAudioKitEX.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + B13000000000000000000001 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B1B000000000000000000001 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B1A000000000000000000002; + remoteInfo = CAudioKitEX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + B12000000000000000000001 /* AtomicDataPtr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/AtomicDataPtr.h; sourceTree = ""; }; + B12000000000000000000002 /* BufferedAudioBus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/BufferedAudioBus.h; sourceTree = ""; }; + B12000000000000000000003 /* CAudioKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/CAudioKit.h; sourceTree = ""; }; + B12000000000000000000004 /* CAudioKitEX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Support/CAudioKitEX.h; sourceTree = ""; }; + B12000000000000000000005 /* DSPBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/DSPBase.h; sourceTree = ""; }; + B12000000000000000000006 /* DebugDSP.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/DebugDSP.h; sourceTree = ""; }; + B12000000000000000000007 /* ExceptionCatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/ExceptionCatcher.h; sourceTree = ""; }; + B12000000000000000000008 /* Interop.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/Interop.h; sourceTree = ""; }; + B12000000000000000000009 /* ParameterAutomation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/ParameterAutomation.h; sourceTree = ""; }; + B1200000000000000000000A /* ParameterRamper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/ParameterRamper.h; sourceTree = ""; }; + B1200000000000000000000B /* RenderMeasurer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/RenderMeasurer.h; sourceTree = ""; }; + B1200000000000000000000C /* SequencerEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ../Sources/CAudioKitEX/include/SequencerEngine.h; sourceTree = ""; }; + B12000000000000000000020 /* AudioKitEX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AudioKitEX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B12000000000000000000021 /* CAudioKitEX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CAudioKitEX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + B13500000000000000000001 /* Public header exceptions */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + include/AtomicDataPtr.h, + include/BufferedAudioBus.h, + include/CAudioKit.h, + include/DSPBase.h, + include/DebugDSP.h, + include/ExceptionCatcher.h, + include/Interop.h, + include/ParameterAutomation.h, + include/ParameterRamper.h, + include/RenderMeasurer.h, + include/SequencerEngine.h, + ); + target = B1A000000000000000000002 /* CAudioKitEX */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + B14000000000000000000001 /* Sources/AudioKitEX */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = ../Sources/AudioKitEX; + sourceTree = ""; + }; + B14000000000000000000002 /* Sources/CAudioKitEX */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + B13500000000000000000001 /* Public header exceptions */, + ); + path = ../Sources/CAudioKitEX; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + B15000000000000000000001 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B11000000000000000000020 /* CAudioKitEX.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B15000000000000000000002 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B16000000000000000000001 = { + isa = PBXGroup; + children = ( + B14000000000000000000001 /* Sources/AudioKitEX */, + B14000000000000000000002 /* Sources/CAudioKitEX */, + B16000000000000000000002 /* Public Headers */, + B16000000000000000000003 /* Products */, + ); + sourceTree = ""; + }; + B16000000000000000000002 /* Public Headers */ = { + isa = PBXGroup; + children = ( + B12000000000000000000001 /* AtomicDataPtr.h */, + B12000000000000000000002 /* BufferedAudioBus.h */, + B12000000000000000000003 /* CAudioKit.h */, + B12000000000000000000004 /* CAudioKitEX.h */, + B12000000000000000000005 /* DSPBase.h */, + B12000000000000000000006 /* DebugDSP.h */, + B12000000000000000000007 /* ExceptionCatcher.h */, + B12000000000000000000008 /* Interop.h */, + B12000000000000000000009 /* ParameterAutomation.h */, + B1200000000000000000000A /* ParameterRamper.h */, + B1200000000000000000000B /* RenderMeasurer.h */, + B1200000000000000000000C /* SequencerEngine.h */, + ); + name = "Public Headers"; + sourceTree = ""; + }; + B16000000000000000000003 /* Products */ = { + isa = PBXGroup; + children = ( + B12000000000000000000020 /* AudioKitEX.framework */, + B12000000000000000000021 /* CAudioKitEX.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + B17000000000000000000001 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + B11000000000000000000001 /* AtomicDataPtr.h in Headers */, + B11000000000000000000002 /* BufferedAudioBus.h in Headers */, + B11000000000000000000003 /* CAudioKit.h in Headers */, + B11000000000000000000004 /* CAudioKitEX.h in Headers */, + B11000000000000000000005 /* DSPBase.h in Headers */, + B11000000000000000000006 /* DebugDSP.h in Headers */, + B11000000000000000000007 /* ExceptionCatcher.h in Headers */, + B11000000000000000000008 /* Interop.h in Headers */, + B11000000000000000000009 /* ParameterAutomation.h in Headers */, + B1100000000000000000000A /* ParameterRamper.h in Headers */, + B1100000000000000000000B /* RenderMeasurer.h in Headers */, + B1100000000000000000000C /* SequencerEngine.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + B1A000000000000000000001 /* AudioKitEX */ = { + isa = PBXNativeTarget; + buildConfigurationList = B1D000000000000000000002 /* Build configuration list for PBXNativeTarget "AudioKitEX" */; + buildPhases = ( + B18000000000000000000001 /* Sources */, + B15000000000000000000001 /* Frameworks */, + B19000000000000000000001 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B1E000000000000000000001 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + B14000000000000000000001 /* Sources/AudioKitEX */, + ); + name = AudioKitEX; + productName = AudioKitEX; + productReference = B12000000000000000000020 /* AudioKitEX.framework */; + productType = "com.apple.product-type.framework"; + }; + B1A000000000000000000002 /* CAudioKitEX */ = { + isa = PBXNativeTarget; + buildConfigurationList = B1D000000000000000000003 /* Build configuration list for PBXNativeTarget "CAudioKitEX" */; + buildPhases = ( + B18000000000000000000002 /* Sources */, + B15000000000000000000002 /* Frameworks */, + B17000000000000000000001 /* Headers */, + B19000000000000000000002 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + B14000000000000000000002 /* Sources/CAudioKitEX */, + ); + name = CAudioKitEX; + productName = CAudioKitEX; + productReference = B12000000000000000000021 /* CAudioKitEX.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B1B000000000000000000001 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2660; + LastUpgradeCheck = 2660; + TargetAttributes = { + B1A000000000000000000001 = { CreatedOnToolsVersion = 26.6; }; + B1A000000000000000000002 = { CreatedOnToolsVersion = 26.6; }; + }; + }; + buildConfigurationList = B1D000000000000000000001 /* Build configuration list for PBXProject "AudioKitEXFramework" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B16000000000000000000001; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 100; + productRefGroup = B16000000000000000000003 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B1A000000000000000000001 /* AudioKitEX */, + B1A000000000000000000002 /* CAudioKitEX */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B19000000000000000000001 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (); runOnlyForDeploymentPostprocessing = 0; }; + B19000000000000000000002 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (); runOnlyForDeploymentPostprocessing = 0; }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B18000000000000000000001 /* Sources */ = {isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = (); runOnlyForDeploymentPostprocessing = 0; }; + B18000000000000000000002 /* Sources */ = {isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = (); runOnlyForDeploymentPostprocessing = 0; }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + B1E000000000000000000001 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B1A000000000000000000002 /* CAudioKitEX */; + targetProxy = B13000000000000000000001 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + B1C000000000000000000001 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_OPTIMIZATION_LEVEL = 0; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + B1C000000000000000000002 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + ONLY_ACTIVE_ARCH = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B1C000000000000000000003 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(AUDIOKIT_FRAMEWORK_SEARCH_PATH)", + ); + GENERATE_INFOPLIST_FILE = YES; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = mh_dylib; + MACOSX_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 5.7.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + AudioKit, + ); + PRODUCT_BUNDLE_IDENTIFIER = io.audiokit.AudioKitEX; + PRODUCT_MODULE_NAME = AudioKitEX; + PRODUCT_NAME = AudioKitEX; + SKIP_INSTALL = NO; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B1C000000000000000000004 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(AUDIOKIT_FRAMEWORK_SEARCH_PATH)", + ); + GENERATE_INFOPLIST_FILE = YES; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = mh_dylib; + MACOSX_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 5.7.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + AudioKit, + ); + PRODUCT_BUNDLE_IDENTIFIER = io.audiokit.AudioKitEX; + PRODUCT_MODULE_NAME = AudioKitEX; + PRODUCT_NAME = AudioKitEX; + SKIP_INSTALL = NO; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_VERIFY_EMITTED_MODULE_INTERFACE = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + B1C000000000000000000005 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../Sources/CAudioKitEX", + "$(SRCROOT)/../Sources/CAudioKitEX/include", + ); + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = mh_dylib; + MACOSX_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 5.7.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + AudioToolbox, + "-framework", + AVFoundation, + "-framework", + CoreAudioKit, + "-framework", + Foundation, + ); + PRODUCT_BUNDLE_IDENTIFIER = io.audiokit.CAudioKitEX; + PRODUCT_MODULE_NAME = CAudioKitEX; + PRODUCT_NAME = CAudioKitEX; + SKIP_INSTALL = NO; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B1C000000000000000000006 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../Sources/CAudioKitEX", + "$(SRCROOT)/../Sources/CAudioKitEX/include", + ); + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = mh_dylib; + MACOSX_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 5.7.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + AudioToolbox, + "-framework", + AVFoundation, + "-framework", + CoreAudioKit, + "-framework", + Foundation, + ); + PRODUCT_BUNDLE_IDENTIFIER = io.audiokit.CAudioKitEX; + PRODUCT_MODULE_NAME = CAudioKitEX; + PRODUCT_NAME = CAudioKitEX; + SKIP_INSTALL = NO; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B1D000000000000000000001 /* Project */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B1C000000000000000000001 /* Debug */, + B1C000000000000000000002 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B1D000000000000000000002 /* AudioKitEX */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B1C000000000000000000003 /* Debug */, + B1C000000000000000000004 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B1D000000000000000000003 /* CAudioKitEX */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B1C000000000000000000005 /* Debug */, + B1C000000000000000000006 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B1B000000000000000000001 /* Project object */; +} diff --git a/XCFramework/AudioKitEXFramework.xcodeproj/xcshareddata/xcschemes/AudioKitEXFramework.xcscheme b/XCFramework/AudioKitEXFramework.xcodeproj/xcshareddata/xcschemes/AudioKitEXFramework.xcscheme new file mode 100644 index 0000000..8158724 --- /dev/null +++ b/XCFramework/AudioKitEXFramework.xcodeproj/xcshareddata/xcschemes/AudioKitEXFramework.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/XCFramework/README.md b/XCFramework/README.md new file mode 100644 index 0000000..b0fd0c1 --- /dev/null +++ b/XCFramework/README.md @@ -0,0 +1,72 @@ +# Building AudioKitEX as XCFrameworks + +This directory contains optional tooling for building AudioKitEX as precompiled +XCFrameworks for iOS and macOS. The existing Swift Package Manager workflow +remains unchanged, and `Package.swift` is not involved in this build path. + +## Requirements + +- Xcode 27 +- A clean checkout based on an AudioKitEX release tag +- iOS 15 deployment support +- macOS 12 deployment support +- A compatible AudioKit build directory containing `AudioKit.xcframework` and + `BUILD_INFO.md` + +The default compatible AudioKit release is `5.7.2`. Set +`AUDIOKIT_REQUIRED_RELEASE` to explicitly build against another compatible +release. + +## Build + +To build with a double-click, open `build.command` in +Finder. The wrapper uses sibling `AudioKitBinary` and `AudioKitEXBinary` +directories when they exist. If the AudioKit input is missing, it asks for its +path. Set `AUDIOKIT_XCFRAMEWORK_INPUT` or `AUDIOKITEX_XCFRAMEWORK_OUTPUT` to +override either directory. + +From the repository root, run: + +```sh +./XCFramework/build-xcframework.sh +``` + +During an interactive terminal run, the script asks for both deployment +targets and whether to include Intel Mac support. Pressing Return selects +iOS 15, macOS 12, and arm64-only macOS slices. Non-interactive builds use the +same defaults without prompting. Use the same choices as the AudioKit input. + +Archive steps use a single updating terminal line that reports the current +build step and elapsed time. `xcodebuild` does not expose reliable per-file +progress or an exact percentage for archive operations. + +The choices can also be supplied as environment variables for automation: + +```sh +XCFRAMEWORK_IOS_DEPLOYMENT_TARGET=18.0 \ +XCFRAMEWORK_MACOS_DEPLOYMENT_TARGET=15.0 \ +XCFRAMEWORK_MACOS_ARCHITECTURES="arm64 x86_64" \ +./XCFramework/build-xcframework.sh +``` + +`XCFRAMEWORK_MACOS_ARCHITECTURES` accepts `arm64` or `arm64 x86_64`. The +script rejects an AudioKit input built with different +deployment targets or architectures. + +The script produces: + +- `AudioKitEX.xcframework` +- `CAudioKitEX.xcframework` +- `BUILD_INFO.md` with source, dependency, and toolchain provenance + +Each XCFramework contains an arm64 iOS device slice, an arm64/x86_64 iOS +Simulator slice, and a native macOS slice with the selected architectures. +The build includes stable module interfaces and matching dSYMs. + +Before installing the output, the script validates architectures, dynamic +linkage, exported symbols, module interfaces, dSYM UUIDs, and source-free iOS +and macOS consumer links. Output replacement is atomic, so a failed build does +not overwrite previously valid artifacts. + +This repository does not publish prebuilt artifacts. The generated output can +be packaged by downstream distribution infrastructure when needed. diff --git a/XCFramework/Support/CAudioKitEX.h b/XCFramework/Support/CAudioKitEX.h new file mode 100644 index 0000000..c44ee7b --- /dev/null +++ b/XCFramework/Support/CAudioKitEX.h @@ -0,0 +1,7 @@ +// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/ + +#pragma once + +#import "AtomicDataPtr.h" +#import "CAudioKit.h" +#import "RenderMeasurer.h" diff --git a/XCFramework/build-xcframework.sh b/XCFramework/build-xcframework.sh new file mode 100755 index 0000000..5605544 --- /dev/null +++ b/XCFramework/build-xcframework.sh @@ -0,0 +1,739 @@ +#!/bin/bash + +set -euo pipefail + +fail() { + printf 'error: %s\n' "$1" >&2 + exit 1 +} + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + fail "required command not found: $1" + fi +} + +readonly SCRIPT_DIRECTORY="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SOURCE_ROOT="$(cd "${SCRIPT_DIRECTORY}/.." && pwd -P)" + +if [[ "$#" -ne 2 ]]; then + fail "usage: $0 " +fi + +for required_command in git xcodebuild xcrun plutil lipo otool file nm shasum ditto; do + require_command "${required_command}" +done + +readonly PROJECT="${SCRIPT_DIRECTORY}/AudioKitEXFramework.xcodeproj" +readonly SCHEME="AudioKitEXFramework" +readonly AUDIOKIT_BINARY_CANDIDATE="$1" +readonly OUTPUT_CANDIDATE="$2" +readonly REQUIRED_AUDIOKIT_RELEASE="${AUDIOKIT_REQUIRED_RELEASE:-5.7.2}" + +ios_deployment_target="${XCFRAMEWORK_IOS_DEPLOYMENT_TARGET:-}" +macos_deployment_target="${XCFRAMEWORK_MACOS_DEPLOYMENT_TARGET:-}" +macos_architectures="${XCFRAMEWORK_MACOS_ARCHITECTURES:-}" + +if [[ -t 0 ]]; then + if [[ -z "${ios_deployment_target}" ]]; then + printf 'Minimum iOS deployment target [15.0]: ' + if IFS= read -r entered_ios_target && [[ -n "${entered_ios_target}" ]]; then + ios_deployment_target="${entered_ios_target}" + fi + fi + + if [[ -z "${macos_deployment_target}" ]]; then + printf 'Minimum macOS deployment target [12.0]: ' + if IFS= read -r entered_macos_target && [[ -n "${entered_macos_target}" ]]; then + macos_deployment_target="${entered_macos_target}" + fi + fi + + if [[ -z "${macos_architectures}" ]]; then + while true; do + printf 'Include Intel Mac support? [y/N]: ' + if ! IFS= read -r include_intel; then + break + fi + + case "${include_intel}" in + ""|n|N|no|No|NO) + macos_architectures="arm64" + break + ;; + y|Y|yes|Yes|YES) + macos_architectures="arm64 x86_64" + break + ;; + *) + printf 'Please answer y or n.\n' >&2 + ;; + esac + done + fi +fi + +readonly MINIMUM_IOS_VERSION="${ios_deployment_target:-15.0}" +readonly MINIMUM_MACOS_VERSION="${macos_deployment_target:-12.0}" +readonly MACOS_ARCHITECTURES="${macos_architectures:-arm64}" + +[[ "${MINIMUM_IOS_VERSION}" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]] \ + || fail "invalid iOS deployment target: ${MINIMUM_IOS_VERSION}" +[[ "${MINIMUM_MACOS_VERSION}" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]] \ + || fail "invalid macOS deployment target: ${MINIMUM_MACOS_VERSION}" + +case "${MACOS_ARCHITECTURES}" in + arm64) + readonly MACOS_SLICE_IDENTIFIER="macos-arm64" + ;; + "arm64 x86_64") + readonly MACOS_SLICE_IDENTIFIER="macos-arm64_x86_64" + ;; + *) + fail "unsupported macOS architectures: ${MACOS_ARCHITECTURES}; use arm64 or arm64 x86_64" + ;; +esac + +[[ -f "${PROJECT}/project.pbxproj" ]] || fail "project not found: ${PROJECT}" +[[ -d "${AUDIOKIT_BINARY_CANDIDATE}" ]] || fail "AudioKit binary directory not found: ${AUDIOKIT_BINARY_CANDIDATE}" +/bin/mkdir -p "${OUTPUT_CANDIDATE}" +readonly AUDIOKIT_BINARY_DIRECTORY="$(cd "${AUDIOKIT_BINARY_CANDIDATE}" && pwd -P)" +readonly OUTPUT_DIRECTORY="$(cd "${OUTPUT_CANDIDATE}" && pwd -P)" +readonly AUDIOKIT_XCFRAMEWORK="${AUDIOKIT_BINARY_DIRECTORY}/AudioKit.xcframework" +readonly AUDIOKIT_BUILD_INFO="${AUDIOKIT_BINARY_DIRECTORY}/BUILD_INFO.md" + +[[ "${AUDIOKIT_BINARY_DIRECTORY}" != "${OUTPUT_DIRECTORY}" ]] || fail "AudioKit input and AudioKitEX output must be different directories" +[[ -d "${AUDIOKIT_XCFRAMEWORK}" ]] || fail "AudioKit.xcframework not found in AudioKit binary directory: ${AUDIOKIT_XCFRAMEWORK}" +[[ -f "${AUDIOKIT_BUILD_INFO}" ]] || fail "AudioKit BUILD_INFO.md not found: ${AUDIOKIT_BUILD_INFO}" +if ! /usr/bin/grep -F -q -- "- AudioKit release: \`${REQUIRED_AUDIOKIT_RELEASE}\`" "${AUDIOKIT_BUILD_INFO}"; then + fail "AudioKit binary does not declare required AudioKit release ${REQUIRED_AUDIOKIT_RELEASE}" +fi +if ! /usr/bin/grep -F -q -- "- Minimum iOS: \`${MINIMUM_IOS_VERSION}\`" "${AUDIOKIT_BUILD_INFO}"; then + fail "AudioKit binary does not declare required iOS minimum ${MINIMUM_IOS_VERSION}" +fi +if ! /usr/bin/grep -F -q -- "- Minimum macOS: \`${MINIMUM_MACOS_VERSION}\`" "${AUDIOKIT_BUILD_INFO}"; then + fail "AudioKit binary does not declare required macOS minimum ${MINIMUM_MACOS_VERSION}" +fi + +readonly SOURCE_COMMIT="$(/usr/bin/git -C "${SOURCE_ROOT}" rev-parse HEAD)" +readonly SOURCE_ORIGIN="$(/usr/bin/git -C "${SOURCE_ROOT}" remote get-url origin)" + +upstream_tag="${AUDIOKITEX_UPSTREAM_TAG:-$(/usr/bin/git -C "${SOURCE_ROOT}" describe --tags --abbrev=0 HEAD 2>/dev/null || true)}" +[[ -n "${upstream_tag}" ]] || fail "unable to determine the upstream release tag; set AUDIOKITEX_UPSTREAM_TAG" +readonly UPSTREAM_TAG="${upstream_tag}" +readonly UPSTREAM_TAG_OBJECT="$(/usr/bin/git -C "${SOURCE_ROOT}" rev-parse "${UPSTREAM_TAG}")" +readonly UPSTREAM_COMMIT="$(/usr/bin/git -C "${SOURCE_ROOT}" rev-parse "${UPSTREAM_TAG}^{}")" + +if ! /usr/bin/git -C "${SOURCE_ROOT}" merge-base --is-ancestor "${UPSTREAM_COMMIT}" HEAD; then + fail "upstream tag ${UPSTREAM_TAG} is not an ancestor of HEAD" +fi + +if ! /usr/bin/git -C "${SOURCE_ROOT}" diff --quiet "${UPSTREAM_TAG}" -- \ + Package.swift \ + Sources/AudioKitEX \ + Sources/CAudioKitEX; then + fail "AudioKitEX production sources differ from upstream release ${UPSTREAM_TAG}" +fi + +source_state="clean" +if [[ -n "$(/usr/bin/git -C "${SOURCE_ROOT}" status --porcelain --untracked-files=all)" ]]; then + source_state="dirty development build" +fi +readonly SOURCE_STATE="${source_state}" + +if [[ "${SOURCE_STATE}" != "clean" && "${ALLOW_DIRTY_BUILD:-0}" != "1" ]]; then + fail "source repository is dirty; commit the build infrastructure or rerun with ALLOW_DIRTY_BUILD=1 for a non-publishable development artifact" +fi + +if [[ "${SOURCE_STATE}" != "clean" ]]; then + printf 'warning: building a non-publishable dirty development artifact\n' >&2 +fi + +readonly CACHE_ROOT="${SOURCE_ROOT}/.build/xcframework" +readonly DERIVED_DATA="${CACHE_ROOT}/DerivedData" +/bin/mkdir -p "${DERIVED_DATA}" + +readonly WORK_ROOT="$(/usr/bin/mktemp -d /private/tmp/AudioKitEXXCFramework.XXXXXX)" +readonly DEVICE_ARCHIVE="${WORK_ROOT}/AudioKitEX-iOS.xcarchive" +readonly SIMULATOR_ARCHIVE="${WORK_ROOT}/AudioKitEX-Simulator.xcarchive" +readonly MACOS_ARCHIVE="${WORK_ROOT}/AudioKitEX-macOS.xcarchive" +readonly BUILT_AUDIOKITEX_XCFRAMEWORK="${WORK_ROOT}/AudioKitEX.xcframework" +readonly BUILT_CAUDIOKITEX_XCFRAMEWORK="${WORK_ROOT}/CAudioKitEX.xcframework" +readonly GENERATED_BUILD_INFO="${WORK_ROOT}/BUILD_INFO.md" +readonly SMOKE_SOURCE="${WORK_ROOT}/AudioKitEXBinarySmoke.swift" +readonly SMOKE_LIBRARY="${WORK_ROOT}/libAudioKitEXBinarySmoke.dylib" +readonly MACOS_ARM64_SMOKE_LIBRARY="${WORK_ROOT}/libAudioKitEXBinarySmoke-macOS-arm64.dylib" +readonly MACOS_X86_64_SMOKE_LIBRARY="${WORK_ROOT}/libAudioKitEXBinarySmoke-macOS-x86_64.dylib" + +readonly DESTINATION_AUDIOKITEX_XCFRAMEWORK="${OUTPUT_DIRECTORY}/AudioKitEX.xcframework" +readonly DESTINATION_CAUDIOKITEX_XCFRAMEWORK="${OUTPUT_DIRECTORY}/CAudioKitEX.xcframework" +readonly DESTINATION_BUILD_INFO="${OUTPUT_DIRECTORY}/BUILD_INFO.md" +readonly AUDIOKITEX_INSTALL_STAGE="${OUTPUT_DIRECTORY}/.AudioKitEX.xcframework.new.$$" +readonly CAUDIOKITEX_INSTALL_STAGE="${OUTPUT_DIRECTORY}/.CAudioKitEX.xcframework.new.$$" +readonly BUILD_INFO_STAGE="${OUTPUT_DIRECTORY}/.BUILD_INFO.md.new.$$" + +AUDIOKITEX_BACKUP="" +CAUDIOKITEX_BACKUP="" +BUILD_INFO_BACKUP="" +INSTALL_STARTED=0 +ACTIVE_BUILD_PID="" + +readonly BUILD_STEP_COUNT=9 + +safe_remove() { + local path="$1" + + case "${path}" in + "${WORK_ROOT}"|"${OUTPUT_DIRECTORY}"/.AudioKitEX.xcframework.new.*|"${OUTPUT_DIRECTORY}"/.AudioKitEX.xcframework.previous.*|"${OUTPUT_DIRECTORY}"/.CAudioKitEX.xcframework.new.*|"${OUTPUT_DIRECTORY}"/.CAudioKitEX.xcframework.previous.*|"${OUTPUT_DIRECTORY}"/.BUILD_INFO.md.new.*|"${OUTPUT_DIRECTORY}"/.BUILD_INFO.md.previous.*) + if [[ -e "${path}" ]]; then + /bin/rm -rf -- "${path}" + fi + ;; + *) + printf 'warning: refused to remove unexpected path %s\n' "${path}" >&2 + ;; + esac +} + +restore_previous_install() { + if [[ -e "${DESTINATION_AUDIOKITEX_XCFRAMEWORK}" ]]; then + /bin/mv "${DESTINATION_AUDIOKITEX_XCFRAMEWORK}" "${AUDIOKITEX_INSTALL_STAGE}" + fi + if [[ -n "${AUDIOKITEX_BACKUP}" && -e "${AUDIOKITEX_BACKUP}" ]]; then + /bin/mv "${AUDIOKITEX_BACKUP}" "${DESTINATION_AUDIOKITEX_XCFRAMEWORK}" + AUDIOKITEX_BACKUP="" + fi + + if [[ -e "${DESTINATION_CAUDIOKITEX_XCFRAMEWORK}" ]]; then + /bin/mv "${DESTINATION_CAUDIOKITEX_XCFRAMEWORK}" "${CAUDIOKITEX_INSTALL_STAGE}" + fi + if [[ -n "${CAUDIOKITEX_BACKUP}" && -e "${CAUDIOKITEX_BACKUP}" ]]; then + /bin/mv "${CAUDIOKITEX_BACKUP}" "${DESTINATION_CAUDIOKITEX_XCFRAMEWORK}" + CAUDIOKITEX_BACKUP="" + fi + + if [[ -e "${DESTINATION_BUILD_INFO}" ]]; then + /bin/mv "${DESTINATION_BUILD_INFO}" "${BUILD_INFO_STAGE}" + fi + if [[ -n "${BUILD_INFO_BACKUP}" && -e "${BUILD_INFO_BACKUP}" ]]; then + /bin/mv "${BUILD_INFO_BACKUP}" "${DESTINATION_BUILD_INFO}" + BUILD_INFO_BACKUP="" + fi +} + +cleanup() { + local status="$?" + + if [[ -n "${ACTIVE_BUILD_PID}" ]] && /bin/kill -0 "${ACTIVE_BUILD_PID}" 2>/dev/null; then + /bin/kill "${ACTIVE_BUILD_PID}" 2>/dev/null || true + wait "${ACTIVE_BUILD_PID}" 2>/dev/null || true + fi + + if [[ "${status}" -ne 0 && "${INSTALL_STARTED}" -eq 1 ]]; then + restore_previous_install + fi + + safe_remove "${AUDIOKITEX_INSTALL_STAGE}" + safe_remove "${CAUDIOKITEX_INSTALL_STAGE}" + safe_remove "${BUILD_INFO_STAGE}" + + if [[ -n "${AUDIOKITEX_BACKUP}" ]]; then + safe_remove "${AUDIOKITEX_BACKUP}" + fi + if [[ -n "${CAUDIOKITEX_BACKUP}" ]]; then + safe_remove "${CAUDIOKITEX_BACKUP}" + fi + if [[ -n "${BUILD_INFO_BACKUP}" ]]; then + safe_remove "${BUILD_INFO_BACKUP}" + fi + + safe_remove "${WORK_ROOT}" + + trap - EXIT + exit "${status}" +} + +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM + +archive_frameworks() { + local destination="$1" + local archive_path="$2" + local audiokit_slice="$3" + local label="$4" + local architectures="$5" + local step_number="$6" + local build_log="${WORK_ROOT}/${label// /-}.log" + local started_at + local current_time + local build_status + local spinner='|/-\' + local spinner_index=0 + + /usr/bin/xcodebuild archive \ + -project "${PROJECT}" \ + -scheme "${SCHEME}" \ + -configuration Release \ + -destination "${destination}" \ + -archivePath "${archive_path}" \ + -derivedDataPath "${DERIVED_DATA}" \ + AUDIOKIT_FRAMEWORK_SEARCH_PATH="${audiokit_slice}" \ + CODE_SIGNING_ALLOWED=NO \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + ARCHS="${architectures}" \ + ONLY_ACTIVE_ARCH=NO \ + IPHONEOS_DEPLOYMENT_TARGET="${MINIMUM_IOS_VERSION}" \ + MACOSX_DEPLOYMENT_TARGET="${MINIMUM_MACOS_VERSION}" \ + DEBUG_INFORMATION_FORMAT=dwarf-with-dsym \ + >"${build_log}" 2>&1 & + + ACTIVE_BUILD_PID="$!" + started_at="$(/bin/date +%s)" + + while /bin/kill -0 "${ACTIVE_BUILD_PID}" 2>/dev/null; do + current_time="$(/bin/date +%s)" + if [[ -t 1 ]]; then + printf '\r\033[2K[%d/%d] %s %c %ss' \ + "${step_number}" "${BUILD_STEP_COUNT}" "${label}" "${spinner:spinner_index%4:1}" \ + "$((current_time - started_at))" + + spinner_index=$((spinner_index + 1)) + fi + /bin/sleep 1 + done + + if wait "${ACTIVE_BUILD_PID}"; then + build_status=0 + else + build_status="$?" + fi + ACTIVE_BUILD_PID="" + + if [[ "${build_status}" -ne 0 ]]; then + if [[ -t 1 ]]; then + printf '\r\033[2K' + fi + printf '[%d/%d] %s failed. Last 200 build-log lines:\n' \ + "${step_number}" "${BUILD_STEP_COUNT}" "${label}" >&2 + /usr/bin/tail -n 200 "${build_log}" >&2 + fail "${label} failed with exit code ${build_status}" + fi + + current_time="$(/bin/date +%s)" + if [[ -t 1 ]]; then + printf '\r\033[2K' + fi + printf '[%d/%d] %s finished in %ss.\n' \ + "${step_number}" "${BUILD_STEP_COUNT}" "${label}" "$((current_time - started_at))" +} + +require_path() { + [[ -e "$1" ]] || fail "expected build output is missing: $1" +} + +uuid_list() { + /usr/bin/xcrun dwarfdump --uuid "$1" \ + | /usr/bin/awk '{ print $2 " " $3 }' \ + | /usr/bin/sort +} + +uuid_summary() { + /usr/bin/xcrun dwarfdump --uuid "$1" \ + | /usr/bin/awk 'BEGIN { separator = "" } { printf "%s%s %s", separator, $2, $3; separator = ", " } END { print "" }' +} + +assert_matching_uuids() { + local binary="$1" + local dsym="$2" + local binary_uuids + local dsym_uuids + + binary_uuids="$(uuid_list "${binary}")" + dsym_uuids="$(uuid_list "${dsym}")" + + if [[ "${binary_uuids}" != "${dsym_uuids}" ]]; then + printf 'Binary UUIDs:\n%s\n' "${binary_uuids}" >&2 + printf 'dSYM UUIDs:\n%s\n' "${dsym_uuids}" >&2 + fail "framework and dSYM UUIDs do not match" + fi +} + +assert_dynamic_framework() { + if ! /usr/bin/file "$1" | /usr/bin/grep -q 'dynamically linked shared library'; then + fail "binary is not a dynamic framework: $1" + fi +} + +assert_expected_linkage() { + local binary="$1" + + if ! /usr/bin/otool -L "${binary}" \ + | /usr/bin/grep -E -q '@rpath/AudioKit\.framework/(Versions/A/)?AudioKit'; then + /usr/bin/otool -L "${binary}" >&2 + fail "AudioKitEX does not link the binary AudioKit framework" + fi + + if ! /usr/bin/otool -L "${binary}" \ + | /usr/bin/grep -E -q '@rpath/CAudioKitEX\.framework/(Versions/A/)?CAudioKitEX'; then + /usr/bin/otool -L "${binary}" >&2 + fail "AudioKitEX does not link CAudioKitEX.framework" + fi +} + +assert_build_version() { + local binary="$1" + local expected_platform="$2" + local expected_minimum="$3" + local build_versions + + build_versions="$(/usr/bin/xcrun vtool -show-build "${binary}")" + + if ! /usr/bin/printf '%s\n' "${build_versions}" \ + | /usr/bin/grep -E -q "^[[:space:]]*platform ${expected_platform}$"; then + fail "unexpected platform in binary: ${binary}" + fi + + if ! /usr/bin/printf '%s\n' "${build_versions}" \ + | /usr/bin/grep -E -q "^[[:space:]]*minos ${expected_minimum}$"; then + fail "unexpected minimum deployment target in binary: ${binary}" + fi +} + +readonly AUDIOKIT_DEVICE_SLICE="${AUDIOKIT_XCFRAMEWORK}/ios-arm64" +readonly AUDIOKIT_SIMULATOR_SLICE="${AUDIOKIT_XCFRAMEWORK}/ios-arm64_x86_64-simulator" +readonly AUDIOKIT_MACOS_SLICE="${AUDIOKIT_XCFRAMEWORK}/${MACOS_SLICE_IDENTIFIER}" +readonly AUDIOKIT_DEVICE_BINARY="${AUDIOKIT_DEVICE_SLICE}/AudioKit.framework/AudioKit" +readonly AUDIOKIT_SIMULATOR_BINARY="${AUDIOKIT_SIMULATOR_SLICE}/AudioKit.framework/AudioKit" +readonly AUDIOKIT_MACOS_BINARY="${AUDIOKIT_MACOS_SLICE}/AudioKit.framework/Versions/A/AudioKit" +require_path "${AUDIOKIT_DEVICE_BINARY}" +require_path "${AUDIOKIT_SIMULATOR_BINARY}" +require_path "${AUDIOKIT_MACOS_BINARY}" + +printf '\nBuilding AudioKitEX %s.\n' "${UPSTREAM_TAG}" + +archive_frameworks "generic/platform=iOS" "${DEVICE_ARCHIVE}" "${AUDIOKIT_DEVICE_SLICE}" "iOS device" "arm64" 1 + +archive_frameworks "generic/platform=iOS Simulator" "${SIMULATOR_ARCHIVE}" "${AUDIOKIT_SIMULATOR_SLICE}" "iOS Simulator" "arm64 x86_64" 2 + +archive_frameworks "generic/platform=macOS" "${MACOS_ARCHIVE}" "${AUDIOKIT_MACOS_SLICE}" "macOS" "${MACOS_ARCHITECTURES}" 3 + +readonly DEVICE_AUDIOKITEX_FRAMEWORK="${DEVICE_ARCHIVE}/Products/Library/Frameworks/AudioKitEX.framework" +readonly DEVICE_AUDIOKITEX_DSYM="${DEVICE_ARCHIVE}/dSYMs/AudioKitEX.framework.dSYM" +readonly DEVICE_CAUDIOKITEX_FRAMEWORK="${DEVICE_ARCHIVE}/Products/Library/Frameworks/CAudioKitEX.framework" +readonly DEVICE_CAUDIOKITEX_DSYM="${DEVICE_ARCHIVE}/dSYMs/CAudioKitEX.framework.dSYM" +readonly SIMULATOR_AUDIOKITEX_FRAMEWORK="${SIMULATOR_ARCHIVE}/Products/Library/Frameworks/AudioKitEX.framework" +readonly SIMULATOR_AUDIOKITEX_DSYM="${SIMULATOR_ARCHIVE}/dSYMs/AudioKitEX.framework.dSYM" +readonly SIMULATOR_CAUDIOKITEX_FRAMEWORK="${SIMULATOR_ARCHIVE}/Products/Library/Frameworks/CAudioKitEX.framework" +readonly SIMULATOR_CAUDIOKITEX_DSYM="${SIMULATOR_ARCHIVE}/dSYMs/CAudioKitEX.framework.dSYM" +readonly MACOS_AUDIOKITEX_FRAMEWORK="${MACOS_ARCHIVE}/Products/Library/Frameworks/AudioKitEX.framework" +readonly MACOS_AUDIOKITEX_DSYM="${MACOS_ARCHIVE}/dSYMs/AudioKitEX.framework.dSYM" +readonly MACOS_CAUDIOKITEX_FRAMEWORK="${MACOS_ARCHIVE}/Products/Library/Frameworks/CAudioKitEX.framework" +readonly MACOS_CAUDIOKITEX_DSYM="${MACOS_ARCHIVE}/dSYMs/CAudioKitEX.framework.dSYM" + +for expected_path in \ + "${DEVICE_AUDIOKITEX_FRAMEWORK}/AudioKitEX" \ + "${DEVICE_AUDIOKITEX_DSYM}" \ + "${DEVICE_CAUDIOKITEX_FRAMEWORK}/CAudioKitEX" \ + "${DEVICE_CAUDIOKITEX_DSYM}" \ + "${SIMULATOR_AUDIOKITEX_FRAMEWORK}/AudioKitEX" \ + "${SIMULATOR_AUDIOKITEX_DSYM}" \ + "${SIMULATOR_CAUDIOKITEX_FRAMEWORK}/CAudioKitEX" \ + "${SIMULATOR_CAUDIOKITEX_DSYM}" \ + "${MACOS_AUDIOKITEX_FRAMEWORK}/Versions/A/AudioKitEX" \ + "${MACOS_AUDIOKITEX_DSYM}" \ + "${MACOS_CAUDIOKITEX_FRAMEWORK}/Versions/A/CAudioKitEX" \ + "${MACOS_CAUDIOKITEX_DSYM}"; do + require_path "${expected_path}" +done + +printf '[4/%d] Creating AudioKitEX.xcframework...\n' "${BUILD_STEP_COUNT}" +/usr/bin/xcodebuild -create-xcframework \ + -framework "${DEVICE_AUDIOKITEX_FRAMEWORK}" \ + -debug-symbols "${DEVICE_AUDIOKITEX_DSYM}" \ + -framework "${SIMULATOR_AUDIOKITEX_FRAMEWORK}" \ + -debug-symbols "${SIMULATOR_AUDIOKITEX_DSYM}" \ + -framework "${MACOS_AUDIOKITEX_FRAMEWORK}" \ + -debug-symbols "${MACOS_AUDIOKITEX_DSYM}" \ + -output "${BUILT_AUDIOKITEX_XCFRAMEWORK}" + +printf '[5/%d] Creating CAudioKitEX.xcframework...\n' "${BUILD_STEP_COUNT}" +/usr/bin/xcodebuild -create-xcframework \ + -framework "${DEVICE_CAUDIOKITEX_FRAMEWORK}" \ + -debug-symbols "${DEVICE_CAUDIOKITEX_DSYM}" \ + -framework "${SIMULATOR_CAUDIOKITEX_FRAMEWORK}" \ + -debug-symbols "${SIMULATOR_CAUDIOKITEX_DSYM}" \ + -framework "${MACOS_CAUDIOKITEX_FRAMEWORK}" \ + -debug-symbols "${MACOS_CAUDIOKITEX_DSYM}" \ + -output "${BUILT_CAUDIOKITEX_XCFRAMEWORK}" + +readonly AUDIOKITEX_DEVICE_SLICE="${BUILT_AUDIOKITEX_XCFRAMEWORK}/ios-arm64" +readonly AUDIOKITEX_SIMULATOR_SLICE="${BUILT_AUDIOKITEX_XCFRAMEWORK}/ios-arm64_x86_64-simulator" +readonly AUDIOKITEX_MACOS_SLICE="${BUILT_AUDIOKITEX_XCFRAMEWORK}/${MACOS_SLICE_IDENTIFIER}" +readonly CAUDIOKITEX_DEVICE_SLICE="${BUILT_CAUDIOKITEX_XCFRAMEWORK}/ios-arm64" +readonly CAUDIOKITEX_SIMULATOR_SLICE="${BUILT_CAUDIOKITEX_XCFRAMEWORK}/ios-arm64_x86_64-simulator" +readonly CAUDIOKITEX_MACOS_SLICE="${BUILT_CAUDIOKITEX_XCFRAMEWORK}/${MACOS_SLICE_IDENTIFIER}" + +readonly AUDIOKITEX_DEVICE_BINARY="${AUDIOKITEX_DEVICE_SLICE}/AudioKitEX.framework/AudioKitEX" +readonly AUDIOKITEX_SIMULATOR_BINARY="${AUDIOKITEX_SIMULATOR_SLICE}/AudioKitEX.framework/AudioKitEX" +readonly AUDIOKITEX_MACOS_BINARY="${AUDIOKITEX_MACOS_SLICE}/AudioKitEX.framework/Versions/A/AudioKitEX" +readonly CAUDIOKITEX_DEVICE_BINARY="${CAUDIOKITEX_DEVICE_SLICE}/CAudioKitEX.framework/CAudioKitEX" +readonly CAUDIOKITEX_SIMULATOR_BINARY="${CAUDIOKITEX_SIMULATOR_SLICE}/CAudioKitEX.framework/CAudioKitEX" +readonly CAUDIOKITEX_MACOS_BINARY="${CAUDIOKITEX_MACOS_SLICE}/CAudioKitEX.framework/Versions/A/CAudioKitEX" + +readonly AUDIOKITEX_DEVICE_XCFRAMEWORK_DSYM="${AUDIOKITEX_DEVICE_SLICE}/dSYMs/AudioKitEX.framework.dSYM" +readonly AUDIOKITEX_SIMULATOR_XCFRAMEWORK_DSYM="${AUDIOKITEX_SIMULATOR_SLICE}/dSYMs/AudioKitEX.framework.dSYM" +readonly AUDIOKITEX_MACOS_XCFRAMEWORK_DSYM="${AUDIOKITEX_MACOS_SLICE}/dSYMs/AudioKitEX.framework.dSYM" +readonly CAUDIOKITEX_DEVICE_XCFRAMEWORK_DSYM="${CAUDIOKITEX_DEVICE_SLICE}/dSYMs/CAudioKitEX.framework.dSYM" +readonly CAUDIOKITEX_SIMULATOR_XCFRAMEWORK_DSYM="${CAUDIOKITEX_SIMULATOR_SLICE}/dSYMs/CAudioKitEX.framework.dSYM" +readonly CAUDIOKITEX_MACOS_XCFRAMEWORK_DSYM="${CAUDIOKITEX_MACOS_SLICE}/dSYMs/CAudioKitEX.framework.dSYM" + +for expected_path in \ + "${BUILT_AUDIOKITEX_XCFRAMEWORK}/Info.plist" \ + "${BUILT_CAUDIOKITEX_XCFRAMEWORK}/Info.plist" \ + "${AUDIOKITEX_DEVICE_BINARY}" \ + "${AUDIOKITEX_SIMULATOR_BINARY}" \ + "${AUDIOKITEX_MACOS_BINARY}" \ + "${CAUDIOKITEX_DEVICE_BINARY}" \ + "${CAUDIOKITEX_SIMULATOR_BINARY}" \ + "${CAUDIOKITEX_MACOS_BINARY}" \ + "${AUDIOKITEX_DEVICE_XCFRAMEWORK_DSYM}" \ + "${AUDIOKITEX_SIMULATOR_XCFRAMEWORK_DSYM}" \ + "${AUDIOKITEX_MACOS_XCFRAMEWORK_DSYM}" \ + "${CAUDIOKITEX_DEVICE_XCFRAMEWORK_DSYM}" \ + "${CAUDIOKITEX_SIMULATOR_XCFRAMEWORK_DSYM}" \ + "${CAUDIOKITEX_MACOS_XCFRAMEWORK_DSYM}" \ + "${CAUDIOKITEX_DEVICE_SLICE}/CAudioKitEX.framework/Headers/CAudioKitEX.h" \ + "${CAUDIOKITEX_SIMULATOR_SLICE}/CAudioKitEX.framework/Headers/CAudioKitEX.h" \ + "${CAUDIOKITEX_MACOS_SLICE}/CAudioKitEX.framework/Versions/A/Headers/CAudioKitEX.h"; do + require_path "${expected_path}" +done + +printf '[6/%d] Validating slices, linkage, interfaces, symbols, and dSYMs...\n' "${BUILD_STEP_COUNT}" +/usr/bin/plutil -lint "${BUILT_AUDIOKITEX_XCFRAMEWORK}/Info.plist" >/dev/null +/usr/bin/plutil -lint "${BUILT_CAUDIOKITEX_XCFRAMEWORK}/Info.plist" >/dev/null + +/usr/bin/lipo "${AUDIOKITEX_DEVICE_BINARY}" -verify_arch arm64 +/usr/bin/lipo "${AUDIOKITEX_SIMULATOR_BINARY}" -verify_arch arm64 +/usr/bin/lipo "${AUDIOKITEX_SIMULATOR_BINARY}" -verify_arch x86_64 +for macos_architecture in ${MACOS_ARCHITECTURES}; do + /usr/bin/lipo "${AUDIOKITEX_MACOS_BINARY}" -verify_arch "${macos_architecture}" +done +/usr/bin/lipo "${CAUDIOKITEX_DEVICE_BINARY}" -verify_arch arm64 +/usr/bin/lipo "${CAUDIOKITEX_SIMULATOR_BINARY}" -verify_arch arm64 +/usr/bin/lipo "${CAUDIOKITEX_SIMULATOR_BINARY}" -verify_arch x86_64 +for macos_architecture in ${MACOS_ARCHITECTURES}; do + /usr/bin/lipo "${CAUDIOKITEX_MACOS_BINARY}" -verify_arch "${macos_architecture}" +done + +assert_dynamic_framework "${AUDIOKITEX_DEVICE_BINARY}" +assert_dynamic_framework "${AUDIOKITEX_SIMULATOR_BINARY}" +assert_dynamic_framework "${AUDIOKITEX_MACOS_BINARY}" +assert_dynamic_framework "${CAUDIOKITEX_DEVICE_BINARY}" +assert_dynamic_framework "${CAUDIOKITEX_SIMULATOR_BINARY}" +assert_dynamic_framework "${CAUDIOKITEX_MACOS_BINARY}" +assert_expected_linkage "${AUDIOKITEX_DEVICE_BINARY}" +assert_expected_linkage "${AUDIOKITEX_SIMULATOR_BINARY}" +assert_expected_linkage "${AUDIOKITEX_MACOS_BINARY}" + +assert_build_version "${AUDIOKITEX_DEVICE_BINARY}" IOS "${MINIMUM_IOS_VERSION}" +assert_build_version "${AUDIOKITEX_SIMULATOR_BINARY}" IOSSIMULATOR "${MINIMUM_IOS_VERSION}" +assert_build_version "${AUDIOKITEX_MACOS_BINARY}" MACOS "${MINIMUM_MACOS_VERSION}" +assert_build_version "${CAUDIOKITEX_DEVICE_BINARY}" IOS "${MINIMUM_IOS_VERSION}" +assert_build_version "${CAUDIOKITEX_SIMULATOR_BINARY}" IOSSIMULATOR "${MINIMUM_IOS_VERSION}" +assert_build_version "${CAUDIOKITEX_MACOS_BINARY}" MACOS "${MINIMUM_MACOS_VERSION}" + +interface_count="$(/usr/bin/find "${BUILT_AUDIOKITEX_XCFRAMEWORK}" -type f -name '*.swiftinterface' | /usr/bin/wc -l | /usr/bin/tr -d ' ')" +[[ "${interface_count}" -ge 3 ]] || fail "stable AudioKitEX Swift module interfaces are missing" + +if ! /usr/bin/grep -R -q '^import AudioKit$' "${BUILT_AUDIOKITEX_XCFRAMEWORK}"; then + fail "AudioKitEX stable interface does not import AudioKit" +fi + +if ! /usr/bin/grep -R -q '^import CAudioKitEX$' "${BUILT_AUDIOKITEX_XCFRAMEWORK}"; then + fail "AudioKitEX stable interface does not import CAudioKitEX" +fi + +if ! /usr/bin/nm -gU "${CAUDIOKITEX_DEVICE_BINARY}" | /usr/bin/grep -q '_akRenderMeasurerCreate$'; then + fail "CAudioKitEX device slice is missing akRenderMeasurerCreate" +fi +if ! /usr/bin/nm -gU "${CAUDIOKITEX_MACOS_BINARY}" | /usr/bin/grep -q '_akRenderMeasurerCreate$'; then + fail "CAudioKitEX macOS slice is missing akRenderMeasurerCreate" +fi + +assert_matching_uuids "${AUDIOKITEX_DEVICE_BINARY}" "${AUDIOKITEX_DEVICE_XCFRAMEWORK_DSYM}" +assert_matching_uuids "${AUDIOKITEX_SIMULATOR_BINARY}" "${AUDIOKITEX_SIMULATOR_XCFRAMEWORK_DSYM}" +assert_matching_uuids "${AUDIOKITEX_MACOS_BINARY}" "${AUDIOKITEX_MACOS_XCFRAMEWORK_DSYM}" +assert_matching_uuids "${CAUDIOKITEX_DEVICE_BINARY}" "${CAUDIOKITEX_DEVICE_XCFRAMEWORK_DSYM}" +assert_matching_uuids "${CAUDIOKITEX_SIMULATOR_BINARY}" "${CAUDIOKITEX_SIMULATOR_XCFRAMEWORK_DSYM}" +assert_matching_uuids "${CAUDIOKITEX_MACOS_BINARY}" "${CAUDIOKITEX_MACOS_XCFRAMEWORK_DSYM}" + +printf '[7/%d] Linking a clean iOS Simulator smoke module against the three binaries only...\n' "${BUILD_STEP_COUNT}" +/usr/bin/printf '%s\n' \ + 'import AudioKit' \ + 'import AudioKitEX' \ + '' \ + 'public func makeBinarySmokeMixer() -> DryWetMixer {' \ + ' DryWetMixer(Mixer(), Mixer())' \ + '}' \ + >"${SMOKE_SOURCE}" + +readonly SIMULATOR_SDK="$(/usr/bin/xcrun --sdk iphonesimulator --show-sdk-path)" +/usr/bin/xcrun --sdk iphonesimulator swiftc \ + "${SMOKE_SOURCE}" \ + -parse-as-library \ + -emit-library \ + -target "arm64-apple-ios${MINIMUM_IOS_VERSION}-simulator" \ + -sdk "${SIMULATOR_SDK}" \ + -F "${AUDIOKIT_SIMULATOR_SLICE}" \ + -F "${AUDIOKITEX_SIMULATOR_SLICE}" \ + -F "${CAUDIOKITEX_SIMULATOR_SLICE}" \ + -framework AudioKit \ + -framework AudioKitEX \ + -framework CAudioKitEX \ + -o "${SMOKE_LIBRARY}" +require_path "${SMOKE_LIBRARY}" + +printf '[8/%d] Linking clean macOS smoke modules against the three binaries only...\n' "${BUILD_STEP_COUNT}" +readonly MACOS_SDK="$(/usr/bin/xcrun --sdk macosx --show-sdk-path)" +for macos_architecture in ${MACOS_ARCHITECTURES}; do + if [[ "${macos_architecture}" == "arm64" ]]; then + macos_smoke_library="${MACOS_ARM64_SMOKE_LIBRARY}" + else + macos_smoke_library="${MACOS_X86_64_SMOKE_LIBRARY}" + fi + + /usr/bin/xcrun --sdk macosx swiftc \ + "${SMOKE_SOURCE}" \ + -parse-as-library \ + -emit-library \ + -target "${macos_architecture}-apple-macosx${MINIMUM_MACOS_VERSION}" \ + -sdk "${MACOS_SDK}" \ + -F "${AUDIOKIT_MACOS_SLICE}" \ + -F "${AUDIOKITEX_MACOS_SLICE}" \ + -F "${CAUDIOKITEX_MACOS_SLICE}" \ + -framework AudioKit \ + -framework AudioKitEX \ + -framework CAudioKitEX \ + -o "${macos_smoke_library}" + require_path "${macos_smoke_library}" +done + +readonly BUILD_DATE="$(/bin/date '+%Y-%m-%d %H:%M:%S %Z')" +readonly XCODE_VERSION="$(/usr/bin/xcodebuild -version | /usr/bin/awk 'NR == 1 { name = $0 } NR == 2 { print name " (" $3 ")" }')" +readonly SWIFT_VERSION="$(/usr/bin/xcrun swift --version 2>&1 | /usr/bin/awk 'NR == 1 { sub(/^.*Apple Swift version/, "Apple Swift version"); print; exit }')" +readonly AUDIOKITEX_DEVICE_UUIDS="$(uuid_summary "${AUDIOKITEX_DEVICE_BINARY}")" +readonly AUDIOKITEX_SIMULATOR_UUIDS="$(uuid_summary "${AUDIOKITEX_SIMULATOR_BINARY}")" +readonly AUDIOKITEX_MACOS_UUIDS="$(uuid_summary "${AUDIOKITEX_MACOS_BINARY}")" +readonly AUDIOKITEX_MACOS_ARCHITECTURES="$(/usr/bin/lipo -archs "${AUDIOKITEX_MACOS_BINARY}")" +readonly CAUDIOKITEX_DEVICE_UUIDS="$(uuid_summary "${CAUDIOKITEX_DEVICE_BINARY}")" +readonly CAUDIOKITEX_SIMULATOR_UUIDS="$(uuid_summary "${CAUDIOKITEX_SIMULATOR_BINARY}")" +readonly CAUDIOKITEX_MACOS_UUIDS="$(uuid_summary "${CAUDIOKITEX_MACOS_BINARY}")" +readonly CAUDIOKITEX_MACOS_ARCHITECTURES="$(/usr/bin/lipo -archs "${CAUDIOKITEX_MACOS_BINARY}")" +readonly AUDIOKITEX_DEVICE_SHA256="$(/usr/bin/shasum -a 256 "${AUDIOKITEX_DEVICE_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly AUDIOKITEX_SIMULATOR_SHA256="$(/usr/bin/shasum -a 256 "${AUDIOKITEX_SIMULATOR_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly AUDIOKITEX_MACOS_SHA256="$(/usr/bin/shasum -a 256 "${AUDIOKITEX_MACOS_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly CAUDIOKITEX_DEVICE_SHA256="$(/usr/bin/shasum -a 256 "${CAUDIOKITEX_DEVICE_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly CAUDIOKITEX_SIMULATOR_SHA256="$(/usr/bin/shasum -a 256 "${CAUDIOKITEX_SIMULATOR_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly CAUDIOKITEX_MACOS_SHA256="$(/usr/bin/shasum -a 256 "${CAUDIOKITEX_MACOS_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly AUDIOKIT_DEVICE_SHA256="$(/usr/bin/shasum -a 256 "${AUDIOKIT_DEVICE_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly AUDIOKIT_SIMULATOR_SHA256="$(/usr/bin/shasum -a 256 "${AUDIOKIT_SIMULATOR_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly AUDIOKIT_MACOS_SHA256="$(/usr/bin/shasum -a 256 "${AUDIOKIT_MACOS_BINARY}" | /usr/bin/awk '{ print $1 }')" +readonly AUDIOKIT_MACOS_ARCHITECTURES="$(/usr/bin/lipo -archs "${AUDIOKIT_MACOS_BINARY}")" + +{ + printf '# AudioKitEX build information\n\n' + printf -- '- Artifacts: `AudioKitEX.xcframework`, `CAudioKitEX.xcframework`\n' + printf -- '- AudioKitEX release: `%s`\n' "${UPSTREAM_TAG}" + printf -- '- Upstream tag object: `%s`\n' "${UPSTREAM_TAG_OBJECT}" + printf -- '- Upstream source commit: `%s`\n' "${UPSTREAM_COMMIT}" + printf -- '- Build-infrastructure commit: `%s`\n' "${SOURCE_COMMIT}" + printf -- '- Source repository: `%s`\n' "${SOURCE_ORIGIN}" + printf -- '- Source state: `%s`\n' "${SOURCE_STATE}" + printf -- '- Built: `%s`\n' "${BUILD_DATE}" + printf -- '- Xcode: `%s`\n' "${XCODE_VERSION}" + printf -- '- Swift: `%s`\n' "${SWIFT_VERSION}" + printf -- '- Configuration: `Release`\n' + printf -- '- Library evolution: enabled\n' + printf -- '- Minimum iOS: `%s`\n' "${MINIMUM_IOS_VERSION}" + printf -- '- Minimum macOS: `%s`\n' "${MINIMUM_MACOS_VERSION}" + printf -- '- Required AudioKit release: `%s`\n\n' "${REQUIRED_AUDIOKIT_RELEASE}" + printf '## Linked AudioKit binary SHA-256\n\n' + printf -- '- iOS arm64: `%s`\n' "${AUDIOKIT_DEVICE_SHA256}" + printf -- '- iOS Simulator x86_64 arm64: `%s`\n' "${AUDIOKIT_SIMULATOR_SHA256}" + printf -- '- macOS %s: `%s`\n\n' "${AUDIOKIT_MACOS_ARCHITECTURES}" "${AUDIOKIT_MACOS_SHA256}" + printf '## AudioKitEX dSYM UUIDs\n\n' + printf -- '- iOS arm64: `%s`\n' "${AUDIOKITEX_DEVICE_UUIDS}" + printf -- '- iOS Simulator x86_64 arm64: `%s`\n' "${AUDIOKITEX_SIMULATOR_UUIDS}" + printf -- '- macOS %s: `%s`\n\n' "${AUDIOKITEX_MACOS_ARCHITECTURES}" "${AUDIOKITEX_MACOS_UUIDS}" + printf '## CAudioKitEX dSYM UUIDs\n\n' + printf -- '- iOS arm64: `%s`\n' "${CAUDIOKITEX_DEVICE_UUIDS}" + printf -- '- iOS Simulator x86_64 arm64: `%s`\n' "${CAUDIOKITEX_SIMULATOR_UUIDS}" + printf -- '- macOS %s: `%s`\n\n' "${CAUDIOKITEX_MACOS_ARCHITECTURES}" "${CAUDIOKITEX_MACOS_UUIDS}" + printf '## Binary SHA-256\n\n' + printf -- '- AudioKitEX iOS arm64: `%s`\n' "${AUDIOKITEX_DEVICE_SHA256}" + printf -- '- AudioKitEX iOS Simulator x86_64 arm64: `%s`\n' "${AUDIOKITEX_SIMULATOR_SHA256}" + printf -- '- AudioKitEX macOS %s: `%s`\n' "${AUDIOKITEX_MACOS_ARCHITECTURES}" "${AUDIOKITEX_MACOS_SHA256}" + printf -- '- CAudioKitEX iOS arm64: `%s`\n' "${CAUDIOKITEX_DEVICE_SHA256}" + printf -- '- CAudioKitEX iOS Simulator x86_64 arm64: `%s`\n' "${CAUDIOKITEX_SIMULATOR_SHA256}" + printf -- '- CAudioKitEX macOS %s: `%s`\n\n' "${CAUDIOKITEX_MACOS_ARCHITECTURES}" "${CAUDIOKITEX_MACOS_SHA256}" + printf 'All slices in both artifacts contain matching dSYMs. `AudioKitEX` contains stable Swift interfaces and links ' + printf '`AudioKit` plus `CAudioKitEX`. The smoke check imports both Swift modules, creates a `DryWetMixer`, ' + printf 'and links all three frameworks on iOS Simulator plus the selected macOS architectures without source dependencies.\n' +} >"${GENERATED_BUILD_INFO}" + +if [[ -e "${AUDIOKITEX_INSTALL_STAGE}" || -e "${CAUDIOKITEX_INSTALL_STAGE}" || -e "${BUILD_INFO_STAGE}" ]]; then + fail "temporary install path already exists in output directory" +fi + +printf '[9/%d] Staging verified artifacts for %s...\n' "${BUILD_STEP_COUNT}" "${OUTPUT_DIRECTORY}" +/usr/bin/ditto "${BUILT_AUDIOKITEX_XCFRAMEWORK}" "${AUDIOKITEX_INSTALL_STAGE}" +/usr/bin/ditto "${BUILT_CAUDIOKITEX_XCFRAMEWORK}" "${CAUDIOKITEX_INSTALL_STAGE}" +/bin/cp "${GENERATED_BUILD_INFO}" "${BUILD_INFO_STAGE}" + +INSTALL_STARTED=1 + +if [[ -e "${DESTINATION_AUDIOKITEX_XCFRAMEWORK}" ]]; then + AUDIOKITEX_BACKUP="${OUTPUT_DIRECTORY}/.AudioKitEX.xcframework.previous.$$" + /bin/mv "${DESTINATION_AUDIOKITEX_XCFRAMEWORK}" "${AUDIOKITEX_BACKUP}" +fi + +if [[ -e "${DESTINATION_CAUDIOKITEX_XCFRAMEWORK}" ]]; then + CAUDIOKITEX_BACKUP="${OUTPUT_DIRECTORY}/.CAudioKitEX.xcframework.previous.$$" + /bin/mv "${DESTINATION_CAUDIOKITEX_XCFRAMEWORK}" "${CAUDIOKITEX_BACKUP}" +fi + +if [[ -e "${DESTINATION_BUILD_INFO}" ]]; then + BUILD_INFO_BACKUP="${OUTPUT_DIRECTORY}/.BUILD_INFO.md.previous.$$" + /bin/mv "${DESTINATION_BUILD_INFO}" "${BUILD_INFO_BACKUP}" +fi + +/bin/mv "${AUDIOKITEX_INSTALL_STAGE}" "${DESTINATION_AUDIOKITEX_XCFRAMEWORK}" +/bin/mv "${CAUDIOKITEX_INSTALL_STAGE}" "${DESTINATION_CAUDIOKITEX_XCFRAMEWORK}" +/bin/mv "${BUILD_INFO_STAGE}" "${DESTINATION_BUILD_INFO}" + +if [[ -n "${AUDIOKITEX_BACKUP}" ]]; then + safe_remove "${AUDIOKITEX_BACKUP}" + AUDIOKITEX_BACKUP="" +fi +if [[ -n "${CAUDIOKITEX_BACKUP}" ]]; then + safe_remove "${CAUDIOKITEX_BACKUP}" + CAUDIOKITEX_BACKUP="" +fi +if [[ -n "${BUILD_INFO_BACKUP}" ]]; then + safe_remove "${BUILD_INFO_BACKUP}" + BUILD_INFO_BACKUP="" +fi + +INSTALL_STARTED=0 + +printf '\nAudioKitEX XCFrameworks are ready:\n%s\n%s\n' \ + "${DESTINATION_AUDIOKITEX_XCFRAMEWORK}" \ + "${DESTINATION_CAUDIOKITEX_XCFRAMEWORK}" +printf 'AudioKitEX device UUIDs: %s\n' "${AUDIOKITEX_DEVICE_UUIDS}" +printf 'AudioKitEX simulator UUIDs: %s\n' "${AUDIOKITEX_SIMULATOR_UUIDS}" +printf 'AudioKitEX macOS UUIDs: %s\n' "${AUDIOKITEX_MACOS_UUIDS}" +printf 'CAudioKitEX device UUIDs: %s\n' "${CAUDIOKITEX_DEVICE_UUIDS}" +printf 'CAudioKitEX simulator UUIDs: %s\n' "${CAUDIOKITEX_SIMULATOR_UUIDS}" +printf 'CAudioKitEX macOS UUIDs: %s\n' "${CAUDIOKITEX_MACOS_UUIDS}" diff --git a/XCFramework/build.command b/XCFramework/build.command new file mode 100755 index 0000000..82e0aa4 --- /dev/null +++ b/XCFramework/build.command @@ -0,0 +1,50 @@ +#!/bin/zsh + +set -u + +readonly COMMAND_DIRECTORY="${0:A:h}" +readonly SOURCE_ROOT="${COMMAND_DIRECTORY:h}" +readonly BUILD_SCRIPT="${COMMAND_DIRECTORY}/build-xcframework.sh" + +audiokit_binary_directory="${AUDIOKIT_XCFRAMEWORK_INPUT:-${SOURCE_ROOT}/../AudioKitBinary}" +if [[ ! -d "${audiokit_binary_directory}" ]]; then + printf 'AudioKit binary directory [%s]: ' "${audiokit_binary_directory}" + if IFS= read -r entered_audiokit_directory && [[ -n "${entered_audiokit_directory}" ]]; then + audiokit_binary_directory="${entered_audiokit_directory}" + fi +fi + +if [[ -n "${AUDIOKITEX_XCFRAMEWORK_OUTPUT:-}" ]]; then + output_directory="${AUDIOKITEX_XCFRAMEWORK_OUTPUT}" +elif [[ -d "${SOURCE_ROOT}/../AudioKitEXBinary" ]]; then + output_directory="${SOURCE_ROOT}/../AudioKitEXBinary" +else + output_directory="${SOURCE_ROOT}/.build/xcframework-output" +fi + +readonly AUDIOKIT_BINARY_DIRECTORY="${audiokit_binary_directory}" +readonly OUTPUT_DIRECTORY="${output_directory}" + +clear +printf 'AudioKitEX XCFramework builder\n\n' +printf 'AudioKit input directory:\n%s\n\n' "${AUDIOKIT_BINARY_DIRECTORY}" +printf 'Output directory:\n%s\n\n' "${OUTPUT_DIRECTORY}" + +if [[ ! -x "${BUILD_SCRIPT}" ]]; then + printf 'Error: build script was not found or is not executable:\n%s\n' "${BUILD_SCRIPT}" >&2 + build_status=1 +else + "${BUILD_SCRIPT}" "${AUDIOKIT_BINARY_DIRECTORY}" "${OUTPUT_DIRECTORY}" + build_status="$?" +fi + +if [[ "${build_status}" -eq 0 ]]; then + printf '\nDone. AudioKitEX XCFrameworks were built successfully.\n' +else + printf '\nThe build failed with exit code %s.\n' "${build_status}" >&2 +fi + +printf '\nPress Return to close this window...' +read -r + +exit "${build_status}"