mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
Co-authored-by: Nick Cooke <36927374+ncooke3@users.noreply.github.com>
254 lines
9.5 KiB
Swift
Executable File
254 lines
9.5 KiB
Swift
Executable File
/*
|
|
* Copyright 2019 Google
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import Foundation
|
|
|
|
/// A struct to build a .modulemap in a given framework directory.
|
|
///
|
|
struct ModuleMapBuilder {
|
|
/// Information associated with a framework
|
|
private class FrameworkInfo {
|
|
let isSourcePod: Bool
|
|
let versionedPod: CocoaPodUtils.VersionedPod
|
|
let subspecs: Set<String>
|
|
var transitiveFrameworks: Set<String>?
|
|
var transitiveLibraries: Set<String>?
|
|
|
|
init(isSourcePod: Bool, versionedPod: CocoaPodUtils.VersionedPod, subspecs: Set<String>) {
|
|
self.isSourcePod = isSourcePod
|
|
self.versionedPod = versionedPod
|
|
self.subspecs = subspecs
|
|
}
|
|
}
|
|
|
|
struct ModuleMapContents {
|
|
/// Placeholder to fill in umbrella header.
|
|
static let umbrellaPlaceholder = "UMBRELLA_PLACEHOLDER"
|
|
let contents: String
|
|
init(module: String, frameworks: Set<String>, libraries: Set<String>) {
|
|
var content = """
|
|
framework module \(module) {
|
|
umbrella header "\(ModuleMapBuilder.ModuleMapContents.umbrellaPlaceholder)"
|
|
export *
|
|
module * { export * }
|
|
|
|
"""
|
|
|
|
if module == "FirebaseFirestoreInternal" {
|
|
content += """
|
|
link framework "openssl_grpc"
|
|
link framework "grpc"
|
|
link framework "grpcpp"
|
|
"""
|
|
}
|
|
|
|
for framework in frameworks.sorted() {
|
|
content += " link framework " + framework + "\n"
|
|
}
|
|
for library in libraries.sorted() {
|
|
content += " link " + library + "\n"
|
|
}
|
|
// The empty line at the end is intentional, do not remove it.
|
|
content += "}\n"
|
|
contents = content
|
|
}
|
|
|
|
func get(umbrellaHeader: String) -> String {
|
|
return contents.replacingOccurrences(of:
|
|
ModuleMapBuilder.ModuleMapContents.umbrellaPlaceholder, with: umbrellaHeader)
|
|
}
|
|
}
|
|
|
|
/// The directory containing the Xcode project and Pods folder.
|
|
private let projectDir: URL
|
|
|
|
/// Custom CocoaPods spec repos to be used. If not provided, the tool will only use the CocoaPods
|
|
/// master repo.
|
|
private let customSpecRepos: [URL]?
|
|
|
|
/// Dictionary of all installed pods. Update moduleMapContents here.
|
|
private var allPods: [String: CocoaPodUtils.PodInfo]
|
|
|
|
/// Dictionary of installed pods required for this module.
|
|
private var installedPods: [String: FrameworkInfo]
|
|
|
|
/// The platform for this build.
|
|
private let platform: Platform
|
|
|
|
/// The path containing local podspec URLs, if specified.
|
|
private let localPodspecPath: URL?
|
|
|
|
/// Default initializer.
|
|
init(customSpecRepos: [URL]?,
|
|
selectedPods: [String: CocoaPodUtils.PodInfo],
|
|
platform: Platform,
|
|
paths: ZipBuilder.FilesystemPaths) {
|
|
projectDir = FileManager.default.temporaryDirectory(withName: "module")
|
|
CocoaPodUtils.podInstallPrepare(inProjectDir: projectDir, templateDir: paths.templateDir)
|
|
|
|
self.customSpecRepos = customSpecRepos
|
|
allPods = selectedPods
|
|
|
|
var installedPods: [String: FrameworkInfo] = [:]
|
|
for pod in selectedPods {
|
|
let versionedPod = CocoaPodUtils.VersionedPod(name: pod.key, version: pod.value.version)
|
|
installedPods[pod.key] = FrameworkInfo(isSourcePod: pod.value.isSourcePod,
|
|
versionedPod: versionedPod,
|
|
subspecs: pod.value.subspecs)
|
|
}
|
|
self.installedPods = installedPods
|
|
|
|
self.platform = platform
|
|
localPodspecPath = paths.localPodspecPath
|
|
}
|
|
|
|
// MARK: - Public Functions
|
|
|
|
/// Build the module map files for the source frameworks.
|
|
///
|
|
func build() {
|
|
for framework in installedPods.values
|
|
where framework.isSourcePod &&
|
|
framework.versionedPod.name != "Firebase" &&
|
|
framework.transitiveFrameworks == nil {
|
|
generate(framework: framework)
|
|
}
|
|
}
|
|
|
|
// MARK: - Internal Functions
|
|
|
|
/// Build a module map for a single framework. A CocoaPod install is run to extract the required
|
|
/// frameworks
|
|
/// and libraries from the generated xcconfig. All previously installed dependent pods are put
|
|
/// into the Podfile
|
|
/// to make sure we install the right version and from the right location.
|
|
private func generate(framework: FrameworkInfo) {
|
|
let podName = framework.versionedPod.name
|
|
let deps = CocoaPodUtils.transitiveVersionedPodDependencies(for: podName, in: allPods).filter {
|
|
// Don't include Interop pods in the module map calculation since they shouldn't add anything
|
|
// and it uses the platform-independent version of the dependency list, which causes a crash
|
|
// for the iOS-only RecaptchaInterop pod when the subsequent code tries to `pod install` it
|
|
// for macOS. All this module code should go away when we switch to building dynamic
|
|
// frameworks.
|
|
!$0.name.hasSuffix("Interop")
|
|
}
|
|
|
|
CocoaPodUtils.installPods(allSubspecList(framework: framework) + deps,
|
|
inDir: projectDir,
|
|
platform: platform,
|
|
customSpecRepos: customSpecRepos,
|
|
localPodspecPath: localPodspecPath,
|
|
linkage: .forcedStatic)
|
|
|
|
let xcconfigFile = projectDir.appendingPathComponents(["Pods", "Target Support Files",
|
|
"Pods-FrameworkMaker",
|
|
"Pods-FrameworkMaker.release.xcconfig"])
|
|
allPods[podName]?
|
|
.moduleMapContents = makeModuleMap(forFramework: framework, withXcconfigFile: xcconfigFile)
|
|
}
|
|
|
|
/// Convert a list of versioned pods to a list of versioned pods specified with all needed
|
|
/// subspecs.
|
|
private func allSubspecList(framework: FrameworkInfo) -> [CocoaPodUtils.VersionedPod] {
|
|
let name = framework.versionedPod.name
|
|
let version = framework.versionedPod.version
|
|
|
|
guard framework.subspecs.count > 0 else {
|
|
return [CocoaPodUtils.VersionedPod(name: name, version: version)]
|
|
}
|
|
|
|
return framework.subspecs.map { subspec in
|
|
CocoaPodUtils.VersionedPod(name: "\(name)/\(subspec)", version: version)
|
|
}
|
|
}
|
|
|
|
// Extract the framework and library dependencies for a framework from
|
|
// the xcconfig file from an app generated from a Podfile only with that
|
|
// CocoaPod and removing the frameworks and libraries from its transitive dependencies.
|
|
private func getModuleDependencies(withXcconfigFile xcconfigFile: URL) ->
|
|
(frameworks: [String], libraries: [String]) {
|
|
do {
|
|
let text = try String(contentsOf: xcconfigFile)
|
|
let lines = text.components(separatedBy: .newlines)
|
|
for line in lines {
|
|
if line.hasPrefix("OTHER_LDFLAGS =") {
|
|
var dependencyFrameworks: [String] = []
|
|
var dependencyLibraries: [String] = []
|
|
let tokens = line.components(separatedBy: " ")
|
|
var addNext = false
|
|
for token in tokens {
|
|
if addNext {
|
|
dependencyFrameworks.append(token)
|
|
addNext = false
|
|
} else if token == "-framework" {
|
|
addNext = true
|
|
} else if token.hasPrefix("-l") {
|
|
let index = token.index(token.startIndex, offsetBy: 2)
|
|
dependencyLibraries.append(String(token[index...]))
|
|
}
|
|
}
|
|
return (dependencyFrameworks, dependencyLibraries)
|
|
}
|
|
}
|
|
} catch {
|
|
fatalError("Failed to open \(xcconfigFile): \(error)")
|
|
}
|
|
return ([], [])
|
|
}
|
|
|
|
private func makeModuleMap(forFramework framework: FrameworkInfo,
|
|
withXcconfigFile xcconfigFile: URL) -> ModuleMapContents {
|
|
let name = framework.versionedPod.name
|
|
let dependencies = getModuleDependencies(withXcconfigFile: xcconfigFile)
|
|
let frameworkDeps = Set(dependencies.frameworks)
|
|
let libraryDeps = Set(dependencies.libraries)
|
|
var transitiveFrameworkDeps = frameworkDeps
|
|
var transitiveLibraryDeps = libraryDeps
|
|
var myFrameworkDeps = frameworkDeps
|
|
var myLibraryDeps = libraryDeps
|
|
|
|
// Iterate through the deps looking for pods. At time of authoring, TensorFlowLiteObjC is the
|
|
// only pod in frameworkDeps instead of libraryDeps.
|
|
for library in libraryDeps.union(frameworkDeps) {
|
|
let lib = library.replacingOccurrences(of: "\"", with: "")
|
|
if let dependencyPod = installedPods[lib] {
|
|
myLibraryDeps.remove(library)
|
|
myFrameworkDeps.remove(library)
|
|
if lib == name {
|
|
continue
|
|
}
|
|
var podFrameworkDeps = dependencyPod.transitiveFrameworks
|
|
var podLibraryDeps = dependencyPod.transitiveLibraries
|
|
if podFrameworkDeps == nil {
|
|
generate(framework: dependencyPod)
|
|
podFrameworkDeps = dependencyPod.transitiveFrameworks
|
|
podLibraryDeps = dependencyPod.transitiveLibraries
|
|
}
|
|
if let fdeps = podFrameworkDeps {
|
|
transitiveFrameworkDeps.formUnion(fdeps)
|
|
}
|
|
if let ldeps = podLibraryDeps {
|
|
transitiveLibraryDeps.formUnion(ldeps)
|
|
}
|
|
}
|
|
}
|
|
installedPods[name]?.transitiveFrameworks = transitiveFrameworkDeps
|
|
installedPods[name]?.transitiveLibraries = transitiveLibraryDeps
|
|
|
|
return ModuleMapContents(module: name, frameworks: myFrameworkDeps, libraries: myLibraryDeps)
|
|
}
|
|
}
|