From b414bfc0d231212e49b1061f042c14cb7d443fa1 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Mon, 6 May 2024 20:22:23 -0400 Subject: [PATCH] [Release Tooling] Add functionality to build dynamic frameworks (#12890) --- .github/workflows/zip.yml | 42 ++++++++++----- .../Sources/ZipBuilder/FrameworkBuilder.swift | 46 ++-------------- .../Sources/ZipBuilder/ZipBuilder.swift | 52 ++++++++++++++++++- scripts/build_zip.sh | 38 ++++++++++---- 4 files changed, 110 insertions(+), 68 deletions(-) diff --git a/.github/workflows/zip.yml b/.github/workflows/zip.yml index ef41e52a963..606a1acdc39 100644 --- a/.github/workflows/zip.yml +++ b/.github/workflows/zip.yml @@ -43,7 +43,9 @@ jobs: run: | mkdir -p release_zip_dir sh -x scripts/build_zip.sh release_zip_dir \ - "${{ github.event.inputs.custom_spec_repos || 'https://github.com/firebase/SpecsStaging.git' }}" + "${{ github.event.inputs.custom_spec_repos || 'https://github.com/firebase/SpecsStaging.git' }}" \ + build-release \ + static - uses: actions/upload-artifact@v4 with: name: Firebase-release-zip-zip @@ -68,6 +70,9 @@ jobs: # Don't run on private repo. if: (github.repository == 'Firebase/firebase-ios-sdk' && github.event_name == 'schedule') || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' needs: build + strategy: + matrix: + linking_type: [static, dynamic] runs-on: macos-14 steps: - uses: actions/checkout@v4 @@ -84,10 +89,11 @@ jobs: mkdir -p zip_output_dir sh -x scripts/build_zip.sh \ zip_output_dir "${{ github.event.inputs.custom_spec_repos || 'https://github.com/firebase/SpecsStaging.git,https://github.com/firebase/SpecsDev.git' }}" \ - build-head + build-head \ + ${{ matrix.linking_type }} - uses: actions/upload-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.linking_type == 'static' && 'Firebase-actions-dir' || 'Firebase-actions-dir-dynamic' }} # Zip the entire output directory since the builder adds subdirectories we don't know the # name of. path: zip_output_dir @@ -103,6 +109,7 @@ jobs: strategy: matrix: os: [macos-13, macos-14] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] include: - os: macos-13 xcode: Xcode_15.2 @@ -114,7 +121,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -164,6 +171,7 @@ jobs: strategy: matrix: os: [macos-13, macos-14] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] include: - os: macos-13 xcode: Xcode_15.2 @@ -175,7 +183,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -217,6 +225,7 @@ jobs: strategy: matrix: os: [macos-13, macos-14] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] include: - os: macos-13 xcode: Xcode_15.2 @@ -228,7 +237,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -268,6 +277,7 @@ jobs: strategy: matrix: os: [macos-13, macos-14] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] include: - os: macos-13 xcode: Xcode_15.2 @@ -279,7 +289,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -343,6 +353,7 @@ jobs: matrix: os: [macos-13] xcode: [Xcode_15.2] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] # TODO: Building FirebaseUI fails on Xcode 15 because it needs to sign the resources. # - os: macos-13 # xcode: Xcode_15.2 @@ -352,7 +363,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -396,6 +407,7 @@ jobs: strategy: matrix: os: [macos-13, macos-14] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] include: - os: macos-13 xcode: Xcode_15.2 @@ -407,7 +419,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -456,6 +468,7 @@ jobs: # matrix: # # TODO: Building FirebaseUI fails on Xcode 15 because it needs to sign the resources. # os: [macos-13] + # artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] # xcode: [Xcode_15.2] # runs-on: ${{ matrix.os }} # steps: @@ -463,7 +476,7 @@ jobs: # - name: Get framework dir # uses: actions/download-artifact@v4 # with: - # name: Firebase-actions-dir + # name: ${{ matrix.artifact }} # - uses: ruby/setup-ruby@v1 # - name: Setup Bundler # run: ./scripts/setup_bundler.sh @@ -536,6 +549,7 @@ jobs: strategy: matrix: os: [macos-13, macos-14] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] include: - os: macos-13 xcode: Xcode_15.2 @@ -547,7 +561,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -592,6 +606,7 @@ jobs: strategy: matrix: os: [macos-13, macos-14] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] include: - os: macos-13 xcode: Xcode_15.2 @@ -603,7 +618,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -647,6 +662,7 @@ jobs: strategy: matrix: os: [macos-13, macos-14] + artifact: [Firebase-actions-dir, Firebase-actions-dir-dynamic] include: - os: macos-13 xcode: Xcode_15.2 @@ -658,7 +674,7 @@ jobs: - name: Get framework dir uses: actions/download-artifact@v4 with: - name: Firebase-actions-dir + name: ${{ matrix.artifact }} - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh diff --git a/ReleaseTooling/Sources/ZipBuilder/FrameworkBuilder.swift b/ReleaseTooling/Sources/ZipBuilder/FrameworkBuilder.swift index 144459bf90e..cd87fd3272e 100755 --- a/ReleaseTooling/Sources/ZipBuilder/FrameworkBuilder.swift +++ b/ReleaseTooling/Sources/ZipBuilder/FrameworkBuilder.swift @@ -306,18 +306,14 @@ struct FrameworkBuilder { logsDir: URL) -> [URL] { // xcframework doesn't lipo things together but accepts fat frameworks for one target. // We group architectures here to deal with this fact. - var thinFrameworks = [URL]() - for targetPlatform in TargetPlatform.allCases { - let buildDir = projectDir.appendingPathComponent(targetPlatform.buildName) - let slicedFramework = buildSlicedFramework( - withName: FrameworkBuilder.frameworkBuildName(framework), + return targetPlatforms.map { targetPlatform in + buildSlicedFramework( + withName: framework, targetPlatform: targetPlatform, - buildDir: buildDir, + buildDir: projectDir.appendingPathComponent(targetPlatform.buildName), logRoot: logsDir ) - thinFrameworks.append(slicedFramework) } - return thinFrameworks } /// Compiles the specified framework in a temporary directory and writes the build logs to file. @@ -565,17 +561,6 @@ struct FrameworkBuilder { "\(framework): \(error)") } - // CocoaPods creates a `_CodeSignature` directory. Delete it. - // Note that the build only produces a `_CodeSignature` directory for - // macOS and macCatalyst, but we try to delete it for other platforms - // just in case it were to appear. - let codeSignatureDir = platformFrameworkDir - .appendingPathComponent( - platform == .catalyst || platform == .macOS ? "Versions/A/" : "" - ) - .appendingPathComponent("_CodeSignature") - try? fileManager.removeItem(at: codeSignatureDir) - // The minimum OS version is set to 100.0 to work around b/327020913. // TODO(ncooke3): Revert this logic once b/327020913 is fixed. // TODO(ncooke3): Does this need to happen on macOS? @@ -604,23 +589,6 @@ struct FrameworkBuilder { ) } - // The macOS slice's `PrivateHeaders` directory may have a - // `PrivateHeaders` file in it that symbolically links to nowhere. Delete - // it here to avoid putting it in the zip or crashing the Carthage hash - // generation. Because this will throw an error for cases where the file - // does not exist, the error is ignored. - let privateHeadersDir = platformFrameworkDir.appendingPathComponent("PrivateHeaders") - if fileManager.directoryExists(at: privateHeadersDir.resolvingSymlinksInPath()) { - try? fileManager - .removeItem(at: privateHeadersDir.resolvingSymlinksInPath() - .appendingPathComponent("PrivateHeaders")) - } else { - try? fileManager.removeItem(at: privateHeadersDir) - } - let headersDir = platformFrameworkDir.appendingPathComponent("Headers") - .resolvingSymlinksInPath() - try? fileManager.removeItem(at: headersDir.appendingPathComponent("Headers")) - // Move privacy manifest containing resource bundles into the framework. let resourceDir = platformFrameworkDir .appendingPathComponent( @@ -638,12 +606,6 @@ struct FrameworkBuilder { // Bundles are moved rather than copied to prevent them from being // packaged in a `Resources` directory at the root of the xcframework. .forEach { - // Delete `gRPCCertificates-Cpp.bundle` since it is not needed (#9184). - guard $0.lastPathComponent != "gRPCCertificates-Cpp.bundle" else { - try fileManager.removeItem(at: $0) - return - } - try fileManager.moveItem( at: $0, to: resourceDir.appendingPathComponent($0.lastPathComponent) diff --git a/ReleaseTooling/Sources/ZipBuilder/ZipBuilder.swift b/ReleaseTooling/Sources/ZipBuilder/ZipBuilder.swift index 2b0b1de7720..992ed2e9d89 100644 --- a/ReleaseTooling/Sources/ZipBuilder/ZipBuilder.swift +++ b/ReleaseTooling/Sources/ZipBuilder/ZipBuilder.swift @@ -277,7 +277,8 @@ struct ZipBuilder { for groupedFramework in groupedFrameworks { let name = groupedFramework.key let xcframework = FrameworkBuilder.makeXCFramework(withName: name, - frameworks: groupedFramework.value, + frameworks: postProcessFrameworks(groupedFramework + .value), xcframeworksDir: xcframeworksDir, resourceContents: resources[name]) xcframeworks[name] = [xcframework] @@ -299,13 +300,60 @@ struct ZipBuilder { let carthageGoogleUtilitiesXcframework = FrameworkBuilder.makeXCFramework( withName: "GoogleUtilities", - frameworks: carthageGoogleUtilitiesFrameworks, + frameworks: postProcessFrameworks(carthageGoogleUtilitiesFrameworks), xcframeworksDir: xcframeworksCarthageDir, resourceContents: nil ) return (podsBuilt, xcframeworks, carthageGoogleUtilitiesXcframework) } + func postProcessFrameworks(_ frameworks: [URL]) -> [URL] { + for framework in frameworks { + // CocoaPods creates a `_CodeSignature` directory. Delete it. + // Note that the build only produces a `_CodeSignature` directory for + // macOS and macCatalyst (`Versions/A/`), but we try to delete it for + // other platforms just in case it were to appear. + for path in ["", "Versions/A/"] { + let codeSignatureDir = framework + .appendingPathComponent(path) + .appendingPathComponent("_CodeSignature") + .resolvingSymlinksInPath() + try? FileManager.default.removeItem(at: codeSignatureDir) + } + + // Delete `gRPCCertificates-Cpp.bundle` since it is not needed (#9184). + // Depending on the platform, it may be at the root of the framework or + // in a symlinked `Resources` directory (for macOS, macCatalyst). Attempt + // to delete at either patch for each framework. + for path in ["", "Resources"] { + let grpcCertsBundle = framework + .appendingPathComponent(path) + .appendingPathComponent("gRPCCertificates-Cpp.bundle") + .resolvingSymlinksInPath() + try? FileManager.default.removeItem(at: grpcCertsBundle) + } + + // The macOS slice's `PrivateHeaders` directory may have a + // `PrivateHeaders` file in it that symbolically links to nowhere. Delete + // it here to avoid putting it in the zip or crashing the Carthage hash + // generation. Because this will throw an error for cases where the file + // does not exist, the error is ignored. + let privateHeadersDir = framework.appendingPathComponent("PrivateHeaders") + if !FileManager.default.directoryExists(at: privateHeadersDir.resolvingSymlinksInPath()) { + try? FileManager.default.removeItem(at: privateHeadersDir) + } + + // The `Headers` and `PrivateHeaders` directories may contain a symlink + // of the same name. Delete it here to avoid putting it in the zip or + // crashing the Carthage hash generation. + for path in ["Headers", "PrivateHeaders"] { + let headersDir = framework.appendingPathComponent(path).resolvingSymlinksInPath() + try? FileManager.default.removeItem(at: headersDir.appendingPathComponent(path)) + } + } + return frameworks + } + /// Try to build and package the contents of the Zip file. This will throw an error as soon as it /// encounters an error, or will quit due to a fatal error with the appropriate log. /// diff --git a/scripts/build_zip.sh b/scripts/build_zip.sh index c71579ca6d5..ee7e9600f62 100755 --- a/scripts/build_zip.sh +++ b/scripts/build_zip.sh @@ -14,10 +14,12 @@ set -x REPO=`pwd` -if [[ $# -lt 2 ]]; then +if [[ $# -ne 4 ]]; then cat 2>&2 <