1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Basics
import PackageModel
public struct ResolvedProduct {
/// The name of this product.
public var name: String {
self.underlying.name
}
/// The type of this product.
public var type: ProductType {
self.underlying.type
}
public let packageIdentity: PackageIdentity
/// The underlying product.
public let underlying: Product
@available(*, deprecated, renamed: "modules")
public var targets: IdentifiableSet<ResolvedModule> { self.modules }
/// The top level modules contained in this product.
public internal(set) var modules: IdentifiableSet<ResolvedModule>
@available(*, deprecated, renamed: "testEntryPointModule")
public var testEntryPointTarget: ResolvedModule? { self.testEntryPointModule }
/// Executable module for test entry point file.
public let testEntryPointModule: ResolvedModule?
/// The default localization for resources.
public let defaultLocalization: String?
/// The list of platforms that are supported by this product.
public let supportedPlatforms: [SupportedPlatform]
@_spi(SwiftPMInternal)
public let platformVersionProvider: PlatformVersionProvider
/// Triple for which this resolved product should be compiled for.
public internal(set) var buildTriple: BuildTriple {
didSet {
self.updateBuildTriplesOfDependencies()
}
}
@available(*, deprecated, renamed: "executableModule")
public var executableTarget: ResolvedModule { get throws { try self.executableModule } }
/// The main executable module of this product.
///
/// Note: This property is only valid for executable products.
public var executableModule: ResolvedModule {
get throws {
guard self.type == .executable || self.type == .snippet || self.type == .macro else {
throw InternalError("`executableTarget` should only be called for executable targets")
}
guard let underlyingExecutableModule = modules.map(\.underlying).executables.first,
let executableModule = modules.first(where: { $0.underlying == underlyingExecutableModule })
else {
throw InternalError("could not determine executable target")
}
return executableModule
}
}
@available(*, deprecated, renamed: "init(packageIdentity:product:modules:)")
public init(
packageIdentity: PackageIdentity,
product: Product,
targets: IdentifiableSet<ResolvedModule>
) {
self.init(packageIdentity: packageIdentity, product: product, modules: targets)
}
public init(
packageIdentity: PackageIdentity,
product: Product,
modules: IdentifiableSet<ResolvedModule>
) {
assert(product.modules.count == modules.count && product.modules.map(\.name).sorted() == modules.map(\.name).sorted())
self.packageIdentity = packageIdentity
self.underlying = product
self.modules = modules
// defaultLocalization is currently shared across the entire package
// this may need to be enhanced if / when we support localization per module or product
let defaultLocalization = self.modules.first?.defaultLocalization
self.defaultLocalization = defaultLocalization
let (platforms, platformVersionProvider) = Self.computePlatforms(modules: modules)
self.supportedPlatforms = platforms
self.platformVersionProvider = platformVersionProvider
self.testEntryPointModule = product.testEntryPointPath.map { testEntryPointPath in
// Create an executable resolved module with the entry point file, adding product's modules as dependencies.
let dependencies: [Module.Dependency] = product.modules.map { .module($0, conditions: []) }
let swiftModule = SwiftModule(
name: product.name,
dependencies: dependencies,
packageAccess: true, // entry point module so treated as a part of the package
testEntryPointPath: testEntryPointPath
)
return ResolvedModule(
packageIdentity: packageIdentity,
underlying: swiftModule,
dependencies: modules.map { .module($0, conditions: []) },
defaultLocalization: defaultLocalization ?? .none, // safe since this is a derived product
supportedPlatforms: platforms,
platformVersionProvider: platformVersionProvider
)
}
if product.type == .test {
// Make sure that test products are built for the tools triple if it has tools as direct dependencies.
// Without this workaround, `assertMacroExpansion` in tests can't be built, as it requires macros
// and SwiftSyntax to be built for the same triple as the tests.
// See https://github.com/swiftlang/swift-package-manager/pull/7349 for more context.
var inferredBuildTriple = BuildTriple.destination
modulesLoop: for module in modules {
for dependency in module.dependencies {
switch dependency {
case .module(let moduleDependency, _):
if moduleDependency.type == .macro {
inferredBuildTriple = .tools
break modulesLoop
}
case .product(let productDependency, _):
if productDependency.type == .macro {
inferredBuildTriple = .tools
break modulesLoop
}
}
}
}
self.buildTriple = inferredBuildTriple
} else {
self.buildTriple = product.buildTriple
}
self.updateBuildTriplesOfDependencies()
}
mutating func updateBuildTriplesOfDependencies() {
self.modules = IdentifiableSet(self.modules.map {
var module = $0
module.buildTriple = self.buildTriple
return module
})
}
@available(*, deprecated, renamed: "containsSwiftModules")
public var containsSwiftTargets: Bool { self.containsSwiftModules }
/// True if this product contains Swift modules.
public var containsSwiftModules: Bool {
// C modules can't import Swift modules in SwiftPM (at least not right
// now), so we can just look at the top-level modules.
//
// If that ever changes, we'll need to do something more complex here,
// recursively checking dependencies for SwiftModules, and considering
// dynamic library modules to be Swift modules (since the dylib could
// contain Swift code we don't know about as part of this build).
self.modules.contains { $0.underlying is SwiftModule }
}
@available(*, deprecated, renamed: "recursiveModuleDependencies")
public func recursiveTargetDependencies() throws -> [ResolvedModule] { try self.recursiveModuleDependencies() }
/// Returns the recursive module dependencies.
public func recursiveModuleDependencies() throws -> [ResolvedModule] {
let recursiveDependencies = try modules.lazy.flatMap { try $0.recursiveModuleDependencies() }
return Array(IdentifiableSet(self.modules).union(recursiveDependencies))
}
private static func computePlatforms(
modules: IdentifiableSet<ResolvedModule>
) -> ([SupportedPlatform], PlatformVersionProvider) {
let declaredPlatforms = modules.reduce(into: [SupportedPlatform]()) { partial, item in
merge(into: &partial, platforms: item.supportedPlatforms)
}
return (
declaredPlatforms.sorted(by: { $0.platform.name < $1.platform.name }),
PlatformVersionProvider(implementation: .mergingFromModules(modules))
)
}
public func getSupportedPlatform(for platform: Platform, usingXCTest: Bool) -> SupportedPlatform {
self.platformVersionProvider.getDerived(
declared: self.supportedPlatforms,
for: platform,
usingXCTest: usingXCTest
)
}
func diagnoseInvalidUseOfUnsafeFlags(_ diagnosticsEmitter: DiagnosticsEmitter) throws {
// Diagnose if any module in this product uses an unsafe flag.
for module in try self.recursiveModuleDependencies() {
if module.underlying.usesUnsafeFlags {
diagnosticsEmitter.emit(.productUsesUnsafeFlags(product: self.name, target: module.name))
}
}
}
}
extension ResolvedProduct: CustomStringConvertible {
public var description: String {
"<ResolvedProduct: \(self.name), \(self.type), \(self.buildTriple)>"
}
}
extension ResolvedProduct {
public var isLinkingXCTest: Bool {
// To retain existing behavior, we have to check both the product type, as well as the types of all of its
// modules.
self.type == .test || self.modules.contains(where: { $0.type == .test })
}
}
extension ResolvedProduct: Identifiable {
/// Resolved module identity that uniquely identifies it in a resolution graph.
public struct ID: Hashable {
public let productName: String
let packageIdentity: PackageIdentity
public var buildTriple: BuildTriple
}
public var id: ID {
ID(productName: self.name, packageIdentity: self.packageIdentity, buildTriple: self.buildTriple)
}
}
@available(*, unavailable, message: "Use `Identifiable` conformance or `IdentifiableSet` instead")
extension ResolvedProduct: Hashable {}
|