From 2660c5754632eda95173235a6deb1385cbb6f07f Mon Sep 17 00:00:00 2001 From: Sergey Khliustin Date: Wed, 28 Dec 2022 17:44:10 +0100 Subject: [PATCH] Better vendored static libraries handing --- Sources/PodToBUILD/BuildFile.swift | 22 +- .../PodToBUILD/Convertible/XCFramework.swift | 68 +++--- Sources/PodToBUILD/RuleUtils.swift | 11 +- .../PodToBUILD/Targets/AppleFramework.swift | 5 - .../Targets/AppleFrameworkImport.swift | 2 +- Sources/PodToBUILD/Targets/Base/Arch.swift | 34 +-- Sources/PodToBUILD/Targets/ObjcImport.swift | 17 +- TestTools/TopPods.json | 1 + .../Recorded/FirebaseCrashlytics/BUILD.bazel | 218 ++++++++++++++++++ 9 files changed, 301 insertions(+), 77 deletions(-) create mode 100644 Tests/Recorded/FirebaseCrashlytics/BUILD.bazel diff --git a/Sources/PodToBUILD/BuildFile.swift b/Sources/PodToBUILD/BuildFile.swift index a1b3af6..0e82148 100644 --- a/Sources/PodToBUILD/BuildFile.swift +++ b/Sources/PodToBUILD/BuildFile.swift @@ -90,12 +90,20 @@ public struct PodBuildFile: StarlarkConvertible { let extraDeps: [BazelTarget] = makeResourceBundles(spec: podSpec, subspecs: subspecs, options: buildOptions) let frameworks = AppleFrameworkImport.vendoredFrameworks(withPodspec: podSpec, subspecs: subspecs, options: buildOptions) - .sorted(by: { $0.name < $1.name }) - let conditionalDeps = frameworks.reduce([String: [Arch]]()) { partialResult, target in - guard let target = target as? AppleFrameworkImport else { return partialResult } - var result = partialResult - result[target.name] = Arch.archs(for: target.frameworkImport, options: buildOptions) - return result + let libraries = ObjcImport.vendoredLibraries(withPodspec: podSpec, subspecs: subspecs, options: buildOptions) + let conditionalDeps = (frameworks + libraries).reduce([String: [Arch]]()) { partialResult, target in + if let target = target as? AppleFrameworkImport { + var result = partialResult + let path = frameworkExecutablePath(target.frameworkImport, options: buildOptions) + result[target.name] = Arch.archs(forExecutable: path, options: buildOptions) + return result + } else if let target = target as? ObjcImport { + var result = partialResult + let path = URL(fileURLWithPath: target.library, relativeTo: URL(fileURLWithPath: buildOptions.podTargetAbsoluteRoot)).path + result[target.name] = Arch.archs(forExecutable: path, options: buildOptions) + return result + } + return partialResult } let sourceLibs = makeSourceLibs(spec: podSpec, @@ -104,7 +112,7 @@ public struct PodBuildFile: StarlarkConvertible { conditionalDeps: conditionalDeps, options: buildOptions) - var output: [BazelTarget] = sourceLibs + extraDeps + frameworks + var output: [BazelTarget] = sourceLibs + extraDeps + frameworks + libraries output = UserConfigurableTransform.transform(convertibles: output, options: buildOptions, diff --git a/Sources/PodToBUILD/Convertible/XCFramework.swift b/Sources/PodToBUILD/Convertible/XCFramework.swift index 2db0cab..ed1a358 100644 --- a/Sources/PodToBUILD/Convertible/XCFramework.swift +++ b/Sources/PodToBUILD/Convertible/XCFramework.swift @@ -55,40 +55,40 @@ struct XCFramework: StarlarkConvertible { return nil } } - /// Wrap dynamic framework into xcframework. - /// Assume that framework can be either device + simulator (x86_64 + arm*) or only for device (arm*). - init?(framework: String, options: BuildOptions) { - guard framework.hasSuffix(".framework") else { return nil } - let isDynamic = isDynamicFramework(framework, options: options) - let archs = frameworkArchs(framework, options: options) - guard !archs.isEmpty else { return nil } - - let frameworkURL = URL(fileURLWithPath: framework, - relativeTo: URL(fileURLWithPath: options.podTargetAbsoluteRoot)) - name = frameworkURL.deletingPathExtension().lastPathComponent - var libraries: [InputData.Library] = [] - let deviceArchs = archs.filter({ $0 != "x86_64" }).sorted() - libraries.append(InputData.Library(LibraryIdentifier: "ios-\(deviceArchs.joined(separator: "_"))", - LibraryPath: framework, - SupportedArchitectures: deviceArchs, - SupportedPlatform: "ios", - SupportedPlatformVariant: nil, - path: framework, - linkage: isDynamic ? .dynamic : .static)) - // TODO: Fix this - if archs.contains("x86_64") { - libraries.append(InputData.Library(LibraryIdentifier: "ios-x86_64-simulator", - LibraryPath: framework, - SupportedArchitectures: ["x86_64"], - SupportedPlatform: "ios", - SupportedPlatformVariant: "simulator", - path: framework, - linkage: isDynamic ? .dynamic : .static)) - - } - - input = InputData(AvailableLibraries: libraries) - } +// /// Wrap dynamic framework into xcframework. +// /// Assume that framework can be either device + simulator (x86_64 + arm*) or only for device (arm*). +// init?(framework: String, options: BuildOptions) { +// guard framework.hasSuffix(".framework") else { return nil } +// let isDynamic = isDynamicFramework(framework, options: options) +// let archs = frameworkArchs(framework, options: options) +// guard !archs.isEmpty else { return nil } +// +// let frameworkURL = URL(fileURLWithPath: framework, +// relativeTo: URL(fileURLWithPath: options.podTargetAbsoluteRoot)) +// name = frameworkURL.deletingPathExtension().lastPathComponent +// var libraries: [InputData.Library] = [] +// let deviceArchs = archs.filter({ $0 != "x86_64" }).sorted() +// libraries.append(InputData.Library(LibraryIdentifier: "ios-\(deviceArchs.joined(separator: "_"))", +// LibraryPath: framework, +// SupportedArchitectures: deviceArchs, +// SupportedPlatform: "ios", +// SupportedPlatformVariant: nil, +// path: framework, +// linkage: isDynamic ? .dynamic : .static)) +// // TODO: Fix this +// if archs.contains("x86_64") { +// libraries.append(InputData.Library(LibraryIdentifier: "ios-x86_64-simulator", +// LibraryPath: framework, +// SupportedArchitectures: ["x86_64"], +// SupportedPlatform: "ios", +// SupportedPlatformVariant: "simulator", +// path: framework, +// linkage: isDynamic ? .dynamic : .static)) +// +// } +// +// input = InputData(AvailableLibraries: libraries) +// } func toStarlark() -> StarlarkNode { let slices = input.AvailableLibraries.map({ diff --git a/Sources/PodToBUILD/RuleUtils.swift b/Sources/PodToBUILD/RuleUtils.swift index aa88b33..2e706db 100644 --- a/Sources/PodToBUILD/RuleUtils.swift +++ b/Sources/PodToBUILD/RuleUtils.swift @@ -133,6 +133,7 @@ public func xcconfigSettingToList(_ value: String) -> [String] { .components(separatedBy: .whitespaces) .map { $0.removingPercentEncoding ?? "" } .map { $0.replacingOccurrences(of: "\"", with: "") } + .map { $0.replacingOccurrences(of: "\\", with: "") } .filter({ $0 != "$(inherited)"}) .filter({ !$0.isEmpty }) } @@ -150,13 +151,3 @@ public func isDynamicFramework(_ framework: String, options: BuildOptions) -> Bo let output = SystemShellContext().command("/usr/bin/file", arguments: [path]).standardOutputAsString return output.contains("dynamically") } - -public func frameworkArchs(_ framework: String, options: BuildOptions) -> [String] { - let path = frameworkExecutablePath(framework, options: options) - let archs = SystemShellContext().command("/usr/bin/lipo", - arguments: ["-archs", path]) - .standardOutputAsString - .trimmingCharacters(in: .whitespacesAndNewlines) - .components(separatedBy: " ") - return archs.filter({ !$0.isEmpty }) -} diff --git a/Sources/PodToBUILD/Targets/AppleFramework.swift b/Sources/PodToBUILD/Targets/AppleFramework.swift index 5575653..2b51d04 100644 --- a/Sources/PodToBUILD/Targets/AppleFramework.swift +++ b/Sources/PodToBUILD/Targets/AppleFramework.swift @@ -38,7 +38,6 @@ struct AppleFramework: BazelTarget, UserConfigurable { let vendoredXCFrameworks: AttrSet<[XCFramework]> let vendoredStaticFrameworks: AttrSet> let vendoredDynamicFrameworks: AttrSet> - let vendoredStaticLibraries: AttrSet<[String]> let objcDefines: AttrSet<[String]> let swiftDefines: AttrSet<[String]> @@ -119,8 +118,6 @@ struct AppleFramework: BazelTarget, UserConfigurable { self.vendoredDynamicFrameworks = .empty self.vendoredStaticFrameworks = .empty - self.vendoredStaticLibraries = spec.collectAttribute(with: subspecs, keyPath: \.vendoredLibraries) - self.swiftDefines = AttrSet(basic: ["COCOAPODS"]) self.objcDefines = AttrSet(basic: ["COCOAPODS=1"]) @@ -246,7 +243,6 @@ struct AppleFramework: BazelTarget, UserConfigurable { let vendoredXCFrameworks = vendoredXCFrameworks.multi.ios ?? [] let vendoredStaticFrameworks = vendoredStaticFrameworks.multi.ios ?? [] let vendoredDynamicFrameworks = vendoredDynamicFrameworks.multi.ios ?? [] - let vendoredStaticLibraries = vendoredStaticLibraries.multi.ios ?? [] let lines: [StarlarkFunctionArgument] = [ .named(name: "name", value: name.toStarlark()), @@ -265,7 +261,6 @@ struct AppleFramework: BazelTarget, UserConfigurable { .named(name: "vendored_xcframeworks", value: vendoredXCFrameworks.toStarlark()), .named(name: "vendored_static_frameworks", value: vendoredStaticFrameworks.toStarlark()), .named(name: "vendored_dynamic_frameworks", value: vendoredDynamicFrameworks.toStarlark()), - .named(name: "vendored_static_libraries", value: vendoredStaticLibraries.toStarlark()), .named(name: "objc_defines", value: objcDefines), .named(name: "swift_defines", value: swiftDefines), .named(name: "sdk_dylibs", value: sdkDylibs.toStarlark()), diff --git a/Sources/PodToBUILD/Targets/AppleFrameworkImport.swift b/Sources/PodToBUILD/Targets/AppleFrameworkImport.swift index ef0d81d..8e49660 100644 --- a/Sources/PodToBUILD/Targets/AppleFrameworkImport.swift +++ b/Sources/PodToBUILD/Targets/AppleFrameworkImport.swift @@ -74,6 +74,6 @@ struct AppleFrameworkImport: BazelTarget { frameworkImport: $0) } as [AppleFrameworkImport] } - return (frameworks.basic ?? []) + (frameworks.multi.ios ?? []) + return (frameworks.basic ?? []) + (frameworks.multi.ios ?? []).sorted(by: { $0.name < $1.name }) } } diff --git a/Sources/PodToBUILD/Targets/Base/Arch.swift b/Sources/PodToBUILD/Targets/Base/Arch.swift index 758ef13..ad972cb 100644 --- a/Sources/PodToBUILD/Targets/Base/Arch.swift +++ b/Sources/PodToBUILD/Targets/Base/Arch.swift @@ -40,22 +40,26 @@ enum Arch: String, CaseIterable { case ios_i386 case ios_x86_64 - static func archs(for framework: String, options: BuildOptions) -> [Arch] { - let path = frameworkExecutablePath(framework, options: options) - let archs = frameworkArchs(framework, options: options).map({ - // TODO: Implement resolver - if $0 == "arm64" { - let output = SystemShellContext() - .shellOut("otool -l -arch arm64 \(path) | grep -m 1 'LC_VERSION_MIN_'") - .standardOutputAsString - if !output.contains("IPHONEOS") { - return "sim_arm64" - } else { - return $0 + static func archs(forExecutable path: String, options: BuildOptions) -> [Arch] { + let archs = SystemShellContext().command("/usr/bin/lipo", + arguments: ["-archs", path]) + .standardOutputAsString + .trimmingCharacters(in: .whitespacesAndNewlines) + .components(separatedBy: " ") + .filter({ !$0.isEmpty }) + .map({ + if $0 == "arm64" { + let output = SystemShellContext() + .shellOut("otool -l -arch arm64 \(path) | grep -m 1 'LC_VERSION_MIN_'") + .standardOutputAsString + if !output.contains("IPHONEOS") { + return "sim_arm64" + } else { + return $0 + } } - } - return $0 - } as (String) -> String) + return $0 + }) return archs.compactMap({ Self.init(rawValue: "ios_" + $0) }) } diff --git a/Sources/PodToBUILD/Targets/ObjcImport.swift b/Sources/PodToBUILD/Targets/ObjcImport.swift index d3d71cf..10d84a9 100644 --- a/Sources/PodToBUILD/Targets/ObjcImport.swift +++ b/Sources/PodToBUILD/Targets/ObjcImport.swift @@ -11,20 +11,27 @@ import Foundation struct ObjcImport: BazelTarget { let loadNode = "" let name: String // A unique name for this rule. - let archives: AttrSet<[String]> // The list of .a files provided to Objective-C targets that depend on this target. + let library: String // The list of .a files provided to Objective-C targets that depend on this target. func toStarlark() -> StarlarkNode { return StarlarkNode.functionCall( name: "objc_import", arguments: [ .named(name: "name", value: name.toStarlark()), - .named(name: "archives", value: archives.toStarlark()) + .named(name: "archives", value: [library].toStarlark()) ] ) } - static func vendoredLibraries(withPodspec spec: PodSpec, subspecs: [PodSpec]) -> [BazelTarget] { - let libraries = spec.collectAttribute(with: subspecs, keyPath: \.vendoredLibraries) - return libraries.isEmpty ? [] : [ObjcImport(name: "\(spec.moduleName ?? spec.name)_VendoredLibraries", archives: libraries)] + static func vendoredLibraries(withPodspec spec: PodSpec, subspecs: [PodSpec], options: BuildOptions) -> [BazelTarget] { + let vendoredLibraries = spec.collectAttribute(with: subspecs, keyPath: \.vendoredLibraries) + let libraries = vendoredLibraries.map { + $0.compactMap { + let libraryName = URL(fileURLWithPath: $0).deletingPathExtension().lastPathComponent + return ObjcImport(name: "\(spec.moduleName ?? spec.name)_\(libraryName)_VendoredLibraries", + library: $0) + } as [ObjcImport] + } + return (libraries.basic ?? []) + (libraries.multi.ios ?? []).sorted(by: { $0.name < $1.name }) } } diff --git a/TestTools/TopPods.json b/TestTools/TopPods.json index 09a9edb..c74461d 100644 --- a/TestTools/TopPods.json +++ b/TestTools/TopPods.json @@ -31,6 +31,7 @@ "FDFullscreenPopGesture" : "", "Firebase" : "", "FirebaseAnalytics" : "9.5.0", + "FirebaseCrashlytics": "", "fishhook" : "", "FLAnimatedImage" : "", "FLEX" : "", diff --git a/Tests/Recorded/FirebaseCrashlytics/BUILD.bazel b/Tests/Recorded/FirebaseCrashlytics/BUILD.bazel new file mode 100644 index 0000000..8122247 --- /dev/null +++ b/Tests/Recorded/FirebaseCrashlytics/BUILD.bazel @@ -0,0 +1,218 @@ +load('@build_bazel_rules_ios//rules:framework.bzl', 'apple_framework') +# Add a config setting release for compilation mode +# Assume that people are using `opt` for release mode +# see the bazel user manual for more information +# https://docs.bazel.build/versions/master/be/general.html#config_setting +config_setting( + name = "release", + values = { + "compilation_mode": "opt" + } +) +config_setting( + name = "osxCase", + values = { + "apple_platform_type": "macos" + } +) +config_setting( + name = "tvosCase", + values = { + "apple_platform_type": "tvos" + } +) +config_setting( + name = "watchosCase", + values = { + "apple_platform_type": "watchos" + } +) +config_setting( + name = "ios_armv7", + values = { + "cpu": "ios_armv7" + } +) +config_setting( + name = "ios_arm64", + values = { + "cpu": "ios_arm64" + } +) +config_setting( + name = "ios_arm64e", + values = { + "cpu": "ios_arm64e" + } +) +config_setting( + name = "ios_sim_arm64", + values = { + "cpu": "ios_sim_arm64" + } +) +config_setting( + name = "ios_i386", + values = { + "cpu": "ios_i386" + } +) +config_setting( + name = "ios_x86_64", + values = { + "cpu": "ios_x86_64" + } +) +genrule( + name = "FirebaseCrashlytics_InfoPlist", + srcs = [], + outs = [ + "FirebaseCrashlytics_InfoPlist.plist" + ], + cmd = """cat < $@ + + + + + CFBundleIdentifier + org.cocoapods.FirebaseCrashlytics + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + FirebaseCrashlytics + CFBundlePackageType + FMWK + CFBundleShortVersionString + 9.6.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + iPhoneSimulator + iPhoneOS + + CFBundleVersion + 1 + MinimumOSVersion + 9.0 + UIDeviceFamily + + 1 + 2 + + + + +EOF""" +) +apple_framework( + name = "FirebaseCrashlytics", + module_name = "FirebaseCrashlytics", + bundle_id = "org.cocoapods.FirebaseCrashlytics", + swift_version = "5", + link_dynamic = True, + infoplists = [ + ":FirebaseCrashlytics_InfoPlist" + ], + platforms = { + "ios": "9.0", + "osx": "10.12", + "tvos": "10.0", + "watchos": "6.0" + }, + srcs = glob( + [ + "Crashlytics/Crashlytics/**/*.c", + "Crashlytics/Crashlytics/**/*.h", + "Crashlytics/Crashlytics/**/*.m", + "Crashlytics/Crashlytics/**/*.mm", + "Crashlytics/Protogen/**/*.c", + "Crashlytics/Protogen/**/*.h", + "Crashlytics/Protogen/**/*.m", + "Crashlytics/Protogen/**/*.mm", + "Crashlytics/Shared/**/*.c", + "Crashlytics/Shared/**/*.h", + "Crashlytics/Shared/**/*.m", + "Crashlytics/Shared/**/*.mm", + "Crashlytics/third_party/**/*.c", + "Crashlytics/third_party/**/*.h", + "Crashlytics/third_party/**/*.m", + "Crashlytics/third_party/**/*.mm", + "FirebaseCore/Extension/*.h", + "FirebaseInstallations/Source/Library/Private/*.h", + "Interop/Analytics/Public/*.h" + ] + ), + public_headers = glob( + [ + "Crashlytics/Crashlytics/Public/FirebaseCrashlytics/*.h" + ] + ), + deps = [ + "//Tests/Pods/FirebaseCore", + "//Tests/Pods/FirebaseInstallations", + "//Tests/Pods/GoogleDataTransport", + "//Tests/Pods/GoogleUtilities", + "//Tests/Pods/PromisesObjC", + "//Tests/Pods/nanopb" + ], + objc_defines = [ + "COCOAPODS=1" + ] + select( + { + "//conditions:default": [ + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1" + ], + ":release": [ + "POD_CONFIGURATION_RELEASE=1" + ] + } + ), + swift_defines = [ + "COCOAPODS" + ] + select( + { + "//conditions:default": [ + "DEBUG" + ] + } + ), + sdk_dylibs = [ + "c++", + "z" + ], + sdk_frameworks = select( + { + "//conditions:default": [ + "Security", + "SystemConfiguration" + ], + ":osxCase": [ + "Security", + "SystemConfiguration" + ], + ":watchosCase": [ + "Security" + ] + } + ), + objc_copts = [ + "-ITests/Pods/FirebaseCrashlytics" + ], + swift_copts = [ + "-Xcc", + "-ITests/Pods/FirebaseCrashlytics" + ], + xcconfig = { + "GCC_PREPROCESSOR_DEFINITIONS": [ + "CLS_SDK_NAME=Crashlytics iOS SDK", + "PB_FIELD_32BIT=1", + "PB_NO_PACKED_STRUCTS=1", + "PB_ENABLE_MALLOC=1" + ] + }, + visibility = [ + "//visibility:public" + ] +) \ No newline at end of file