From 9205183e7ee86680b7b1d13db32d6956716a58dd Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Thu, 13 Jul 2023 16:03:00 -0400 Subject: [PATCH] [Release Tooling] Make 'zip-builder' compatible with Xcode 14 (#11522) * Require Xcode 14.1 * [Release Tooling] Make 'zip-builder' compatible with Xcode 14 * Cleanup approach and restore disabled code * Remove unneeded comment and whitespace * Delete empty symbol link on all platforms * Fix build issue left over from previous commit * [skip ci] Remove irrelevant comment * [skip ci] Remove irrelevant newline --------- Co-authored-by: Paul Beusterien --- .github/workflows/zip.yml | 16 ++--- .../Sources/ZipBuilder/CocoaPodUtils.swift | 5 +- .../Sources/ZipBuilder/FrameworkBuilder.swift | 71 +++++++++++-------- 3 files changed, 51 insertions(+), 41 deletions(-) diff --git a/.github/workflows/zip.yml b/.github/workflows/zip.yml index c49f3d06b2c..abc585c387c 100644 --- a/.github/workflows/zip.yml +++ b/.github/workflows/zip.yml @@ -34,8 +34,8 @@ jobs: - uses: mikehardy/buildcache-action@c87cea0ccd718971d6cc39e672c4f26815b6c126 with: cache_key: ${{ matrix.os }} - - name: Xcode 13.3.1 - run: sudo xcode-select -s /Applications/Xcode_13.3.1.app/Contents/Developer + - name: Xcode 14.1 + run: sudo xcode-select -s /Applications/Xcode_14.1.app/Contents/Developer - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -57,8 +57,8 @@ jobs: runs-on: macos-12 steps: - uses: actions/checkout@v3 - - name: Xcode 13.3.1 - run: sudo xcode-select -s /Applications/Xcode_13.3.1.app/Contents/Developer + - name: Xcode 14.1 + run: sudo xcode-select -s /Applications/Xcode_14.1.app/Contents/Developer - name: Build run: | cd ReleaseTooling @@ -74,8 +74,8 @@ jobs: - uses: mikehardy/buildcache-action@c87cea0ccd718971d6cc39e672c4f26815b6c126 with: cache_key: ${{ matrix.os }} - - name: Xcode 13.3.1 - run: sudo xcode-select -s /Applications/Xcode_13.3.1.app/Contents/Developer + - name: Xcode 14.1 + run: sudo xcode-select -s /Applications/Xcode_14.1.app/Contents/Developer - uses: ruby/setup-ruby@v1 - name: Setup Bundler run: ./scripts/setup_bundler.sh @@ -444,8 +444,8 @@ jobs: FIREBASECI_USE_LATEST_GOOGLEAPPMEASUREMENT: 1 runs-on: macos-12 steps: - - name: Xcode 13.3.1 - run: sudo xcode-select -s /Applications/Xcode_13.3.1.app/Contents/Developer + - name: Xcode 14.1 + run: sudo xcode-select -s /Applications/Xcode_14.1.app/Contents/Developer - uses: actions/checkout@v3 - name: Get framework dir uses: actions/download-artifact@v1 diff --git a/ReleaseTooling/Sources/ZipBuilder/CocoaPodUtils.swift b/ReleaseTooling/Sources/ZipBuilder/CocoaPodUtils.swift index c61bce4ef7f..0cff691283b 100644 --- a/ReleaseTooling/Sources/ZipBuilder/CocoaPodUtils.swift +++ b/ReleaseTooling/Sources/ZipBuilder/CocoaPodUtils.swift @@ -136,8 +136,9 @@ enum CocoaPodUtils { let result = Shell.executeCommandFromScript("pod cache clean --all", outputToConsole: false) switch result { case let .error(code, _): - fatalError("Could not clean the pod cache, the command exited with \(code). Try running the" + - "command in Terminal to see what's wrong.") + fatalError("Could not clean the pod cache, the command exited with " + + "\(code). Try running the command in Terminal to see " + + "what's wrong.") case .success: // No need to do anything else, continue on. print("Successfully cleaned pod cache.") diff --git a/ReleaseTooling/Sources/ZipBuilder/FrameworkBuilder.swift b/ReleaseTooling/Sources/ZipBuilder/FrameworkBuilder.swift index 8ceee0845d0..0b1bd141450 100755 --- a/ReleaseTooling/Sources/ZipBuilder/FrameworkBuilder.swift +++ b/ReleaseTooling/Sources/ZipBuilder/FrameworkBuilder.swift @@ -346,34 +346,23 @@ struct FrameworkBuilder { "\(error)") } - // Find the location of the public headers, any platform will do. guard let anyPlatform = targetPlatforms.first, let archivePath = slicedFrameworks[anyPlatform] else { fatalError("Could not get a path to an archive to fetch headers in \(frameworkName).") } - // Get the framework Headers directory. On macOS, it's a symbolic link. - let headersDir = archivePath.appendingPathComponent("Headers").resolvingSymlinksInPath() - - // The macOS Headers directory can have a Headers file in it symbolically linked to nowhere. - // Delete it here to avoid putting it in the zip or crashing the Carthage hash generation. - // For example,in the 8.0.0 zip distribution see - // Firebase/FirebaseAnalytics/PromisesObjC.xcframework/macos-arm64_x86_64/PromisesObjc - // .framework/Headers/Headers - do { - try fileManager.removeItem(at: headersDir.appendingPathComponent("Headers")) - } catch { - // Ignore - } - // Find CocoaPods generated umbrella header. var umbrellaHeader = "" + // TODO(ncooke3): Evaluate if `TensorFlowLiteObjC` is needed? if framework == "gRPC-Core" || framework == "TensorFlowLiteObjC" { // TODO: Proper handling of podspec-specified module.modulemap files with customized umbrella // headers. This is good enough for Firebase since it doesn't need these modules. + // TODO(ncooke3): Is this needed for gRPC-Core? umbrellaHeader = "\(framework)-umbrella.h" } else { var umbrellaHeaderURL: URL + // Get the framework Headers directory. On macOS, it's a symbolic link. + let headersDir = archivePath.appendingPathComponent("Headers").resolvingSymlinksInPath() do { let files = try fileManager.contentsOfDirectory(at: headersDir, includingPropertiesForKeys: nil) @@ -391,14 +380,6 @@ struct FrameworkBuilder { } umbrellaHeader = umbrellaHeaderURL.lastPathComponent } - // Copy the Headers over. - let headersDestination = frameworkDir.appendingPathComponent("Headers") - do { - try fileManager.copyItem(at: headersDir, to: headersDestination) - } catch { - fatalError("Could not copy headers from \(headersDir) to Headers directory in " + - "\(headersDestination): \(error)") - } // Add an Info.plist. Required by Carthage and SPM binary xcframeworks. CarthageUtils.generatePlistContents(forName: frameworkName, withVersion: podInfo.version, @@ -603,18 +584,46 @@ struct FrameworkBuilder { // `Both ios-arm64 and ios-armv7 represent two equivalent library definitions` var frameworksBuilt: [URL] = [] for (platform, frameworkPath) in slicedFrameworks { - let platformDir = platformFrameworksDir.appendingPathComponent(platform.buildName) + // Create the following structure in the platform frameworks directory: + // - platform_frameworks + // └── $(PLATFORM) + // └── $(FRAMEWORK).framework + let platformFrameworkDir = platformFrameworksDir + .appendingPathComponent(platform.buildName) + .appendingPathComponent(fromFolder.lastPathComponent) do { - try fileManager.createDirectory(at: platformDir, withIntermediateDirectories: true) + try fileManager.createDirectory(at: platformFrameworkDir, withIntermediateDirectories: true) } catch { fatalError("Could not create directory for architecture slices on \(platform) for " + "\(framework): \(error)") } - // Package a normal .framework given the `fromFolder` and the binary from `slicedFrameworks`. - let destination = platformDir.appendingPathComponent(fromFolder.lastPathComponent) + // Headers from slice + do { + let headersSrc: URL = frameworkPath.appendingPathComponent("Headers") + .resolvingSymlinksInPath() + // The macOS slice's `Headers` directory may have a `Headers` file in + // it that symbolically links to nowhere. For example, in the 8.0.0 + // zip distribution, see the `Headers` directory in the macOS slice + // of the `PromisesObjC.xcframework`. 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. + try? fileManager.removeItem(at: headersSrc.appendingPathComponent("Headers")) + + try fileManager.copyItem( + at: headersSrc, + to: platformFrameworkDir.appendingPathComponent("Headers") + ) + } catch { + fatalError("Could not create framework directory needed to build \(framework): \(error)") + } + + // Info.plist from `fromFolder` do { - try fileManager.copyItem(at: fromFolder, to: destination) + let infoPlistSrc = fromFolder.appendingPathComponent("Info.plist").resolvingSymlinksInPath() + let infoPlistDst = platformFrameworkDir.appendingPathComponent("Info.plist") + try fileManager.copyItem(at: infoPlistSrc, to: infoPlistDst) } catch { fatalError("Could not create framework directory needed to build \(framework): \(error)") } @@ -623,7 +632,7 @@ struct FrameworkBuilder { let binaryName = frameworkPath.lastPathComponent.replacingOccurrences(of: ".framework", with: "") let fatBinary = frameworkPath.appendingPathComponent(binaryName).resolvingSymlinksInPath() - let fatBinaryDestination = destination.appendingPathComponent(framework) + let fatBinaryDestination = platformFrameworkDir.appendingPathComponent(framework) do { try fileManager.copyItem(at: fatBinary, to: fatBinaryDestination) } catch { @@ -633,9 +642,9 @@ struct FrameworkBuilder { // Use the appropriate moduleMaps packageModuleMaps(inFrameworks: [frameworkPath], moduleMapContents: moduleMapContents, - destination: destination) + destination: platformFrameworkDir) - frameworksBuilt.append(destination) + frameworksBuilt.append(platformFrameworkDir) } return frameworksBuilt }