Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/Build/LLBuildManifestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ extension LLBuildManifestBuilder {
outputs: cmdOutputs,
executable: buildParameters.toolchain.swiftCompiler,
moduleName: target.target.c99name,
moduleAliases: target.target.moduleAliases,
moduleOutputPath: target.moduleOutputPath,
importPath: buildParameters.buildPath,
tempsPath: target.tempsPath,
Expand Down
2 changes: 2 additions & 0 deletions Sources/LLBuildManifest/BuildManifest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ public struct BuildManifest {
outputs: [Node],
executable: AbsolutePath,
moduleName: String,
moduleAliases: [String: String]?,
moduleOutputPath: AbsolutePath,
importPath: AbsolutePath,
tempsPath: AbsolutePath,
Expand All @@ -178,6 +179,7 @@ public struct BuildManifest {
outputs: outputs,
executable: executable,
moduleName: moduleName,
moduleAliases: moduleAliases,
moduleOutputPath: moduleOutputPath,
importPath: importPath,
tempsPath: tempsPath,
Expand Down
6 changes: 6 additions & 0 deletions Sources/LLBuildManifest/Tools.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ public struct SwiftCompilerTool: ToolProtocol {

public var executable: AbsolutePath
public var moduleName: String
public var moduleAliases: [String: String]?
Comment thread
elsh marked this conversation as resolved.
public var moduleOutputPath: AbsolutePath
public var importPath: AbsolutePath
public var tempsPath: AbsolutePath
Expand All @@ -235,6 +236,7 @@ public struct SwiftCompilerTool: ToolProtocol {
outputs: [Node],
executable: AbsolutePath,
moduleName: String,
moduleAliases: [String: String]?,
moduleOutputPath: AbsolutePath,
importPath: AbsolutePath,
tempsPath: AbsolutePath,
Expand All @@ -248,6 +250,7 @@ public struct SwiftCompilerTool: ToolProtocol {
self.outputs = outputs
self.executable = executable
self.moduleName = moduleName
self.moduleAliases = moduleAliases
self.moduleOutputPath = moduleOutputPath
self.importPath = importPath
self.tempsPath = tempsPath
Expand All @@ -261,6 +264,9 @@ public struct SwiftCompilerTool: ToolProtocol {
public func write(to stream: ManifestToolStream) {
stream["executable"] = executable
stream["module-name"] = moduleName
if let moduleAliases = moduleAliases {
stream["module-alias"] = moduleAliases
}
stream["module-output-path"] = moduleOutputPath
stream["import-paths"] = [importPath]
stream["temps-path"] = tempsPath
Expand Down
4 changes: 4 additions & 0 deletions Sources/PackageDescription/PackageDependency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ extension Package {
}
}

/// Module aliases to build this dependency.
@available(_PackageDescription, introduced: 999.0)
public var moduleAliases: [String: String]?

/// The requirement of the dependency.
@available(_PackageDescription, deprecated: 5.6, message: "use kind instead")
public var requirement: Requirement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ extension Target.Dependency: Encodable {
case type
case name
case package
case moduleAliases
case condition
}

Expand All @@ -308,10 +309,11 @@ extension Target.Dependency: Encodable {
try container.encode(Kind.target, forKey: .type)
try container.encode(name, forKey: .name)
try container.encode(condition, forKey: .condition)
case .productItem(let name, let package, let condition):
case .productItem(let name, let package, let moduleAliases, let condition):
try container.encode(Kind.product, forKey: .type)
try container.encode(name, forKey: .name)
try container.encode(package, forKey: .package)
try container.encode(moduleAliases, forKey: .moduleAliases)
try container.encode(condition, forKey: .condition)
case .byNameItem(let name, let condition):
try container.encode(Kind.byName, forKey: .type)
Expand Down
29 changes: 22 additions & 7 deletions Sources/PackageDescription/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public final class Target {
/// The different types of a target's dependency on another entity.
public enum Dependency {
case targetItem(name: String, condition: TargetDependencyCondition?)
case productItem(name: String, package: String?, condition: TargetDependencyCondition?)
case productItem(name: String, package: String?, moduleAliases: [String: String]?, condition: TargetDependencyCondition?)
case byNameItem(name: String, condition: TargetDependencyCondition?)
}

Expand Down Expand Up @@ -936,9 +936,14 @@ extension Target.Dependency {
/// - package: The name of the package.
@available(_PackageDescription, obsoleted: 5.2, message: "the 'package' argument is mandatory as of tools version 5.2")
public static func product(name: String, package: String? = nil) -> Target.Dependency {
return .productItem(name: name, package: package, condition: nil)
return .productItem(name: name, package: package, moduleAliases: nil, condition: nil)
}


@available(_PackageDescription, introduced: 999.0)
public static func product(name: String, package: String? = nil, moduleAliases: [String: String]? = nil) -> Target.Dependency {
return .productItem(name: name, package: package, moduleAliases: moduleAliases, condition: nil)
}

/// Creates a dependency that resolves to either a target or a product with the specified name.
///
/// - parameters:
Expand All @@ -960,7 +965,7 @@ extension Target.Dependency {
name: String,
package: String
) -> Target.Dependency {
return .productItem(name: name, package: package, condition: nil)
return .productItem(name: name, package: package, moduleAliases: nil, condition: nil)
}

/// Creates a dependency on a target in the same package.
Expand All @@ -981,15 +986,25 @@ extension Target.Dependency {
/// - package: The name of the package.
/// - condition: A condition that limits the application of the target dependency. For example, only apply a
/// dependency for a specific platform.
@available(_PackageDescription, introduced: 5.3)
@available(_PackageDescription, introduced: 5.3, obsoleted: 999.0)
public static func product(
name: String,
package: String,
condition: TargetDependencyCondition? = nil
) -> Target.Dependency {
return .productItem(name: name, package: package, condition: condition)
return .productItem(name: name, package: package, moduleAliases: nil, condition: condition)
}


@available(_PackageDescription, introduced: 999.0)
public static func product(
Comment thread
elsh marked this conversation as resolved.
name: String,
package: String,
moduleAliases: [String: String]? = nil,
condition: TargetDependencyCondition? = nil
) -> Target.Dependency {
return .productItem(name: name, package: package, moduleAliases: moduleAliases, condition: condition)
}

/// Creates a by-name dependency that resolves to either a target or a product but after the Swift Package Manager
/// has loaded the package graph.
///
Expand Down
67 changes: 65 additions & 2 deletions Sources/PackageGraph/PackageGraph+Loading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,32 @@ private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], obse
}
}

extension Package {
// Add module aliases specified for applicable targets
fileprivate func setModuleAliasesForTargets(with moduleAliasMap: [String: String]) {
// Set module aliases for each target's dependencies
for (entryName, entryAlias) in moduleAliasMap {
for target in self.targets {
// First add dependency module aliases for this target
if entryName != target.name {
target.addModuleAlias(for: entryName, as: entryAlias)
}
}
}

// This loop should run after the loop above as it may rename the target
// as an alias if specified
for (entryName, entryAlias) in moduleAliasMap {
for target in self.targets {
// Then set this target to be aliased if specified
if entryName == target.name {
target.addModuleAlias(for: target.name, as: entryAlias)
}
}
}
}
}

fileprivate extension ResolvedProduct {
/// Returns true if and only if the product represents a command plugin target.
var isCommandPlugin: Bool {
Expand Down Expand Up @@ -239,14 +265,21 @@ private func createResolvedPackages(
return ($0.package.identity, $0)
}

// Gather all module aliases specified for dependencies from each package
let pkgToAliasesMap = gatherModuleAliases(from: packageBuilders)

// Scan and validate the dependencies
for packageBuilder in packageBuilders {
let package = packageBuilder.package

Comment thread
elsh marked this conversation as resolved.
Outdated
Comment thread
elsh marked this conversation as resolved.
Outdated
let packageObservabilityScope = observabilityScope.makeChildScope(
description: "Validating package dependencies",
metadata: package.diagnosticsMetadata
)

if let aliasMap = pkgToAliasesMap[package.manifest.displayName] {
package.setModuleAliasesForTargets(with: aliasMap)
}

var dependencies = OrderedCollections.OrderedDictionary<PackageIdentity, ResolvedPackageBuilder>()
var dependenciesByNameForTargetDependencyResolution = [String: ResolvedPackageBuilder]()
Expand Down Expand Up @@ -473,7 +506,7 @@ private func createResolvedPackages(
}
}
}

// If a target with similar name was encountered before, we emit a diagnostic.
if foundDuplicateTarget {
for targetName in allTargetNames.sorted() {
Expand All @@ -490,6 +523,36 @@ private func createResolvedPackages(
return try packageBuilders.map{ try $0.construct() }
}

// Create a map between a package and module aliases specified for its targets
private func gatherModuleAliases(from packageBuilders: [ResolvedPackageBuilder]) -> [String: [String: String]] {
// Use a list to track all the module aliases specified for a given target to handle an override later
var nameToAliasAndPkgMap = [String: [(String, String)]]()
packageBuilders.forEach { $0.package.targets.forEach { $0.dependencies.forEach { dep in
if case let .product(prodRef, _) = dep {
if let prodPkg = prodRef.package {
if let prodModuleAliases = prodRef.moduleAliases {
for (depName, depAlias) in prodModuleAliases {
// Add a module alias to a map if specified
nameToAliasAndPkgMap[depName, default: []].append((depAlias, prodPkg))
}
}
}
}
}}}

var pkgToAliasesMap = [String: [String: String]]()
nameToAliasAndPkgMap.forEach { (keyName, aliasAndPkgList) in
aliasAndPkgList.forEach { (elemAlias, elemPkg) in
// FIXME: handle an override for multiple aliases
// Use one of the module aliases specified for now
if pkgToAliasesMap[elemPkg]?[keyName] == nil, !elemAlias.isEmpty {
pkgToAliasesMap[elemPkg] = [keyName: elemAlias]
}
}
}
return pkgToAliasesMap
}

/// A generic builder for `Resolved` models.
private class ResolvedBuilder<T> {
/// The constructed object, available after the first call to `construct()`.
Expand Down
5 changes: 5 additions & 0 deletions Sources/PackageGraph/ResolvedTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ public final class ResolvedTarget {
return underlyingTarget.c99name
}

/// The module aliases to build this target.
public var moduleAliases: [String: String]? {
return underlyingTarget.moduleAliases
}

/// The "type" of target.
public var type: Target.Kind {
return underlyingTarget.type
Expand Down
3 changes: 2 additions & 1 deletion Sources/PackageLoading/ManifestJSONParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,8 @@ extension TargetDescription.Dependency {

case "product":
let name = try json.get(String.self, forKey: "name")
self = .product(name: name, package: json.get("package"), condition: condition)
let moduleAliases: [String: String]? = try? json.get("moduleAliases")
self = .product(name: name, package: json.get("package"), moduleAliases: moduleAliases, condition: condition)

case "byname":
self = try .byName(name: json.get("name"), condition: condition)
Expand Down
2 changes: 1 addition & 1 deletion Sources/PackageLoading/ManifestLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ public final class ManifestLoader: ManifestLoaderProtocol {
case .target:
// If this is a target dependency, we don't need to check anything.
break
case .product(_, let packageName, _):
case .product(_, let packageName, _, _):
if manifest.packageDependency(referencedBy: targetDependency) == nil {
observabilityScope.emit(.unknownTargetPackageDependency(
packageName: packageName ?? "unknown package name",
Expand Down
11 changes: 5 additions & 6 deletions Sources/PackageLoading/PackageBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ public final class PackageBuilder {
let path = try findPath(for: target)
return PotentialModule(name: target.name, path: path, type: target.type)
})

Comment thread
elsh marked this conversation as resolved.
Outdated
let targets = try createModules(potentialTargets)

let snippetTargets: [Target]
Expand Down Expand Up @@ -661,7 +661,7 @@ public final class PackageBuilder {
var targets = [String: Target]()
// If a direcotry is empty, we don't create a target object for them.
var emptyModules = Set<String>()

Comment thread
elsh marked this conversation as resolved.
Outdated
// Start iterating the potential targets.
for potentialModule in potentialModules.lazy.reversed() {
// Validate the target name. This function will throw an error if it detects a problem.
Expand All @@ -680,12 +680,11 @@ public final class PackageBuilder {
guard let target = targets[name] else { return nil }
return .target(target, conditions: buildConditions(from: condition))

case .product(let name, let package, let condition):
case .product(let name, let package, let moduleAliases, let condition):
return .product(
.init(name: name, package: package),
.init(name: name, package: package, moduleAliases: moduleAliases),
conditions: buildConditions(from: condition)
)

case .byName(let name, let condition):
// We don't create an object for targets which have no sources.
if emptyModules.contains(name) { return nil }
Expand Down Expand Up @@ -718,7 +717,7 @@ public final class PackageBuilder {
}
}
} ?? []

Comment thread
elsh marked this conversation as resolved.
Outdated
// Create the target, adding the inferred dependencies from plugin usages to the declared dependencies.
let target = try createTarget(
potentialModule: potentialModule,
Expand Down
6 changes: 4 additions & 2 deletions Sources/PackageModel/Manifest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ public final class Manifest {
/// The system package providers of a system package.
public let providers: [SystemPackageProviderDescription]?

public var moduleAliases: [String: (String, String)]?

/// Targets required for building particular product filters.
private var _requiredTargets = ThreadSafeKeyValueStore<ProductFilter, [TargetDescription]>()

Expand Down Expand Up @@ -298,7 +300,7 @@ public final class Manifest {
let packageName: String

switch targetDependency {
case .product(_, package: let name?, _),
case .product(_, package: let name?, _, _),
.byName(name: let name, _):
packageName = name
default:
Expand Down Expand Up @@ -340,7 +342,7 @@ public final class Manifest {
switch targetDependency {
case .target:
break
case .product(let product, let package, _):
case .product(let product, let package, _, _):
if let package = package { // ≥ 5.2
if !register(
product: product,
Expand Down
Loading