From a0fbf35868d3588314f1c9be1215305954565870 Mon Sep 17 00:00:00 2001 From: Paulo Mattos Date: Fri, 4 Apr 2025 13:38:16 -0700 Subject: [PATCH 01/33] Minor Codable conformance improvements in SwiftBuildSupport (#8449) ### Motivation: Some of the `SwiftBuildSupport/PIFBuilder.swift` encoding/decoding code was using unsafe string keys. This replaces with proper typesafe keys. This part of the ground work to support the new PIF builder in `SwiftBuildSupport` (i.e., rdar://147527170). ### Modifications: When conforming to `Codable`, replace string keys with `enum` based keys instead. ### Result: The resulting code is a tad safer and easier to understand :-) Tracked by rdar://148546582. --- Sources/SwiftBuildSupport/PIF.swift | 143 ++++++++++++++++------------ 1 file changed, 82 insertions(+), 61 deletions(-) diff --git a/Sources/SwiftBuildSupport/PIF.swift b/Sources/SwiftBuildSupport/PIF.swift index 8d14a6a4b16..fa295895139 100644 --- a/Sources/SwiftBuildSupport/PIF.swift +++ b/Sources/SwiftBuildSupport/PIF.swift @@ -60,7 +60,25 @@ public enum PIF { } } - public class TypedObject: Codable { + /// Represents a high-level PIF object. + /// + /// For instance, a JSON serialized *workspace* might look like this: + /// ```json + /// { + /// "type" : "workspace", + /// "signature" : "22e9436958aec481799", + /// "contents" : { + /// "guid" : "Workspace:/Users/foo/BarPackage", + /// "name" : "BarPackage", + /// "path" : "/Users/foo/BarPackage", + /// "projects" : [ + /// "70a588f37dcfcddbc1f", + /// "c1d9cb257bd42cafbb8" + /// ] + /// } + /// } + /// ``` + public class HighLevelObject: Codable { class var type: String { fatalError("\(self) missing implementation") } @@ -71,8 +89,9 @@ public enum PIF { type = Swift.type(of: self).type } - private enum CodingKeys: CodingKey { + fileprivate enum CodingKeys: CodingKey { case type + case signature, contents // Used by subclasses. } public func encode(to encoder: Encoder) throws { @@ -86,7 +105,7 @@ public enum PIF { } } - public final class Workspace: TypedObject { + public final class Workspace: HighLevelObject { override class var type: String { "workspace" } public let guid: GUID @@ -113,8 +132,8 @@ public enum PIF { public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) - var container = encoder.container(keyedBy: StringKey.self) - var contents = container.nestedContainer(keyedBy: CodingKeys.self, forKey: "contents") + var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self) + var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid) try contents.encode(name, forKey: .name) try contents.encode(path, forKey: .path) @@ -123,7 +142,7 @@ public enum PIF { guard let signature else { throw InternalError("Expected to have workspace signature when encoding for SwiftBuild") } - try container.encode(signature, forKey: "signature") + try superContainer.encode(signature, forKey: .signature) try contents.encode(projects.map({ $0.signature }), forKey: .projects) } else { try contents.encode(projects, forKey: .projects) @@ -131,21 +150,21 @@ public enum PIF { } public required init(from decoder: Decoder) throws { - let superContainer = try decoder.container(keyedBy: StringKey.self) - let container = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: "contents") + let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self) + let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - let guidString = try container.decode(GUID.self, forKey: .guid) + let guidString = try contents.decode(GUID.self, forKey: .guid) self.guid = String(guidString.dropLast("\(schemaVersion)".count + 1)) - self.name = try container.decode(String.self, forKey: .name) - self.path = try container.decode(AbsolutePath.self, forKey: .path) - self.projects = try container.decode([Project].self, forKey: .projects) + self.name = try contents.decode(String.self, forKey: .name) + self.path = try contents.decode(AbsolutePath.self, forKey: .path) + self.projects = try contents.decode([Project].self, forKey: .projects) try super.init(from: decoder) } } /// A PIF project, consisting of a tree of groups and file references, a list of targets, and some additional /// information. - public final class Project: TypedObject { + public final class Project: HighLevelObject { override class var type: String { "project" } public let guid: GUID @@ -191,8 +210,8 @@ public enum PIF { public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) - var container = encoder.container(keyedBy: StringKey.self) - var contents = container.nestedContainer(keyedBy: CodingKeys.self, forKey: "contents") + var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self) + var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid) try contents.encode(name, forKey: .projectName) try contents.encode("true", forKey: .projectIsPackage) @@ -206,7 +225,7 @@ public enum PIF { guard let signature else { throw InternalError("Expected to have project signature when encoding for SwiftBuild") } - try container.encode(signature, forKey: "signature") + try superContainer.encode(signature, forKey: .signature) try contents.encode(targets.map{ $0.signature }, forKey: .targets) } else { try contents.encode(targets, forKey: .targets) @@ -216,19 +235,19 @@ public enum PIF { } public required init(from decoder: Decoder) throws { - let superContainer = try decoder.container(keyedBy: StringKey.self) - let container = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: "contents") + let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self) + let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - let guidString = try container.decode(GUID.self, forKey: .guid) + let guidString = try contents.decode(GUID.self, forKey: .guid) self.guid = String(guidString.dropLast("\(schemaVersion)".count + 1)) - self.name = try container.decode(String.self, forKey: .projectName) - self.path = try container.decode(AbsolutePath.self, forKey: .path) - self.projectDirectory = try container.decode(AbsolutePath.self, forKey: .projectDirectory) - self.developmentRegion = try container.decode(String.self, forKey: .developmentRegion) - self.buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) - - let untypedTargets = try container.decode([UntypedTarget].self, forKey: .targets) - var targetContainer = try container.nestedUnkeyedContainer(forKey: .targets) + self.name = try contents.decode(String.self, forKey: .projectName) + self.path = try contents.decode(AbsolutePath.self, forKey: .path) + self.projectDirectory = try contents.decode(AbsolutePath.self, forKey: .projectDirectory) + self.developmentRegion = try contents.decode(String.self, forKey: .developmentRegion) + self.buildConfigurations = try contents.decode([BuildConfiguration].self, forKey: .buildConfigurations) + + let untypedTargets = try contents.decode([UntypedTarget].self, forKey: .targets) + var targetContainer = try contents.nestedUnkeyedContainer(forKey: .targets) self.targets = try untypedTargets.map { target in let type = target.contents.type switch type { @@ -241,13 +260,13 @@ public enum PIF { } } - self.groupTree = try container.decode(Group.self, forKey: .groupTree) + self.groupTree = try contents.decode(Group.self, forKey: .groupTree) try super.init(from: decoder) } } /// Abstract base class for all items in the group hierarchy. - public class Reference: TypedObject { + public class Reference: HighLevelObject { /// Determines the base path for a reference's relative path. public enum SourceTree: String, Codable { @@ -387,7 +406,7 @@ public enum PIF { public required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - let untypedChildren = try container.decode([TypedObject].self, forKey: .children) + let untypedChildren = try container.decode([HighLevelObject].self, forKey: .children) var childrenContainer = try container.nestedUnkeyedContainer(forKey: .children) self.children = try untypedChildren.map { child in @@ -440,7 +459,7 @@ public enum PIF { } } - public class BaseTarget: TypedObject { + public class BaseTarget: HighLevelObject { class override var type: String { "target" } public let guid: GUID public var name: String @@ -500,8 +519,8 @@ public enum PIF { public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) - var container = encoder.container(keyedBy: StringKey.self) - var contents = container.nestedContainer(keyedBy: CodingKeys.self, forKey: "contents") + var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self) + var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) try contents.encode("aggregate", forKey: .type) try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid) try contents.encode(name, forKey: .name) @@ -514,22 +533,22 @@ public enum PIF { guard let signature else { throw InternalError("Expected to have \(Swift.type(of: self)) signature when encoding for SwiftBuild") } - try container.encode(signature, forKey: "signature") + try superContainer.encode(signature, forKey: .signature) } } public required init(from decoder: Decoder) throws { - let superContainer = try decoder.container(keyedBy: StringKey.self) - let container = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: "contents") + let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self) + let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - let guidString = try container.decode(GUID.self, forKey: .guid) + let guidString = try contents.decode(GUID.self, forKey: .guid) let guid = String(guidString.dropLast("\(schemaVersion)".count + 1)) - let name = try container.decode(String.self, forKey: .name) - let buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) + let name = try contents.decode(String.self, forKey: .name) + let buildConfigurations = try contents.decode([BuildConfiguration].self, forKey: .buildConfigurations) - let untypedBuildPhases = try container.decode([TypedObject].self, forKey: .buildPhases) - var buildPhasesContainer = try container.nestedUnkeyedContainer(forKey: .buildPhases) + let untypedBuildPhases = try contents.decode([HighLevelObject].self, forKey: .buildPhases) + var buildPhasesContainer = try contents.nestedUnkeyedContainer(forKey: .buildPhases) let buildPhases: [BuildPhase] = try untypedBuildPhases.map { guard let type = $0.type else { @@ -538,8 +557,8 @@ public enum PIF { return try BuildPhase.decode(container: &buildPhasesContainer, type: type) } - let dependencies = try container.decode([TargetDependency].self, forKey: .dependencies) - let impartedBuildProperties = try container.decode(BuildSettings.self, forKey: .impartedBuildProperties) + let dependencies = try contents.decode([TargetDependency].self, forKey: .dependencies) + let impartedBuildProperties = try contents.decode(BuildSettings.self, forKey: .impartedBuildProperties) super.init( guid: guid, @@ -600,8 +619,8 @@ public enum PIF { override public func encode(to encoder: Encoder) throws { try super.encode(to: encoder) - var container = encoder.container(keyedBy: StringKey.self) - var contents = container.nestedContainer(keyedBy: CodingKeys.self, forKey: "contents") + var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self) + var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid) try contents.encode(name, forKey: .name) try contents.encode(dependencies, forKey: .dependencies) @@ -611,7 +630,7 @@ public enum PIF { guard let signature else { throw InternalError("Expected to have \(Swift.type(of: self)) signature when encoding for SwiftBuild") } - try container.encode(signature, forKey: "signature") + try superContainer.encode(signature, forKey: .signature) } if productType == .packageProduct { @@ -639,16 +658,16 @@ public enum PIF { } public required init(from decoder: Decoder) throws { - let superContainer = try decoder.container(keyedBy: StringKey.self) - let container = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: "contents") + let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self) + let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - let guidString = try container.decode(GUID.self, forKey: .guid) + let guidString = try contents.decode(GUID.self, forKey: .guid) let guid = String(guidString.dropLast("\(schemaVersion)".count + 1)) - let name = try container.decode(String.self, forKey: .name) - let buildConfigurations = try container.decode([BuildConfiguration].self, forKey: .buildConfigurations) - let dependencies = try container.decode([TargetDependency].self, forKey: .dependencies) + let name = try contents.decode(String.self, forKey: .name) + let buildConfigurations = try contents.decode([BuildConfiguration].self, forKey: .buildConfigurations) + let dependencies = try contents.decode([TargetDependency].self, forKey: .dependencies) - let type = try container.decode(String.self, forKey: .type) + let type = try contents.decode(String.self, forKey: .type) let buildPhases: [BuildPhase] let impartedBuildProperties: ImpartedBuildProperties @@ -656,17 +675,17 @@ public enum PIF { if type == "packageProduct" { self.productType = .packageProduct self.productName = "" - let fwkBuildPhase = try container.decodeIfPresent(FrameworksBuildPhase.self, forKey: .frameworksBuildPhase) + let fwkBuildPhase = try contents.decodeIfPresent(FrameworksBuildPhase.self, forKey: .frameworksBuildPhase) buildPhases = fwkBuildPhase.map{ [$0] } ?? [] impartedBuildProperties = ImpartedBuildProperties(settings: BuildSettings()) } else if type == "standard" { - self.productType = try container.decode(ProductType.self, forKey: .productTypeIdentifier) + self.productType = try contents.decode(ProductType.self, forKey: .productTypeIdentifier) - let productReference = try container.decode([String: String].self, forKey: .productReference) + let productReference = try contents.decode([String: String].self, forKey: .productReference) self.productName = productReference["name"]! - let untypedBuildPhases = try container.decodeIfPresent([TypedObject].self, forKey: .buildPhases) ?? [] - var buildPhasesContainer = try container.nestedUnkeyedContainer(forKey: .buildPhases) + let untypedBuildPhases = try contents.decodeIfPresent([HighLevelObject].self, forKey: .buildPhases) ?? [] + var buildPhasesContainer = try contents.nestedUnkeyedContainer(forKey: .buildPhases) buildPhases = try untypedBuildPhases.map { guard let type = $0.type else { @@ -675,7 +694,7 @@ public enum PIF { return try BuildPhase.decode(container: &buildPhasesContainer, type: type) } - impartedBuildProperties = try container.decode(ImpartedBuildProperties.self, forKey: .impartedBuildProperties) + impartedBuildProperties = try contents.decode(ImpartedBuildProperties.self, forKey: .impartedBuildProperties) } else { throw InternalError("Unhandled target type \(type)") } @@ -693,7 +712,7 @@ public enum PIF { } /// Abstract base class for all build phases in a target. - public class BuildPhase: TypedObject { + public class BuildPhase: HighLevelObject { static func decode(container: inout UnkeyedDecodingContainer, type: String) throws -> BuildPhase { switch type { case HeadersBuildPhase.type: @@ -1158,7 +1177,7 @@ public struct SwiftBuildFileType: CaseIterable { } } -struct StringKey: CodingKey, ExpressibleByStringInterpolation { +fileprivate struct StringKey: CodingKey, ExpressibleByStringInterpolation { var stringValue: String var intValue: Int? @@ -1253,6 +1272,8 @@ private struct UntypedTarget: Decodable { let contents: TargetContents } +// MARK: - PIF Signature Support + protocol PIFSignableObject: AnyObject { var signature: String? { get set } } From c8d439a2ccc679477165d6dc992ef4a44b23b77c Mon Sep 17 00:00:00 2001 From: Paulo Mattos Date: Fri, 4 Apr 2025 20:31:36 -0700 Subject: [PATCH 02/33] Switch to ProjectModel API in the new SwiftBuildSupport PIF builder (#8441) ### Motivation: The goal is to adopt the new `SwiftBuild.ProjectModel` API. This new API provides a *typesafer* and *modern* way of building PIFs programmatically. This continues the work I started in PR #8405, introducing now our new PIF builder for packages. ### Modifications: Replaces all `SwiftBuild.PIF` (aka, `SWBProjectModel.PIF`) API usage, in `PackagePIFBuilder`, with the new `SwiftBuild.ProjectModel` API instead. ### Result: `PackagePIFBuilder` is now modernized... but still not actually used. This will come in my next pull request. Tracked by rdar://147526957. --- Sources/SwiftBuildSupport/PIFBuilder.swift | 6 +- .../PackagePIFBuilder+Helpers.swift | 295 +++++----- .../PackagePIFBuilder+Plugins.swift | 5 +- .../SwiftBuildSupport/PackagePIFBuilder.swift | 226 +++---- .../PackagePIFProjectBuilder+Modules.swift | 444 ++++++++------ .../PackagePIFProjectBuilder+Products.swift | 556 +++++++++++------- .../PackagePIFProjectBuilder.swift | 262 +++++---- 7 files changed, 1028 insertions(+), 766 deletions(-) diff --git a/Sources/SwiftBuildSupport/PIFBuilder.swift b/Sources/SwiftBuildSupport/PIFBuilder.swift index c658d0bb4b6..e5a4762c926 100644 --- a/Sources/SwiftBuildSupport/PIFBuilder.swift +++ b/Sources/SwiftBuildSupport/PIFBuilder.swift @@ -1641,7 +1641,7 @@ private struct PIFBuildSettingAssignment { let platforms: [PIF.BuildSettings.Platform]? } -extension BuildSettings.AssignmentTable { +extension PackageModel.BuildSettings.AssignmentTable { fileprivate var pifAssignments: [PIF.BuildSettings.MultipleValueSetting: [PIFBuildSettingAssignment]] { var pifAssignments: [PIF.BuildSettings.MultipleValueSetting: [PIFBuildSettingAssignment]] = [:] @@ -1679,7 +1679,7 @@ extension BuildSettings.AssignmentTable { } } -extension BuildSettings.Assignment { +extension PackageModel.BuildSettings.Assignment { fileprivate var configurations: [BuildConfiguration] { if let configurationCondition = conditions.lazy.compactMap(\.configurationCondition).first { [configurationCondition.configuration] @@ -1866,7 +1866,7 @@ extension PIF.BuildSettings { .filter { isSupportedVersion($0) }.map(\.description) } - func computeEffectiveTargetVersion(for assignment: BuildSettings.Assignment) throws -> String { + func computeEffectiveTargetVersion(for assignment: PackageModel.BuildSettings.Assignment) throws -> String { let versions = assignment.values.compactMap { SwiftLanguageVersion(string: $0) } if let effectiveVersion = computeEffectiveSwiftVersions(for: versions).last { return effectiveVersion diff --git a/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift b/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift index 0b2f782b4c2..f8819eaf0cf 100644 --- a/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift +++ b/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift @@ -56,7 +56,8 @@ import struct PackageGraph.ResolvedProduct import func PackageLoading.pkgConfigArgs #if canImport(SwiftBuild) -import enum SwiftBuild.PIF + +import enum SwiftBuild.ProjectModel // MARK: - PIF GUID Helpers @@ -76,25 +77,25 @@ extension TargetGUIDSuffix? { } extension PackageModel.Module { - func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> String { - PIFPackageBuilder.targetGUID(forModuleName: self.name, suffix: suffix) + func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> GUID { + PackagePIFBuilder.targetGUID(forModuleName: self.name, suffix: suffix) } } extension PackageGraph.ResolvedModule { - func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> String { + func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> GUID { self.underlying.pifTargetGUID(suffix: suffix) } } extension PackageModel.Product { - func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> String { - PIFPackageBuilder.targetGUID(forProductName: self.name, suffix: suffix) + func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> GUID { + PackagePIFBuilder.targetGUID(forProductName: self.name, suffix: suffix) } } extension PackageGraph.ResolvedProduct { - func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> String { + func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> GUID { self.underlying.pifTargetGUID(suffix: suffix) } @@ -106,12 +107,12 @@ extension PackageGraph.ResolvedProduct { } } -extension PIFPackageBuilder { +extension PackagePIFBuilder { /// Helper function to consistently generate a PIF target identifier string for a module in a package. /// /// This format helps make sure that there is no collision with any other PIF targets, /// and in particular that a PIF target and a PIF product can have the same name (as they often do). - static func targetGUID(forModuleName name: String, suffix: TargetGUIDSuffix? = nil) -> String { + static func targetGUID(forModuleName name: String, suffix: TargetGUIDSuffix? = nil) -> GUID { let suffixDescription = suffix.description(forName: name) return "PACKAGE-TARGET:\(name)\(suffixDescription)" } @@ -120,7 +121,7 @@ extension PIFPackageBuilder { /// /// This format helps make sure that there is no collision with any other PIF targets, /// and in particular that a PIF target and a PIF product can have the same name (as they often do). - static func targetGUID(forProductName name: String, suffix: TargetGUIDSuffix? = nil) -> String { + static func targetGUID(forProductName name: String, suffix: TargetGUIDSuffix? = nil) -> GUID { let suffixDescription = suffix.description(forName: name) return "PACKAGE-PRODUCT:\(name)\(suffixDescription)" } @@ -140,14 +141,14 @@ extension PackageModel.Package { self.manifest.displayName } - var packageBaseBuildSettings: SwiftBuild.PIF.BuildSettings { - var settings = SwiftBuild.PIF.BuildSettings() - settings.SDKROOT = "auto" - settings.SDK_VARIANT = "auto" + var packageBaseBuildSettings: ProjectModel.BuildSettings { + var settings = BuildSettings() + settings[.SDKROOT] = "auto" + settings[.SDK_VARIANT] = "auto" if self.manifest.toolsVersion >= ToolsVersion.v6_0 { if let version = manifest.version, !version.isPrerelease && !version.hasBuildMetadata { - settings.SWIFT_USER_MODULE_VERSION = version.stringRepresentation + settings[.SWIFT_USER_MODULE_VERSION] = version.stringRepresentation } } return settings @@ -204,14 +205,14 @@ extension PackageModel.Platform { } extension Sequence { - func toPlatformFilter(toolsVersion: ToolsVersion) -> Set { - let pifPlatforms = self.flatMap { packageCondition -> [SwiftBuild.PIF.BuildSettings.Platform] in + func toPlatformFilter(toolsVersion: ToolsVersion) -> Set { + let pifPlatforms = self.flatMap { packageCondition -> [ProjectModel.BuildSettings.Platform] in guard let platforms = packageCondition.platformsCondition?.platforms else { return [] } - var pifPlatformsForCondition: [SwiftBuild.PIF.BuildSettings.Platform] = platforms - .map { SwiftBuild.PIF.BuildSettings.Platform(from: $0) } + var pifPlatformsForCondition: [ProjectModel.BuildSettings.Platform] = platforms + .map { ProjectModel.BuildSettings.Platform(from: $0) } // Treat catalyst like macOS for backwards compatibility with older tools versions. if pifPlatformsForCondition.contains(.macOS), toolsVersion < ToolsVersion.v5_5 { @@ -219,7 +220,7 @@ extension Sequence { } return pifPlatformsForCondition } - return pifPlatforms.toPlatformFilter() + return Set(pifPlatforms.flatMap { $0.toPlatformFilter() }) } var splitIntoConcreteConditions: ( @@ -291,7 +292,7 @@ extension PackageGraph.ResolvedPackage { } /// The options declared per platform. - func sdkOptions(delegate: PIFPackageBuilder.BuildDelegate) -> [PackageModel.Platform: [String]] { + func sdkOptions(delegate: PackagePIFBuilder.BuildDelegate) -> [PackageModel.Platform: [String]] { let platformDescriptionsByName: [String: PlatformDescription] = Dictionary( uniqueKeysWithValues: self.manifest.platforms.map { platformDescription in let key = platformDescription.platformName.lowercased() @@ -314,7 +315,7 @@ extension PackageGraph.ResolvedPackage { } extension PackageGraph.ResolvedPackage { - public var packageBaseBuildSettings: SwiftBuild.PIF.BuildSettings { + public var packageBaseBuildSettings: ProjectModel.BuildSettings { self.underlying.packageBaseBuildSettings } } @@ -338,7 +339,7 @@ extension PackageGraph.ResolvedModule { } /// Minimum deployment targets for particular platforms, as declared in the manifest. - func deploymentTargets(using delegate: PIFPackageBuilder.BuildDelegate) -> [PackageModel.Platform: String] { + func deploymentTargets(using delegate: PackagePIFBuilder.BuildDelegate) -> [PackageModel.Platform: String] { let isUsingXCTest = (self.type == .test) let derivedSupportedPlatforms: [SupportedPlatform] = Platform.knownPlatforms.map { self.getSupportedPlatform(for: $0, usingXCTest: isUsingXCTest) @@ -489,8 +490,7 @@ extension PackageGraph.ResolvedModule { func productRepresentingDependencyOfBuildPlugin(in mainModuleProducts: [ResolvedProduct]) -> ResolvedProduct? { mainModuleProducts.only { (mainModuleProduct: ResolvedProduct) -> Bool in // NOTE: We can't use the 'id' here as we need to explicitly ignore the build triple because our build - // triple - // will be '.tools' while the target we want to depend on will have a build triple of '.destination'. + // triple will be '.tools' while the target we want to depend on will have a build triple of '.destination'. // See for more details: // https://github.com/swiftlang/swift-package-manager/commit/b22168ec41061ddfa3438f314a08ac7a776bef7a. return mainModuleProduct.mainModule!.packageIdentity == self.packageIdentity && @@ -500,7 +500,8 @@ extension PackageGraph.ResolvedModule { } struct AllBuildSettings { - typealias BuildSettingsByPlatform = [PackageModel.Platform?: [BuildSettings.Declaration: [String]]] + typealias BuildSettingsByPlatform = + [ProjectModel.BuildSettings.Platform?: [BuildSettings.Declaration: [String]]] /// Target-specific build settings declared in the manifest and that apply to the target itself. var targetSettings: [BuildConfiguration: BuildSettingsByPlatform] = [:] @@ -519,22 +520,22 @@ extension PackageGraph.ResolvedModule { for (declaration, settingsAssigments) in self.underlying.buildSettings.assignments { for settingAssignment in settingsAssigments { - // Create a build setting value; in some cases there isn't a direct mapping to Swift Build build - // settings. - let swbDeclaration: BuildSettings.Declaration + // Create a build setting value; in some cases there + // isn't a direct mapping to Swift Build build settings. + let pifDeclaration: BuildSettings.Declaration let values: [String] switch declaration { case .LINK_FRAMEWORKS: - swbDeclaration = .OTHER_LDFLAGS + pifDeclaration = .OTHER_LDFLAGS values = settingAssignment.values.flatMap { ["-framework", $0] } case .LINK_LIBRARIES: - swbDeclaration = .OTHER_LDFLAGS + pifDeclaration = .OTHER_LDFLAGS values = settingAssignment.values.map { "-l\($0)" } case .HEADER_SEARCH_PATHS: - swbDeclaration = .HEADER_SEARCH_PATHS + pifDeclaration = .HEADER_SEARCH_PATHS values = settingAssignment.values.map { self.sourceDirAbsolutePath.pathString + "/" + $0 } default: - swbDeclaration = declaration + pifDeclaration = ProjectModel.BuildSettings.Declaration(from: declaration) values = settingAssignment.values } @@ -542,24 +543,28 @@ extension PackageGraph.ResolvedModule { let (platforms, configurations, _) = settingAssignment.conditions.splitIntoConcreteConditions for platform in platforms { - if swbDeclaration == .OTHER_LDFLAGS { - var settingsByDeclaration: [BuildSettings.Declaration: [String]] = allSettings - .impartedSettings[platform] ?? [:] - settingsByDeclaration[swbDeclaration, default: []].append(contentsOf: values) + let pifPlatform = platform.map { ProjectModel.BuildSettings.Platform(from: $0) } + + if pifDeclaration == .OTHER_LDFLAGS { + var settingsByDeclaration: [ProjectModel.BuildSettings.Declaration: [String]] + + settingsByDeclaration = allSettings.impartedSettings[pifPlatform] ?? [:] + settingsByDeclaration[pifDeclaration, default: []].append(contentsOf: values) - allSettings.impartedSettings[platform] = settingsByDeclaration + allSettings.impartedSettings[pifPlatform] = settingsByDeclaration } for configuration in configurations { - var settingsByDeclaration: [BuildSettings.Declaration: [String]] = allSettings - .targetSettings[configuration]?[platform] ?? [:] - if swbDeclaration.allowsMultipleValues { - settingsByDeclaration[swbDeclaration, default: []].append(contentsOf: values) + var settingsByDeclaration: [ProjectModel.BuildSettings.Declaration: [String]] + settingsByDeclaration = allSettings.targetSettings[configuration]?[pifPlatform] ?? [:] + + if declaration.allowsMultipleValues { + settingsByDeclaration[pifDeclaration, default: []].append(contentsOf: values) } else { - settingsByDeclaration[swbDeclaration] = values.only.flatMap { [$0] } ?? [] + settingsByDeclaration[pifDeclaration] = values.only.flatMap { [$0] } ?? [] } - allSettings.targetSettings[configuration, default: [:]][platform] = settingsByDeclaration + allSettings.targetSettings[configuration, default: [:]][pifPlatform] = settingsByDeclaration } } } @@ -793,110 +798,128 @@ extension TSCUtility.Version { // MARK: - Swift Build PIF Helpers -/// Helpers for building custom PIF targets by `PIFPackageBuilder` clients. -extension SwiftBuild.PIF.Project { +extension ProjectModel.BuildSettings { + subscript(_ setting: MultipleValueSetting, default defaultValue: [String]) -> [String] { + get { self[setting] ?? defaultValue } + set { self[setting] = newValue } + } +} + +/// Helpers for building custom PIF targets by `PackagePIFBuilder` clients. +extension ProjectModel.Project { @discardableResult - public func addTarget( + public mutating func addTarget( packageProductName: String, - productType: SwiftBuild.PIF.Target.ProductType - ) throws -> SwiftBuild.PIF.Target { - let pifTarget = try self.addTargetThrowing( - id: PIFPackageBuilder.targetGUID(forProductName: packageProductName), - productType: productType, - name: packageProductName, - productName: packageProductName - ) - return pifTarget + productType: ProjectModel.Target.ProductType + ) throws -> WritableKeyPath { + let targetKeyPath = try self.addTarget { _ in + ProjectModel.Target( + id: PackagePIFBuilder.targetGUID(forProductName: packageProductName), + productType: productType, + name: packageProductName, + productName: packageProductName + ) + } + return targetKeyPath } @discardableResult - public func addTarget( + public mutating func addTarget( packageModuleName: String, - productType: SwiftBuild.PIF.Target.ProductType - ) throws -> SwiftBuild.PIF.Target { - let pifTarget = try self.addTargetThrowing( - id: PIFPackageBuilder.targetGUID(forModuleName: packageModuleName), - productType: productType, - name: packageModuleName, - productName: packageModuleName - ) - return pifTarget + productType: ProjectModel.Target.ProductType + ) throws -> WritableKeyPath { + let targetKeyPath = try self.addTarget { _ in + ProjectModel.Target( + id: PackagePIFBuilder.targetGUID(forModuleName: packageModuleName), + productType: productType, + name: packageModuleName, + productName: packageModuleName + ) + } + return targetKeyPath } } -extension SwiftBuild.PIF.BuildSettings { +extension ProjectModel.BuildSettings { /// Internal helper function that appends list of string values to a declaration. /// If a platform is specified, then the values are appended to the `platformSpecificSettings`, /// otherwise they are appended to the platform-neutral settings. /// /// Note that this restricts the settings that can be set by this function to those that can have platform-specific - /// values, - /// i.e. those in `PIF.Declaration`. If a platform is specified, it must be one of the known platforms in - /// `PIF.Platform`. + /// values, i.e. those in `ProjectModel.BuildSettings.Declaration`. If a platform is specified, + /// it must be one of the known platforms in `ProjectModel.BuildSettings.Platform`. mutating func append(values: [String], to setting: Declaration, platform: Platform? = nil) { - // This dichotomy is quite unfortunate but that's currently the underlying model in `PIF.BuildSettings`. + // This dichotomy is quite unfortunate but that's currently the underlying model in ProjectModel.BuildSettings. if let platform { - // FIXME: The force unwraps here are pretty bad, - // but are the same as in the existing code before it was factored into this function. - // We should get rid of the force unwraps. And fix the PIF generation model. - // NOTE: Appending implies the setting is resilient to having ["$(inherited)"] switch setting { - case .FRAMEWORK_SEARCH_PATHS: - self.platformSpecificSettings[platform]![setting]!.append(contentsOf: values) - case .GCC_PREPROCESSOR_DEFINITIONS: - self.platformSpecificSettings[platform]![setting]!.append(contentsOf: values) - case .HEADER_SEARCH_PATHS: - self.platformSpecificSettings[platform]![setting]!.append(contentsOf: values) - case .OTHER_CFLAGS: - self.platformSpecificSettings[platform]![setting]!.append(contentsOf: values) - case .OTHER_CPLUSPLUSFLAGS: - self.platformSpecificSettings[platform]![setting]!.append(contentsOf: values) - case .OTHER_LDFLAGS: - self.platformSpecificSettings[platform]![setting]!.append(contentsOf: values) - case .OTHER_SWIFT_FLAGS: + case .FRAMEWORK_SEARCH_PATHS, + .GCC_PREPROCESSOR_DEFINITIONS, + .HEADER_SEARCH_PATHS, + .OTHER_CFLAGS, + .OTHER_CPLUSPLUSFLAGS, + .OTHER_LDFLAGS, + .OTHER_SWIFT_FLAGS, + .SWIFT_ACTIVE_COMPILATION_CONDITIONS: + // Appending implies the setting is resilient to having ["$(inherited)"] self.platformSpecificSettings[platform]![setting]!.append(contentsOf: values) + case .SWIFT_VERSION: - self.platformSpecificSettings[platform]![setting] = values // we are not resilient to $(inherited) - case .SWIFT_ACTIVE_COMPILATION_CONDITIONS: - self.platformSpecificSettings[platform]![setting]!.append(contentsOf: values) - default: - fatalError("Unsupported PIF.Declaration: \(setting)") + self.platformSpecificSettings[platform]![setting] = values // We are not resilient to $(inherited). + + case .ARCHS, .IPHONEOS_DEPLOYMENT_TARGET, .SPECIALIZATION_SDK_OPTIONS: + fatalError("Unexpected BuildSettings.Declaration: \(setting)") } } else { - // FIXME: This is pretty ugly. - // The whole point of this helper function is to hide this ugliness from the rest of the logic. - // We need to fix the PIF generation model. switch setting { - case .FRAMEWORK_SEARCH_PATHS: - self.FRAMEWORK_SEARCH_PATHS = (self.FRAMEWORK_SEARCH_PATHS ?? ["$(inherited)"]) + values - case .GCC_PREPROCESSOR_DEFINITIONS: - self.GCC_PREPROCESSOR_DEFINITIONS = (self.GCC_PREPROCESSOR_DEFINITIONS ?? ["$(inherited)"]) + values - case .HEADER_SEARCH_PATHS: - self.HEADER_SEARCH_PATHS = (self.HEADER_SEARCH_PATHS ?? ["$(inherited)"]) + values - case .OTHER_CFLAGS: - self.OTHER_CFLAGS = (self.OTHER_CFLAGS ?? ["$(inherited)"]) + values - case .OTHER_CPLUSPLUSFLAGS: - self.OTHER_CPLUSPLUSFLAGS = (self.OTHER_CPLUSPLUSFLAGS ?? ["$(inherited)"]) + values - case .OTHER_LDFLAGS: - self.OTHER_LDFLAGS = (self.OTHER_LDFLAGS ?? ["$(inherited)"]) + values - case .OTHER_SWIFT_FLAGS: - self.OTHER_SWIFT_FLAGS = (self.OTHER_SWIFT_FLAGS ?? ["$(inherited)"]) + values + case .FRAMEWORK_SEARCH_PATHS, + .GCC_PREPROCESSOR_DEFINITIONS, + .HEADER_SEARCH_PATHS, + .OTHER_CFLAGS, + .OTHER_CPLUSPLUSFLAGS, + .OTHER_LDFLAGS, + .OTHER_SWIFT_FLAGS, + .SWIFT_ACTIVE_COMPILATION_CONDITIONS: + let multipleSetting = MultipleValueSetting(from: setting)! + self[multipleSetting, default: ["$(inherited)"]].append(contentsOf: values) + case .SWIFT_VERSION: - self.SWIFT_VERSION = values.only.unwrap(orAssert: "Invalid values for 'SWIFT_VERSION': \(values)") - case .SWIFT_ACTIVE_COMPILATION_CONDITIONS: - self - .SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( - self - .SWIFT_ACTIVE_COMPILATION_CONDITIONS ?? ["$(inherited)"] - ) + values - default: - fatalError("Unsupported PIF.Declaration: \(setting)") + self[.SWIFT_VERSION] = values.only.unwrap(orAssert: "Invalid values for 'SWIFT_VERSION': \(values)") + + case .ARCHS, .IPHONEOS_DEPLOYMENT_TARGET, .SPECIALIZATION_SDK_OPTIONS: + fatalError("Unexpected BuildSettings.Declaration: \(setting)") } } } } -extension SwiftBuild.PIF.BuildSettings.Platform { +extension ProjectModel.BuildSettings.MultipleValueSetting { + init?(from declaration: ProjectModel.BuildSettings.Declaration) { + switch declaration { + case .GCC_PREPROCESSOR_DEFINITIONS: + self = .GCC_PREPROCESSOR_DEFINITIONS + case .FRAMEWORK_SEARCH_PATHS: + self = .FRAMEWORK_SEARCH_PATHS + case .HEADER_SEARCH_PATHS: + self = .HEADER_SEARCH_PATHS + case .OTHER_CFLAGS: + self = .OTHER_CFLAGS + case .OTHER_CPLUSPLUSFLAGS: + self = .OTHER_CPLUSPLUSFLAGS + case .OTHER_LDFLAGS: + self = .OTHER_LDFLAGS + case .OTHER_SWIFT_FLAGS: + self = .OTHER_SWIFT_FLAGS + case .SPECIALIZATION_SDK_OPTIONS: + self = .SPECIALIZATION_SDK_OPTIONS + case .SWIFT_ACTIVE_COMPILATION_CONDITIONS: + self = .SWIFT_ACTIVE_COMPILATION_CONDITIONS + case .ARCHS, .IPHONEOS_DEPLOYMENT_TARGET, .SWIFT_VERSION: + return nil + } + } +} + +extension ProjectModel.BuildSettings.Platform { init(from platform: PackageModel.Platform) { self = switch platform { case .macOS: .macOS @@ -916,7 +939,7 @@ extension SwiftBuild.PIF.BuildSettings.Platform { } } -extension SwiftBuild.PIF.BuildSettings { +extension ProjectModel.BuildSettings { /// Configure necessary settings for a dynamic library/framework. mutating func configureDynamicSettings( productName: String, @@ -926,41 +949,41 @@ extension SwiftBuild.PIF.BuildSettings { packageName: String?, createDylibForDynamicProducts: Bool, installPath: String, - delegate: PIFPackageBuilder.BuildDelegate + delegate: PackagePIFBuilder.BuildDelegate ) { - self.TARGET_NAME = targetName - self.PRODUCT_NAME = createDylibForDynamicProducts ? productName : executableName - self.PRODUCT_MODULE_NAME = productName - self.PRODUCT_BUNDLE_IDENTIFIER = "\(packageIdentity).\(productName)".spm_mangledToBundleIdentifier() - self.EXECUTABLE_NAME = executableName - self.CLANG_ENABLE_MODULES = "YES" - self.SWIFT_PACKAGE_NAME = packageName ?? nil + self[.TARGET_NAME] = targetName + self[.PRODUCT_NAME] = createDylibForDynamicProducts ? productName : executableName + self[.PRODUCT_MODULE_NAME] = productName + self[.PRODUCT_BUNDLE_IDENTIFIER] = "\(packageIdentity).\(productName)".spm_mangledToBundleIdentifier() + self[.EXECUTABLE_NAME] = executableName + self[.CLANG_ENABLE_MODULES] = "YES" + self[.SWIFT_PACKAGE_NAME] = packageName ?? nil if !createDylibForDynamicProducts { - self.GENERATE_INFOPLIST_FILE = "YES" + self[.GENERATE_INFOPLIST_FILE] = "YES" // If the built framework is named same as one of the target in the package, // it can be picked up automatically during indexing since the build system always adds a -F flag // to the built products dir. // To avoid this problem, we build all package frameworks in a subdirectory. - self.TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/PackageFrameworks" + self[.TARGET_BUILD_DIR] = "$(TARGET_BUILD_DIR)/PackageFrameworks" // Set the project and marketing version for the framework because the app store requires these to be // present. // The AppStore requires bumping the project version when ingesting new builds but that's for top-level apps // and not frameworks embedded inside it. - self.MARKETING_VERSION = "1.0" // Version - self.CURRENT_PROJECT_VERSION = "1" // Build + self[.MARKETING_VERSION] = "1.0" // Version + self[.CURRENT_PROJECT_VERSION] = "1" // Build } // Might set install path depending on build delegate. if delegate.shouldSetInstallPathForDynamicLib(productName: productName) { - self.SKIP_INSTALL = "NO" - self.INSTALL_PATH = installPath + self[.SKIP_INSTALL] = "NO" + self[.INSTALL_PATH] = installPath } } } -extension SwiftBuild.PIF.BuildSettings.Declaration { +extension ProjectModel.BuildSettings.Declaration { init(from declaration: PackageModel.BuildSettings.Declaration) { self = switch declaration { // Swift. diff --git a/Sources/SwiftBuildSupport/PackagePIFBuilder+Plugins.swift b/Sources/SwiftBuildSupport/PackagePIFBuilder+Plugins.swift index 693f15a5a21..3934f30984e 100644 --- a/Sources/SwiftBuildSupport/PackagePIFBuilder+Plugins.swift +++ b/Sources/SwiftBuildSupport/PackagePIFBuilder+Plugins.swift @@ -18,9 +18,10 @@ import enum Basics.Sandbox import struct Basics.SourceControlURL #if canImport(SwiftBuild) -import enum SwiftBuild.PIF -extension PIFPackageBuilder { +import enum SwiftBuild.ProjectModel + +extension PackagePIFBuilder { /// Contains all of the information resulting from applying a build tool plugin to a package target thats affect how /// a target is built. /// diff --git a/Sources/SwiftBuildSupport/PackagePIFBuilder.swift b/Sources/SwiftBuildSupport/PackagePIFBuilder.swift index 5549ab693a6..c2582c5c8bd 100644 --- a/Sources/SwiftBuildSupport/PackagePIFBuilder.swift +++ b/Sources/SwiftBuildSupport/PackagePIFBuilder.swift @@ -31,10 +31,17 @@ import struct PackageGraph.ResolvedModule import struct PackageGraph.ResolvedPackage #if canImport(SwiftBuild) -import enum SwiftBuild.PIF + +import enum SwiftBuild.ProjectModel + +typealias GUID = SwiftBuild.ProjectModel.GUID +typealias BuildFile = SwiftBuild.ProjectModel.BuildFile +typealias BuildConfig = SwiftBuild.ProjectModel.BuildConfig +typealias BuildSettings = SwiftBuild.ProjectModel.BuildSettings +typealias FileReference = SwiftBuild.ProjectModel.FileReference /// A builder for generating the PIF object from a package. -public final class PIFPackageBuilder { +public final class PackagePIFBuilder { let modulesGraph: ModulesGraph private let package: ResolvedPackage @@ -42,12 +49,12 @@ public final class PIFPackageBuilder { let packageManifest: PackageModel.Manifest // FIXME: Can't we just use `package.manifest` instead? —— Paulo /// The built PIF project object. - public var pifProject: SwiftBuild.PIF.Project { + public var pifProject: ProjectModel.Project { assert(self._pifProject != nil, "Call build() method to build the PIF first") return self._pifProject! } - private var _pifProject: SwiftBuild.PIF.Project? + private var _pifProject: ProjectModel.Project? /// Scope for logging informational debug messages (intended for developers, not end users). let observabilityScope: ObservabilityScope @@ -86,7 +93,7 @@ public final class PIFPackageBuilder { /// For executables — only executables for now — we check to see if there is a custom package product type /// provider that can provide this information. - func customProductType(forExecutable product: PackageModel.Product) -> SwiftBuild.PIF.Target.ProductType? + func customProductType(forExecutable product: PackageModel.Product) -> ProjectModel.Target.ProductType? /// Returns all *device family* IDs for all SDK variants. func deviceFamilyIDs() -> Set @@ -98,12 +105,12 @@ public final class PIFPackageBuilder { var isPluginExecutionSandboxingDisabled: Bool { get } /// Hook to customize the project-wide build settings. - func configureProjectBuildSettings(_ buildSettings: inout SwiftBuild.PIF.BuildSettings) + func configureProjectBuildSettings(_ buildSettings: inout ProjectModel.BuildSettings) /// Hook to customize source module build settings. func configureSourceModuleBuildSettings( sourceModule: PackageGraph.ResolvedModule, - settings: inout SwiftBuild.PIF.BuildSettings + settings: inout ProjectModel.BuildSettings ) /// Custom install path for the specified product, if any. @@ -119,23 +126,25 @@ public final class PIFPackageBuilder { func customSDKOptions(forPlatform: PackageModel.Platform) -> [String] /// Create additional custom PIF targets after all targets have been built. - func addCustomTargets(pifProject: SwiftBuild.PIF.Project) throws -> [PIFPackageBuilder.ModuleOrProduct] + func addCustomTargets(pifProject: ProjectModel.Project) throws -> [PackagePIFBuilder.ModuleOrProduct] /// Should we suppresses the specific product dependency, updating the provided build settings if necessary? /// The specified product may be in the same package or a different one. func shouldSuppressProductDependency( product: PackageModel.Product, - buildSettings: inout SwiftBuild.PIF.BuildSettings + buildSettings: inout ProjectModel.BuildSettings ) -> Bool /// Should we set the install path for a dynamic library/framework? func shouldSetInstallPathForDynamicLib(productName: String) -> Bool + // FIXME: Let's try to replace `WritableKeyPath><_, Foo>` with `inout Foo` —— Paulo + /// Provides additional configuration and files for the specified library product. func configureLibraryProduct( product: PackageModel.Product, - pifTarget: SwiftBuild.PIF.Target, - additionalFiles: SwiftBuild.PIF.Group + target: WritableKeyPath, + additionalFiles: WritableKeyPath ) /// The design intention behind this is to set a value for `watchOS`, `tvOS`, and `visionOS` @@ -153,7 +162,7 @@ public final class PIFPackageBuilder { } /// Records the results of applying build tool plugins to modules in the package. - let buildToolPluginResultsByTargetName: [String: PIFPackageBuilder.BuildToolPluginInvocationResult] + let buildToolPluginResultsByTargetName: [String: PackagePIFBuilder.BuildToolPluginInvocationResult] /// Whether to create dynamic libraries for dynamic products. /// @@ -182,7 +191,7 @@ public final class PIFPackageBuilder { modulesGraph: ModulesGraph, resolvedPackage: ResolvedPackage, packageManifest: PackageModel.Manifest, - delegate: PIFPackageBuilder.BuildDelegate, + delegate: PackagePIFBuilder.BuildDelegate, buildToolPluginResultsByTargetName: [String: BuildToolPluginInvocationResult], createDylibForDynamicProducts: Bool = false, packageDisplayVersion: String?, @@ -200,12 +209,12 @@ public final class PIFPackageBuilder { /// Build an empty PIF project. public func buildEmptyPIF() { - self._pifProject = PIFPackageBuilder.buildEmptyPIF(package: self.package.underlying) + self._pifProject = PackagePIFBuilder.buildEmptyPIF(package: self.package.underlying) } /// Build an empty PIF project for the specified `Package`. - public class func buildEmptyPIF(package: PackageModel.Package) -> SwiftBuild.PIF.Project { + public class func buildEmptyPIF(package: PackageModel.Package) -> ProjectModel.Project { self.buildEmptyPIF( id: "PACKAGE:\(package.identity)", path: package.manifest.path.pathString, @@ -222,37 +231,46 @@ public final class PIFPackageBuilder { projectDir: String, name: String, developmentRegion: String? = nil - ) -> SwiftBuild.PIF.Project { - let project = SwiftBuild.PIF.Project( - id: id, + ) -> ProjectModel.Project { + var project = ProjectModel.Project( + id: GUID(id), path: path, projectDir: projectDir, name: name, developmentRegion: developmentRegion ) - let settings = SwiftBuild.PIF.BuildSettings() + let settings = ProjectModel.BuildSettings() - project.addBuildConfig(name: "Debug", settings: settings) - project.addBuildConfig(name: "Release", settings: settings) + project.addBuildConfig { id in ProjectModel.BuildConfig(id: id, name: "Debug", settings: settings) } + project.addBuildConfig { id in ProjectModel.BuildConfig(id: id, name: "Release", settings: settings) } return project } public func buildPlaceholderPIF(id: String, path: String, projectDir: String, name: String) -> ModuleOrProduct { - let project = SwiftBuild.PIF.Project( - id: id, + var project = ProjectModel.Project( + id: GUID(id), path: path, projectDir: projectDir, name: name ) - let projectSettings = SwiftBuild.PIF.BuildSettings() - project.addBuildConfig(name: "Debug", settings: projectSettings) - project.addBuildConfig(name: "Release", settings: projectSettings) - let target = project.addAggregateTarget(id: "PACKAGE-PLACEHOLDER:\(id)", name: id) - let targetSettings: SwiftBuild.PIF.BuildSettings = self.package.underlying.packageBaseBuildSettings - target.addBuildConfig(name: "Debug", settings: targetSettings) - target.addBuildConfig(name: "Release", settings: targetSettings) + let projectSettings = ProjectModel.BuildSettings() + + project.addBuildConfig { id in ProjectModel.BuildConfig(id: id, name: "Debug", settings: projectSettings) } + project.addBuildConfig { id in ProjectModel.BuildConfig(id: id, name: "Release", settings: projectSettings) } + + let targetKeyPath = try! project.addAggregateTarget { _ in + ProjectModel.AggregateTarget(id: "PACKAGE-PLACEHOLDER:\(id)", name: id) + } + let targetSettings: ProjectModel.BuildSettings = self.package.underlying.packageBaseBuildSettings + + project[keyPath: targetKeyPath].common.addBuildConfig { id in + ProjectModel.BuildConfig(id: id, name: "Debug", settings: targetSettings) + } + project[keyPath: targetKeyPath].common.addBuildConfig { id in + ProjectModel.BuildConfig(id: id, name: "Release", settings: targetSettings) + } self._pifProject = project @@ -260,7 +278,7 @@ public final class PIFPackageBuilder { type: .placeholder, name: name, moduleName: name, - pifTarget: target, + pifTarget: .aggregate(project[keyPath: targetKeyPath]), indexableFileURLs: [], headerFiles: [], linkedPackageBinaries: [], @@ -281,7 +299,7 @@ public final class PIFPackageBuilder { public var moduleName: String? public var isDynamicLibraryVariant: Bool = false - public var pifTarget: SwiftBuild.PIF.BaseTarget? + public var pifTarget: ProjectModel.BaseTarget? public var indexableFileURLs: [SourceControlURL] public var headerFiles: Set @@ -334,7 +352,7 @@ public final class PIFPackageBuilder { public var description: String { rawValue } - init(from pifProductType: SwiftBuild.PIF.Target.ProductType) { + init(from pifProductType: ProjectModel.Target.ProductType) { self = switch pifProductType { case .application: .application case .staticArchive: .staticArchive @@ -355,12 +373,12 @@ public final class PIFPackageBuilder { /// Build the PIF. @discardableResult public func build() throws -> [ModuleOrProduct] { - self.log(.info, "building PIF for package \(self.package.identity)") + self.log(.info, "Building PIF for package \(self.package.identity)") - var project = PackagePIFProjectBuilder(createForPackage: package, builder: self) - self.addProjectBuildSettings(project: project) + var builder = PackagePIFProjectBuilder(createForPackage: package, builder: self) + self.addProjectBuildSettings(&builder) - self._pifProject = project.pif + self._pifProject = builder.project // // Construct PIF *targets* (for modules, products, and test bundles) based on the contents of the parsed @@ -389,27 +407,27 @@ public final class PIFPackageBuilder { switch product.type { case .library(.static): let libraryType = self.delegate.customLibraryType(product: product.underlying) ?? .static - try project.makeLibraryProduct(product, type: libraryType) + try builder.makeLibraryProduct(product, type: libraryType) case .library(.dynamic): let libraryType = self.delegate.customLibraryType(product: product.underlying) ?? .dynamic - try project.makeLibraryProduct(product, type: libraryType) + try builder.makeLibraryProduct(product, type: libraryType) case .library(.automatic): // Check if this is a system library product. if product.isSystemLibraryProduct { - try project.makeSystemLibraryProduct(product) + try builder.makeSystemLibraryProduct(product) } else { // Otherwise, it is a regular library product. let libraryType = self.delegate.customLibraryType(product: product.underlying) ?? .automatic - try project.makeLibraryProduct(product, type: libraryType) + try builder.makeLibraryProduct(product, type: libraryType) } case .executable, .test: - try project.makeMainModuleProduct(product) + try builder.makeMainModuleProduct(product) case .plugin: - try project.makePluginProduct(product) + try builder.makePluginProduct(product) case .snippet, .macro: break // TODO: Double-check what's going on here as we skip snippet modules too (rdar://147705448) @@ -421,17 +439,17 @@ public final class PIFPackageBuilder { for module in self.package.modules { switch module.type { case .executable: - try project.makeTestableExecutableSourceModule(module) + try builder.makeTestableExecutableSourceModule(module) case .snippet: // Already handled as a product. Note that snippets don't need testable modules. break case .library: - try project.makeLibraryModule(module) + try builder.makeLibraryModule(module) case .systemModule: - try project.makeSystemLibraryModule(module) + try builder.makeSystemLibraryModule(module) case .test: // Skip test module targets. @@ -443,89 +461,89 @@ public final class PIFPackageBuilder { break case .plugin: - try project.makePluginModule(module) + try builder.makePluginModule(module) case .macro: - try project.makeMacroModule(module) + try builder.makeMacroModule(module) } } - let customModulesAndProducts = try delegate.addCustomTargets(pifProject: project.pif) - project.builtModulesAndProducts.append(contentsOf: customModulesAndProducts) + let customModulesAndProducts = try delegate.addCustomTargets(pifProject: builder.project) + builder.builtModulesAndProducts.append(contentsOf: customModulesAndProducts) - return project.builtModulesAndProducts + return builder.builtModulesAndProducts } /// Configure the project-wide build settings. /// First we set those that are in common between the "Debug" and "Release" configurations, and then we set those /// that are different. - private func addProjectBuildSettings(project: PackagePIFProjectBuilder) { - var settings = SwiftBuild.PIF.BuildSettings() - settings.PRODUCT_NAME = "$(TARGET_NAME)" - settings.SUPPORTED_PLATFORMS = ["$(AVAILABLE_PLATFORMS)"] - settings.SKIP_INSTALL = "YES" - settings.MACOSX_DEPLOYMENT_TARGET = project.deploymentTargets[.macOS] ?? nil - settings.IPHONEOS_DEPLOYMENT_TARGET = project.deploymentTargets[.iOS] ?? nil - if let deploymentTarget_macCatalyst = project.deploymentTargets[.macCatalyst] ?? nil { + private func addProjectBuildSettings(_ builder: inout PackagePIFProjectBuilder) { + var settings = ProjectModel.BuildSettings() + settings[.PRODUCT_NAME] = "$(TARGET_NAME)" + settings[.SUPPORTED_PLATFORMS] = ["$(AVAILABLE_PLATFORMS)"] + settings[.SKIP_INSTALL] = "YES" + settings[.MACOSX_DEPLOYMENT_TARGET] = builder.deploymentTargets[.macOS] ?? nil + settings[.IPHONEOS_DEPLOYMENT_TARGET] = builder.deploymentTargets[.iOS] ?? nil + if let deploymentTarget_macCatalyst = builder.deploymentTargets[.macCatalyst] ?? nil { settings .platformSpecificSettings[.macCatalyst]![.IPHONEOS_DEPLOYMENT_TARGET] = [deploymentTarget_macCatalyst] } - settings.TVOS_DEPLOYMENT_TARGET = project.deploymentTargets[.tvOS] ?? nil - settings.WATCHOS_DEPLOYMENT_TARGET = project.deploymentTargets[.watchOS] ?? nil - settings.DRIVERKIT_DEPLOYMENT_TARGET = project.deploymentTargets[.driverKit] ?? nil - settings.XROS_DEPLOYMENT_TARGET = project.deploymentTargets[.visionOS] ?? nil - settings.DYLIB_INSTALL_NAME_BASE = "@rpath" - settings.USE_HEADERMAP = "NO" - settings.OTHER_SWIFT_FLAGS.lazilyInitializeAndMutate(initialValue: ["$(inherited)"]) { $0.append("-DXcode") } + settings[.TVOS_DEPLOYMENT_TARGET] = builder.deploymentTargets[.tvOS] ?? nil + settings[.WATCHOS_DEPLOYMENT_TARGET] = builder.deploymentTargets[.watchOS] ?? nil + settings[.DRIVERKIT_DEPLOYMENT_TARGET] = builder.deploymentTargets[.driverKit] ?? nil + settings[.XROS_DEPLOYMENT_TARGET] = builder.deploymentTargets[.visionOS] ?? nil + settings[.DYLIB_INSTALL_NAME_BASE] = "@rpath" + settings[.USE_HEADERMAP] = "NO" + settings[.OTHER_SWIFT_FLAGS].lazilyInitializeAndMutate(initialValue: ["$(inherited)"]) { $0.append("-DXcode") } // TODO: Might be relevant to make customizable —— Paulo // (If we want to be extra careful with differences to the existing PIF in the SwiftPM.) - settings.OTHER_CFLAGS = ["$(inherited)", "-DXcode"] + settings[.OTHER_CFLAGS] = ["$(inherited)", "-DXcode"] if !self.delegate.isRootPackage { if self.suppressWarningsForPackageDependencies { - settings.SUPPRESS_WARNINGS = "YES" + settings[.SUPPRESS_WARNINGS] = "YES" } if self.skipStaticAnalyzerForPackageDependencies { - settings.SKIP_CLANG_STATIC_ANALYZER = "YES" + settings[.SKIP_CLANG_STATIC_ANALYZER] = "YES" } } - settings.SWIFT_ACTIVE_COMPILATION_CONDITIONS + settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS] .lazilyInitializeAndMutate(initialValue: ["$(inherited)"]) { $0.append("SWIFT_PACKAGE") } - settings.GCC_PREPROCESSOR_DEFINITIONS = ["$(inherited)", "SWIFT_PACKAGE"] - settings.CLANG_ENABLE_OBJC_ARC = "YES" - settings.KEEP_PRIVATE_EXTERNS = "NO" + settings[.GCC_PREPROCESSOR_DEFINITIONS] = ["$(inherited)", "SWIFT_PACKAGE"] + settings[.CLANG_ENABLE_OBJC_ARC] = "YES" + settings[.KEEP_PRIVATE_EXTERNS] = "NO" // We currently deliberately do not support Swift ObjC interface headers. - settings.SWIFT_INSTALL_OBJC_HEADER = "NO" - settings.SWIFT_OBJC_INTERFACE_HEADER_NAME = "" - settings.OTHER_LDRFLAGS = [] + settings[.SWIFT_INSTALL_OBJC_HEADER] = "NO" + settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME] = "" + settings[.OTHER_LDRFLAGS] = [] // Packages use the SwiftPM workspace's cache directory as a compiler working directory to maximize module // sharing. - settings.COMPILER_WORKING_DIRECTORY = "$(WORKSPACE_DIR)" + settings[.COMPILER_WORKING_DIRECTORY] = "$(WORKSPACE_DIR)" // Hook to customize the project-wide build settings. self.delegate.configureProjectBuildSettings(&settings) for (platform, platformOptions) in self.package.sdkOptions(delegate: self.delegate) { - let pifPlatform = SwiftBuild.PIF.BuildSettings.Platform(from: platform) + let pifPlatform = ProjectModel.BuildSettings.Platform(from: platform) settings.platformSpecificSettings[pifPlatform]![.SPECIALIZATION_SDK_OPTIONS]! .append(contentsOf: platformOptions) } let deviceFamilyIDs: Set = self.delegate.deviceFamilyIDs() - settings.TARGETED_DEVICE_FAMILY = deviceFamilyIDs.sorted().map { String($0) }.joined(separator: ",") + settings[.TARGETED_DEVICE_FAMILY] = deviceFamilyIDs.sorted().map { String($0) }.joined(separator: ",") // This will add the XCTest related search paths automatically, // including the Swift overlays. - settings.ENABLE_TESTING_SEARCH_PATHS = "YES" + settings[.ENABLE_TESTING_SEARCH_PATHS] = "YES" // Disable signing for all the things since there is no way // to configure signing information in packages right now. - settings.ENTITLEMENTS_REQUIRED = "NO" - settings.CODE_SIGNING_REQUIRED = "NO" - settings.CODE_SIGN_IDENTITY = "" + settings[.ENTITLEMENTS_REQUIRED] = "NO" + settings[.CODE_SIGNING_REQUIRED] = "NO" + settings[.CODE_SIGN_IDENTITY] = "" // If in a workspace that's set to build packages for arm64e, pass that along to Swift Build. if self.delegate.shouldiOSPackagesBuildForARM64e { @@ -534,26 +552,24 @@ public final class PIFPackageBuilder { // Add the build settings that are specific to debug builds, and set those as the "Debug" configuration. var debugSettings = settings - debugSettings.COPY_PHASE_STRIP = "NO" - debugSettings.DEBUG_INFORMATION_FORMAT = "dwarf" - debugSettings.ENABLE_NS_ASSERTIONS = "YES" - debugSettings.GCC_OPTIMIZATION_LEVEL = "0" - debugSettings.ONLY_ACTIVE_ARCH = "YES" - debugSettings.SWIFT_OPTIMIZATION_LEVEL = "-Onone" - debugSettings.ENABLE_TESTABILITY = "YES" - debugSettings - .SWIFT_ACTIVE_COMPILATION_CONDITIONS = (settings.SWIFT_ACTIVE_COMPILATION_CONDITIONS ?? []) + ["DEBUG"] - debugSettings - .GCC_PREPROCESSOR_DEFINITIONS = (settings.GCC_PREPROCESSOR_DEFINITIONS ?? ["$(inherited)"]) + ["DEBUG=1"] - project.pif.addBuildConfig(name: "Debug", settings: debugSettings) + debugSettings[.COPY_PHASE_STRIP] = "NO" + debugSettings[.DEBUG_INFORMATION_FORMAT] = "dwarf" + debugSettings[.ENABLE_NS_ASSERTIONS] = "YES" + debugSettings[.GCC_OPTIMIZATION_LEVEL] = "0" + debugSettings[.ONLY_ACTIVE_ARCH] = "YES" + debugSettings[.SWIFT_OPTIMIZATION_LEVEL] = "-Onone" + debugSettings[.ENABLE_TESTABILITY] = "YES" + debugSettings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS, default: []].append(contentsOf: ["DEBUG"]) + debugSettings[.GCC_PREPROCESSOR_DEFINITIONS, default: ["$(inherited)"]].append(contentsOf: ["DEBUG=1"]) + builder.project.addBuildConfig { id in BuildConfig(id: id, name: "Debug", settings: debugSettings) } // Add the build settings that are specific to release builds, and set those as the "Release" configuration. var releaseSettings = settings - releaseSettings.COPY_PHASE_STRIP = "YES" - releaseSettings.DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym" - releaseSettings.GCC_OPTIMIZATION_LEVEL = "s" - releaseSettings.SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule" - project.pif.addBuildConfig(name: "Release", settings: releaseSettings) + releaseSettings[.COPY_PHASE_STRIP] = "YES" + releaseSettings[.DEBUG_INFORMATION_FORMAT] = "dwarf-with-dsym" + releaseSettings[.GCC_OPTIMIZATION_LEVEL] = "s" + releaseSettings[.SWIFT_OPTIMIZATION_LEVEL] = "-Owholemodule" + builder.project.addBuildConfig { id in BuildConfig(id: id, name: "Release", settings: releaseSettings) } } private enum SourceModuleType { @@ -587,15 +603,15 @@ public final class PIFPackageBuilder { // MARK: - Helpers -extension PIFPackageBuilder.ModuleOrProduct { +extension PackagePIFBuilder.ModuleOrProduct { public init( - type moduleOrProductType: PIFPackageBuilder.ModuleOrProductType, + type moduleOrProductType: PackagePIFBuilder.ModuleOrProductType, name: String, moduleName: String?, - pifTarget: SwiftBuild.PIF.BaseTarget?, + pifTarget: ProjectModel.BaseTarget?, indexableFileURLs: [SourceControlURL] = [], headerFiles: Set = [], - linkedPackageBinaries: [PIFPackageBuilder.LinkedPackageBinary] = [], + linkedPackageBinaries: [PackagePIFBuilder.LinkedPackageBinary] = [], swiftLanguageVersion: String? = nil, declaredPlatforms: [PackageModel.Platform]? = [], deploymentTargets: [PackageModel.Platform: String?]? = [:] @@ -617,7 +633,7 @@ enum PIFBuildingError: Error { case packageExtensionFeatureNotEnabled } -extension PIFPackageBuilder.LinkedPackageBinary { +extension PackagePIFBuilder.LinkedPackageBinary { init?(module: ResolvedModule, package: ResolvedPackage) { let packageName = package.manifest.displayName diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift index cfdda27048c..89d7d1f7b16 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift @@ -26,7 +26,8 @@ import struct PackageGraph.ResolvedModule import struct PackageGraph.ResolvedPackage #if canImport(SwiftBuild) -import enum SwiftBuild.PIF + +import enum SwiftBuild.ProjectModel /// Extension to create PIF **modules** for a given package. extension PackagePIFProjectBuilder { @@ -36,15 +37,20 @@ extension PackagePIFProjectBuilder { precondition(pluginModule.type == .plugin) // Create an executable PIF target in order to get specialization. - let pluginPifTarget = try self.pif.addTargetThrowing( - id: pluginModule.pifTargetGUID(), - productType: .executable, - name: pluginModule.name, - productName: pluginModule.name - ) - log(.debug, "created \(type(of: pluginPifTarget)) '\(pluginPifTarget.id)' with name '\(pluginPifTarget.name)'") + let pluginTargetKeyPath = try self.project.addTarget { _ in + ProjectModel.Target( + id: pluginModule.pifTargetGUID(), + productType: .executable, + name: pluginModule.name, + productName: pluginModule.name + ) + } + do { + let pluginTarget = self.project[keyPath: pluginTargetKeyPath] + log(.debug, "Created \(pluginTarget.productType) '\(pluginTarget.id)' with name '\(pluginTarget.name)'") + } - var buildSettings: SwiftBuild.PIF.BuildSettings = self.package.underlying.packageBaseBuildSettings + var buildSettings: ProjectModel.BuildSettings = self.package.underlying.packageBaseBuildSettings // Add the dependencies. pluginModule.recursivelyTraverseDependencies { dependency in @@ -53,7 +59,7 @@ extension PackagePIFProjectBuilder { // This assertion is temporarily disabled since we may see targets from // _other_ packages, but this should be resolved; see rdar://95467710. /* assert(moduleDependency.packageName == self.package.name) */ - + let dependencyPlatformFilters = packageConditions .toPlatformFilter(toolsVersion: self.package.manifest.toolsVersion) @@ -66,25 +72,26 @@ extension PackagePIFProjectBuilder { .productRepresentingDependencyOfBuildPlugin(in: moduleProducts) if let productDependency { - pluginPifTarget.addDependency( + self.project[keyPath: pluginTargetKeyPath].common.addDependency( on: productDependency.pifTargetGUID(), platformFilters: dependencyPlatformFilters ) - log(.debug, ".. added dependency on product '\(productDependency.pifTargetGUID())'") + log(.debug, indent: 1, "Added dependency on product '\(productDependency.pifTargetGUID())'") } else { log( .debug, - ".. could not find a build plugin product to depend on for target '\(moduleDependency.pifTargetGUID())'" + indent: 1, + "Could not find a build plugin product to depend on for target '\(moduleDependency.pifTargetGUID())'" ) } case .library, .systemModule, .test, .binary, .plugin, .macro: let dependencyGUID = moduleDependency.pifTargetGUID() - pluginPifTarget.addDependency( + self.project[keyPath: pluginTargetKeyPath].common.addDependency( on: dependencyGUID, platformFilters: dependencyPlatformFilters ) - log(.debug, ".. added dependency on target '\(dependencyGUID)'") + log(.debug, indent: 1, "Added dependency on target '\(dependencyGUID)'") } case .product(let productDependency, let packageConditions): @@ -101,26 +108,30 @@ extension PackagePIFProjectBuilder { let dependencyPlatformFilters = packageConditions .toPlatformFilter(toolsVersion: self.package.manifest.toolsVersion) - pluginPifTarget.addDependency( + self.project[keyPath: pluginTargetKeyPath].common.addDependency( on: dependencyGUID, platformFilters: dependencyPlatformFilters ) - log(.debug, ".. added dependency on product '\(dependencyGUID)'") + log(.debug, indent: 1, "Added dependency on product '\(dependencyGUID)'") } } } // Any dependencies of plugin targets need to be built for the host. - buildSettings.SUPPORTED_PLATFORMS = ["$(HOST_PLATFORM)"] + buildSettings[.SUPPORTED_PLATFORMS] = ["$(HOST_PLATFORM)"] - pluginPifTarget.addBuildConfig(name: "Debug", settings: buildSettings) - pluginPifTarget.addBuildConfig(name: "Release", settings: buildSettings) + self.project[keyPath: pluginTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Debug", settings: buildSettings) + } + self.project[keyPath: pluginTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Release", settings: buildSettings) + } - let pluginModuleMetadata = PIFPackageBuilder.ModuleOrProduct( + let pluginModuleMetadata = PackagePIFBuilder.ModuleOrProduct( type: .plugin, name: pluginModule.name, moduleName: pluginModule.name, - pifTarget: pluginPifTarget, + pifTarget: .target(self.project[keyPath: pluginTargetKeyPath]), indexableFileURLs: [], headerFiles: [], linkedPackageBinaries: [], @@ -168,13 +179,13 @@ extension PackagePIFProjectBuilder { dynamicLibraryVariant.isDynamicLibraryVariant = true self.builtModulesAndProducts.append(dynamicLibraryVariant) - let pifTarget = staticLibrary.pifTarget as? SwiftBuild.PIF.Target - let dynamicPifTarget = dynamicLibraryVariant.pifTarget as? SwiftBuild.PIF.Target - - guard let pifTarget, let dynamicPifTarget else { + guard let pifTarget = staticLibrary.pifTarget, + let pifTargetKeyPath = self.project.findTarget(id: pifTarget.id), + let dynamicPifTarget = dynamicLibraryVariant.pifTarget + else { fatalError("Could not assign dynamic PIF target") } - pifTarget.dynamicTargetVariant = dynamicPifTarget + self.project[keyPath: pifTargetKeyPath].dynamicTargetVariantId = dynamicPifTarget.id } } @@ -220,12 +231,12 @@ extension PackagePIFProjectBuilder { targetSuffix: TargetGUIDSuffix? = nil, addBuildToolPluginCommands: Bool = true, inputResourceBundleName: String? = nil - ) throws -> (PIFPackageBuilder.ModuleOrProduct, resourceBundleName: String?) { + ) throws -> (PackagePIFBuilder.ModuleOrProduct, resourceBundleName: String?) { precondition(sourceModule.isSourceModule) let pifTargetName: String let executableName: String - let productType: SwiftBuild.PIF.Target.ProductType + let productType: ProjectModel.Target.ProductType switch desiredModuleType { case .dynamicLibrary: @@ -261,22 +272,28 @@ extension PackagePIFProjectBuilder { true } - let sourceModulePifTarget = try self.pif.addTargetThrowing( - id: sourceModule.pifTargetGUID(suffix: targetSuffix), - productType: productType, - name: sourceModule.name, - productName: pifTargetName, - approvedByUser: approvedByUser - ) - log( - .debug, - "created \(type(of: sourceModulePifTarget)) '\(sourceModulePifTarget.id)' of type '\(sourceModulePifTarget.productType.asString)' with name '\(sourceModulePifTarget.name)' and product name '\(sourceModulePifTarget.productName)'" - ) + let sourceModuleTargetKeyPath = try self.project.addTarget { _ in + ProjectModel.Target( + id: sourceModule.pifTargetGUID(suffix: targetSuffix), + productType: productType, + name: sourceModule.name, + productName: pifTargetName, + approvedByUser: approvedByUser + ) + } + do { + let sourceModuleTarget = self.project[keyPath: sourceModuleTargetKeyPath] + log( + .debug, + "Created \(sourceModuleTarget.productType) '\(sourceModuleTarget.id)' " + + "with name '\(sourceModuleTarget.name)' and product name '\(sourceModuleTarget.productName)'" + ) + } // Deal with any generated source files or resource files. let (generatedSourceFiles, generatedResourceFiles) = computePluginGeneratedFiles( module: sourceModule, - pifTarget: sourceModulePifTarget, + targetKeyPath: sourceModuleTargetKeyPath, addBuildToolPluginCommands: false ) @@ -287,7 +304,7 @@ extension PackagePIFProjectBuilder { if resourceBundleName == nil && desiredModuleType != .executable && desiredModuleType != .macro { let (result, resourceBundle) = try addResourceBundle( for: sourceModule, - pifTarget: sourceModulePifTarget, + targetKeyPath: sourceModuleTargetKeyPath, generatedResourceFiles: generatedResourceFiles ) if let resourceBundle { self.builtModulesAndProducts.append(resourceBundle) } @@ -307,31 +324,32 @@ extension PackagePIFProjectBuilder { } // Find the PIF target for the resource bundle, if any. Otherwise fall back to the module. - let resourceBundlePifTarget = self - .resourceBundleTarget(forModuleName: sourceModule.name) ?? sourceModulePifTarget + let resourceBundleTargetKeyPath = self.resourceBundleTargetKeyPath( + forModuleName: sourceModule.name + ) ?? sourceModuleTargetKeyPath // Add build tool commands to the resource bundle target. if desiredModuleType != .executable && desiredModuleType != .macro && addBuildToolPluginCommands { addBuildToolCommands( module: sourceModule, - sourceModulePifTarget: sourceModulePifTarget, - resourceBundlePifTarget: resourceBundlePifTarget, + sourceModuleTargetKeyPath: sourceModuleTargetKeyPath, + resourceBundleTargetKeyPath: resourceBundleTargetKeyPath, sourceFilePaths: generatedSourceFiles, resourceFilePaths: generatedResourceFiles ) } // Create a set of build settings that will be imparted to any target that depends on this one. - var impartedSettings = SwiftBuild.PIF.BuildSettings() + var impartedSettings = BuildSettings() // Configure the target-wide build settings. The details depend on the kind of product we're building. - var settings: SwiftBuild.PIF.BuildSettings = self.package.underlying.packageBaseBuildSettings + var settings: BuildSettings = self.package.underlying.packageBaseBuildSettings if shouldGenerateBundleAccessor { - settings.GENERATE_RESOURCE_ACCESSORS = "YES" + settings[.GENERATE_RESOURCE_ACCESSORS] = "YES" } if shouldGenerateEmbedInCodeAccessor { - settings.GENERATE_EMBED_IN_CODE_ACCESSORS = "YES" + settings[.GENERATE_EMBED_IN_CODE_ACCESSORS] = "YES" } // Generate a module map file, if needed. @@ -341,8 +359,8 @@ extension PackagePIFProjectBuilder { if sourceModule.usesSwift && desiredModuleType != .macro { // Generate ObjC compatibility header for Swift library targets. - settings.SWIFT_OBJC_INTERFACE_HEADER_DIR = generatedModuleMapDir - settings.SWIFT_OBJC_INTERFACE_HEADER_NAME = "\(sourceModule.name)-Swift.h" + settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR] = generatedModuleMapDir + settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME] = "\(sourceModule.name)-Swift.h" moduleMapFileContents = """ module \(sourceModule.c99name) { @@ -353,7 +371,7 @@ extension PackagePIFProjectBuilder { moduleMapFile = "\(generatedModuleMapDir)/\(sourceModule.name).modulemap" // We only need to impart this to C clients. - impartedSettings.OTHER_CFLAGS = ["-fmodule-map-file=\(moduleMapFile)", "$(inherited)"] + impartedSettings[.OTHER_CFLAGS] = ["-fmodule-map-file=\(moduleMapFile)", "$(inherited)"] } else if sourceModule.moduleMapFileRelativePath == nil { // Otherwise, this is a C library module and we generate a modulemap if one is already not provided. if case .umbrellaHeader(let path) = sourceModule.moduleMapType { @@ -376,8 +394,8 @@ extension PackagePIFProjectBuilder { if moduleMapFileContents.hasContent { // Pass the path of the module map up to all direct and indirect clients. moduleMapFile = "\(generatedModuleMapDir)/\(sourceModule.name).modulemap" - impartedSettings.OTHER_CFLAGS = ["-fmodule-map-file=\(moduleMapFile)", "$(inherited)"] - impartedSettings.OTHER_SWIFT_FLAGS = ["-Xcc", "-fmodule-map-file=\(moduleMapFile)", "$(inherited)"] + impartedSettings[.OTHER_CFLAGS] = ["-fmodule-map-file=\(moduleMapFile)", "$(inherited)"] + impartedSettings[.OTHER_SWIFT_FLAGS] = ["-Xcc", "-fmodule-map-file=\(moduleMapFile)", "$(inherited)"] } } @@ -393,28 +411,28 @@ extension PackagePIFProjectBuilder { delegate: pifBuilder.delegate ) } else { - settings.TARGET_NAME = sourceModule.name - settings.PRODUCT_NAME = "$(TARGET_NAME)" - settings.PRODUCT_MODULE_NAME = sourceModule.c99name - settings.PRODUCT_BUNDLE_IDENTIFIER = "\(self.package.identity).\(sourceModule.name)" + settings[.TARGET_NAME] = sourceModule.name + settings[.PRODUCT_NAME] = "$(TARGET_NAME)" + settings[.PRODUCT_MODULE_NAME] = sourceModule.c99name + settings[.PRODUCT_BUNDLE_IDENTIFIER] = "\(self.package.identity).\(sourceModule.name)" .spm_mangledToBundleIdentifier() - settings.EXECUTABLE_NAME = executableName - settings.CLANG_ENABLE_MODULES = "YES" - settings.GENERATE_MASTER_OBJECT_FILE = "NO" - settings.STRIP_INSTALLED_PRODUCT = "NO" + settings[.EXECUTABLE_NAME] = executableName + settings[.CLANG_ENABLE_MODULES] = "YES" + settings[.GENERATE_MASTER_OBJECT_FILE] = "NO" + settings[.STRIP_INSTALLED_PRODUCT] = "NO" // Macros build as executables, so they need slightly different // build settings from other module types which build a "*.o". if desiredModuleType == .macro { - settings.MACH_O_TYPE = "mh_execute" + settings[.MACH_O_TYPE] = "mh_execute" } else { - settings.MACH_O_TYPE = "mh_object" + settings[.MACH_O_TYPE] = "mh_object" // Disable code coverage linker flags since we're producing .o files. // Otherwise, we will run into duplicated symbols when there are more than one targets that produce .o // as their product. - settings.CLANG_COVERAGE_MAPPING_LINKER_ARGS = "NO" + settings[.CLANG_COVERAGE_MAPPING_LINKER_ARGS] = "NO" } - settings.SWIFT_PACKAGE_NAME = sourceModule.packageName + settings[.SWIFT_PACKAGE_NAME] = sourceModule.packageName if desiredModuleType == .executable { // Tell the Swift compiler to produce an alternate entry point rather than the standard `_main` entry @@ -422,16 +440,16 @@ extension PackagePIFProjectBuilder { // so that we can link one or more testable executable modules together into a single test bundle. // This allows the test bundle to treat the executable as if it were any regular library module, // and will have access to all symbols except the main entry point its. - settings.OTHER_SWIFT_FLAGS.lazilyInitializeAndMutate(initialValue: ["$(inherited)"]) { + settings[.OTHER_SWIFT_FLAGS].lazilyInitializeAndMutate(initialValue: ["$(inherited)"]) { $0.append(contentsOf: ["-Xfrontend", "-entry-point-function-name"]) $0.append(contentsOf: ["-Xfrontend", "\(sourceModule.c99name)_main"]) } // We have to give each target a unique name. - settings.TARGET_NAME = sourceModule.name + targetSuffix.description(forName: sourceModule.name) + settings[.TARGET_NAME] = sourceModule.name + targetSuffix.description(forName: sourceModule.name) // Redirect the built executable into a separate directory so it won't conflict with the real one. - settings.TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/ExecutableModules" + settings[.TARGET_BUILD_DIR] = "$(TARGET_BUILD_DIR)/ExecutableModules" // Don't install the Swift module of the testable side-built artifact, lest it conflict with the regular // one. @@ -439,45 +457,45 @@ extension PackagePIFProjectBuilder { // different in the Swift module // (the actual runtime artifact is of course very different, and that's why we're building a separate // testable artifact). - settings.SWIFT_INSTALL_MODULE = "NO" + settings[.SWIFT_INSTALL_MODULE] = "NO" } if let aliases = sourceModule.moduleAliases { // Format each entry as "original_name=alias" let list = aliases.map { $0.0 + "=" + $0.1 } - settings.SWIFT_MODULE_ALIASES = list.isEmpty ? nil : list + settings[.SWIFT_MODULE_ALIASES] = list.isEmpty ? nil : list } // We mark in the PIF that we are intentionally not offering a dynamic target here, // so we can emit a diagnostic if it is being requested by Swift Build. if !self.shouldOfferDynamicTarget(sourceModule.name) { - settings.PACKAGE_TARGET_NAME_CONFLICTS_WITH_PRODUCT_NAME = "YES" + settings[.PACKAGE_TARGET_NAME_CONFLICTS_WITH_PRODUCT_NAME] = "YES" } // We are setting this instead of `LD_DYLIB_INSTALL_NAME` because `mh_object` files // don't actually have install names, so we should not pass an install name to the linker. - settings.TAPI_DYLIB_INSTALL_NAME = sourceModule.name + settings[.TAPI_DYLIB_INSTALL_NAME] = sourceModule.name } - settings.PACKAGE_RESOURCE_TARGET_KIND = "regular" - settings.MODULEMAP_FILE_CONTENTS = moduleMapFileContents - settings.MODULEMAP_PATH = moduleMapFile - settings.DEFINES_MODULE = "YES" + settings[.PACKAGE_RESOURCE_TARGET_KIND] = "regular" + settings[.MODULEMAP_FILE_CONTENTS] = moduleMapFileContents + settings[.MODULEMAP_PATH] = moduleMapFile + settings[.DEFINES_MODULE] = "YES" // Settings for text-based API. // Due to rdar://78331694 (Cannot use TAPI for packages in contexts where we need to code-sign (e.g. apps)) // we are only enabling TAPI in `configureSourceModuleBuildSettings`, if desired. - settings.SUPPORTS_TEXT_BASED_API = "NO" + settings[.SUPPORTS_TEXT_BASED_API] = "NO" // If the module includes C headers, we set up the HEADER_SEARCH_PATHS setting appropriately. if let includeDirAbsPath = sourceModule.includeDirAbsolutePath { // Let the target itself find its own headers. - settings.HEADER_SEARCH_PATHS = [includeDirAbsPath.pathString, "$(inherited)"] - log(.debug, ".. added '\(includeDirAbsPath)' to HEADER_SEARCH_PATHS") + settings[.HEADER_SEARCH_PATHS] = [includeDirAbsPath.pathString, "$(inherited)"] + log(.debug, indent: 1, "Added '\(includeDirAbsPath)' to HEADER_SEARCH_PATHS") // Also propagate this search path to all direct and indirect clients. - impartedSettings.HEADER_SEARCH_PATHS = [includeDirAbsPath.pathString, "$(inherited)"] - log(.debug, ".. added '\(includeDirAbsPath)' to imparted HEADER_SEARCH_PATHS") + impartedSettings[.HEADER_SEARCH_PATHS] = [includeDirAbsPath.pathString, "$(inherited)"] + log(.debug, indent: 1, "Added '\(includeDirAbsPath)' to imparted HEADER_SEARCH_PATHS") } // Additional settings for the linker. @@ -494,24 +512,29 @@ extension PackagePIFProjectBuilder { } else { baselineOTHER_LDFLAGS = ["$(inherited)"] } - impartedSettings.OTHER_LDFLAGS = (sourceModule.isCxx ? ["-lc++"] : []) + baselineOTHER_LDFLAGS - impartedSettings.OTHER_LDRFLAGS = [] - log(.debug, ".. added '\(String(describing: impartedSettings.OTHER_LDFLAGS))' to imparted OTHER_LDFLAGS") + impartedSettings[.OTHER_LDFLAGS] = (sourceModule.isCxx ? ["-lc++"] : []) + baselineOTHER_LDFLAGS + impartedSettings[.OTHER_LDRFLAGS] = [] + log( + .debug, + indent: 1, + "Added '\(String(describing: impartedSettings[.OTHER_LDFLAGS]))' to imparted OTHER_LDFLAGS" + ) // This should be only for dynamic targets, but that isn't possible today. // Improvement is tracked by rdar://77403529 (Only impart `PackageFrameworks` search paths to clients of dynamic // package targets and products). - impartedSettings.FRAMEWORK_SEARCH_PATHS = ["$(BUILT_PRODUCTS_DIR)/PackageFrameworks", "$(inherited)"] + impartedSettings[.FRAMEWORK_SEARCH_PATHS] = ["$(BUILT_PRODUCTS_DIR)/PackageFrameworks", "$(inherited)"] log( .debug, - ".. added '\(String(describing: impartedSettings.FRAMEWORK_SEARCH_PATHS))' to imparted FRAMEWORK_SEARCH_PATHS" + indent: 1, + "Added '\(String(describing: impartedSettings[.FRAMEWORK_SEARCH_PATHS]))' to imparted FRAMEWORK_SEARCH_PATHS" ) // Set the appropriate language versions. - settings.SWIFT_VERSION = sourceModule.packageSwiftLanguageVersion(manifest: packageManifest) - settings.GCC_C_LANGUAGE_STANDARD = sourceModule.cLanguageStandard - settings.CLANG_CXX_LANGUAGE_STANDARD = sourceModule.cxxLanguageStandard - settings.SWIFT_ENABLE_BARE_SLASH_REGEX = "NO" + settings[.SWIFT_VERSION] = sourceModule.packageSwiftLanguageVersion(manifest: packageManifest) + settings[.GCC_C_LANGUAGE_STANDARD] = sourceModule.cLanguageStandard + settings[.CLANG_CXX_LANGUAGE_STANDARD] = sourceModule.cxxLanguageStandard + settings[.SWIFT_ENABLE_BARE_SLASH_REGEX] = "NO" // Create a group for the target's source files. // @@ -520,11 +543,17 @@ extension PackagePIFProjectBuilder { // be a mismatch between the paths that the index service is using for Swift Build queries, // and what paths Swift Build uses in its build description; such a mismatch would result // in the index service failing to get compiler arguments for source files of the target. - let targetSourceFileGroup = self.pif.mainGroup.addGroup( - path: try! resolveSymlinks(sourceModule.sourceDirAbsolutePath).pathString, - pathBase: .absolute - ) - log(.debug, ".. added source file group '\(targetSourceFileGroup.path)'") + let targetSourceFileGroupKeyPath = self.project.mainGroup.addGroup { id in + ProjectModel.Group( + id: id, + path: try! resolveSymlinks(sourceModule.sourceDirAbsolutePath).pathString, + pathBase: .absolute + ) + } + do { + let targetSourceFileGroup = self.project.mainGroup[keyPath: targetSourceFileGroupKeyPath] + log(.debug, indent: 1, "Added source file group '\(targetSourceFileGroup.path)'") + } // Add a source file reference for each of the source files, // and also an indexable-file URL for each one. @@ -532,16 +561,19 @@ extension PackagePIFProjectBuilder { // Symlinks should be resolved externally. var indexableFileURLs: [SourceControlURL] = [] for sourcePath in sourceModule.sourceFileRelativePaths { - sourceModulePifTarget.addSourceFile( - ref: targetSourceFileGroup.addFileReference(path: sourcePath.pathString, pathBase: .groupDir) - ) - log(.debug, ".. .. added source file '\(sourcePath)'") + let sourceFileRef = self.project.mainGroup[keyPath: targetSourceFileGroupKeyPath].addFileReference { id in + FileReference(id: id, path: sourcePath.pathString, pathBase: .groupDir) + } + self.project[keyPath: sourceModuleTargetKeyPath].addSourceFile { id in + BuildFile(id: id, fileRef: sourceFileRef) + } indexableFileURLs.append( SourceControlURL(fileURLWithPath: sourceModule.sourceDirAbsolutePath.appending(sourcePath)) ) + log(.debug, indent: 2, "Added source file '\(sourcePath)'") } for resource in sourceModule.resources { - log(.debug, ".. .. added resource file '\(resource.path)'") + log(.debug, indent: 2, "Added resource file '\(resource.path)'") indexableFileURLs.append(SourceControlURL(fileURLWithPath: resource.path)) } @@ -549,24 +581,27 @@ extension PackagePIFProjectBuilder { // Add any additional source files emitted by custom build commands. for path in generatedSourceFiles { - sourceModulePifTarget.addSourceFile( - ref: targetSourceFileGroup.addFileReference(path: path.pathString, pathBase: .absolute) - ) - log(.debug, ".. .. added generated source file '\(path)'") + let sourceFileRef = self.project.mainGroup[keyPath: targetSourceFileGroupKeyPath].addFileReference { id in + FileReference(id: id, path: path.pathString, pathBase: .absolute) + } + self.project[keyPath: sourceModuleTargetKeyPath].addSourceFile { id in + BuildFile(id: id, fileRef: sourceFileRef) + } + log(.debug, indent: 2, "Added generated source file '\(path)'") } if let resourceBundle = resourceBundleName { - impartedSettings.EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES = ["$(inherited)", resourceBundle] - settings.PACKAGE_RESOURCE_BUNDLE_NAME = resourceBundle - settings.COREML_CODEGEN_LANGUAGE = sourceModule.usesSwift ? "Swift" : "Objective-C" - settings.COREML_COMPILER_CONTAINER = "swift-package" + impartedSettings[.EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES] = ["$(inherited)", resourceBundle] + settings[.PACKAGE_RESOURCE_BUNDLE_NAME] = resourceBundle + settings[.COREML_CODEGEN_LANGUAGE] = sourceModule.usesSwift ? "Swift" : "Objective-C" + settings[.COREML_COMPILER_CONTAINER] = "swift-package" } if desiredModuleType == .macro { - settings.SWIFT_IMPLEMENTS_MACROS_FOR_MODULE_NAMES = [sourceModule.c99name] + settings[.SWIFT_IMPLEMENTS_MACROS_FOR_MODULE_NAMES] = [sourceModule.c99name] } if sourceModule.type == .macro { - settings.SKIP_BUILDING_DOCUMENTATION = "YES" + settings[.SKIP_BUILDING_DOCUMENTATION] = "YES" } // Handle the target's dependencies (but only link against them if needed). @@ -577,7 +612,7 @@ extension PackagePIFProjectBuilder { // This assertion is temporarily disabled since we may see targets from // _other_ packages, but this should be resolved; see rdar://95467710. /* assert(moduleDependency.packageName == self.package.name) */ - + let dependencyPlatformFilters = packageConditions .toPlatformFilter(toolsVersion: self.package.manifest.toolsVersion) @@ -589,56 +624,66 @@ extension PackagePIFProjectBuilder { if let product = moduleDependency .productRepresentingDependencyOfBuildPlugin(in: moduleMainProducts) { - sourceModulePifTarget.addDependency( + self.project[keyPath: sourceModuleTargetKeyPath].common.addDependency( on: product.pifTargetGUID(), platformFilters: dependencyPlatformFilters, linkProduct: false ) - log(.debug, ".. added dependency on product '\(product.pifTargetGUID)'") + log(.debug, indent: 1, "Added dependency on product '\(product.pifTargetGUID())'") } else { log( .debug, - ".. could not find a build plugin product to depend on for target '\(moduleDependency.pifTargetGUID())'" + indent: 1, + "Could not find a build plugin product to depend on for target '\(moduleDependency.pifTargetGUID())'" ) } case .binary: - let binaryReference = self.binaryGroup.addFileReference(path: moduleDependency.path.pathString) + let binaryReference = self.binaryGroup.addFileReference { id in + FileReference(id: id, path: moduleDependency.path.pathString) + } if shouldLinkProduct { - sourceModulePifTarget.addLibrary( - ref: binaryReference, - platformFilters: dependencyPlatformFilters, - codeSignOnCopy: true, - removeHeadersOnCopy: true - ) + self.project[keyPath: sourceModuleTargetKeyPath].addLibrary { id in + BuildFile( + id: id, + fileRef: binaryReference, + platformFilters: dependencyPlatformFilters, + codeSignOnCopy: true, + removeHeadersOnCopy: true + ) + } } else { // If we are producing a single ".o", don't link binaries since they // could be static which would cause them to become part of the ".o". - sourceModulePifTarget.addResourceFile( - ref: binaryReference, - platformFilters: dependencyPlatformFilters - ) + self.project[keyPath: sourceModuleTargetKeyPath].addResourceFile { id in + BuildFile( + id: id, + fileRef: binaryReference, + platformFilters: dependencyPlatformFilters + ) + } } - log(.debug, ".. added use of binary library '\(moduleDependency.path)'") + log(.debug, indent: 1, "Added use of binary library '\(moduleDependency.path)'") case .plugin: let dependencyGUID = moduleDependency.pifTargetGUID() - sourceModulePifTarget.addDependency( + self.project[keyPath: sourceModuleTargetKeyPath].common.addDependency( on: dependencyGUID, platformFilters: dependencyPlatformFilters, linkProduct: false ) - log(.debug, ".. added use of plugin target '\(dependencyGUID)'") + log(.debug, indent: 1, "Added use of plugin target '\(dependencyGUID)'") case .library, .test, .macro, .systemModule: - sourceModulePifTarget.addDependency( + self.project[keyPath: sourceModuleTargetKeyPath].common.addDependency( on: moduleDependency.pifTargetGUID(), platformFilters: dependencyPlatformFilters, linkProduct: shouldLinkProduct ) log( .debug, - ".. added \(shouldLinkProduct ? "linked " : "")dependency on target '\(moduleDependency.pifTargetGUID())'" + indent: 1, + "Added \(shouldLinkProduct ? "linked " : "")dependency on target '\(moduleDependency.pifTargetGUID())'" ) } @@ -656,14 +701,15 @@ extension PackagePIFProjectBuilder { .toPlatformFilter(toolsVersion: self.package.manifest.toolsVersion) let shouldLinkProduct = shouldLinkProduct && productDependency.isLinkable - sourceModulePifTarget.addDependency( + self.project[keyPath: sourceModuleTargetKeyPath].common.addDependency( on: productDependency.pifTargetGUID(), platformFilters: dependencyPlatformFilters, linkProduct: shouldLinkProduct ) log( .debug, - ".. added \(shouldLinkProduct ? "linked " : "")dependency on product '\(productDependency.pifTargetGUID)'" + indent: 1, + "Added \(shouldLinkProduct ? "linked " : "")dependency on product '\(productDependency.pifTargetGUID)'" ) } } @@ -683,16 +729,13 @@ extension PackagePIFProjectBuilder { // Apply target-specific build settings defined in the manifest. for (buildConfig, declarationsByPlatform) in allBuildSettings.targetSettings { for (platform, settingsByDeclaration) in declarationsByPlatform { - // A `nil` platform means that the declaration applies to *all* platforms. - let pifPlatform = platform.map { SwiftBuild.PIF.BuildSettings.Platform(from: $0) } - + // Note: A `nil` platform means that the declaration applies to *all* platforms. for (declaration, stringValues) in settingsByDeclaration { - let pifDeclaration = SwiftBuild.PIF.BuildSettings.Declaration(from: declaration) switch buildConfig { case .debug: - debugSettings.append(values: stringValues, to: pifDeclaration, platform: pifPlatform) + debugSettings.append(values: stringValues, to: declaration, platform: platform) case .release: - releaseSettings.append(values: stringValues, to: pifDeclaration, platform: pifPlatform) + releaseSettings.append(values: stringValues, to: declaration, platform: platform) } } } @@ -700,38 +743,41 @@ extension PackagePIFProjectBuilder { // Impart the linker flags. for (platform, settingsByDeclaration) in sourceModule.allBuildSettings.impartedSettings { - // A `nil` platform means that the declaration applies to *all* platforms. - let pifPlatform = platform.map { SwiftBuild.PIF.BuildSettings.Platform(from: $0) } - + // Note: A `nil` platform means that the declaration applies to *all* platforms. for (declaration, stringValues) in settingsByDeclaration { - let pifDeclaration = SwiftBuild.PIF.BuildSettings.Declaration(from: declaration) - impartedSettings.append(values: stringValues, to: pifDeclaration, platform: pifPlatform) + impartedSettings.append(values: stringValues, to: declaration, platform: platform) } } // Set the imparted settings, which are ones that clients (both direct and indirect ones) use. var debugImpartedSettings = impartedSettings - debugImpartedSettings.LD_RUNPATH_SEARCH_PATHS = + debugImpartedSettings[.LD_RUNPATH_SEARCH_PATHS] = ["$(BUILT_PRODUCTS_DIR)/PackageFrameworks"] + - (debugImpartedSettings.LD_RUNPATH_SEARCH_PATHS ?? ["$(inherited)"]) - - sourceModulePifTarget.addBuildConfig( - name: "Debug", - settings: debugSettings, - impartedBuildSettings: debugImpartedSettings - ) - sourceModulePifTarget.addBuildConfig( - name: "Release", - settings: releaseSettings, - impartedBuildSettings: impartedSettings - ) + (debugImpartedSettings[.LD_RUNPATH_SEARCH_PATHS] ?? ["$(inherited)"]) + + self.project[keyPath: sourceModuleTargetKeyPath].common.addBuildConfig { id in + BuildConfig( + id: id, + name: "Debug", + settings: debugSettings, + impartedBuildSettings: debugImpartedSettings + ) + } + self.project[keyPath: sourceModuleTargetKeyPath].common.addBuildConfig { id in + BuildConfig( + id: id, + name: "Release", + settings: releaseSettings, + impartedBuildSettings: impartedSettings + ) + } // Collect linked binaries. - let linkedPackageBinaries: [PIFPackageBuilder.LinkedPackageBinary] = sourceModule.dependencies.compactMap { - PIFPackageBuilder.LinkedPackageBinary(dependency: $0, package: self.package) + let linkedPackageBinaries: [PackagePIFBuilder.LinkedPackageBinary] = sourceModule.dependencies.compactMap { + PackagePIFBuilder.LinkedPackageBinary(dependency: $0, package: self.package) } - let productOrModuleType: PIFPackageBuilder.ModuleOrProductType = if desiredModuleType == .dynamicLibrary { + let productOrModuleType: PackagePIFBuilder.ModuleOrProductType = if desiredModuleType == .dynamicLibrary { pifBuilder.createDylibForDynamicProducts ? .dynamicLibrary : .framework } else if desiredModuleType == .macro { .macro @@ -739,11 +785,11 @@ extension PackagePIFProjectBuilder { .module } - let moduleOrProduct = PIFPackageBuilder.ModuleOrProduct( + let moduleOrProduct = PackagePIFBuilder.ModuleOrProduct( type: productOrModuleType, name: sourceModule.name, moduleName: sourceModule.c99name, - pifTarget: sourceModulePifTarget, + pifTarget: .target(self.project[keyPath: sourceModuleTargetKeyPath]), indexableFileURLs: indexableFileURLs, headerFiles: headerFiles, linkedPackageBinaries: linkedPackageBinaries, @@ -759,52 +805,61 @@ extension PackagePIFProjectBuilder { mutating func makeSystemLibraryModule(_ resolvedSystemLibrary: PackageGraph.ResolvedModule) throws { precondition(resolvedSystemLibrary.type == .systemModule) - let systemLibrary = resolvedSystemLibrary.underlying as! SystemLibraryModule // Create an aggregate PIF target (which doesn't have an actual product). - let systemLibraryPifTarget = self.pif.addAggregateTarget( - id: resolvedSystemLibrary.pifTargetGUID(), - name: resolvedSystemLibrary.name - ) - log( - .debug, - "created \(type(of: systemLibraryPifTarget)) '\(systemLibraryPifTarget.id)' with name '\(systemLibraryPifTarget.name)'" - ) + let systemLibraryTargetKeyPath = try self.project.addAggregateTarget { _ in + ProjectModel.AggregateTarget( + id: resolvedSystemLibrary.pifTargetGUID(), + name: resolvedSystemLibrary.name + ) + } + do { + let systemLibraryTarget = self.project[keyPath: systemLibraryTargetKeyPath] + log( + .debug, + "Created \(type(of: systemLibraryTarget)) '\(systemLibraryTarget.id)' with name '\(systemLibraryTarget.name)'" + ) + } - let settings: SwiftBuild.PIF.BuildSettings = self.package.underlying.packageBaseBuildSettings + let settings: ProjectModel.BuildSettings = self.package.underlying.packageBaseBuildSettings let pkgConfig = try systemLibrary.pkgConfig( package: self.package, observabilityScope: pifBuilder.observabilityScope ) // Impart the header search path to all direct and indirect clients. - var impartedSettings = SwiftBuild.PIF.BuildSettings() - impartedSettings.OTHER_CFLAGS = ["-fmodule-map-file=\(systemLibrary.modulemapFileAbsolutePath)"] + pkgConfig - .cFlags.prepending("$(inherited)") - impartedSettings.OTHER_LDFLAGS = pkgConfig.libs.prepending("$(inherited)") - impartedSettings.OTHER_LDRFLAGS = [] - impartedSettings.OTHER_SWIFT_FLAGS = ["-Xcc"] + impartedSettings.OTHER_CFLAGS! - log(.debug, ".. added '\(systemLibrary.path.pathString)' to imparted HEADER_SEARCH_PATHS") - - systemLibraryPifTarget.addBuildConfig( - name: "Debug", - settings: settings, - impartedBuildSettings: impartedSettings - ) - systemLibraryPifTarget.addBuildConfig( - name: "Release", - settings: settings, - impartedBuildSettings: impartedSettings - ) - + var impartedSettings = ProjectModel.BuildSettings() + impartedSettings[.OTHER_CFLAGS] = ["-fmodule-map-file=\(systemLibrary.modulemapFileAbsolutePath)"] + + pkgConfig.cFlags.prepending("$(inherited)") + impartedSettings[.OTHER_LDFLAGS] = pkgConfig.libs.prepending("$(inherited)") + impartedSettings[.OTHER_LDRFLAGS] = [] + impartedSettings[.OTHER_SWIFT_FLAGS] = ["-Xcc"] + impartedSettings[.OTHER_CFLAGS]! + log(.debug, indent: 1, "Added '\(systemLibrary.path.pathString)' to imparted HEADER_SEARCH_PATHS") + + self.project[keyPath: systemLibraryTargetKeyPath].common.addBuildConfig { id in + BuildConfig( + id: id, + name: "Debug", + settings: settings, + impartedBuildSettings: impartedSettings + ) + } + self.project[keyPath: systemLibraryTargetKeyPath].common.addBuildConfig { id in + BuildConfig( + id: id, + name: "Release", + settings: settings, + impartedBuildSettings: impartedSettings + ) + } // FIXME: Should we also impart linkage? - let systemModule = PIFPackageBuilder.ModuleOrProduct( + let systemModule = PackagePIFBuilder.ModuleOrProduct( type: .module, name: resolvedSystemLibrary.name, moduleName: resolvedSystemLibrary.c99name, - pifTarget: systemLibraryPifTarget, + pifTarget: .aggregate(self.project[keyPath: systemLibraryTargetKeyPath]), indexableFileURLs: [], headerFiles: [], linkedPackageBinaries: [], @@ -815,4 +870,5 @@ extension PackagePIFProjectBuilder { self.builtModulesAndProducts.append(systemModule) } } + #endif diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift index 2d83afd0bc5..e44f2b099ff 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift @@ -28,7 +28,8 @@ import struct PackageGraph.ResolvedPackage import struct PackageGraph.ResolvedProduct #if canImport(SwiftBuild) -import enum SwiftBuild.PIF + +import enum SwiftBuild.ProjectModel /// Extension to create PIF **products** for a given package. extension PackagePIFProjectBuilder { @@ -49,15 +50,15 @@ extension PackagePIFProjectBuilder { } // Determine the kind of PIF target *product type* to create for the package product. - let pifProductType: SwiftBuild.PIF.Target.ProductType - let moduleOrProductType: PIFPackageBuilder.ModuleOrProductType - let synthesizedResourceGeneratingPluginInvocationResults: [PIFPackageBuilder.BuildToolPluginInvocationResult] = + let pifProductType: ProjectModel.Target.ProductType + let moduleOrProductType: PackagePIFBuilder.ModuleOrProductType + let synthesizedResourceGeneratingPluginInvocationResults: [PackagePIFBuilder.BuildToolPluginInvocationResult] = [] if product.type == .executable { if let customPIFProductType = pifBuilder.delegate.customProductType(forExecutable: product.underlying) { pifProductType = customPIFProductType - moduleOrProductType = PIFPackageBuilder.ModuleOrProductType(from: customPIFProductType) + moduleOrProductType = PackagePIFBuilder.ModuleOrProductType(from: customPIFProductType) } else { // No custom type provider. Current behavior is to fall back on regular executable. pifProductType = .executable @@ -71,16 +72,22 @@ extension PackagePIFProjectBuilder { } // It's not a library product, so create a regular PIF target of the appropriate product type. - let mainModulePifTarget = try self.pif.addTargetThrowing( - id: product.pifTargetGUID(), - productType: pifProductType, - name: product.name, - productName: product.name - ) - log( - .debug, - "created \(type(of: mainModulePifTarget)) '\(mainModulePifTarget.id)' of type '\(mainModulePifTarget.productType.asString)' with name '\(mainModulePifTarget.name)' and product name '\(mainModulePifTarget.productName)'" - ) + let mainModuleTargetKeyPath = try self.project.addTarget { _ in + ProjectModel.Target( + id: product.pifTargetGUID(), + productType: pifProductType, + name: product.name, + productName: product.name + ) + } + do { + let mainModuleTarget = self.project[keyPath: mainModuleTargetKeyPath] + log( + .debug, + "Created \(mainModuleTarget.productType)) '\(mainModuleTarget.id)' " + + "with name '\(mainModuleTarget.name)' and product name '\(mainModuleTarget.productName)'" + ) + } // We're currently *not* handling other module targets (and SwiftPM should never return them) for // a main-module product but, for diagnostic purposes, we warn about any that we do come across. @@ -92,7 +99,7 @@ extension PackagePIFProjectBuilder { // Deal with any generated source files or resource files. let (generatedSourceFiles, pluginGeneratedResourceFiles) = computePluginGeneratedFiles( module: mainModule, - pifTarget: mainModulePifTarget, + targetKeyPath: mainModuleTargetKeyPath, addBuildToolPluginCommands: pifProductType == .application ) if mainModule.resources.hasContent || pluginGeneratedResourceFiles.hasContent { @@ -101,87 +108,114 @@ extension PackagePIFProjectBuilder { // Configure the target-wide build settings. The details depend on the kind of product we're building, // but are in general the ones that are suitable for end-product artifacts such as executables and test bundles. - var settings: SwiftBuild.PIF.BuildSettings = package.underlying.packageBaseBuildSettings - settings.TARGET_NAME = product.name - settings.PACKAGE_RESOURCE_TARGET_KIND = "regular" - settings.PRODUCT_NAME = "$(TARGET_NAME)" - settings.PRODUCT_MODULE_NAME = product.c99name - settings.PRODUCT_BUNDLE_IDENTIFIER = "\(self.package.identity).\(product.name)" + var settings: ProjectModel.BuildSettings = package.underlying.packageBaseBuildSettings + settings[.TARGET_NAME] = product.name + settings[.PACKAGE_RESOURCE_TARGET_KIND] = "regular" + settings[.PRODUCT_NAME] = "$(TARGET_NAME)" + settings[.PRODUCT_MODULE_NAME] = product.c99name + settings[.PRODUCT_BUNDLE_IDENTIFIER] = "\(self.package.identity).\(product.name)" .spm_mangledToBundleIdentifier() - settings.EXECUTABLE_NAME = product.name - settings.CLANG_ENABLE_MODULES = "YES" - settings.SWIFT_PACKAGE_NAME = mainModule.packageName + settings[.EXECUTABLE_NAME] = product.name + settings[.CLANG_ENABLE_MODULES] = "YES" + settings[.SWIFT_PACKAGE_NAME] = mainModule.packageName if mainModule.type == .test { // FIXME: we shouldn't always include both the deep and shallow bundle paths here, but for that we'll need rdar://31867023 - settings.LD_RUNPATH_SEARCH_PATHS = ["@loader_path/Frameworks", "@loader_path/../Frameworks", "$(inherited)"] - settings.GENERATE_INFOPLIST_FILE = "YES" - settings.SKIP_INSTALL = "NO" - settings.SWIFT_ACTIVE_COMPILATION_CONDITIONS.lazilyInitialize { ["$(inherited)"] } + settings[.LD_RUNPATH_SEARCH_PATHS] = [ + "@loader_path/Frameworks", + "@loader_path/../Frameworks", + "$(inherited)", + ] + settings[.GENERATE_INFOPLIST_FILE] = "YES" + settings[.SKIP_INSTALL] = "NO" + settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS].lazilyInitialize { ["$(inherited)"] } } else if mainModule.type == .executable { // Setup install path for executables if it's in root of a pure Swift package. if pifBuilder.delegate.hostsOnlyPackages && pifBuilder.delegate.isRootPackage { - settings.SKIP_INSTALL = "NO" - settings.INSTALL_PATH = "/usr/local/bin" - settings.LD_RUNPATH_SEARCH_PATHS = ["$(inherited)", "@executable_path/../lib"] + settings[.SKIP_INSTALL] = "NO" + settings[.INSTALL_PATH] = "/usr/local/bin" + settings[.LD_RUNPATH_SEARCH_PATHS] = ["$(inherited)", "@executable_path/../lib"] } } let mainTargetDeploymentTargets = mainModule.deploymentTargets(using: pifBuilder.delegate) - settings.MACOSX_DEPLOYMENT_TARGET = mainTargetDeploymentTargets[.macOS] ?? nil - settings.IPHONEOS_DEPLOYMENT_TARGET = mainTargetDeploymentTargets[.iOS] ?? nil + settings[.MACOSX_DEPLOYMENT_TARGET] = mainTargetDeploymentTargets[.macOS] ?? nil + settings[.IPHONEOS_DEPLOYMENT_TARGET] = mainTargetDeploymentTargets[.iOS] ?? nil if let deploymentTarget_macCatalyst = mainTargetDeploymentTargets[.macCatalyst] { settings .platformSpecificSettings[.macCatalyst]![.IPHONEOS_DEPLOYMENT_TARGET] = [deploymentTarget_macCatalyst] } - settings.TVOS_DEPLOYMENT_TARGET = mainTargetDeploymentTargets[.tvOS] ?? nil - settings.WATCHOS_DEPLOYMENT_TARGET = mainTargetDeploymentTargets[.watchOS] ?? nil - settings.DRIVERKIT_DEPLOYMENT_TARGET = mainTargetDeploymentTargets[.driverKit] ?? nil - settings.XROS_DEPLOYMENT_TARGET = mainTargetDeploymentTargets[.visionOS] ?? nil + settings[.TVOS_DEPLOYMENT_TARGET] = mainTargetDeploymentTargets[.tvOS] ?? nil + settings[.WATCHOS_DEPLOYMENT_TARGET] = mainTargetDeploymentTargets[.watchOS] ?? nil + settings[.DRIVERKIT_DEPLOYMENT_TARGET] = mainTargetDeploymentTargets[.driverKit] ?? nil + settings[.XROS_DEPLOYMENT_TARGET] = mainTargetDeploymentTargets[.visionOS] ?? nil // If the main module includes C headers, then we need to set up the HEADER_SEARCH_PATHS setting appropriately. if let includeDirAbsolutePath = mainModule.includeDirAbsolutePath { // Let the main module itself find its own headers. - settings.HEADER_SEARCH_PATHS = [includeDirAbsolutePath.pathString, "$(inherited)"] - log(.debug, ".. added '\(includeDirAbsolutePath)' to HEADER_SEARCH_PATHS") + settings[.HEADER_SEARCH_PATHS] = [includeDirAbsolutePath.pathString, "$(inherited)"] + log(.debug, indent: 1, "Added '\(includeDirAbsolutePath)' to HEADER_SEARCH_PATHS") } // Set the appropriate language versions. - settings.SWIFT_VERSION = mainModule.packageSwiftLanguageVersion(manifest: packageManifest) - settings.GCC_C_LANGUAGE_STANDARD = mainModule.cLanguageStandard - settings.CLANG_CXX_LANGUAGE_STANDARD = mainModule.cxxLanguageStandard - settings.SWIFT_ENABLE_BARE_SLASH_REGEX = "NO" + settings[.SWIFT_VERSION] = mainModule.packageSwiftLanguageVersion(manifest: packageManifest) + settings[.GCC_C_LANGUAGE_STANDARD] = mainModule.cLanguageStandard + settings[.CLANG_CXX_LANGUAGE_STANDARD] = mainModule.cxxLanguageStandard + settings[.SWIFT_ENABLE_BARE_SLASH_REGEX] = "NO" // Create a group for the source files of the main module // For now we use an absolute path for it, but we should really make it // container-relative, since it's always inside the package directory. - let mainTargetSourceFileGroup = self.pif.mainGroup.addGroup( - path: mainModule.sourceDirAbsolutePath.pathString, - pathBase: .absolute - ) - log(.debug, ".. added source file group '\(mainTargetSourceFileGroup.path)'") + let mainTargetSourceFileGroupKeyPath = self.project.mainGroup.addGroup { id in + ProjectModel.Group( + id: id, + path: mainModule.sourceDirAbsolutePath.pathString, + pathBase: .absolute + ) + } + do { + let mainTargetSourceFileGroup = self.project.mainGroup[keyPath: mainTargetSourceFileGroupKeyPath] + log(.debug, indent: 1, "Added source file group '\(mainTargetSourceFileGroup.path)'") + } // Add a source file reference for each of the source files, and also an indexable-file URL for each one. // Note that the indexer requires them to have any symbolic links resolved. var indexableFileURLs: [SourceControlURL] = [] for sourcePath in mainModule.sourceFileRelativePaths { - mainModulePifTarget.addSourceFile( - ref: mainTargetSourceFileGroup.addFileReference(path: sourcePath.pathString, pathBase: .groupDir) + let sourceFileRef = self.project.mainGroup[keyPath: mainTargetSourceFileGroupKeyPath] + .addFileReference { id in + FileReference( + id: id, + path: sourcePath.pathString, + pathBase: .groupDir + ) + } + self.project[keyPath: mainModuleTargetKeyPath].addSourceFile { id in + BuildFile(id: id, fileRef: sourceFileRef) + } + log(.debug, indent: 2, "Added source file '\(sourcePath)'") + indexableFileURLs.append( + SourceControlURL(fileURLWithPath: mainModule.sourceDirAbsolutePath.appending(sourcePath)) ) - log(.debug, ".. .. added source file '\(sourcePath)'") - indexableFileURLs - .append(SourceControlURL(fileURLWithPath: mainModule.sourceDirAbsolutePath.appending(sourcePath))) } let headerFiles = Set(mainModule.headerFileAbsolutePaths) // Add any additional source files emitted by custom build commands. for path in generatedSourceFiles { - mainModulePifTarget.addSourceFile( - ref: mainTargetSourceFileGroup.addFileReference(path: path.pathString, pathBase: .absolute) - ) - log(.debug, ".. .. added generated source file '\(path)'") + let sourceFileRef = self.project.mainGroup[keyPath: mainTargetSourceFileGroupKeyPath] + .addFileReference { id in + FileReference( + id: id, + path: path.pathString, + pathBase: .absolute + ) + } + self.project[keyPath: mainModuleTargetKeyPath].addSourceFile { id in + BuildFile(id: id, fileRef: sourceFileRef) + } + log(.debug, indent: 2, "Added generated source file '\(path)'") } // Add any additional resource files emitted by synthesized build commands @@ -190,7 +224,7 @@ extension PackagePIFProjectBuilder { generatedResourceFiles.append( contentsOf: addBuildToolCommands( from: synthesizedResourceGeneratingPluginInvocationResults, - pifTarget: mainModulePifTarget, + targetKeyPath: mainModuleTargetKeyPath, addBuildToolPluginCommands: pifProductType == .application ) ) @@ -202,76 +236,80 @@ extension PackagePIFProjectBuilder { if pifProductType == .application { let result = processResources( for: mainModule, - sourceModulePifTarget: mainModulePifTarget, + sourceModuleTargetKeyPath: mainModuleTargetKeyPath, // For application products we embed the resources directly into the PIF target. - resourceBundlePifTarget: nil, + resourceBundleTargetKeyPath: nil, generatedResourceFiles: generatedResourceFiles ) if result.shouldGenerateBundleAccessor { - settings.GENERATE_RESOURCE_ACCESSORS = "YES" + settings[.GENERATE_RESOURCE_ACCESSORS] = "YES" } if result.shouldGenerateEmbedInCodeAccessor { - settings.GENERATE_EMBED_IN_CODE_ACCESSORS = "YES" + settings[.GENERATE_EMBED_IN_CODE_ACCESSORS] = "YES" } - // FIXME: We should also adjust the generated module bundle glue so that `Bundle.module` is a synonym for `Bundle.main` in this case. } else { let (result, resourceBundle) = try addResourceBundle( for: mainModule, - pifTarget: mainModulePifTarget, + targetKeyPath: mainModuleTargetKeyPath, generatedResourceFiles: generatedResourceFiles ) if let resourceBundle { self.builtModulesAndProducts.append(resourceBundle) } if let resourceBundle = result.bundleName { // Associate the resource bundle with the target. - settings.PACKAGE_RESOURCE_BUNDLE_NAME = resourceBundle + settings[.PACKAGE_RESOURCE_BUNDLE_NAME] = resourceBundle if result.shouldGenerateBundleAccessor { - settings.GENERATE_RESOURCE_ACCESSORS = "YES" + settings[.GENERATE_RESOURCE_ACCESSORS] = "YES" } if result.shouldGenerateEmbedInCodeAccessor { - settings.GENERATE_EMBED_IN_CODE_ACCESSORS = "YES" + settings[.GENERATE_EMBED_IN_CODE_ACCESSORS] = "YES" } // If it's a kind of product that can contain resources, we also add a use of it. - let ref = self.pif.mainGroup - .addFileReference(path: "$(CONFIGURATION_BUILD_DIR)/\(resourceBundle).bundle") + let resourceBundleRef = self.project.mainGroup.addFileReference { id in + FileReference(id: id, path: "$(CONFIGURATION_BUILD_DIR)/\(resourceBundle).bundle") + } if pifProductType == .bundle || pifProductType == .unitTest { - settings.COREML_CODEGEN_LANGUAGE = mainModule.usesSwift ? "Swift" : "Objective-C" - settings.COREML_COMPILER_CONTAINER = "swift-package" + settings[.COREML_CODEGEN_LANGUAGE] = mainModule.usesSwift ? "Swift" : "Objective-C" + settings[.COREML_COMPILER_CONTAINER] = "swift-package" - mainModulePifTarget.addResourceFile(ref: ref) - log(.debug, ".. added use of resource bundle '\(ref.path)'") + self.project[keyPath: mainModuleTargetKeyPath].addResourceFile { id in + BuildFile(id: id, fileRef: resourceBundleRef) + } + log(.debug, indent: 2, "Added use of resource bundle '\(resourceBundleRef.path)'") } else { log( .debug, - ".. ignored resource bundle '\(ref.path)' for main module of type \(type(of: mainModule))" + indent: 2, + "Ignored resource bundle '\(resourceBundleRef.path)' for main module of type \(type(of: mainModule))" ) } // Add build tool commands to the resource bundle target. - let resourceBundlePifTarget = self - .resourceBundleTarget(forModuleName: mainModule.name) ?? mainModulePifTarget + let mainResourceBundleTargetKeyPath = self.resourceBundleTargetKeyPath(forModuleName: mainModule.name) + let resourceBundleTargetKeyPath = mainResourceBundleTargetKeyPath ?? mainModuleTargetKeyPath + addBuildToolCommands( module: mainModule, - sourceModulePifTarget: mainModulePifTarget, - resourceBundlePifTarget: resourceBundlePifTarget, + sourceModuleTargetKeyPath: mainModuleTargetKeyPath, + resourceBundleTargetKeyPath: resourceBundleTargetKeyPath, sourceFilePaths: generatedSourceFiles, resourceFilePaths: generatedResourceFiles ) } else { // Generated resources always trigger the creation of a bundle accessor. - settings.GENERATE_RESOURCE_ACCESSORS = "YES" - settings.GENERATE_EMBED_IN_CODE_ACCESSORS = "NO" + settings[.GENERATE_RESOURCE_ACCESSORS] = "YES" + settings[.GENERATE_EMBED_IN_CODE_ACCESSORS] = "NO" - // If we did not create a resource bundle target, we still need to add build tool commands for any - // generated files. + // If we did not create a resource bundle target, + // we still need to add build tool commands for any generated files. addBuildToolCommands( module: mainModule, - sourceModulePifTarget: mainModulePifTarget, - resourceBundlePifTarget: mainModulePifTarget, + sourceModuleTargetKeyPath: mainModuleTargetKeyPath, + resourceBundleTargetKeyPath: mainModuleTargetKeyPath, sourceFilePaths: generatedSourceFiles, resourceFilePaths: generatedResourceFiles ) @@ -288,39 +326,44 @@ extension PackagePIFProjectBuilder { switch moduleDependency.type { case .binary: - let binaryReference = self.binaryGroup.addFileReference(path: moduleDependency.path.pathString) - mainModulePifTarget.addLibrary( - ref: binaryReference, - platformFilters: packageConditions - .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), - codeSignOnCopy: true, - removeHeadersOnCopy: true - ) - log(.debug, ".. added use of binary library '\(moduleDependency.path)'") + let binaryFileRef = self.binaryGroup.addFileReference { id in + FileReference(id: id, path: moduleDependency.path.pathString) + } + let toolsVersion = self.package.manifest.toolsVersion + self.project[keyPath: mainModuleTargetKeyPath].addLibrary { id in + BuildFile( + id: id, + fileRef: binaryFileRef, + platformFilters: packageConditions.toPlatformFilter(toolsVersion: toolsVersion), + codeSignOnCopy: true, + removeHeadersOnCopy: true + ) + } + log(.debug, indent: 1, "Added use of binary library '\(moduleDependency.path)'") case .plugin: let dependencyId = moduleDependency.pifTargetGUID() - mainModulePifTarget.addDependency( + self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: dependencyId, platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: false ) - log(.debug, ".. added use of plugin target '\(dependencyId)'") + log(.debug, indent: 1, "Added use of plugin target '\(dependencyId)'") case .macro: let dependencyId = moduleDependency.pifTargetGUID() - mainModulePifTarget.addDependency( + self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: dependencyId, platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: false ) - log(.debug, ".. added dependency on product '\(dependencyId)'") + log(.debug, indent: 1, "Added dependency on product '\(dependencyId)'") // Link with a testable version of the macro if appropriate. if product.type == .test { - mainModulePifTarget.addDependency( + self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: moduleDependency.pifTargetGUID(suffix: .testable), platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), @@ -328,7 +371,8 @@ extension PackagePIFProjectBuilder { ) log( .debug, - ".. added linked dependency on target '\(moduleDependency.pifTargetGUID(suffix: .testable))'" + indent: 1, + "Added linked dependency on target '\(moduleDependency.pifTargetGUID(suffix: .testable))'" ) // FIXME: Manually propagate product dependencies of macros but the build system should really handle this. @@ -340,7 +384,7 @@ extension PackagePIFProjectBuilder { productDependency, with: packageConditions, isLinkable: isLinkable, - pifTarget: mainModulePifTarget, + targetKeyPath: mainModuleTargetKeyPath, settings: &settings ) case .module: @@ -355,32 +399,32 @@ extension PackagePIFProjectBuilder { let productDependency = modulesGraph.allProducts.only { $0.name == moduleDependency.name } if let productDependency { let productDependencyGUID = productDependency.pifTargetGUID() - mainModulePifTarget.addDependency( + self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: productDependencyGUID, platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: false ) - log(.debug, ".. added dependency on product '\(productDependencyGUID)'") + log(.debug, indent: 1, "Added dependency on product '\(productDependencyGUID)'") } // If we're linking against an executable and the tools version is new enough, // we also link against a testable version of the executable. if product.type == .test, self.package.manifest.toolsVersion >= .v5_5 { let moduleDependencyGUID = moduleDependency.pifTargetGUID(suffix: .testable) - mainModulePifTarget.addDependency( + self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: moduleDependencyGUID, platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: true ) - log(.debug, ".. added linked dependency on target '\(moduleDependencyGUID)'") + log(.debug, indent: 1, "Added linked dependency on target '\(moduleDependencyGUID)'") } case .library, .systemModule, .test: let shouldLinkProduct = moduleDependency.type != .systemModule let dependencyGUID = moduleDependency.pifTargetGUID() - mainModulePifTarget.addDependency( + self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: dependencyGUID, platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), @@ -388,7 +432,8 @@ extension PackagePIFProjectBuilder { ) log( .debug, - ".. added \(shouldLinkProduct ? "linked " : "")dependency on target '\(dependencyGUID)'" + indent: 1, + "Added \(shouldLinkProduct ? "linked " : "")dependency on target '\(dependencyGUID)'" ) } @@ -398,7 +443,7 @@ extension PackagePIFProjectBuilder { productDependency, with: packageConditions, isLinkable: isLinkable, - pifTarget: mainModulePifTarget, + targetKeyPath: mainModuleTargetKeyPath, settings: &settings ) } @@ -407,38 +452,40 @@ extension PackagePIFProjectBuilder { // Until this point the build settings for the target have been the same between debug and release // configurations. // The custom manifest settings might cause them to diverge. - var debugSettings: SwiftBuild.PIF.BuildSettings = settings - var releaseSettings: SwiftBuild.PIF.BuildSettings = settings + var debugSettings: ProjectModel.BuildSettings = settings + var releaseSettings: ProjectModel.BuildSettings = settings // Apply target-specific build settings defined in the manifest. for (buildConfig, declarationsByPlatform) in mainModule.allBuildSettings.targetSettings { for (platform, declarations) in declarationsByPlatform { // A `nil` platform means that the declaration applies to *all* platforms. - let pifPlatform = platform.map { SwiftBuild.PIF.BuildSettings.Platform(from: $0) } for (declaration, stringValues) in declarations { - let pifDeclaration = SwiftBuild.PIF.BuildSettings.Declaration(from: declaration) switch buildConfig { case .debug: - debugSettings.append(values: stringValues, to: pifDeclaration, platform: pifPlatform) + debugSettings.append(values: stringValues, to: declaration, platform: platform) case .release: - releaseSettings.append(values: stringValues, to: pifDeclaration, platform: pifPlatform) + releaseSettings.append(values: stringValues, to: declaration, platform: platform) } } } } - mainModulePifTarget.addBuildConfig(name: "Debug", settings: debugSettings) - mainModulePifTarget.addBuildConfig(name: "Release", settings: releaseSettings) + self.project[keyPath: mainModuleTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Debug", settings: debugSettings) + } + self.project[keyPath: mainModuleTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Release", settings: releaseSettings) + } // Collect linked binaries. - let linkedPackageBinaries: [PIFPackageBuilder.LinkedPackageBinary] = mainModule.dependencies.compactMap { - PIFPackageBuilder.LinkedPackageBinary(dependency: $0, package: self.package) + let linkedPackageBinaries: [PackagePIFBuilder.LinkedPackageBinary] = mainModule.dependencies.compactMap { + PackagePIFBuilder.LinkedPackageBinary(dependency: $0, package: self.package) } - let moduleOrProduct = PIFPackageBuilder.ModuleOrProduct( + let moduleOrProduct = PackagePIFBuilder.ModuleOrProduct( type: moduleOrProductType, name: product.name, moduleName: product.c99name, - pifTarget: mainModulePifTarget, + pifTarget: .target(self.project[keyPath: mainModuleTargetKeyPath]), indexableFileURLs: indexableFileURLs, headerFiles: headerFiles, linkedPackageBinaries: linkedPackageBinaries, @@ -449,12 +496,12 @@ extension PackagePIFProjectBuilder { self.builtModulesAndProducts.append(moduleOrProduct) } - private func handleProduct( + private mutating func handleProduct( _ product: PackageGraph.ResolvedProduct, with packageConditions: [PackageModel.PackageCondition], isLinkable: Bool, - pifTarget: SwiftBuild.PIF.Target, - settings: inout SwiftBuild.PIF.BuildSettings + targetKeyPath: WritableKeyPath, + settings: inout ProjectModel.BuildSettings ) { // Do not add a dependency for binary-only executable products since they are not part of the build. if product.isBinaryOnlyExecutableProduct { @@ -463,14 +510,15 @@ extension PackagePIFProjectBuilder { if !pifBuilder.delegate.shouldSuppressProductDependency(product: product.underlying, buildSettings: &settings) { let shouldLinkProduct = isLinkable - pifTarget.addDependency( + self.project[keyPath: targetKeyPath].common.addDependency( on: product.pifTargetGUID(), platformFilters: packageConditions.toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: shouldLinkProduct ) log( .debug, - ".. added \(shouldLinkProduct ? "linked " : "")dependency on product '\(product.pifTargetGUID()))'" + indent: 1, + "Added \(shouldLinkProduct ? "linked " : "")dependency on product '\(product.pifTargetGUID()))'" ) } } @@ -503,14 +551,13 @@ extension PackagePIFProjectBuilder { dynamicLibraryVariant.isDynamicLibraryVariant = true self.builtModulesAndProducts.append(dynamicLibraryVariant) - let pifTarget = library.pifTarget as? SwiftBuild.PIF.Target - let dynamicPifTarget = dynamicLibraryVariant.pifTarget as? SwiftBuild.PIF.Target - - if let pifTarget, let dynamicPifTarget { - pifTarget.dynamicTargetVariant = dynamicPifTarget - } else { - assertionFailure("Could not assign dynamic PIF target") + guard let pifTarget = library.pifTarget, + let pifTargetKeyPath = self.project.findTarget(id: pifTarget.id), + let dynamicPifTarget = dynamicLibraryVariant.pifTarget + else { + fatalError("Could not assign dynamic PIF target") } + self.project[keyPath: pifTargetKeyPath].dynamicTargetVariantId = dynamicPifTarget.id } } @@ -520,19 +567,19 @@ extension PackagePIFProjectBuilder { /// all SwiftPM library products are represented by two PIF targets: /// one of the "native" manifestation that gets linked into the client, /// and another for a dynamic framework specifically for use by the development-time features. - private func buildLibraryProduct( + private mutating func buildLibraryProduct( _ product: PackageGraph.ResolvedProduct, type desiredProductType: ProductType.LibraryType, targetSuffix: TargetGUIDSuffix? = nil, embedResources: Bool - ) throws -> PIFPackageBuilder.ModuleOrProduct { + ) throws -> PackagePIFBuilder.ModuleOrProduct { precondition(product.type.isLibrary) // FIXME: Cleanup this mess with let pifTargetProductName: String let executableName: String - let productType: SwiftBuild.PIF.Target.ProductType + let productType: ProjectModel.Target.ProductType if desiredProductType == .dynamic { if pifBuilder.createDylibForDynamicProducts { @@ -540,8 +587,8 @@ extension PackagePIFProjectBuilder { executableName = pifTargetProductName productType = .dynamicLibrary } else { - // If a product is explicitly declared dynamic, we preserve its name, otherwise we will compute an - // automatic one. + // If a product is explicitly declared dynamic, we preserve its name, + // otherwise we will compute an automatic one. if product.libraryType == .dynamic { if let customExecutableName = pifBuilder.delegate .customExecutableName(product: product.underlying) @@ -551,7 +598,7 @@ extension PackagePIFProjectBuilder { executableName = product.name } } else { - executableName = PIFPackageBuilder.computePackageProductFrameworkName(productName: product.name) + executableName = PackagePIFBuilder.computePackageProductFrameworkName(productName: product.name) } pifTargetProductName = "\(executableName).framework" productType = .framework @@ -563,51 +610,77 @@ extension PackagePIFProjectBuilder { } // Create a special kind of PIF target that just "groups" a set of targets for clients to depend on. - // SwiftBuild will *not* produce a separate artifact for a package product, but will instead consider any - // dependency on - // the package product to be a dependency on the whole set of targets on which the package product depends. - let pifTarget = try self.pif.addTargetThrowing( - id: product.pifTargetGUID(suffix: targetSuffix), - productType: productType, - name: product.name, - productName: pifTargetProductName - ) - log( - .debug, - "created \(type(of: pifTarget)) '\(pifTarget.id)' of type '\(pifTarget.productType.asString)' with name '\(pifTarget.name)' and product name '\(pifTarget.productName)'" - ) + // Swift Build will *not* produce a separate artifact for a package product, but will instead consider any + // dependency on the package product to be a dependency on the whole set of targets + // on which the package product depends. + let librayUmbrellaTargetKeyPath = try self.project.addTarget { _ in + ProjectModel.Target( + id: product.pifTargetGUID(suffix: targetSuffix), + productType: productType, + name: product.name, + productName: pifTargetProductName + ) + } + do { + let librayTarget = self.project[keyPath: librayUmbrellaTargetKeyPath] + log( + .debug, + "Created target '\(librayTarget.id)' of type '\(librayTarget.productType)' with " + + "name '\(librayTarget.name)' and product name '\(librayTarget.productName)'" + ) + } // Add linked dependencies on the *targets* that comprise the product. for module in product.modules { // Binary targets are special in that they are just linked, not built. if let binaryTarget = module.underlying as? BinaryModule { - let binaryReference = self.binaryGroup.addFileReference(path: binaryTarget.artifactPath.pathString) - pifTarget.addLibrary(ref: binaryReference, codeSignOnCopy: true, removeHeadersOnCopy: true) - log(.debug, ".. added use of binary library '\(binaryTarget.artifactPath.pathString)'") + let binaryFileRef = self.binaryGroup.addFileReference { id in + FileReference(id: id, path: binaryTarget.artifactPath.pathString) + } + self.project[keyPath: librayUmbrellaTargetKeyPath].addLibrary { id in + BuildFile(id: id, fileRef: binaryFileRef, codeSignOnCopy: true, removeHeadersOnCopy: true) + } + log(.debug, indent: 1, "Added use of binary library '\(binaryTarget.artifactPath)'") continue } // We add these as linked dependencies; because the product type is `.packageProduct`, // SwiftBuild won't actually link them, but will instead impart linkage to any clients that // link against the package product. - pifTarget.addDependency(on: module.pifTargetGUID(), platformFilters: [], linkProduct: true) - log(.debug, ".. added linked dependency on target '\(module.pifTargetGUID())'") + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( + on: module.pifTargetGUID(), + platformFilters: [], + linkProduct: true + ) + log(.debug, indent: 1, "Added linked dependency on target '\(module.pifTargetGUID())'") } for module in product.modules where module.underlying.isSourceModule && module.resources.hasContent { - // FIXME: Find a way to determine whether a module has generated resources here so that we can embed resources into dynamic targets. - pifTarget.addDependency(on: pifTargetIdForResourceBundle(module.name), platformFilters: []) + // FIXME: Find a way to determine whether a module has generated resources + // here so that we can embed resources into dynamic targets. + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( + on: pifTargetIdForResourceBundle(module.name), + platformFilters: [] + ) - let filreRef = self.pif.mainGroup - .addFileReference(path: "$(CONFIGURATION_BUILD_DIR)/\(package.name)_\(module.name).bundle") + let packageName = self.package.name + let fileRef = self.project.mainGroup.addFileReference { id in + FileReference(id: id, path: "$(CONFIGURATION_BUILD_DIR)/\(packageName)_\(module.name).bundle") + } if embedResources { - pifTarget.addResourceFile(ref: filreRef) - log(.debug, ".. added use of resource bundle '\(filreRef.path)'") + self.project[keyPath: librayUmbrellaTargetKeyPath].addResourceFile { id in + BuildFile(id: id, fileRef: fileRef) + } + log(.debug, indent: 1, "Added use of resource bundle '\(fileRef.path)'") } else { - log(.debug, ".. ignored resource bundle '\(filreRef.path)' because resource embedding is disabled") + log( + .debug, + indent: 1, + "Ignored resource bundle '\(fileRef.path)' because resource embedding is disabled" + ) } } - var settings: SwiftBuild.PIF.BuildSettings = package.underlying.packageBaseBuildSettings + var settings: ProjectModel.BuildSettings = package.underlying.packageBaseBuildSettings // Add other build settings when we're building an actual dylib. if desiredProductType == .dynamic { @@ -621,22 +694,23 @@ extension PackagePIFProjectBuilder { installPath: installPath(for: product.underlying), delegate: pifBuilder.delegate ) - - pifTarget.addSourcesBuildPhase() + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addSourcesBuildPhase { id in + ProjectModel.SourcesBuildPhase(id: id) + } } // Additional configuration and files for this library product. pifBuilder.delegate.configureLibraryProduct( product: product.underlying, - pifTarget: pifTarget, - additionalFiles: self.additionalFilesGroup + target: librayUmbrellaTargetKeyPath, + additionalFiles: additionalFilesGroupKeyPath ) // If the given package is a root package or it is used via a branch/revision, we allow unsafe flags. - let implicitlyAllowAllUnsafeFlags = pifBuilder.delegate.isBranchOrRevisionBased || pifBuilder.delegate - .isUserManaged + let implicitlyAllowAllUnsafeFlags = pifBuilder.delegate.isBranchOrRevisionBased || + pifBuilder.delegate.isUserManaged let recordUsesUnsafeFlags = try !implicitlyAllowAllUnsafeFlags && product.usesUnsafeFlags - settings.USES_SWIFTPM_UNSAFE_FLAGS = recordUsesUnsafeFlags ? "YES" : "NO" + settings[.USES_SWIFTPM_UNSAFE_FLAGS] = recordUsesUnsafeFlags ? "YES" : "NO" // Handle the dependencies of the targets in the product // (and link against them, which in the case of a package product, really just means that clients should link @@ -647,34 +721,39 @@ extension PackagePIFProjectBuilder { // This assertion is temporarily disabled since we may see targets from // _other_ packages, but this should be resolved; see rdar://95467710. /* assert(moduleDependency.packageName == self.package.name) */ - + if moduleDependency.type == .systemModule { - log(.debug, ".. noted use of system module '\(moduleDependency.name)'") + log(.debug, indent: 1, "Noted use of system module '\(moduleDependency.name)'") return } if let binaryTarget = moduleDependency.underlying as? BinaryModule { - let binaryReference = self.binaryGroup.addFileReference(path: binaryTarget.path.pathString) - pifTarget.addLibrary( - ref: binaryReference, - platformFilters: packageConditions - .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), - codeSignOnCopy: true, - removeHeadersOnCopy: true - ) - log(.debug, ".. added use of binary library '\(binaryTarget.path)'") + let binaryFileRef = self.binaryGroup.addFileReference { id in + FileReference(id: id, path: binaryTarget.path.pathString) + } + let toolsVersion = package.manifest.toolsVersion + self.project[keyPath: librayUmbrellaTargetKeyPath].addLibrary { id in + BuildFile( + id: id, + fileRef: binaryFileRef, + platformFilters: packageConditions.toPlatformFilter(toolsVersion: toolsVersion), + codeSignOnCopy: true, + removeHeadersOnCopy: true + ) + } + log(.debug, indent: 1, "Added use of binary library '\(binaryTarget.path)'") return } if moduleDependency.type == .plugin { let dependencyId = moduleDependency.pifTargetGUID() - pifTarget.addDependency( + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( on: dependencyId, platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: false ) - log(.debug, ".. added use of plugin target '\(dependencyId)'") + log(.debug, indent: 1, "Added use of plugin target '\(dependencyId)'") return } @@ -689,28 +768,29 @@ extension PackagePIFProjectBuilder { if let product = moduleDependency .productRepresentingDependencyOfBuildPlugin(in: mainModuleProducts) { - pifTarget.addDependency( + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( on: product.pifTargetGUID(), platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: false ) - log(.debug, ".. added dependency on product '\(product.pifTargetGUID())'") + log(.debug, indent: 1, "Added dependency on product '\(product.pifTargetGUID())'") return } else { log( .debug, - ".. could not find a build plugin product to depend on for target '\(product.pifTargetGUID()))'" + indent: 1, + "Could not find a build plugin product to depend on for target '\(product.pifTargetGUID()))'" ) } } - pifTarget.addDependency( + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( on: moduleDependency.pifTargetGUID(), platformFilters: packageConditions.toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: true ) - log(.debug, ".. added linked dependency on target '\(moduleDependency.pifTargetGUID()))'") + log(.debug, indent: 1, "Added linked dependency on target '\(moduleDependency.pifTargetGUID()))'") case .product(let productDependency, let packageConditions): // Do not add a dependency for binary-only executable products since they are not part of the build. @@ -723,7 +803,7 @@ extension PackagePIFProjectBuilder { buildSettings: &settings ) { let shouldLinkProduct = productDependency.isLinkable - pifTarget.addDependency( + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( on: productDependency.pifTargetGUID(), platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), @@ -731,7 +811,8 @@ extension PackagePIFProjectBuilder { ) log( .debug, - ".. added \(shouldLinkProduct ? "linked" : "") dependency on product '\(productDependency.pifTargetGUID()))'" + indent: 1, + "Added \(shouldLinkProduct ? "linked" : "") dependency on product '\(productDependency.pifTargetGUID()))'" ) } } @@ -754,29 +835,33 @@ extension PackagePIFProjectBuilder { let encoder = PropertyListEncoder() encoder.outputFormat = .xml let data = try encoder.encode(signatureData) - settings.PACKAGE_REGISTRY_SIGNATURE = String(data: data, encoding: .utf8) + settings[.PACKAGE_REGISTRY_SIGNATURE] = String(data: data, encoding: .utf8) } - pifTarget.addBuildConfig(name: "Debug", settings: settings) - pifTarget.addBuildConfig(name: "Release", settings: settings) + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Debug", settings: settings) + } + self.project[keyPath: librayUmbrellaTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Release", settings: settings) + } // Collect linked binaries. let linkedPackageBinaries = product.modules.compactMap { - PIFPackageBuilder.LinkedPackageBinary(module: $0, package: self.package) + PackagePIFBuilder.LinkedPackageBinary(module: $0, package: self.package) } - let moduleOrProductType: PIFPackageBuilder.ModuleOrProductType = switch product.libraryType { + let moduleOrProductType: PackagePIFBuilder.ModuleOrProductType = switch product.libraryType { case .dynamic: pifBuilder.createDylibForDynamicProducts ? .dynamicLibrary : .framework default: .staticArchive } - return PIFPackageBuilder.ModuleOrProduct( + return PackagePIFBuilder.ModuleOrProduct( type: moduleOrProductType, name: product.name, moduleName: product.c99name, - pifTarget: pifTarget, + pifTarget: .target(self.project[keyPath: librayUmbrellaTargetKeyPath]), indexableFileURLs: [], headerFiles: [], linkedPackageBinaries: linkedPackageBinaries, @@ -791,34 +876,42 @@ extension PackagePIFProjectBuilder { mutating func makeSystemLibraryProduct(_ product: PackageGraph.ResolvedProduct) throws { precondition(product.type == .library(.automatic)) - let pifTarget = try self.pif.addTargetThrowing( - id: product.pifTargetGUID(), - productType: .packageProduct, - name: product.name, - productName: product.name - ) - - log( - .debug, - "created \(type(of: pifTarget)) '\(pifTarget.id)' of type '\(pifTarget.productType.asString)' " + - "with name '\(pifTarget.name)' and product name '\(pifTarget.productName)'" - ) + let systemLibraryTargetKeyPath = try self.project.addTarget { _ in + ProjectModel.Target( + id: product.pifTargetGUID(), + productType: .packageProduct, + name: product.name, + productName: product.name + ) + } + do { + let systemLibraryTarget = self.project[keyPath: systemLibraryTargetKeyPath] + log( + .debug, + "Created target '\(systemLibraryTarget.id)' of type '\(systemLibraryTarget.productType)' " + + "with name '\(systemLibraryTarget.name)' and product name '\(systemLibraryTarget.productName)'" + ) + } let buildSettings = self.package.underlying.packageBaseBuildSettings - pifTarget.addBuildConfig(name: "Debug", settings: buildSettings) - pifTarget.addBuildConfig(name: "Release", settings: buildSettings) + self.project[keyPath: systemLibraryTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Debug", settings: buildSettings) + } + self.project[keyPath: systemLibraryTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Release", settings: buildSettings) + } - pifTarget.addDependency( + self.project[keyPath: systemLibraryTargetKeyPath].common.addDependency( on: product.systemModule!.pifTargetGUID(), platformFilters: [], linkProduct: false ) - let systemLibrary = PIFPackageBuilder.ModuleOrProduct( + let systemLibrary = PackagePIFBuilder.ModuleOrProduct( type: .staticArchive, name: product.name, moduleName: product.c99name, - pifTarget: pifTarget, + pifTarget: .target(self.project[keyPath: systemLibraryTargetKeyPath]), indexableFileURLs: [], headerFiles: [], linkedPackageBinaries: [], @@ -834,24 +927,33 @@ extension PackagePIFProjectBuilder { mutating func makePluginProduct(_ pluginProduct: PackageGraph.ResolvedProduct) throws { precondition(pluginProduct.type == .plugin) - let pluginPifTarget = self.pif.addAggregateTarget( - id: pluginProduct.pifTargetGUID(), - name: pluginProduct.name - ) - log(.debug, "created \(type(of: pluginPifTarget)) '\(pluginPifTarget.id)' with name '\(pluginPifTarget.name)'") + let pluginTargetKeyPath = try self.project.addAggregateTarget { _ in + ProjectModel.AggregateTarget( + id: pluginProduct.pifTargetGUID(), + name: pluginProduct.name + ) + } + do { + let pluginTarget = self.project[keyPath: pluginTargetKeyPath] + log(.debug, "Created aggregate target '\(pluginTarget.id)' with name '\(pluginTarget.name)'") + } - let buildSettings: SwiftBuild.PIF.BuildSettings = package.underlying.packageBaseBuildSettings - pluginPifTarget.addBuildConfig(name: "Debug", settings: buildSettings) - pluginPifTarget.addBuildConfig(name: "Release", settings: buildSettings) + let buildSettings: ProjectModel.BuildSettings = package.underlying.packageBaseBuildSettings + self.project[keyPath: pluginTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Debug", settings: buildSettings) + } + self.project[keyPath: pluginTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Release", settings: buildSettings) + } for pluginModule in pluginProduct.pluginModules! { - pluginPifTarget.addDependency( + self.project[keyPath: pluginTargetKeyPath].common.addDependency( on: pluginModule.pifTargetGUID(), platformFilters: [] ) } - let pluginType: PIFPackageBuilder.ModuleOrProductType = { + let pluginType: PackagePIFBuilder.ModuleOrProductType = { if let pluginTarget = pluginProduct.pluginModules!.only { switch pluginTarget.capability { case .buildTool: @@ -867,11 +969,11 @@ extension PackagePIFProjectBuilder { } }() - let pluginProductMetadata = PIFPackageBuilder.ModuleOrProduct( + let pluginProductMetadata = PackagePIFBuilder.ModuleOrProduct( type: pluginType, name: pluginProduct.name, moduleName: pluginProduct.c99name, - pifTarget: pluginPifTarget, + pifTarget: .aggregate(self.project[keyPath: pluginTargetKeyPath]), indexableFileURLs: [], headerFiles: [], linkedPackageBinaries: [], diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift index e41074799ef..149d97e84ab 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift @@ -31,19 +31,31 @@ import struct PackageLoading.FileRuleDescription import struct PackageLoading.TargetSourcesBuilder #if canImport(SwiftBuild) -import enum SwiftBuild.PIF + +import struct SwiftBuild.Pair +import enum SwiftBuild.ProjectModel import struct SwiftBuild.SwiftBuildFileType /// Helper type to create PIF **project** and **targets** for a given package. struct PackagePIFProjectBuilder { - let pifBuilder: PIFPackageBuilder + let pifBuilder: PackagePIFBuilder let package: PackageGraph.ResolvedPackage let packageManifest: PackageModel.Manifest let modulesGraph: PackageGraph.ModulesGraph - let pif: SwiftBuild.PIF.Project - let binaryGroup: SwiftBuild.PIF.Group - let additionalFilesGroup: SwiftBuild.PIF.Group + var project: ProjectModel.Project + + let binaryGroupKeyPath: WritableKeyPath + var binaryGroup: ProjectModel.Group { + get { self.project.mainGroup[keyPath: self.binaryGroupKeyPath] } + set { self.project.mainGroup[keyPath: self.binaryGroupKeyPath] = newValue } + } + + let additionalFilesGroupKeyPath: WritableKeyPath + var additionalFilesGroup: ProjectModel.Group { + get { self.project.mainGroup[keyPath: self.additionalFilesGroupKeyPath] } + set { self.project.mainGroup[keyPath: self.additionalFilesGroupKeyPath] = newValue } + } let declaredPlatforms: [PackageModel.Platform]? let deploymentTargets: [PackageModel.Platform: String?] @@ -55,23 +67,25 @@ struct PackagePIFProjectBuilder { /// bit of information from processing the *products* to processing the *targets*. var mainModuleTargetNamesWithResources: Set = [] - var builtModulesAndProducts: [PIFPackageBuilder.ModuleOrProduct] + var builtModulesAndProducts: [PackagePIFBuilder.ModuleOrProduct] func log( _ severity: Diagnostic.Severity, + indent: Int = 0, _ message: String, sourceFile: StaticString = #fileID, sourceLine: UInt = #line ) { - self.pifBuilder.log(severity, message, sourceFile: sourceFile, sourceLine: sourceLine) + let levelPrefix = String(repeating: " ", count: indent) + self.pifBuilder.log(severity, levelPrefix + message, sourceFile: sourceFile, sourceLine: sourceLine) } - init(createForPackage package: PackageGraph.ResolvedPackage, builder: PIFPackageBuilder) { + init(createForPackage package: PackageGraph.ResolvedPackage, builder: PackagePIFBuilder) { // Create a PIF project using an identifier that's based on the normalized absolute path of the package. // We use the package manifest path as the project path, and the package path as the project's base source // directory. // FIXME: The PIF creation should ideally be done on a background thread. - let pifProject = SwiftBuild.PIF.Project( + var pifProject = ProjectModel.Project( id: "PACKAGE:\(package.identity)", path: package.manifest.path.pathString, projectDir: package.path.pathString, @@ -79,12 +93,22 @@ struct PackagePIFProjectBuilder { developmentRegion: package.manifest.defaultLocalization ) - let additionalFilesGroup = pifProject.mainGroup.addGroup( - path: "/", - pathBase: .absolute, - name: "AdditionalFiles" - ) - let binaryGroup = pifProject.mainGroup.addGroup(path: "/", pathBase: .absolute, name: "Binaries") + let additionalFilesGroupKeyPath = pifProject.mainGroup.addGroup { id in + ProjectModel.Group( + id: id, + path: "/", + pathBase: .absolute, + name: "AdditionalFiles" + ) + } + let binaryGroupKeyPath = pifProject.mainGroup.addGroup { id in + ProjectModel.Group( + id: id, + path: "/", + pathBase: .absolute, + name: "Binaries" + ) + } // Test modules have a higher minimum deployment target by default, // so we favor non-test modules as representative for the package's deployment target. @@ -119,9 +143,9 @@ struct PackagePIFProjectBuilder { self.package = package self.packageManifest = self.pifBuilder.packageManifest self.modulesGraph = self.pifBuilder.modulesGraph - self.pif = pifProject - self.binaryGroup = binaryGroup - self.additionalFilesGroup = additionalFilesGroup + self.project = pifProject + self.binaryGroupKeyPath = binaryGroupKeyPath + self.additionalFilesGroupKeyPath = additionalFilesGroupKeyPath self.declaredPlatforms = declaredPlatforms self.deploymentTargets = deploymentTargets self.dynamicLibraryProductNames = dynamicLibraryProductNames @@ -130,13 +154,13 @@ struct PackagePIFProjectBuilder { // MARK: - Handling Resources - func addResourceBundle( + mutating func addResourceBundle( for module: PackageGraph.ResolvedModule, - pifTarget: SwiftBuild.PIF.Target, + targetKeyPath: WritableKeyPath, generatedResourceFiles: [String] - ) throws -> (PIFPackageBuilder.EmbedResourcesResult, PIFPackageBuilder.ModuleOrProduct?) { + ) throws -> (PackagePIFBuilder.EmbedResourcesResult, PackagePIFBuilder.ModuleOrProduct?) { if module.resources.isEmpty && generatedResourceFiles.isEmpty { - return (PIFPackageBuilder.EmbedResourcesResult( + return (PackagePIFBuilder.EmbedResourcesResult( bundleName: nil, shouldGenerateBundleAccessor: false, shouldGenerateEmbedInCodeAccessor: false @@ -144,53 +168,71 @@ struct PackagePIFProjectBuilder { } let bundleName = self.resourceBundleName(forModuleName: module.name) - let resourcesTarget = try self.pif.addTargetThrowing( - id: self.pifTargetIdForResourceBundle(module.name), - productType: .bundle, - name: bundleName, - productName: bundleName - ) + let resourceBundleGUID = self.pifTargetIdForResourceBundle(module.name) + let resourcesTargetKeyPath = try self.project.addTarget { _ in + ProjectModel.Target( + id: resourceBundleGUID, + productType: .bundle, + name: bundleName, + productName: bundleName + ) + } + var resourcesTarget: ProjectModel.Target { self.project[keyPath: resourcesTargetKeyPath] } - pifTarget.addDependency(on: resourcesTarget.id, platformFilters: [], linkProduct: false) - self.log(.debug, ".. added dependency on resource target '\(resourcesTarget.id)'") + self.project[keyPath: targetKeyPath].common.addDependency( + on: resourcesTarget.id, + platformFilters: [], + linkProduct: false + ) + self.log(.debug, indent: 1, "Added dependency on resource target '\(resourcesTarget.id)'") for pluginModule in module.pluginsAppliedToModule { - resourcesTarget.addDependency(on: pluginModule.pifTargetGUID(), linkProduct: false) + self.project[keyPath: resourcesTargetKeyPath].common.addDependency( + on: pluginModule.pifTargetGUID(), + platformFilters: [], + linkProduct: false + ) } self.log( .debug, - ".. created \(type(of: resourcesTarget)) '\(resourcesTarget.id)' of type '\(resourcesTarget.productType.asString)' with name '\(resourcesTarget.name)' and product name '\(resourcesTarget.productName)'" + indent: 1, + "Created \(type(of: resourcesTarget)) '\(resourcesTarget.id)' of type '\(resourcesTarget.productType)' " + + "with name '\(resourcesTarget.name)' and product name '\(resourcesTarget.productName)'" ) - var settings: SwiftBuild.PIF.BuildSettings = self.package.underlying.packageBaseBuildSettings - settings.TARGET_NAME = bundleName - settings.PRODUCT_NAME = "$(TARGET_NAME)" - settings.PRODUCT_MODULE_NAME = bundleName - settings.PRODUCT_BUNDLE_IDENTIFIER = "\(self.package.identity).\(module.name).resources" + var settings: ProjectModel.BuildSettings = self.package.underlying.packageBaseBuildSettings + settings[.TARGET_NAME] = bundleName + settings[.PRODUCT_NAME] = "$(TARGET_NAME)" + settings[.PRODUCT_MODULE_NAME] = bundleName + settings[.PRODUCT_BUNDLE_IDENTIFIER] = "\(self.package.identity).\(module.name).resources" .spm_mangledToBundleIdentifier() - settings.EXECUTABLE_NAME = "" - settings.GENERATE_INFOPLIST_FILE = "YES" - settings.PACKAGE_RESOURCE_TARGET_KIND = "resource" + settings[.EXECUTABLE_NAME] = "" + settings[.GENERATE_INFOPLIST_FILE] = "YES" + settings[.PACKAGE_RESOURCE_TARGET_KIND] = "resource" - settings.COREML_COMPILER_CONTAINER = "swift-package" - settings.COREML_CODEGEN_LANGUAGE = "None" + settings[.COREML_COMPILER_CONTAINER] = "swift-package" + settings[.COREML_CODEGEN_LANGUAGE] = "None" - resourcesTarget.addBuildConfig(name: "Debug", settings: settings) - resourcesTarget.addBuildConfig(name: "Release", settings: settings) + self.project[keyPath: resourcesTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Debug", settings: settings) + } + self.project[keyPath: resourcesTargetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: "Release", settings: settings) + } let result = self.processResources( for: module, - sourceModulePifTarget: pifTarget, - resourceBundlePifTarget: resourcesTarget, + sourceModuleTargetKeyPath: targetKeyPath, + resourceBundleTargetKeyPath: resourcesTargetKeyPath, generatedResourceFiles: generatedResourceFiles ) - let resourceBundle = PIFPackageBuilder.ModuleOrProduct( + let resourceBundle = PackagePIFBuilder.ModuleOrProduct( type: .resourceBundle, name: bundleName, moduleName: bundleName, - pifTarget: resourcesTarget, + pifTarget: .target(resourcesTarget), indexableFileURLs: [], headerFiles: [], linkedPackageBinaries: [], @@ -202,28 +244,29 @@ struct PackagePIFProjectBuilder { return (result, resourceBundle) } - func processResources( + mutating func processResources( for module: PackageGraph.ResolvedModule, - sourceModulePifTarget: SwiftBuild.PIF.Target, - resourceBundlePifTarget: SwiftBuild.PIF.Target?, + sourceModuleTargetKeyPath: WritableKeyPath, + resourceBundleTargetKeyPath: WritableKeyPath?, generatedResourceFiles: [String] - ) -> PIFPackageBuilder.EmbedResourcesResult { + ) -> PackagePIFBuilder.EmbedResourcesResult { if module.resources.isEmpty && generatedResourceFiles.isEmpty { - return PIFPackageBuilder.EmbedResourcesResult( + return PackagePIFBuilder.EmbedResourcesResult( bundleName: nil, shouldGenerateBundleAccessor: false, shouldGenerateEmbedInCodeAccessor: false ) } - // If `resourceBundlePifTarget` is nil, we add resources to the `sourceModulePifTarget`. - let pifTargetForResources = resourceBundlePifTarget ?? sourceModulePifTarget - + // If resourceBundleTarget is nil, we add resources to the sourceModuleTarget instead. + let targetForResourcesKeyPath: WritableKeyPath = + resourceBundleTargetKeyPath ?? sourceModuleTargetKeyPath + // Generated resources get a default treatment for rule and localization. let generatedResources = generatedResourceFiles.compactMap { - PIFPackageBuilder.Resource(path: $0, rule: .process(localization: nil)) + PackagePIFBuilder.Resource(path: $0, rule: .process(localization: nil)) } - let resources = module.resources.map { PIFPackageBuilder.Resource($0) } + generatedResources + let resources = module.resources.map { PackagePIFBuilder.Resource($0) } + generatedResources let shouldGenerateBundleAccessor = resources.anySatisfy { $0.rule != .embedInCode } let shouldGenerateEmbedInCodeAccessor = resources.anySatisfy { $0.rule == .embedInCode } @@ -231,9 +274,9 @@ struct PackagePIFProjectBuilder { let resourcePath = resource.path // Add a file reference for the resource. We use an absolute path, as for all the other files, // but we should be able to optimize this later by making it group-relative. - let ref = self.pif.mainGroup.addFileReference( - path: resourcePath, pathBase: .absolute - ) + let ref = self.project.mainGroup.addFileReference { id in + ProjectModel.FileReference(id: id, path: resourcePath, pathBase: .absolute) + } // CoreData files should also be in the actual target because they // can end up generating code during the build. @@ -242,8 +285,10 @@ struct PackagePIFProjectBuilder { .contains { $0.fileTypes.contains(resourcePath.pathExtension) } if isCoreDataFile { - sourceModulePifTarget.addSourceFile(ref: ref) - self.log(.debug, ".. .. added core data resource as source file '\(resourcePath)'") + self.project[keyPath: sourceModuleTargetKeyPath].addSourceFile { id in + BuildFile(id: id, fileRef: ref) + } + self.log(.debug, indent: 2, "Added core data resource as source file '\(resourcePath)'") } // Core ML files need to be included in the source module as well, because there is code generation. @@ -251,49 +296,68 @@ struct PackagePIFProjectBuilder { let isCoreMLFile = coreMLFileTypes.contains { $0.fileTypes.contains(resourcePath.pathExtension) } if isCoreMLFile { - sourceModulePifTarget.addSourceFile(ref: ref, generatedCodeVisibility: .public) - self.log(.debug, ".. .. added coreml resource as source file '\(resourcePath)'") + self.project[keyPath: sourceModuleTargetKeyPath].addSourceFile { id in + BuildFile(id: id, fileRef: ref, generatedCodeVisibility: .public) + } + self.log(.debug, indent: 2, "Added coreml resource as source file '\(resourcePath)'") } // Metal source code needs to be added to the source build phase. let isMetalFile = SwiftBuild.SwiftBuildFileType.metal.fileTypes.contains(resourcePath.pathExtension) if isMetalFile { - pifTargetForResources.addSourceFile(ref: ref) + self.project[keyPath: targetForResourcesKeyPath].addSourceFile { id in + BuildFile(id: id, fileRef: ref) + } } else { // FIXME: Handle additional rules here (e.g. `.copy`). - pifTargetForResources.addResourceFile( - ref: ref, - platformFilters: [], - resourceRule: resource.rule == .embedInCode ? .embedInCode : .process - ) + self.project[keyPath: targetForResourcesKeyPath].addResourceFile { id in + BuildFile( + id: id, + fileRef: ref, + platformFilters: [], + resourceRule: resource.rule == .embedInCode ? .embedInCode : .process + ) + } } // Asset Catalogs need to be included in the sources modules for generated asset symbols. let isAssetCatalog = resourcePath.pathExtension == "xcassets" if isAssetCatalog { - sourceModulePifTarget.addSourceFile(ref: ref) - self.log(.debug, ".. .. added asset catalog as source file '\(resourcePath)'") + self.project[keyPath: sourceModuleTargetKeyPath].addSourceFile { id in + BuildFile(id: id, fileRef: ref) + } + self.log(.debug, indent: 2, "Added asset catalog as source file '\(resourcePath)'") } - self.log(.debug, ".. .. added resource file '\(resourcePath)'") + self.log(.debug, indent: 2, "Added resource file '\(resourcePath)'") } - return PIFPackageBuilder.EmbedResourcesResult( - bundleName: resourceBundlePifTarget?.name, + let resourceBundleTargetName: String? + if let resourceBundleTargetKeyPath { + let resourceBundleTarget = self.project[keyPath: resourceBundleTargetKeyPath] + resourceBundleTargetName = resourceBundleTarget.name + } else { + resourceBundleTargetName = nil + } + + return PackagePIFBuilder.EmbedResourcesResult( + bundleName: resourceBundleTargetName, shouldGenerateBundleAccessor: shouldGenerateBundleAccessor, shouldGenerateEmbedInCodeAccessor: shouldGenerateEmbedInCodeAccessor ) } - func resourceBundleTarget(forModuleName name: String) -> SwiftBuild.PIF.Target? { + func resourceBundleTargetKeyPath( + forModuleName name: String + ) -> WritableKeyPath? { let resourceBundleGUID = self.pifTargetIdForResourceBundle(name) - let target = self.pif.targets.only { $0.id == resourceBundleGUID } as? SwiftBuild.PIF.Target - return target + let targetKeyPath = self.project.findTarget(id: resourceBundleGUID) + return targetKeyPath } - func pifTargetIdForResourceBundle(_ name: String) -> String { - "PACKAGE-RESOURCE:\(name)" + func pifTargetIdForResourceBundle(_ name: String) -> GUID { + GUID("PACKAGE-RESOURCE:\(name)") } func resourceBundleName(forModuleName name: String) -> String { @@ -307,9 +371,9 @@ struct PackagePIFProjectBuilder { /// /// The reason we might not add them is that some targets are derivatives of other targets — in such cases, /// only the primary target adds the build tool commands to the PIF target. - func computePluginGeneratedFiles( + mutating func computePluginGeneratedFiles( module: PackageGraph.ResolvedModule, - pifTarget: SwiftBuild.PIF.Target, + targetKeyPath: WritableKeyPath, addBuildToolPluginCommands: Bool ) -> (sourceFilePaths: [AbsolutePath], resourceFilePaths: [String]) { guard let pluginResult = pifBuilder.buildToolPluginResultsByTargetName[module.name] else { @@ -321,7 +385,7 @@ struct PackagePIFProjectBuilder { // If we've been asked to add build tool commands for the result, we do so now. if addBuildToolPluginCommands { for command in pluginResult.buildCommands { - self.addBuildToolCommand(command, to: pifTarget) + self.addBuildToolCommand(command, to: targetKeyPath) } } @@ -339,10 +403,10 @@ struct PackagePIFProjectBuilder { /// Helper function for adding build tool commands to the right PIF target depending on whether they generate /// sources or resources. - func addBuildToolCommands( + mutating func addBuildToolCommands( module: PackageGraph.ResolvedModule, - sourceModulePifTarget: SwiftBuild.PIF.Target, - resourceBundlePifTarget: SwiftBuild.PIF.Target, + sourceModuleTargetKeyPath: WritableKeyPath, + resourceBundleTargetKeyPath: WritableKeyPath, sourceFilePaths: [AbsolutePath], resourceFilePaths: [String] ) { @@ -354,9 +418,9 @@ struct PackagePIFProjectBuilder { let producesResources = Set(command.outputPaths).intersection(resourceFilePaths).hasContent if producesResources { - self.addBuildToolCommand(command, to: resourceBundlePifTarget) + self.addBuildToolCommand(command, to: resourceBundleTargetKeyPath) } else { - self.addBuildToolCommand(command, to: sourceModulePifTarget) + self.addBuildToolCommand(command, to: sourceModuleTargetKeyPath) } } } @@ -364,9 +428,9 @@ struct PackagePIFProjectBuilder { /// Adds build rules to `pifTarget` for any build tool commands from invocation results. /// Returns the absolute paths of any generated source files that should be added to the sources build phase of the /// PIF target. - func addBuildToolCommands( - from pluginInvocationResults: [PIFPackageBuilder.BuildToolPluginInvocationResult], - pifTarget: SwiftBuild.PIF.Target, + mutating func addBuildToolCommands( + from pluginInvocationResults: [PackagePIFBuilder.BuildToolPluginInvocationResult], + targetKeyPath: WritableKeyPath, addBuildToolPluginCommands: Bool ) -> [String] { var generatedSourceFileAbsPaths: [String] = [] @@ -374,7 +438,7 @@ struct PackagePIFProjectBuilder { // Create build rules for all the commands in the result. if addBuildToolPluginCommands { for command in result.buildCommands { - self.addBuildToolCommand(command, to: pifTarget) + self.addBuildToolCommand(command, to: targetKeyPath) } } // Add the paths of the generated source files, so that they can be added to the Sources build phase. @@ -384,19 +448,19 @@ struct PackagePIFProjectBuilder { } /// Adds a single plugin-created build command to a PIF target. - func addBuildToolCommand( - _ command: PIFPackageBuilder.CustomBuildCommand, - to pifTarget: SwiftBuild.PIF.Target + mutating func addBuildToolCommand( + _ command: PackagePIFBuilder.CustomBuildCommand, + to targetKeyPath: WritableKeyPath ) { var commandLine = [command.executable] + command.arguments if let sandbox = command.sandboxProfile, !pifBuilder.delegate.isPluginExecutionSandboxingDisabled { commandLine = try! sandbox.apply(to: commandLine) } - pifTarget.customTasks.append( - SwiftBuild.PIF.CustomTask( + self.project[keyPath: targetKeyPath].customTasks.append( + ProjectModel.CustomTask( commandLine: commandLine, - environment: command.environment.map { ($0, $1) }.sorted(by: <), + environment: command.environment.map { Pair($0, $1) }.sorted(by: <), workingDirectory: command.workingDir?.pathString, executionDescription: command.displayName ?? "Performing build tool plugin command", inputFilePaths: [command.executable] + command.inputPaths.map(\.pathString), From 60976eecce01ba858db96c10e5a17afff0b51831 Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Tue, 8 Apr 2025 13:16:16 -0400 Subject: [PATCH 03/33] Tests: Enabled SourceControlTests on Windows (#8453) Some tests in Tests/SourceControlTests are passing on Windows since upgrading the Git Version in the associated docker image, which was completd in https://github.com/swiftlang/swift-docker/pulls/452 Enable the tests that are skipped. Relates to: #8433 rdar://148248105 --- .../GitRepositoryTests.swift | 34 ------------------- .../RepositoryManagerTests.swift | 4 --- 2 files changed, 38 deletions(-) diff --git a/Tests/SourceControlTests/GitRepositoryTests.swift b/Tests/SourceControlTests/GitRepositoryTests.swift index ac905968bc5..daadddc3fd5 100644 --- a/Tests/SourceControlTests/GitRepositoryTests.swift +++ b/Tests/SourceControlTests/GitRepositoryTests.swift @@ -110,10 +110,6 @@ class GitRepositoryTests: XCTestCase { /// Check hash validation. func testGitRepositoryHash() throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - let validHash = "0123456789012345678901234567890123456789" XCTAssertNotEqual(GitRepository.Hash(validHash), nil) @@ -190,10 +186,6 @@ class GitRepositoryTests: XCTestCase { } func testSubmoduleRead() throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - try testWithTemporaryDirectory { path in let testRepoPath = path.appending("test-repo") try makeDirectories(testRepoPath) @@ -305,10 +297,6 @@ class GitRepositoryTests: XCTestCase { /// Test the handling of local checkouts. func testCheckouts() throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - try testWithTemporaryDirectory { path in // Create a test repository. let testRepoPath = path.appending("test-repo") @@ -355,10 +343,6 @@ class GitRepositoryTests: XCTestCase { } func testFetch() throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - try testWithTemporaryDirectory { path in // Create a repo. let testRepoPath = path.appending("test-repo") @@ -398,10 +382,6 @@ class GitRepositoryTests: XCTestCase { } func testHasUnpushedCommits() throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - try testWithTemporaryDirectory { path in // Create a repo. let testRepoPath = path.appending("test-repo") @@ -438,10 +418,6 @@ class GitRepositoryTests: XCTestCase { } func testSetRemote() throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - try testWithTemporaryDirectory { path in // Create a repo. let testRepoPath = path.appending("test-repo") @@ -554,10 +530,6 @@ class GitRepositoryTests: XCTestCase { } func testCheckoutRevision() throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - try testWithTemporaryDirectory { path in // Create a repo. let testRepoPath = path.appending("test-repo") @@ -690,8 +662,6 @@ class GitRepositoryTests: XCTestCase { } func testAlternativeObjectStoreValidation() throws { - try skipOnWindowsAsTestCurrentlyFails(because: "test might hang in CI") - try testWithTemporaryDirectory { path in // Create a repo. let testRepoPath = path.appending("test-repo") @@ -762,10 +732,6 @@ class GitRepositoryTests: XCTestCase { } func testMissingDefaultBranch() throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - try testWithTemporaryDirectory { path in // Create a repository. let testRepoPath = path.appending("test-repo") diff --git a/Tests/SourceControlTests/RepositoryManagerTests.swift b/Tests/SourceControlTests/RepositoryManagerTests.swift index cf74e43dd25..81b32496df4 100644 --- a/Tests/SourceControlTests/RepositoryManagerTests.swift +++ b/Tests/SourceControlTests/RepositoryManagerTests.swift @@ -19,10 +19,6 @@ import XCTest final class RepositoryManagerTests: XCTestCase { func testBasics() async throws { - try skipOnWindowsAsTestCurrentlyFails(because: """ - Test failed with: 0 [main] sh (9736) C:\\Program Files\\Git\\usr\\bin\\sh.exe: *** fatal error - add_item ("\\??\\C:\\Program Files\\Git", "/", ...) failed, errno 1 - """) - let fs = localFileSystem let observability = ObservabilitySystem.makeForTesting() From d4368fee54e69a11281ba5cc7f161a82f55a013f Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Tue, 8 Apr 2025 18:14:10 -0500 Subject: [PATCH 04/33] Clarify documented behavior of versioned manifest files in usage docs (#8462) This updates and clarifies the [Version-specific Manifest Selection](https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/Usage.md#version-specific-manifest-selection) section of the usage documentation to clarify the behavior when having either newer or older tools versions. ### Motivation: In general, when using the versioned manifest feature, it's preferable to have the unversioned `Package.swift` represent the newest-supported tools version, and only use version-specific manifest files for older versions. This provides a better workflow because it allows you to more easily drop support for older versions when ready (by avoiding the need to modify the unversioned file), among other benefits. ### Modifications: - "Reverse" the example shown in this documentation by having the unversioned file be newest. - Modernize the version numbers shown in examples. - Fix some non-inclusive language usages. - Add a footnote with some helpful historical context, and giving a recommendation. --- Documentation/Usage.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Documentation/Usage.md b/Documentation/Usage.md index 29b31495c5e..88e3a92a2b3 100644 --- a/Documentation/Usage.md +++ b/Documentation/Usage.md @@ -497,7 +497,7 @@ This feature is intended for use in the following scenarios: It is *not* expected that the packages would ever use this feature unless absolutely necessary to support existing clients. Specifically, packages *should not* -adopt this syntax for tagging versions supporting the _latest GM_ Swift +adopt this syntax for tagging versions supporting the _latest released_ Swift version. The package manager supports looking for any of the following marked tags, in @@ -511,7 +511,7 @@ order of preference: The package manager will additionally look for a version-specific marked manifest version when loading the particular version of a package, by searching -for a manifest in the form of `Package@swift-3.swift`. The set of markers +for a manifest in the form of `Package@swift-6.swift`. The set of markers looked for is the same as for version-specific tag selection. This feature is intended for use in cases where a package wishes to maintain @@ -521,20 +521,28 @@ changes in the manifest API). It is *not* expected the packages would ever use this feature unless absolutely necessary to support existing clients. Specifically, packages *should not* -adopt this syntax for tagging versions supporting the _latest GM_ Swift +adopt this syntax for tagging versions supporting the _latest released_ Swift version. In case the current Swift version doesn't match any version-specific manifest, the package manager will pick the manifest with the most compatible tools version. For example, if there are three manifests: -`Package.swift` (tools version 3.0) -`Package@swift-4.swift` (tools version 4.0) -`Package@swift-4.2.swift` (tools version 4.2) +- `Package.swift` (tools version 6.0) +- `Package@swift-5.10.swift` (tools version 5.10) +- `Package@swift-5.9.swift` (tools version 5.9) -The package manager will pick `Package.swift` on Swift 3, `Package@swift-4.swift` on -Swift 4, and `Package@swift-4.2.swift` on Swift 4.2 and above because its tools -version will be most compatible with future version of the package manager. +The package manager will pick `Package.swift` on Swift 6 and above, because its +tools version will be most compatible with future version of the package manager. +When using Swift 5.10, it will pick `Package@swift-5.10.swift`. Otherwise, when +using Swift 5.9 it will pick `Package@swift-5.9.swift`, and this is the minimum +tools version this package may be used with. + +A package may have versioned manifest files which specify newer tools versions +than its unversioned `Package.swift` file[^1]. In this scenario, the manifest +corresponding to the newest-compatible tools version will be used. + +[^1]: Support for having a versioned manifest file with a _newer_ tools version was required when the feature was first introduced, because prior versions of the package manager were not aware of the concept and only knew to look for the unversioned `Package.swift`. This is still supported, but there have been many Swift releases since the feature was introduced and it is now considered best practice to have `Package.swift` declare the newest-supported tools version and for versioned manifest files to only specifer older versions. ## Editing a Package From 451196234d81f82a147293105fd4467cdd69b6ac Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Tue, 8 Apr 2025 20:59:54 -0400 Subject: [PATCH 05/33] Tests: Enable some WorkspaceTests on Windows (#8463) Enable WorkspaceTests on Windows that, for some reason, previously failed. Relates: #8433 rdar://148248105 --- Tests/WorkspaceTests/WorkspaceTests.swift | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index a6948417aac..bb479d83893 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -171,8 +171,6 @@ final class WorkspaceTests: XCTestCase { } func testInterpreterFlags() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let fs = localFileSystem try testWithTemporaryDirectory { path in @@ -297,8 +295,6 @@ final class WorkspaceTests: XCTestCase { } func testManifestParseError() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let observability = ObservabilitySystem.makeForTesting() try await testWithTemporaryDirectory { path in @@ -15899,8 +15895,6 @@ final class WorkspaceTests: XCTestCase { } func testSigningEntityVerification_SignedCorrectly() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let actualMetadata = RegistryReleaseMetadata.createWithSigningEntity( .recognized( type: "adp", @@ -15920,8 +15914,6 @@ final class WorkspaceTests: XCTestCase { } func testSigningEntityVerification_SignedIncorrectly() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let actualMetadata = RegistryReleaseMetadata.createWithSigningEntity( .recognized( type: "adp", @@ -15953,8 +15945,6 @@ final class WorkspaceTests: XCTestCase { } func testSigningEntityVerification_Unsigned() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let expectedSigningEntity: RegistryReleaseMetadata.SigningEntity = .recognized( type: "adp", commonName: "Jane Doe", @@ -15977,8 +15967,6 @@ final class WorkspaceTests: XCTestCase { } func testSigningEntityVerification_NotFound() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let expectedSigningEntity: RegistryReleaseMetadata.SigningEntity = .recognized( type: "adp", commonName: "Jane Doe", @@ -16001,8 +15989,6 @@ final class WorkspaceTests: XCTestCase { } func testSigningEntityVerification_MirroredSignedCorrectly() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let mirrors = try DependencyMirrors() try mirrors.set(mirror: "ecorp.bar", for: "org.bar") @@ -16032,8 +16018,6 @@ final class WorkspaceTests: XCTestCase { } func testSigningEntityVerification_MirrorSignedIncorrectly() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let mirrors = try DependencyMirrors() try mirrors.set(mirror: "ecorp.bar", for: "org.bar") @@ -16071,8 +16055,6 @@ final class WorkspaceTests: XCTestCase { } func testSigningEntityVerification_MirroredUnsigned() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let mirrors = try DependencyMirrors() try mirrors.set(mirror: "ecorp.bar", for: "org.bar") @@ -16098,8 +16080,6 @@ final class WorkspaceTests: XCTestCase { } func testSigningEntityVerification_MirroredToSCM() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let mirrors = try DependencyMirrors() try mirrors.set(mirror: "https://scm.com/org/bar-mirror", for: "org.bar") @@ -16485,8 +16465,6 @@ final class WorkspaceTests: XCTestCase { archiver: Archiver? = .none, fileSystem: FileSystem ) throws -> RegistryClient { - try skipOnWindowsAsTestCurrentlyFails() - let jsonEncoder = JSONEncoder.makeWithDefaults() guard let identity = packageIdentity.registry else { From cc0b1a5d166872c07a4725c8893fb661ea2957b3 Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Wed, 9 Apr 2025 06:51:59 -0400 Subject: [PATCH 06/33] Tests: Enable tests on Windows (#8466) Enable additional tests on Windows host platform - Enable a few `AsyncProcessTests` - Enable the lone skipped test in `EnvironmentTests` - Enable the only skipped test in `Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift` - Enable the only skipped test in `Tests/BuildTests/BuildSystemDelegateTests.swift` - Enable the lone skipped test in `Tests/BuildTests/LLBuildManifestBuilderTests.swift` - Replace usages of `fixwin` with `AbsolutePath.pathString` Related to: #8433 rdar://148248105 --- Tests/BasicsTests/AsyncProcessTests.swift | 106 +++++++++++++----- .../Environment/EnvironmentTests.swift | 3 - .../BuildTests/BuildSystemDelegateTests.swift | 9 +- .../LLBuildManifestBuilderTests.swift | 4 +- .../SourceKitLSPAPITests.swift | 20 +--- Tests/WorkspaceTests/PrebuiltsTests.swift | 28 ++--- 6 files changed, 102 insertions(+), 68 deletions(-) diff --git a/Tests/BasicsTests/AsyncProcessTests.swift b/Tests/BasicsTests/AsyncProcessTests.swift index 8d7c36fa3bc..8ffb8bc7f7d 100644 --- a/Tests/BasicsTests/AsyncProcessTests.swift +++ b/Tests/BasicsTests/AsyncProcessTests.swift @@ -23,14 +23,19 @@ import func TSCBasic.withTemporaryFile import func TSCTestSupport.withCustomEnv final class AsyncProcessTests: XCTestCase { + #if os(Windows) + let executableExt = ".exe" + #else + let executableExt = "" + #endif - override func setUp() async throws { - try skipOnWindowsAsTestCurrentlyFails() - } - func testBasics() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "missingExecutableProgram(program: "echo.exe")" + """) + do { - let process = AsyncProcess(args: "echo", "hello") + let process = AsyncProcess(args: "echo\(executableExt)", "hello") try process.launch() let result = try process.waitUntilExit() XCTAssertEqual(try result.utf8Output(), "hello\n") @@ -46,27 +51,26 @@ final class AsyncProcessTests: XCTestCase { } } - func testPopen() throws { - #if os(Windows) - let echo = "echo.exe" - #else - let echo = "echo" - #endif + func testPopenBasic() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "missingExecutableProgram(program: "echo.exe")" + """) + // Test basic echo. - XCTAssertEqual(try AsyncProcess.popen(arguments: [echo, "hello"]).utf8Output(), "hello\n") + XCTAssertEqual(try AsyncProcess.popen(arguments: ["echo\(executableExt)", "hello"]).utf8Output(), "hello\n") + } + func testPopenWithBufferLargerThanAllocated() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "missingExecutableProgram(program: "cat.exe")" + """) // Test buffer larger than that allocated. try withTemporaryFile { file in let count = 10000 let stream = BufferedOutputByteStream() stream.send(Format.asRepeating(string: "a", count: count)) try localFileSystem.writeFileContents(file.path, bytes: stream.bytes) - #if os(Windows) - let cat = "cat.exe" - #else - let cat = "cat" - #endif - let outputCount = try AsyncProcess.popen(args: cat, file.path.pathString).utf8Output().count + let outputCount = try AsyncProcess.popen(args: "cat\(executableExt)", file.path.pathString).utf8Output().count XCTAssert(outputCount == count) } } @@ -119,8 +123,12 @@ final class AsyncProcessTests: XCTestCase { } func testCheckNonZeroExit() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "missingExecutableProgram(program: "echo.exe")" + """) + do { - let output = try AsyncProcess.checkNonZeroExit(args: "echo", "hello") + let output = try AsyncProcess.checkNonZeroExit(args: "echo\(executableExt)", "hello") XCTAssertEqual(output, "hello\n") } @@ -134,8 +142,12 @@ final class AsyncProcessTests: XCTestCase { @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) func testCheckNonZeroExitAsync() async throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "missingExecutableProgram(program: "echo.exe")" + """) + do { - let output = try await AsyncProcess.checkNonZeroExit(args: "echo", "hello") + let output = try await AsyncProcess.checkNonZeroExit(args: "echo\(executableExt)", "hello") XCTAssertEqual(output, "hello\n") } @@ -148,6 +160,8 @@ final class AsyncProcessTests: XCTestCase { } func testFindExecutable() throws { + try skipOnWindowsAsTestCurrentlyFails(because: "Assertion failure when trying to find ls executable") + try testWithTemporaryDirectory { tmpdir in // This process should always work. XCTAssertTrue(AsyncProcess.findExecutable("ls") != nil) @@ -192,7 +206,11 @@ final class AsyncProcessTests: XCTestCase { } func testThreadSafetyOnWaitUntilExit() throws { - let process = AsyncProcess(args: "echo", "hello") + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "missingExecutableProgram(program: "echo.exe")" + """) + + let process = AsyncProcess(args: "echo\(executableExt)", "hello") try process.launch() var result1 = "" @@ -217,7 +235,11 @@ final class AsyncProcessTests: XCTestCase { @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) func testThreadSafetyOnWaitUntilExitAsync() async throws { - let process = AsyncProcess(args: "echo", "hello") + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "missingExecutableProgram(program: "echo.exe")" + """) + + let process = AsyncProcess(args: "echo\(executableExt)", "hello") try process.launch() let t1 = Task { @@ -236,6 +258,10 @@ final class AsyncProcessTests: XCTestCase { } func testStdin() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "Error Domain=NSCocoaErrorDomain Code=3584 "(null)"UserInfo={NSUnderlyingError=Error Domain=org.swift.Foundation.WindowsError Code=193 "(null)"}" + """) + var stdout = [UInt8]() let process = AsyncProcess(scriptName: "in-to-out", outputRedirection: .stream(stdout: { stdoutBytes in stdout += stdoutBytes @@ -253,6 +279,10 @@ final class AsyncProcessTests: XCTestCase { } func testStdoutStdErr() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "Error Domain=NSCocoaErrorDomain Code=3584 "(null)"UserInfo={NSUnderlyingError=Error Domain=org.swift.Foundation.WindowsError Code=193 "(null)"}" + """) + // A simple script to check that stdout and stderr are captured separatly. do { let result = try AsyncProcess.popen(scriptName: "simple-stdout-stderr") @@ -279,6 +309,10 @@ final class AsyncProcessTests: XCTestCase { @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) func testStdoutStdErrAsync() async throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "Error Domain=NSCocoaErrorDomain Code=3584 "(null)"UserInfo={NSUnderlyingError=Error Domain=org.swift.Foundation.WindowsError Code=193 "(null)"}" + """) + // A simple script to check that stdout and stderr are captured separatly. do { let result = try await AsyncProcess.popen(scriptName: "simple-stdout-stderr") @@ -304,6 +338,10 @@ final class AsyncProcessTests: XCTestCase { } func testStdoutStdErrRedirected() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "Error Domain=NSCocoaErrorDomain Code=3584 "(null)"UserInfo={NSUnderlyingError=Error Domain=org.swift.Foundation.WindowsError Code=193 "(null)"}" + """) + // A simple script to check that stdout and stderr are captured in the same location. do { let process = AsyncProcess( @@ -332,6 +370,10 @@ final class AsyncProcessTests: XCTestCase { } func testStdoutStdErrStreaming() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "Error Domain=NSCocoaErrorDomain Code=3584 "(null)"UserInfo={NSUnderlyingError=Error Domain=org.swift.Foundation.WindowsError Code=193 "(null)"}" + """) + var stdout = [UInt8]() var stderr = [UInt8]() let process = AsyncProcess(scriptName: "long-stdout-stderr", outputRedirection: .stream(stdout: { stdoutBytes in @@ -348,6 +390,10 @@ final class AsyncProcessTests: XCTestCase { } func testStdoutStdErrStreamingRedirected() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "Error Domain=NSCocoaErrorDomain Code=3584 "(null)"UserInfo={NSUnderlyingError=Error Domain=org.swift.Foundation.WindowsError Code=193 "(null)"}" + """) + var stdout = [UInt8]() var stderr = [UInt8]() let process = AsyncProcess(scriptName: "long-stdout-stderr", outputRedirection: .stream(stdout: { stdoutBytes in @@ -364,6 +410,10 @@ final class AsyncProcessTests: XCTestCase { } func testWorkingDirectory() throws { + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "missingExecutableProgram(program: "cat.exe")" + """) + guard #available(macOS 10.15, *) else { // Skip this test since it's not supported in this OS. return @@ -385,7 +435,7 @@ final class AsyncProcessTests: XCTestCase { try localFileSystem.writeFileContents(childPath, bytes: ByteString("child")) do { - let process = AsyncProcess(arguments: ["cat", "file"], workingDirectory: tempDirPath) + let process = AsyncProcess(arguments: ["cat\(executableExt)", "file"], workingDirectory: tempDirPath) try process.launch() let result = try process.waitUntilExit() XCTAssertEqual(try result.utf8Output(), "parent") @@ -403,12 +453,15 @@ final class AsyncProcessTests: XCTestCase { func testAsyncStream() async throws { // rdar://133548796 try XCTSkipIfCI() + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "Error Domain=NSCocoaErrorDomain Code=3584 "(null)"UserInfo={NSUnderlyingError=Error Domain=org.swift.Foundation.WindowsError Code=193 "(null)"}" + """) let (stdoutStream, stdoutContinuation) = AsyncProcess.ReadableStream.makeStream() let (stderrStream, stderrContinuation) = AsyncProcess.ReadableStream.makeStream() let process = AsyncProcess( - scriptName: "echo", + scriptName: "echo\(executableExt)", outputRedirection: .stream { stdoutContinuation.yield($0) } stderr: { @@ -460,9 +513,12 @@ final class AsyncProcessTests: XCTestCase { func testAsyncStreamHighLevelAPI() async throws { // rdar://133548796 try XCTSkipIfCI() + try skipOnWindowsAsTestCurrentlyFails(because: """ + threw error "Error Domain=NSCocoaErrorDomain Code=3584 "(null)"UserInfo={NSUnderlyingError=Error Domain=org.swift.Foundation.WindowsError Code=193 "(null)"}" + """) let result = try await AsyncProcess.popen( - scriptName: "echo", + scriptName: "echo\(executableExt)", stdout: { stdin, stdout in var counter = 0 stdin.write("Hello \(counter)\n") diff --git a/Tests/BasicsTests/Environment/EnvironmentTests.swift b/Tests/BasicsTests/Environment/EnvironmentTests.swift index f03bdbf8fa3..5b0388470c6 100644 --- a/Tests/BasicsTests/Environment/EnvironmentTests.swift +++ b/Tests/BasicsTests/Environment/EnvironmentTests.swift @@ -15,7 +15,6 @@ import Basics import XCTest -import _InternalTestSupport // for skipOnWindowsAsTestCurrentlyFails() final class EnvironmentTests: XCTestCase { func test_init() { @@ -103,8 +102,6 @@ final class EnvironmentTests: XCTestCase { /// Important: This test is inherently race-prone, if it is proven to be /// flaky, it should run in a singled threaded environment/removed entirely. func test_current() throws { - try skipOnWindowsAsTestCurrentlyFails(because: "ProcessInfo.processInfo.environment[pathEnvVarName] return nil in the docker container") - #if os(Windows) let pathEnvVarName = "Path" #else diff --git a/Tests/BuildTests/BuildSystemDelegateTests.swift b/Tests/BuildTests/BuildSystemDelegateTests.swift index 90907ed6758..8e07b6a3d1a 100644 --- a/Tests/BuildTests/BuildSystemDelegateTests.swift +++ b/Tests/BuildTests/BuildSystemDelegateTests.swift @@ -30,13 +30,18 @@ final class BuildSystemDelegateTests: XCTestCase { } func testFilterNonFatalCodesignMessages() async throws { - try skipOnWindowsAsTestCurrentlyFails() + try skipOnWindowsAsTestCurrentlyFails(because: "Package fails to build when the test is being executed") try XCTSkipIf(!UserToolchain.default.supportsSDKDependentTests(), "skipping because test environment doesn't support this test") // Note: we can re-use the `TestableExe` fixture here since we just need an executable. + #if os(Windows) + let executableExt = ".exe" + #else + let executableExt = "" + #endif try await fixture(name: "Miscellaneous/TestableExe") { fixturePath in _ = try await executeSwiftBuild(fixturePath) - let execPath = fixturePath.appending(components: ".build", "debug", "TestableExe1") + let execPath = fixturePath.appending(components: ".build", "debug", "TestableExe1\(executableExt)") XCTAssertTrue(localFileSystem.exists(execPath), "executable not found at '\(execPath)'") try localFileSystem.removeFileTree(execPath) let (fullLog, _) = try await executeSwiftBuild(fixturePath) diff --git a/Tests/BuildTests/LLBuildManifestBuilderTests.swift b/Tests/BuildTests/LLBuildManifestBuilderTests.swift index 057d4514568..7fa466a4545 100644 --- a/Tests/BuildTests/LLBuildManifestBuilderTests.swift +++ b/Tests/BuildTests/LLBuildManifestBuilderTests.swift @@ -195,8 +195,6 @@ final class LLBuildManifestBuilderTests: XCTestCase { /// Verifies that two modules with the same name but different triples don't share same build manifest keys. func testToolsBuildTriple() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let (graph, fs, scope) = try macrosPackageGraph() let productsTriple = Triple.x86_64MacOS let toolsTriple = Triple.arm64Linux @@ -221,6 +219,6 @@ final class LLBuildManifestBuilderTests: XCTestCase { XCTAssertNotNil(manifest.commands["C.SwiftSyntax-aarch64-unknown-linux-gnu-debug-tool.module"]) // Ensure that Objects.LinkFileList is -tool suffixed. - XCTAssertNotNil(manifest.commands["/path/to/build/aarch64-unknown-linux-gnu/debug/MMIOMacros-tool.product/Objects.LinkFileList"]) + XCTAssertNotNil(manifest.commands[AbsolutePath("/path/to/build/aarch64-unknown-linux-gnu/debug/MMIOMacros-tool.product/Objects.LinkFileList").pathString]) } } diff --git a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift index 437be5757e2..a920599bdf3 100644 --- a/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift +++ b/Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift @@ -24,8 +24,6 @@ import XCTest final class SourceKitLSPAPITests: XCTestCase { func testBasicSwiftPackage() async throws { - try skipOnWindowsAsTestCurrentlyFails() - let fs = InMemoryFileSystem(emptyFiles: "/Pkg/Sources/exe/main.swift", "/Pkg/Sources/exe/README.md", @@ -89,7 +87,7 @@ final class SourceKitLSPAPITests: XCTestCase { "-package-name", "pkg", "-emit-dependencies", "-emit-module", - "-emit-module-path", "/path/to/build/\(plan.destinationBuildParameters.triple)/debug/Modules/exe.swiftmodule" + "-emit-module-path", AbsolutePath("/path/to/build/\(plan.destinationBuildParameters.triple)/debug/Modules/exe.swiftmodule").pathString ], resources: [.init(filePath: "/Pkg/Sources/exe/Resources/some_file.txt")], ignoredFiles: [.init(filePath: "/Pkg/Sources/exe/exe.docc")], @@ -104,7 +102,7 @@ final class SourceKitLSPAPITests: XCTestCase { "-package-name", "pkg", "-emit-dependencies", "-emit-module", - "-emit-module-path", "/path/to/build/\(plan.destinationBuildParameters.triple)/debug/Modules/lib.swiftmodule" + "-emit-module-path", AbsolutePath("/path/to/build/\(plan.destinationBuildParameters.triple)/debug/Modules/lib.swiftmodule").pathString ], resources: [.init(filePath: "/Pkg/Sources/lib/Resources/some_file.txt")], ignoredFiles: [.init(filePath: "/Pkg/Sources/lib/lib.docc")], @@ -115,7 +113,7 @@ final class SourceKitLSPAPITests: XCTestCase { for: "plugin", graph: graph, partialArguments: [ - "-I", "/fake/manifestLib/path" + "-I", AbsolutePath("/fake/manifestLib/path").pathString ], isPartOfRootPackage: true, destination: .host @@ -318,7 +316,7 @@ final class SourceKitLSPAPITests: XCTestCase { "-package-name", "pkg", "-emit-dependencies", "-emit-module", - "-emit-module-path", "/path/to/build/\(destinationBuildParameters.triple)/debug/Modules/lib.swiftmodule".fixwin + "-emit-module-path", AbsolutePath("/path/to/build/\(destinationBuildParameters.triple)/debug/Modules/lib.swiftmodule").pathString ], isPartOfRootPackage: true ) @@ -402,13 +400,3 @@ extension SourceKitLSPAPI.BuildDescription { return result } } - -extension String { - var fixwin: String { - #if os(Windows) - return self.replacingOccurrences(of: "/", with: "\\") - #else - return self - #endif - } -} diff --git a/Tests/WorkspaceTests/PrebuiltsTests.swift b/Tests/WorkspaceTests/PrebuiltsTests.swift index 7739dbd75f2..8af88b8464e 100644 --- a/Tests/WorkspaceTests/PrebuiltsTests.swift +++ b/Tests/WorkspaceTests/PrebuiltsTests.swift @@ -105,10 +105,10 @@ final class PrebuiltsTests: XCTestCase { func checkSettings(_ target: Module, usePrebuilt: Bool) throws { if usePrebuilt { let swiftFlags = try XCTUnwrap(target.buildSettings.assignments[.OTHER_SWIFT_FLAGS]).flatMap({ $0.values }) - XCTAssertTrue(swiftFlags.contains("-I/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64/Modules".fixwin)) - XCTAssertTrue(swiftFlags.contains("-I/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64/include/_SwiftSyntaxCShims".fixwin)) + XCTAssertTrue(swiftFlags.contains("-I\(AbsolutePath("/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64/Modules").pathString)")) + XCTAssertTrue(swiftFlags.contains("-I\(AbsolutePath("/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64/include/_SwiftSyntaxCShims").pathString)")) let ldFlags = try XCTUnwrap(target.buildSettings.assignments[.OTHER_LDFLAGS]).flatMap({ $0.values }) - XCTAssertTrue(ldFlags.contains("/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64/lib/libMacroSupport.a".fixwin)) + XCTAssertTrue(ldFlags.contains(AbsolutePath("/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64/lib/libMacroSupport.a").pathString)) } else { XCTAssertNil(target.buildSettings.assignments[.OTHER_SWIFT_FLAGS]) XCTAssertNil(target.buildSettings.assignments[.OTHER_LDFLAGS]) @@ -141,8 +141,8 @@ final class PrebuiltsTests: XCTestCase { } let archiver = MockArchiver(handler: { _, archivePath, destination, completion in - XCTAssertEqual(archivePath.pathString, "/home/user/caches/org.swift.swiftpm/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64.zip".fixwin) - XCTAssertEqual(destination.pathString, "/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64".fixwin) + XCTAssertEqual(archivePath.pathString, AbsolutePath("/home/user/caches/org.swift.swiftpm/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64.zip").pathString) + XCTAssertEqual(destination.pathString, AbsolutePath("/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64").pathString) completion(.success(())) }) @@ -202,8 +202,8 @@ final class PrebuiltsTests: XCTestCase { } let archiver = MockArchiver(handler: { _, archivePath, destination, completion in - XCTAssertEqual(archivePath.pathString, "/home/user/caches/org.swift.swiftpm/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64.zip".fixwin) - XCTAssertEqual(destination.pathString, "/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64".fixwin) + XCTAssertEqual(archivePath.pathString, AbsolutePath("/home/user/caches/org.swift.swiftpm/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64.zip").pathString) + XCTAssertEqual(destination.pathString, AbsolutePath("/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64").pathString) completion(.success(())) }) @@ -468,8 +468,8 @@ final class PrebuiltsTests: XCTestCase { } let archiver = MockArchiver(handler: { _, archivePath, destination, completion in - XCTAssertEqual(archivePath.pathString, "/home/user/caches/org.swift.swiftpm/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64.zip".fixwin) - XCTAssertEqual(destination.pathString, "/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64".fixwin) + XCTAssertEqual(archivePath.pathString, AbsolutePath("/home/user/caches/org.swift.swiftpm/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64.zip").pathString) + XCTAssertEqual(destination.pathString, AbsolutePath("/tmp/ws/.build/prebuilts/swift-syntax/\(self.swiftVersion)-MacroSupport-macos_aarch64").pathString) completion(.success(())) }) @@ -581,13 +581,3 @@ final class PrebuiltsTests: XCTestCase { } } } - -extension String { - var fixwin: String { - #if os(Windows) - return self.replacingOccurrences(of: "/", with: "\\") - #else - return self - #endif - } -} From 21e333620ab27947e77f9455a5116800dd978ee8 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 9 Apr 2025 22:51:47 +0900 Subject: [PATCH 07/33] SwiftSDK: Remove hardcoded WASI sysroot path derivation (#8468) Remove hardcoded WASI sysroot path derivation just for wasi-sysroot embedded in toolchains ### Motivation: The previous implementation assumed there is $TOOLCHAIN_ROOT/share/wasi-sysroot when `--triple wasm32-unknown-wasi` is passed, but this is no longer the case with the [deprecation of the Wasm toolchain installation](https://github.com/swiftwasm/swift/issues/5604) in favor of Swift SDKs. Due to this unnecessary branch, when `--triple wasm32-unknown-wasi` is passed together with `--swift-sdk`, `--swift-sdk` is just ignored: https://github.com/swiftlang/swift-package-manager/issues/8465 ### Modifications: This change removes the hardcoded path derivation for the WASI sysroot from the `SwiftSDK.deriveTargetSwiftSDK` method. ### Result: When `--triple wasm32-unknown-wasi` is passed without `--swift-sdk`, no user visible change and they will keep getting the following: ``` error: emit-module command failed with exit code 1 (use -v to see invocation) :0: warning: libc not found for 'wasm32-unknown-wasi'; C stdlib may be unavailable :0: error: unable to load standard library for target 'wasm32-unknown-wasi' ``` When `--triple wasm32-unknown-wasi` is passed together with `--swift-sdk`, `--triple` is ignored and `--swift-sdk` is respected. --- Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 11 ----------- Tests/PackageModelTests/SwiftSDKBundleTests.swift | 13 ------------- 2 files changed, 24 deletions(-) diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index 313d454a965..066d29c1147 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -691,17 +691,6 @@ public struct SwiftSDK: Equatable { hostSDK: SwiftSDK, environment: Environment = .current ) -> SwiftSDK? { - if targetTriple.isWASI() { - let wasiSysroot = hostSDK.toolset.rootPaths.first? - .parentDirectory // usr - .appending(components: "share", "wasi-sysroot") - return SwiftSDK( - targetTriple: targetTriple, - toolset: hostSDK.toolset, - pathsConfiguration: .init(sdkRootPath: wasiSysroot) - ) - } - #if os(macOS) if let darwinPlatform = targetTriple.darwinPlatform { // the Darwin SDKs are trivially available on macOS diff --git a/Tests/PackageModelTests/SwiftSDKBundleTests.swift b/Tests/PackageModelTests/SwiftSDKBundleTests.swift index b2618cc9594..0a65b116675 100644 --- a/Tests/PackageModelTests/SwiftSDKBundleTests.swift +++ b/Tests/PackageModelTests/SwiftSDKBundleTests.swift @@ -450,19 +450,6 @@ final class SwiftSDKBundleTests: XCTestCase { XCTAssertEqual(targetSwiftSDK.toolset.rootPaths, [toolsetRootPath] + hostSwiftSDK.toolset.rootPaths) } - do { - let targetSwiftSDK = try SwiftSDK.deriveTargetSwiftSDK( - hostSwiftSDK: hostSwiftSDK, - hostTriple: hostTriple, - swiftSDKSelector: "wasm32-unknown-wasi", - store: store, - observabilityScope: system.topScope, - fileSystem: fileSystem - ) - // Ensure that triples that have a `defaultSwiftSDK` are handled - XCTAssertEqual(targetSwiftSDK.targetTriple?.triple, "wasm32-unknown-wasi") - } - do { // Check explicit overriding options. let customCompileSDK = AbsolutePath("/path/to/sdk") From e303b898a7791285626493e74745cb8565a3659e Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Wed, 9 Apr 2025 10:58:41 -0400 Subject: [PATCH 08/33] Copy helpers internally (#8467) Until #8223 is fixed copy some helpers from `IntergrationTests/Source/IntegrationTests` to `Test/_InternalTestSupport` so we can re-use the functionality. Related to: #8433 rdar://148248105 --- Sources/_InternalTestSupport/Process.swift | 43 ++++++++++++ .../SkippedTestSupport.swift | 70 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 Sources/_InternalTestSupport/Process.swift create mode 100644 Sources/_InternalTestSupport/SkippedTestSupport.swift diff --git a/Sources/_InternalTestSupport/Process.swift b/Sources/_InternalTestSupport/Process.swift new file mode 100644 index 00000000000..eacfcbc2896 --- /dev/null +++ b/Sources/_InternalTestSupport/Process.swift @@ -0,0 +1,43 @@ +/* + This source file is part of the Swift.org open source project + + Copyright (c) 2014 - 2025 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 Swift project authors + */ + +public import Foundation + +public enum OperatingSystem: Hashable, Sendable { + case macOS + case windows + case linux + case android + case unknown +} + +extension ProcessInfo { + #if os(macOS) + public static let hostOperatingSystem = OperatingSystem.macOS + #elseif os(Linux) + public static let hostOperatingSystem = OperatingSystem.linux + #elseif os(Windows) + public static let hostOperatingSystem = OperatingSystem.windows + #else + public static let hostOperatingSystem = OperatingSystem.unknown + #endif + + #if os(Windows) + public static let EOL = "\r\n" + #else + public static let EOL = "\n" + #endif + + #if os(Windows) + public static let exeSuffix = ".exe" + #else + public static let exeSuffix = "" + #endif +} diff --git a/Sources/_InternalTestSupport/SkippedTestSupport.swift b/Sources/_InternalTestSupport/SkippedTestSupport.swift new file mode 100644 index 00000000000..b73727d1c64 --- /dev/null +++ b/Sources/_InternalTestSupport/SkippedTestSupport.swift @@ -0,0 +1,70 @@ + +/* + This source file is part of the Swift.org open source project + + Copyright (c) 2014 - 2025 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 Swift project authors + */ + +import class Foundation.FileManager +import class Foundation.ProcessInfo +import Testing + +extension Trait where Self == Testing.ConditionTrait { + /// Skip test if the host operating system does not match the running OS. + public static func requireHostOS(_ os: OperatingSystem, when condition: Bool = true) -> Self { + enabled("This test requires a \(os) host OS.") { + ProcessInfo.hostOperatingSystem == os && condition + } + } + + /// Skip test if the host operating system matches the running OS. + public static func skipHostOS(_ os: OperatingSystem, _ comment: Comment? = nil) -> Self { + disabled(comment ?? "This test cannot run on a \(os) host OS.") { + ProcessInfo.hostOperatingSystem == os + } + } + + /// Skip test unconditionally + public static func skip(_ comment: Comment? = nil) -> Self { + disabled(comment ?? "Unconditional skip, a comment should be added for the reason") { true } + } + + /// Skip test if the environment is self hosted. + public static func skipSwiftCISelfHosted(_ comment: Comment? = nil) -> Self { + disabled(comment ?? "SwiftCI is self hosted") { + ProcessInfo.processInfo.environment["SWIFTCI_IS_SELF_HOSTED"] != nil + } + } + + /// Skip test if the test environment has a restricted network access, i.e. cannot get to internet. + public static func requireUnrestrictedNetworkAccess(_ comment: Comment? = nil) -> Self { + disabled(comment ?? "CI Environment has restricted network access") { + ProcessInfo.processInfo.environment["SWIFTCI_RESTRICTED_NETWORK_ACCESS"] != nil + } + } + + /// Skip test if built by XCode. + public static func skipIfXcodeBuilt() -> Self { + disabled("Tests built by Xcode") { + #if Xcode + true + #else + false + #endif + } + } + + /// Constructs a condition trait that causes a test to be disabled if the Foundation process spawning implementation + /// is not using `posix_spawn_file_actions_addchdir`. + public static var requireThreadSafeWorkingDirectory: Self { + disabled("Thread-safe process working directory support is unavailable.") { + // Amazon Linux 2 has glibc 2.26, and glibc 2.29 is needed for posix_spawn_file_actions_addchdir_np support + FileManager.default.contents(atPath: "/etc/system-release") + .map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false + } + } +} From cd5d9dd6103be137977f7a4d5c62019bcc02c046 Mon Sep 17 00:00:00 2001 From: nate-chandler <46721658+nate-chandler@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:27:52 -0700 Subject: [PATCH 09/33] Revert "Copy helpers internally" (#8474) Reverts swiftlang/swift-package-manager#8467 --- Sources/_InternalTestSupport/Process.swift | 43 ------------ .../SkippedTestSupport.swift | 70 ------------------- 2 files changed, 113 deletions(-) delete mode 100644 Sources/_InternalTestSupport/Process.swift delete mode 100644 Sources/_InternalTestSupport/SkippedTestSupport.swift diff --git a/Sources/_InternalTestSupport/Process.swift b/Sources/_InternalTestSupport/Process.swift deleted file mode 100644 index eacfcbc2896..00000000000 --- a/Sources/_InternalTestSupport/Process.swift +++ /dev/null @@ -1,43 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2014 - 2025 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 Swift project authors - */ - -public import Foundation - -public enum OperatingSystem: Hashable, Sendable { - case macOS - case windows - case linux - case android - case unknown -} - -extension ProcessInfo { - #if os(macOS) - public static let hostOperatingSystem = OperatingSystem.macOS - #elseif os(Linux) - public static let hostOperatingSystem = OperatingSystem.linux - #elseif os(Windows) - public static let hostOperatingSystem = OperatingSystem.windows - #else - public static let hostOperatingSystem = OperatingSystem.unknown - #endif - - #if os(Windows) - public static let EOL = "\r\n" - #else - public static let EOL = "\n" - #endif - - #if os(Windows) - public static let exeSuffix = ".exe" - #else - public static let exeSuffix = "" - #endif -} diff --git a/Sources/_InternalTestSupport/SkippedTestSupport.swift b/Sources/_InternalTestSupport/SkippedTestSupport.swift deleted file mode 100644 index b73727d1c64..00000000000 --- a/Sources/_InternalTestSupport/SkippedTestSupport.swift +++ /dev/null @@ -1,70 +0,0 @@ - -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2014 - 2025 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 Swift project authors - */ - -import class Foundation.FileManager -import class Foundation.ProcessInfo -import Testing - -extension Trait where Self == Testing.ConditionTrait { - /// Skip test if the host operating system does not match the running OS. - public static func requireHostOS(_ os: OperatingSystem, when condition: Bool = true) -> Self { - enabled("This test requires a \(os) host OS.") { - ProcessInfo.hostOperatingSystem == os && condition - } - } - - /// Skip test if the host operating system matches the running OS. - public static func skipHostOS(_ os: OperatingSystem, _ comment: Comment? = nil) -> Self { - disabled(comment ?? "This test cannot run on a \(os) host OS.") { - ProcessInfo.hostOperatingSystem == os - } - } - - /// Skip test unconditionally - public static func skip(_ comment: Comment? = nil) -> Self { - disabled(comment ?? "Unconditional skip, a comment should be added for the reason") { true } - } - - /// Skip test if the environment is self hosted. - public static func skipSwiftCISelfHosted(_ comment: Comment? = nil) -> Self { - disabled(comment ?? "SwiftCI is self hosted") { - ProcessInfo.processInfo.environment["SWIFTCI_IS_SELF_HOSTED"] != nil - } - } - - /// Skip test if the test environment has a restricted network access, i.e. cannot get to internet. - public static func requireUnrestrictedNetworkAccess(_ comment: Comment? = nil) -> Self { - disabled(comment ?? "CI Environment has restricted network access") { - ProcessInfo.processInfo.environment["SWIFTCI_RESTRICTED_NETWORK_ACCESS"] != nil - } - } - - /// Skip test if built by XCode. - public static func skipIfXcodeBuilt() -> Self { - disabled("Tests built by Xcode") { - #if Xcode - true - #else - false - #endif - } - } - - /// Constructs a condition trait that causes a test to be disabled if the Foundation process spawning implementation - /// is not using `posix_spawn_file_actions_addchdir`. - public static var requireThreadSafeWorkingDirectory: Self { - disabled("Thread-safe process working directory support is unavailable.") { - // Amazon Linux 2 has glibc 2.26, and glibc 2.29 is needed for posix_spawn_file_actions_addchdir_np support - FileManager.default.contents(atPath: "/etc/system-release") - .map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false - } - } -} From a0d1600e45a5ce476c0b3da473d9f8f4851c0437 Mon Sep 17 00:00:00 2001 From: Doug Schaefer <167107236+dschaefer2@users.noreply.github.com> Date: Thu, 10 Apr 2025 16:55:00 -0400 Subject: [PATCH 10/33] Fix duplicate modulemap errors with macro and plugin deps (#8472) We were including flags to hook up modulemaps and include files to C library dependencies in macros and plugin tools through to the modules that depend on those. This adds the capability to prune the depth first searches through the dependencies to ensure these are skipped when crossing macro and plugin boundaries. --- Sources/Basics/Graph/GraphAlgorithms.swift | 47 +++++++++++++ .../ModuleBuildDescription.swift | 22 +++++++ .../SwiftModuleBuildDescription.swift | 6 ++ Sources/Build/BuildPlan/BuildPlan+Swift.swift | 3 +- Sources/Build/BuildPlan/BuildPlan.swift | 12 ++-- Tests/BuildTests/BuildPlanTests.swift | 66 +++++++++++++++++++ .../BuildTests/BuildPlanTraversalTests.swift | 2 + 7 files changed, 150 insertions(+), 8 deletions(-) diff --git a/Sources/Basics/Graph/GraphAlgorithms.swift b/Sources/Basics/Graph/GraphAlgorithms.swift index 8ccc6038cc0..deee4941985 100644 --- a/Sources/Basics/Graph/GraphAlgorithms.swift +++ b/Sources/Basics/Graph/GraphAlgorithms.swift @@ -131,3 +131,50 @@ public func depthFirstSearch( } } } + +/// Implements a pre-order depth-first search that traverses the whole graph and +/// doesn't distinguish between unique and duplicate nodes. The visitor can abort +/// a path as needed to prune the tree. +/// The method expects the graph to be acyclic but doesn't check that. +/// +/// - Parameters: +/// - nodes: The list of input nodes to sort. +/// - successors: A closure for fetching the successors of a particular node. +/// - onNext: A callback to indicate the node currently being processed +/// including its parent (if any) and its depth. Returns whether to +/// continue down the current path. +/// +/// - Complexity: O(v + e) where (v, e) are the number of vertices and edges +/// reachable from the input nodes via the relation. +public enum DepthFirstContinue { + case `continue` + case abort +} + +public func depthFirstSearch( + _ nodes: [T], + successors: (T) throws -> [T], + visitNext: (T, _ parent: T?) throws -> DepthFirstContinue +) rethrows { + var stack = OrderedSet>() + + for node in nodes { + precondition(stack.isEmpty) + stack.append(TraversalNode(parent: nil, curr: node)) + + while !stack.isEmpty { + let node = stack.removeLast() + + if try visitNext(node.curr, node.parent) == .continue { + for succ in try successors(node.curr) { + stack.append( + TraversalNode( + parent: node.curr, + curr: succ + ) + ) + } + } + } + } +} diff --git a/Sources/Build/BuildDescription/ModuleBuildDescription.swift b/Sources/Build/BuildDescription/ModuleBuildDescription.swift index 8f52d45f370..06c52486045 100644 --- a/Sources/Build/BuildDescription/ModuleBuildDescription.swift +++ b/Sources/Build/BuildDescription/ModuleBuildDescription.swift @@ -187,8 +187,30 @@ extension ModuleBuildDescription { var dependencies: [Dependency] = [] plan.traverseDependencies(of: self) { product, _, description in dependencies.append(.product(product, description)) + return .continue } onModule: { module, _, description in dependencies.append(.module(module, description)) + return .continue + } + return dependencies + } + + package func recursiveLinkDependencies(using plan: BuildPlan) -> [Dependency] { + var dependencies: [Dependency] = [] + plan.traverseDependencies(of: self) { product, _, description in + guard product.type != .macro && product.type != .plugin else { + return .abort + } + + dependencies.append(.product(product, description)) + return .continue + } onModule: { module, _, description in + guard module.type != .macro && module.type != .plugin else { + return .abort + } + + dependencies.append(.module(module, description)) + return .continue } return dependencies } diff --git a/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift b/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift index fc9dc8c317a..4eac7878727 100644 --- a/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift @@ -1042,6 +1042,12 @@ extension SwiftModuleBuildDescription { ModuleBuildDescription.swift(self).dependencies(using: plan) } + package func recursiveLinkDependencies( + using plan: BuildPlan + ) -> [ModuleBuildDescription.Dependency] { + ModuleBuildDescription.swift(self).recursiveLinkDependencies(using: plan) + } + package func recursiveDependencies( using plan: BuildPlan ) -> [ModuleBuildDescription.Dependency] { diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index 7d387f8cfd5..f0c0b256cbb 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -19,7 +19,7 @@ import class PackageModel.SystemLibraryModule extension BuildPlan { func plan(swiftTarget: SwiftModuleBuildDescription) throws { // We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target - // depends on. + // builds against for case .module(let dependency, let description) in swiftTarget.recursiveDependencies(using: self) { switch dependency.underlying { case let underlyingTarget as ClangModule where underlyingTarget.type == .library: @@ -53,5 +53,4 @@ extension BuildPlan { } } } - } diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index bf6ac9bd12f..01ffe8ec665 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -1154,8 +1154,8 @@ extension BuildPlan { package func traverseDependencies( of description: ModuleBuildDescription, - onProduct: (ResolvedProduct, BuildParameters.Destination, ProductBuildDescription?) -> Void, - onModule: (ResolvedModule, BuildParameters.Destination, ModuleBuildDescription?) -> Void + onProduct: (ResolvedProduct, BuildParameters.Destination, ProductBuildDescription?) -> DepthFirstContinue, + onModule: (ResolvedModule, BuildParameters.Destination, ModuleBuildDescription?) -> DepthFirstContinue ) { var visited = Set() func successors( @@ -1196,16 +1196,16 @@ extension BuildPlan { case .package: [] } - } onNext: { module, _ in + } visitNext: { module, _ in switch module { case .package: - break + return .continue case .product(let product, let destination): - onProduct(product, destination, self.description(for: product, context: destination)) + return onProduct(product, destination, self.description(for: product, context: destination)) case .module(let module, let destination): - onModule(module, destination, self.description(for: module, context: destination)) + return onModule(module, destination, self.description(for: module, context: destination)) } } } diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 6caaae88baf..423afcd0e04 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -6909,6 +6909,72 @@ class BuildPlanTestCase: BuildSystemProviderTestCase { XCTAssertMatch(contents, .regex(#"args: \[.*"-I","/testpackagedep/SomeArtifact.xcframework/macos/Headers".*,"/testpackage/Sources/CLib/lib.c".*]"#)) XCTAssertMatch(contents, .regex(#"args: \[.*"-module-name","SwiftLib",.*"-I","/testpackagedep/SomeArtifact.xcframework/macos/Headers".*]"#)) } + + func testMacroPluginDependencyLeakage() async throws { + // Make sure the include paths from macro and plugin executables don't leak into dependents + let observability = ObservabilitySystem.makeForTesting() + let fs = InMemoryFileSystem(emptyFiles: [ + "/LeakTest/Sources/CLib/include/Clib.h", + "/LeakTest/Sources/CLib/Clib.c", + "/LeakTest/Sources/MyMacro/MyMacro.swift", + "/LeakTest/Sources/MyPluginTool/MyPluginTool.swift", + "/LeakTest/Plugins/MyPlugin/MyPlugin.swift", + "/LeakTest/Sources/MyLib/MyLib.swift", + "/LeakLib/Sources/CLib2/include/Clib.h", + "/LeakLib/Sources/CLib2/Clib.c", + "/LeakLib/Sources/MyMacro2/MyMacro.swift", + "/LeakLib/Sources/MyPluginTool2/MyPluginTool.swift", + "/LeakLib/Plugins/MyPlugin2/MyPlugin.swift", + "/LeakLib/Sources/MyLib2/MyLib.swift" + ]) + + let graph = try loadModulesGraph(fileSystem: fs, manifests: [ + Manifest.createFileSystemManifest( + displayName: "LeakLib", + path: "/LeakLib", + products: [ + ProductDescription(name: "MyLib2", type: .library(.automatic), targets: ["MyLib2"]), + ], + targets: [ + TargetDescription(name: "CLib2"), + TargetDescription(name: "MyMacro2", dependencies: ["CLib2"], type: .macro), + TargetDescription(name: "MyPluginTool2", dependencies: ["CLib2"], type: .executable), + TargetDescription(name: "MyPlugin2", dependencies: ["MyPluginTool2"], type: .plugin, pluginCapability: .buildTool), + TargetDescription(name: "MyLib2", dependencies: ["CLib2", "MyMacro2"], pluginUsages: [.plugin(name: "MyPlugin2", package: nil)]), + ] + ), + Manifest.createRootManifest( + displayName: "LeakTest", + path: "/LeakTest", + dependencies: [ + .fileSystem(path: "/LeakLib") + ], + targets: [ + TargetDescription(name: "CLib"), + TargetDescription(name: "MyMacro", dependencies: ["CLib"], type: .macro), + TargetDescription(name: "MyPluginTool", dependencies: ["CLib"], type: .executable), + TargetDescription(name: "MyPlugin", dependencies: ["MyPluginTool"], type: .plugin, pluginCapability: .buildTool), + TargetDescription( + name: "MyLib", + dependencies: ["CLib", "MyMacro", .product(name: "MyLib2", package: "LeakLib")], + pluginUsages: [.plugin(name: "MyPlugin", package: nil)] + ), + ] + ) + ], observabilityScope: observability.topScope) + XCTAssertNoDiagnostics(observability.diagnostics) + + let plan = try await mockBuildPlan( + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + let myLib = try XCTUnwrap(plan.targets.first(where: { $0.module.name == "MyLib" })).swift() + print(myLib.additionalFlags) + XCTAssertFalse(myLib.additionalFlags.contains(where: { $0.contains("-tool/include")}), "flags shouldn't contain tools items") + } } class BuildPlanNativeTests: BuildPlanTestCase { diff --git a/Tests/BuildTests/BuildPlanTraversalTests.swift b/Tests/BuildTests/BuildPlanTraversalTests.swift index 2b898cabd76..ed469887d60 100644 --- a/Tests/BuildTests/BuildPlanTraversalTests.swift +++ b/Tests/BuildTests/BuildPlanTraversalTests.swift @@ -146,8 +146,10 @@ final class BuildPlanTraversalTests: XCTestCase { XCTAssertEqual(product.name, "SwiftSyntax") XCTAssertEqual(destination, .host) XCTAssertNil(description) + return .continue } onModule: { module, destination, description in moduleDependencies.append((module, destination, description)) + return .continue } XCTAssertEqual(moduleDependencies.count, 2) From 1d48e0a8e14d84e54feaabc24fc366f5a694b7fe Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Fri, 11 Apr 2025 13:12:30 -0400 Subject: [PATCH 11/33] Copy helpers internally (#8476) Until #8223 is fixed copy some helpers from `IntergrationTests/Source/IntegrationTests` to `Test/_InternalTestSupport` so we can re-use the functionality. Related to: #8433 rdar://148248105 Test with: https://github.com/swiftlang/swift/pull/80717 --- Package.swift | 6 +- Sources/_InternalTestSupport/Process.swift | 43 ++++++++++++ .../SkippedTestSupport.swift | 70 +++++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 Sources/_InternalTestSupport/Process.swift create mode 100644 Sources/_InternalTestSupport/SkippedTestSupport.swift diff --git a/Package.swift b/Package.swift index 216810019bc..15d291a3eba 100644 --- a/Package.swift +++ b/Package.swift @@ -725,7 +725,7 @@ let package = Package( // MARK: Additional Test Dependencies - .target( + .testTarget( /** SwiftPM internal build test suite support library */ name: "_InternalBuildTestSupport", dependencies: [ @@ -734,12 +734,13 @@ let package = Package( "SwiftBuildSupport", "_InternalTestSupport" ], + path: "Sources/_InternalBuildTestSupport", swiftSettings: [ .unsafeFlags(["-static"]), ] ), - .target( + .testTarget( /** SwiftPM internal test suite support library */ name: "_InternalTestSupport", dependencies: [ @@ -754,6 +755,7 @@ let package = Package( .product(name: "OrderedCollections", package: "swift-collections"), "Workspace", ], + path: "./Sources/_InternalTestSupport", swiftSettings: [ .unsafeFlags(["-static"]), ] diff --git a/Sources/_InternalTestSupport/Process.swift b/Sources/_InternalTestSupport/Process.swift new file mode 100644 index 00000000000..eacfcbc2896 --- /dev/null +++ b/Sources/_InternalTestSupport/Process.swift @@ -0,0 +1,43 @@ +/* + This source file is part of the Swift.org open source project + + Copyright (c) 2014 - 2025 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 Swift project authors + */ + +public import Foundation + +public enum OperatingSystem: Hashable, Sendable { + case macOS + case windows + case linux + case android + case unknown +} + +extension ProcessInfo { + #if os(macOS) + public static let hostOperatingSystem = OperatingSystem.macOS + #elseif os(Linux) + public static let hostOperatingSystem = OperatingSystem.linux + #elseif os(Windows) + public static let hostOperatingSystem = OperatingSystem.windows + #else + public static let hostOperatingSystem = OperatingSystem.unknown + #endif + + #if os(Windows) + public static let EOL = "\r\n" + #else + public static let EOL = "\n" + #endif + + #if os(Windows) + public static let exeSuffix = ".exe" + #else + public static let exeSuffix = "" + #endif +} diff --git a/Sources/_InternalTestSupport/SkippedTestSupport.swift b/Sources/_InternalTestSupport/SkippedTestSupport.swift new file mode 100644 index 00000000000..b73727d1c64 --- /dev/null +++ b/Sources/_InternalTestSupport/SkippedTestSupport.swift @@ -0,0 +1,70 @@ + +/* + This source file is part of the Swift.org open source project + + Copyright (c) 2014 - 2025 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 Swift project authors + */ + +import class Foundation.FileManager +import class Foundation.ProcessInfo +import Testing + +extension Trait where Self == Testing.ConditionTrait { + /// Skip test if the host operating system does not match the running OS. + public static func requireHostOS(_ os: OperatingSystem, when condition: Bool = true) -> Self { + enabled("This test requires a \(os) host OS.") { + ProcessInfo.hostOperatingSystem == os && condition + } + } + + /// Skip test if the host operating system matches the running OS. + public static func skipHostOS(_ os: OperatingSystem, _ comment: Comment? = nil) -> Self { + disabled(comment ?? "This test cannot run on a \(os) host OS.") { + ProcessInfo.hostOperatingSystem == os + } + } + + /// Skip test unconditionally + public static func skip(_ comment: Comment? = nil) -> Self { + disabled(comment ?? "Unconditional skip, a comment should be added for the reason") { true } + } + + /// Skip test if the environment is self hosted. + public static func skipSwiftCISelfHosted(_ comment: Comment? = nil) -> Self { + disabled(comment ?? "SwiftCI is self hosted") { + ProcessInfo.processInfo.environment["SWIFTCI_IS_SELF_HOSTED"] != nil + } + } + + /// Skip test if the test environment has a restricted network access, i.e. cannot get to internet. + public static func requireUnrestrictedNetworkAccess(_ comment: Comment? = nil) -> Self { + disabled(comment ?? "CI Environment has restricted network access") { + ProcessInfo.processInfo.environment["SWIFTCI_RESTRICTED_NETWORK_ACCESS"] != nil + } + } + + /// Skip test if built by XCode. + public static func skipIfXcodeBuilt() -> Self { + disabled("Tests built by Xcode") { + #if Xcode + true + #else + false + #endif + } + } + + /// Constructs a condition trait that causes a test to be disabled if the Foundation process spawning implementation + /// is not using `posix_spawn_file_actions_addchdir`. + public static var requireThreadSafeWorkingDirectory: Self { + disabled("Thread-safe process working directory support is unavailable.") { + // Amazon Linux 2 has glibc 2.26, and glibc 2.29 is needed for posix_spawn_file_actions_addchdir_np support + FileManager.default.contents(atPath: "/etc/system-release") + .map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false + } + } +} From 6dfb613fab808061d37d5ef3427ead7f6e0070b7 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sat, 12 Apr 2025 02:42:51 +0100 Subject: [PATCH 12/33] Fix `SwiftBuildSupport/README.md` (#8483) Fixed up incorrect wording: 1. Wasm is not an acronym, thus it's not uppercased [per the spec](https://webassembly.github.io/spec/core/intro/introduction.html#wasm). 2. Existing Swift SDKs support only WASI, while WASI-less Wasm modules can be created with Embedded Swift and Swift SDKs are not needed for that. --- Sources/SwiftBuildSupport/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftBuildSupport/README.md b/Sources/SwiftBuildSupport/README.md index 76fd69e1c97..82701fc78b2 100644 --- a/Sources/SwiftBuildSupport/README.md +++ b/Sources/SwiftBuildSupport/README.md @@ -15,7 +15,7 @@ Work is continuing in these areas: * Conditional target dependencies (i.e. dependencies that are conditional on ".when()" specific platforms) * Plugin support * Friendly Error and Warning Descriptions and Fixups -* Cross compiling Swift SDK's (e.g. Static Linux SDK, and WASM) +* Cross compiling Swift SDK's (e.g. Static Linux SDK, and Wasm with WASI) * Improvements to test coverage * Task execution reporting From 57a67b4a34cec4cd0f014ebf5caf650e2fb8eda8 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Mon, 14 Apr 2025 09:35:03 -0400 Subject: [PATCH 13/33] Fix bootstrapping on OpenBSD (#8451) ### Motivation: Ensure swiftpm bootstraps successfully to fully build an OpenBSD toolchain. ### Modifications: * Add the nobtcfi linker flag to the bootstrap script for OpenBSD. This is unconditional for now since swiftpm uses Concurrency liberally and this triggers #80059, so swiftpm only builds on the configuration where BTCFI is disabled. We can revisit some of that perhaps later. * Ensure SPMSQLite3 builds with the correct link library search path flag in CMakeLists.txt. * Update Package.swift conditionals for SPMSQLite3. ### Result: swiftpm successfully builds and bootstraps on OpenBSD. --- Package.swift | 2 +- Sources/SPMSQLite3/CMakeLists.txt | 3 +++ Utilities/bootstrap | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 15d291a3eba..28d9d2869f5 100644 --- a/Package.swift +++ b/Package.swift @@ -227,7 +227,7 @@ let package = Package( name: "Basics", dependencies: [ "_AsyncFileSystem", - .target(name: "SPMSQLite3", condition: .when(platforms: [.macOS, .iOS, .tvOS, .watchOS, .visionOS, .macCatalyst, .linux, .custom("freebsd")])), + .target(name: "SPMSQLite3", condition: .when(platforms: [.macOS, .iOS, .tvOS, .watchOS, .visionOS, .macCatalyst, .linux, .openbsd, .custom("freebsd")])), .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.windows, .android])), .product(name: "DequeModule", package: "swift-collections"), .product(name: "OrderedCollections", package: "swift-collections"), diff --git a/Sources/SPMSQLite3/CMakeLists.txt b/Sources/SPMSQLite3/CMakeLists.txt index fb7fa18a1fe..46777305bd4 100644 --- a/Sources/SPMSQLite3/CMakeLists.txt +++ b/Sources/SPMSQLite3/CMakeLists.txt @@ -11,3 +11,6 @@ target_include_directories(SPMSQLite3 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(SPMSQLite3 INTERFACE SQLite::SQLite3) +if(CMAKE_SYSTEM_NAME STREQUAL OpenBSD) + target_link_options(SPMSQLite3 INTERFACE "-L/usr/local/lib") +endif() diff --git a/Utilities/bootstrap b/Utilities/bootstrap index e87e21faed6..58920f40b82 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -926,6 +926,10 @@ def get_swiftpm_flags(args): ]) if '-openbsd' in args.build_target: + # Because of swiftlang/swift#80059, swiftpm only works + # with BTCFI disabled. + if 'aarch64' in args.build_target: + build_flags.extend(["-Xlinker", "-z", "-Xlinker", "nobtcfi"]) build_flags.extend(["-Xlinker", "-z", "-Xlinker", "origin"]) build_flags.extend(["-Xcc", "-I/usr/local/include"]) build_flags.extend(["-Xlinker", "-L/usr/local/lib"]) From 3d063d02076d3848db8b1c5c9e69d07f8a62f1c5 Mon Sep 17 00:00:00 2001 From: Chris McGee <87777443+cmcgee1024@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:16:02 -0400 Subject: [PATCH 14/33] Link test command test failures on Linux to issue (#8459) The TestCommandTests fail on Linux due to a common linker problem with swift build build system, which is the missing 'main' symbol problem. This is described further detail in #8439. Provide the link in the skip messages and remove the TODO tags since the investigation is complete and the reason for the failures is now known. Update the common test case logic so that it dumps the command execution stdout and stderr in the case of failure to help diagnose these result.xml failures immediately in the logs. --- Tests/CommandsTests/TestCommandTests.swift | 26 +++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Tests/CommandsTests/TestCommandTests.swift b/Tests/CommandsTests/TestCommandTests.swift index 4e27e48fc68..e4e79a69b61 100644 --- a/Tests/CommandsTests/TestCommandTests.swift +++ b/Tests/CommandsTests/TestCommandTests.swift @@ -225,8 +225,8 @@ class TestCommandTestCase: CommandsBuildProviderTestCase { let xUnitUnderTest = fixturePath.appending("result\(testRunner.fileSuffix).xml") // WHEN we execute swift-test in parallel while specifying xUnit generation - let extraCommandArgs = enableExperimentalFlag ? ["--experimental-xunit-message-failure"]: [], - _ = try await execute( + let extraCommandArgs = enableExperimentalFlag ? ["--experimental-xunit-message-failure"]: [] + let (stdout, stderr) = try await execute( [ "--parallel", "--verbose", @@ -239,6 +239,12 @@ class TestCommandTestCase: CommandsBuildProviderTestCase { throwIfCommandFails: false ) + if !FileManager.default.fileExists(atPath: xUnitUnderTest.pathString) { + // If the build failed then produce a output dump of what happened during the execution + print("\(stdout)") + print("\(stderr)") + } + // THEN we expect \(xUnitUnderTest) to exists XCTAssertFileExists(xUnitUnderTest) let contents: String = try localFileSystem.readFileContents(xUnitUnderTest) @@ -694,35 +700,35 @@ class TestCommandSwiftBuildTests: TestCommandTestCase { #if !os(macOS) override func testSwiftTestXMLOutputVerifySingleTestFailureMessageWithFlagDisabledXCTest() async throws { - throw XCTSkip("SWBINTTODO: Result XML could not be found. This looks to be a build layout issue. Further investigation is needed.") + throw XCTSkip("Result XML could not be found. The build fails due to an LD_LIBRARY_PATH issue finding swift core libraries. https://github.com/swiftlang/swift-package-manager/issues/8416") } override func testSwiftTestXMLOutputVerifyMultipleTestFailureMessageWithFlagEnabledXCTest() async throws { - throw XCTSkip("SWBINTTODO: Result XML could not be found. This looks to be a build layout issue. Further investigation is needed.") + throw XCTSkip("Result XML could not be found. The build fails due to an LD_LIBRARY_PATH issue finding swift core libraries. https://github.com/swiftlang/swift-package-manager/issues/8416") } override func testSwiftTestXMLOutputVerifySingleTestFailureMessageWithFlagEnabledXCTest() async throws { - throw XCTSkip("SWBINTTODO: Result XML could not be found. This looks to be a build layout issue. Further investigation is needed.") + throw XCTSkip("Result XML could not be found. The build fails due to an LD_LIBRARY_PATH issue finding swift core libraries. https://github.com/swiftlang/swift-package-manager/issues/8416") } override func testSwiftTestXMLOutputVerifyMultipleTestFailureMessageWithFlagDisabledXCTest() async throws { - throw XCTSkip("SWBINTTODO: Result XML could not be found. This looks to be a build layout issue. Further investigation is needed.") + throw XCTSkip("Result XML could not be found. The build fails due to an LD_LIBRARY_PATH issue finding swift core libraries. https://github.com/swiftlang/swift-package-manager/issues/8416") } override func testSwiftTestSkip() async throws { - throw XCTSkip("SWBINTTODO: This fails due to a linker error on Linux. Further investigation is needed.") + throw XCTSkip("This fails due to a linker error on Linux. https://github.com/swiftlang/swift-package-manager/issues/8439") } override func testSwiftTestXMLOutputWhenEmpty() async throws { - throw XCTSkip("SWBINTTODO: This fails due to a linker error on Linux 'undefined reference to main'. Further investigation is needed.") + throw XCTSkip("This fails due to a linker error on Linux. https://github.com/swiftlang/swift-package-manager/issues/8439") } override func testSwiftTestFilter() async throws { - throw XCTSkip("SWBINTTODO: This fails due to an unknown linker error on Linux. Further investigation is needed.") + throw XCTSkip("This fails due to an unknown linker error on Linux. https://github.com/swiftlang/swift-package-manager/issues/8439") } override func testSwiftTestParallel() async throws { - throw XCTSkip("SWBINTTODO: This fails due to the test expecting specific test output that appears to be empty on Linux. Further investigation is needed.") + throw XCTSkip("This fails due to an unknown linker error on Linux. https://github.com/swiftlang/swift-package-manager/issues/8439") } #endif } From 49e69c78cf6c54198b39cb701bfcc8d418dfbc95 Mon Sep 17 00:00:00 2001 From: Paulo Mattos Date: Mon, 14 Apr 2025 09:46:44 -0700 Subject: [PATCH 15/33] Revert "Copy helpers internally" again (#8494) ### Motivation: Pull request #8476 was breaking SwiftPM builds in Xcode (i.e., the _package resolution_ step): ### Modifications: This reverts commit 1d48e0a8e14d84e54feaabc24fc366f5a694b7fe. --- .../Tests/IntegrationTests/BasicTests.swift | 2 +- Package.swift | 6 +- Sources/_InternalTestSupport/Process.swift | 43 ------------ .../SkippedTestSupport.swift | 70 ------------------- .../RegistryClientTests.swift | 4 +- 5 files changed, 5 insertions(+), 120 deletions(-) delete mode 100644 Sources/_InternalTestSupport/Process.swift delete mode 100644 Sources/_InternalTestSupport/SkippedTestSupport.swift diff --git a/IntegrationTests/Tests/IntegrationTests/BasicTests.swift b/IntegrationTests/Tests/IntegrationTests/BasicTests.swift index f02fd1faa69..91d6abd483a 100644 --- a/IntegrationTests/Tests/IntegrationTests/BasicTests.swift +++ b/IntegrationTests/Tests/IntegrationTests/BasicTests.swift @@ -94,7 +94,7 @@ private struct BasicTests { } @Test( - .skipHostOS(.windows, "'try!' expression unexpectedly raised an error: TSCBasic.Process.Error.missingExecutableProgram(program: \"which\")"), + .skipHostOS(.windows, "'try!' expression unexpectedly raised an error: TSCBasic.Process.Error.missingExecutableProgram(program: \"which\")") ) func testSwiftCompiler() throws { try withTemporaryDirectory { tempDir in diff --git a/Package.swift b/Package.swift index 28d9d2869f5..93baca641c2 100644 --- a/Package.swift +++ b/Package.swift @@ -725,7 +725,7 @@ let package = Package( // MARK: Additional Test Dependencies - .testTarget( + .target( /** SwiftPM internal build test suite support library */ name: "_InternalBuildTestSupport", dependencies: [ @@ -734,13 +734,12 @@ let package = Package( "SwiftBuildSupport", "_InternalTestSupport" ], - path: "Sources/_InternalBuildTestSupport", swiftSettings: [ .unsafeFlags(["-static"]), ] ), - .testTarget( + .target( /** SwiftPM internal test suite support library */ name: "_InternalTestSupport", dependencies: [ @@ -755,7 +754,6 @@ let package = Package( .product(name: "OrderedCollections", package: "swift-collections"), "Workspace", ], - path: "./Sources/_InternalTestSupport", swiftSettings: [ .unsafeFlags(["-static"]), ] diff --git a/Sources/_InternalTestSupport/Process.swift b/Sources/_InternalTestSupport/Process.swift deleted file mode 100644 index eacfcbc2896..00000000000 --- a/Sources/_InternalTestSupport/Process.swift +++ /dev/null @@ -1,43 +0,0 @@ -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2014 - 2025 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 Swift project authors - */ - -public import Foundation - -public enum OperatingSystem: Hashable, Sendable { - case macOS - case windows - case linux - case android - case unknown -} - -extension ProcessInfo { - #if os(macOS) - public static let hostOperatingSystem = OperatingSystem.macOS - #elseif os(Linux) - public static let hostOperatingSystem = OperatingSystem.linux - #elseif os(Windows) - public static let hostOperatingSystem = OperatingSystem.windows - #else - public static let hostOperatingSystem = OperatingSystem.unknown - #endif - - #if os(Windows) - public static let EOL = "\r\n" - #else - public static let EOL = "\n" - #endif - - #if os(Windows) - public static let exeSuffix = ".exe" - #else - public static let exeSuffix = "" - #endif -} diff --git a/Sources/_InternalTestSupport/SkippedTestSupport.swift b/Sources/_InternalTestSupport/SkippedTestSupport.swift deleted file mode 100644 index b73727d1c64..00000000000 --- a/Sources/_InternalTestSupport/SkippedTestSupport.swift +++ /dev/null @@ -1,70 +0,0 @@ - -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2014 - 2025 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 Swift project authors - */ - -import class Foundation.FileManager -import class Foundation.ProcessInfo -import Testing - -extension Trait where Self == Testing.ConditionTrait { - /// Skip test if the host operating system does not match the running OS. - public static func requireHostOS(_ os: OperatingSystem, when condition: Bool = true) -> Self { - enabled("This test requires a \(os) host OS.") { - ProcessInfo.hostOperatingSystem == os && condition - } - } - - /// Skip test if the host operating system matches the running OS. - public static func skipHostOS(_ os: OperatingSystem, _ comment: Comment? = nil) -> Self { - disabled(comment ?? "This test cannot run on a \(os) host OS.") { - ProcessInfo.hostOperatingSystem == os - } - } - - /// Skip test unconditionally - public static func skip(_ comment: Comment? = nil) -> Self { - disabled(comment ?? "Unconditional skip, a comment should be added for the reason") { true } - } - - /// Skip test if the environment is self hosted. - public static func skipSwiftCISelfHosted(_ comment: Comment? = nil) -> Self { - disabled(comment ?? "SwiftCI is self hosted") { - ProcessInfo.processInfo.environment["SWIFTCI_IS_SELF_HOSTED"] != nil - } - } - - /// Skip test if the test environment has a restricted network access, i.e. cannot get to internet. - public static func requireUnrestrictedNetworkAccess(_ comment: Comment? = nil) -> Self { - disabled(comment ?? "CI Environment has restricted network access") { - ProcessInfo.processInfo.environment["SWIFTCI_RESTRICTED_NETWORK_ACCESS"] != nil - } - } - - /// Skip test if built by XCode. - public static func skipIfXcodeBuilt() -> Self { - disabled("Tests built by Xcode") { - #if Xcode - true - #else - false - #endif - } - } - - /// Constructs a condition trait that causes a test to be disabled if the Foundation process spawning implementation - /// is not using `posix_spawn_file_actions_addchdir`. - public static var requireThreadSafeWorkingDirectory: Self { - disabled("Thread-safe process working directory support is unavailable.") { - // Amazon Linux 2 has glibc 2.26, and glibc 2.29 is needed for posix_spawn_file_actions_addchdir_np support - FileManager.default.contents(atPath: "/etc/system-release") - .map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false - } - } -} diff --git a/Tests/PackageRegistryTests/RegistryClientTests.swift b/Tests/PackageRegistryTests/RegistryClientTests.swift index b8390c1900f..809fdf32b89 100644 --- a/Tests/PackageRegistryTests/RegistryClientTests.swift +++ b/Tests/PackageRegistryTests/RegistryClientTests.swift @@ -803,7 +803,7 @@ final class RegistryClientTests: XCTestCase { let availableManifests = try await registryClient.getAvailableManifests( package: identity, version: version, - observabilityScope: ObservabilitySystem.NOOP, + observabilityScope: ObservabilitySystem.NOOP ) XCTAssertEqual(availableManifests["Package.swift"]?.toolsVersion, .v5_5) @@ -3987,7 +3987,7 @@ extension RegistryClient { package: package.underlying, version: version, fileSystem: InMemoryFileSystem(), - observabilityScope: ObservabilitySystem.NOOP, + observabilityScope: ObservabilitySystem.NOOP ) } From 453e6d38dfdab79cb7ccab27ad974e1f3fe89e00 Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Tue, 15 Apr 2025 14:22:22 -0400 Subject: [PATCH 16/33] Tests: Split SerializedJSONTests (#8496) Split the SerializedJSONTests into tests that pass and fail on Windows Related: #8433 rdar://148248105 --- .../Serialization/SerializedJSONTests.swift | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Tests/BasicsTests/Serialization/SerializedJSONTests.swift b/Tests/BasicsTests/Serialization/SerializedJSONTests.swift index a7841df439f..b499a4eb1dc 100644 --- a/Tests/BasicsTests/Serialization/SerializedJSONTests.swift +++ b/Tests/BasicsTests/Serialization/SerializedJSONTests.swift @@ -16,8 +16,6 @@ import _InternalTestSupport // for skipOnWindowsAsTestCurrentlyFails final class SerializedJSONTests: XCTestCase { func testPathInterpolation() throws { - try skipOnWindowsAsTestCurrentlyFails() - var path = try AbsolutePath(validating: #"/test\backslashes"#) var json: SerializedJSON = "\(path)" @@ -28,20 +26,26 @@ final class SerializedJSONTests: XCTestCase { #endif #if os(Windows) - path = try AbsolutePath(validating: #"\\?\C:\Users"#) + path = try AbsolutePath(validating: #"\??\Volumes{b79de17a-a1ed-4c58-a353-731b7c4885a6}\\"#) json = "\(path)" + XCTAssertEqual(json.underlying, #"\\??\\Volumes{b79de17a-a1ed-4c58-a353-731b7c4885a6}"#) + #endif + } + + func testPathInterpolationFailsOnWindows() throws { + try skipOnWindowsAsTestCurrentlyFails(because: "Expectations are not met") + +#if os(Windows) + var path = try AbsolutePath(validating: #"\\?\C:\Users"#) + var json: SerializedJSON = "\(path)" + XCTAssertEqual(json.underlying, #"C:\\Users"#) path = try AbsolutePath(validating: #"\\.\UNC\server\share\"#) json = "\(path)" XCTAssertEqual(json.underlying, #"\\.\\UNC\\server\\share"#) - - path = try AbsolutePath(validating: #"\??\Volumes{b79de17a-a1ed-4c58-a353-731b7c4885a6}\\"#) - json = "\(path)" - - XCTAssertEqual(json.underlying, #"\\??\\Volumes{b79de17a-a1ed-4c58-a353-731b7c4885a6}"#) - #endif +#endif } } From b69b7924f507938bb3cd66421badde422386971c Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Tue, 15 Apr 2025 15:43:19 -0400 Subject: [PATCH 17/33] Mark skipped test with a GitHub issue (#8502) --- Tests/BasicsTests/HTTPClientTests.swift | 2 +- Tests/BasicsTests/LegacyHTTPClientTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/BasicsTests/HTTPClientTests.swift b/Tests/BasicsTests/HTTPClientTests.swift index ef313889238..de4434e2190 100644 --- a/Tests/BasicsTests/HTTPClientTests.swift +++ b/Tests/BasicsTests/HTTPClientTests.swift @@ -226,7 +226,7 @@ final class HTTPClientTests: XCTestCase { } func testExponentialBackoff() async throws { - try skipOnWindowsAsTestCurrentlyFails() + try skipOnWindowsAsTestCurrentlyFails(because: "https://github.com/swiftlang/swift-package-manager/issues/8501") let counter = SendableBox(0) let lastCall = SendableBox() diff --git a/Tests/BasicsTests/LegacyHTTPClientTests.swift b/Tests/BasicsTests/LegacyHTTPClientTests.swift index 38a8300ce83..b4de3965733 100644 --- a/Tests/BasicsTests/LegacyHTTPClientTests.swift +++ b/Tests/BasicsTests/LegacyHTTPClientTests.swift @@ -350,7 +350,7 @@ final class LegacyHTTPClientTests: XCTestCase { } func testExponentialBackoff() throws { - try skipOnWindowsAsTestCurrentlyFails() + try skipOnWindowsAsTestCurrentlyFails(because: "https://github.com/swiftlang/swift-package-manager/issues/8501") let count = ThreadSafeBox(0) let lastCall = ThreadSafeBox() From dec1dede6011708d88475136a9a5a6c6b9d9775f Mon Sep 17 00:00:00 2001 From: Paulo Mattos Date: Tue, 15 Apr 2025 13:21:19 -0700 Subject: [PATCH 18/33] Improve Swift Build error formatting (#8493) ### Motivation: This improves a bit the error/warning output when building with the new `--build-system swiftbuild`. This was the current output: ```shell warning: unknown Enabling the Swift language feature 'MemberImportVisibility' is recommended; set 'SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES' [] error: path("/Users/pmattos/Development/SampleProjects/SamplePackages/PackageAccessCLI/Sources/ExampleApp/main.swift", fileLocation: Optional(SwiftBuild.SwiftBuildMessage.DiagnosticInfo.Location.FileLocation.textual(line: 6, column: Optional(5)))) cannot find 'bar' in scope [] ``` ...and this is the improved one: ```shell warning: Enabling the Swift language feature 'MemberImportVisibility' is recommended; set 'SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES' error: /Users/pmattos/Development/SampleProjects/SamplePackages/PackageAccessCLI/Sources/ExampleApp/main.swift:5:5 cannot find 'foo2' in scope ``` Eventually, we should consider adopting the error output style from the `--build-system native` instead, i.e.: ```shell /Users/pmattos/Development/SampleProjects/SamplePackages/PackageAccessCLI/Sources/ExampleApp/main.swift:5:5: error: cannot find 'foo2' in scope 3 | print("Hello, world!") 4 | 5 | _ = foo2() | `- error: cannot find 'foo2' in scope 6 | _ = bar() 7 | ``` ### Modifications: These formatting changes are strictly local to the new `SwiftBuildSupport` target. ### Result: Slightly better error formatting :-) --- .../Tests/IntegrationTests/SwiftPMTests.swift | 41 ++++++------- .../SwiftBuildSupport/SwiftBuildSystem.swift | 57 ++++++++++++++++--- .../_InternalTestSupport/Observability.swift | 6 +- .../XCBuildSupportTests/PIFBuilderTests.swift | 2 +- 4 files changed, 74 insertions(+), 32 deletions(-) diff --git a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift index 19a6307a9c6..b35d0f6e274 100644 --- a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift +++ b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift @@ -110,29 +110,30 @@ private struct SwiftPMTests { .requireThreadSafeWorkingDirectory, .bug(id: 0, "SWBINTTODO: Linux: /lib/x86_64-linux-gnu/Scrt1.o:function _start: error:"), .bug("https://github.com/swiftlang/swift-package-manager/issues/8380", "lld-link: error: subsystem must be defined"), - .bug(id:0, "SWBINTTODO: MacOS: Could not find or use auto-linked library 'Testing': library 'Testing' not found"), + .bug(id: 0, "SWBINTTODO: MacOS: Could not find or use auto-linked library 'Testing': library 'Testing' not found"), arguments: BuildSystemProvider.allCases ) func packageInitLibrary(_ buildSystemProvider: BuildSystemProvider) throws { - do { - try withTemporaryDirectory { tmpDir in - let packagePath = tmpDir.appending(component: "foo") - try localFileSystem.createDirectory(packagePath) - try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "library") - try withKnownIssue(""" - Linux: /lib/x86_64-linux-gnu/Scrt1.o:function _start: error: undefined reference to 'main' - Windows: lld-link: error: subsystem must be defined - MacOS: Could not find or use auto-linked library 'Testing': library 'Testing' not found - """) { - try sh(swiftBuild, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue, "--vv") - let (stdout, stderr) = try sh( - swiftTest, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue, "--vv" - ) - #expect(!stderr.contains("error:")) - #expect(stdout.contains("Test Suite 'All tests' passed")) - } when: { - buildSystemProvider == .swiftbuild - } + try withTemporaryDirectory { tmpDir in + let packagePath = tmpDir.appending(component: "foo") + try localFileSystem.createDirectory(packagePath) + try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "library") + try withKnownIssue( + """ + Linux: /lib/x86_64-linux-gnu/Scrt1.o:function _start: error: undefined reference to 'main' + Windows: lld-link: error: subsystem must be defined + MacOS: Could not find or use auto-linked library 'Testing': library 'Testing' not found + """, + isIntermittent: true + ) { + try sh(swiftBuild, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue, "--vv") + let (stdout, stderr) = try sh( + swiftTest, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue, "--vv" + ) + #expect(!stderr.contains("error:")) + #expect(stdout.contains("Test Suite 'All tests' passed")) + } when: { + buildSystemProvider == .swiftbuild } } } diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index 9127648ce3f..fe774d54173 100644 --- a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift +++ b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift @@ -353,15 +353,23 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { } progressAnimation.update(step: step, total: 100, text: message) case .diagnostic(let info): - if info.kind == .error { - self.observabilityScope.emit(error: "\(info.location) \(info.message) \(info.fixIts)") - } else if info.kind == .warning { - self.observabilityScope.emit(warning: "\(info.location) \(info.message) \(info.fixIts)") - } else if info.kind == .note { - self.observabilityScope.emit(info: "\(info.location) \(info.message) \(info.fixIts)") - } else if info.kind == .remark { - self.observabilityScope.emit(debug: "\(info.location) \(info.message) \(info.fixIts)") + let fixItsDescription = if info.fixIts.hasContent { + ": " + info.fixIts.map { String(describing: $0) }.joined(separator: ", ") + } else { + "" + } + let message = if let locationDescription = info.location.userDescription { + "\(locationDescription) \(info.message)\(fixItsDescription)" + } else { + "\(info.message)\(fixItsDescription)" + } + let severity: Diagnostic.Severity = switch info.kind { + case .error: .error + case .warning: .warning + case .note: .info + case .remark: .debug } + self.observabilityScope.emit(severity: severity, message: message) case .taskOutput(let info): self.observabilityScope.emit(info: "\(info.data)") case .taskStarted(let info): @@ -509,6 +517,8 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { } } +// MARK: - Helpers + extension String { /// Escape the usual shell related things, such as quoting, but also handle Windows /// back-slashes. @@ -541,3 +551,34 @@ extension Basics.Diagnostic.Severity { self <= .info } } + +#if canImport(SwiftBuild) + +fileprivate extension SwiftBuild.SwiftBuildMessage.DiagnosticInfo.Location { + var userDescription: String? { + switch self { + case .path(let path, let fileLocation): + switch fileLocation { + case .textual(let line, let column): + var description = "\(path):\(line)" + if let column { description += ":\(column)" } + return description + case .object(let identifier): + return "\(path):\(identifier)" + case .none: + return path + } + + case .buildSettings(let names): + return names.joined(separator: ", ") + + case .buildFiles(let buildFiles, let targetGUID): + return "\(targetGUID): " + buildFiles.map { String(describing: $0) }.joined(separator: ", ") + + case .unknown: + return nil + } + } +} + +#endif diff --git a/Sources/_InternalTestSupport/Observability.swift b/Sources/_InternalTestSupport/Observability.swift index 36c260ede79..74a9594f4bd 100644 --- a/Sources/_InternalTestSupport/Observability.swift +++ b/Sources/_InternalTestSupport/Observability.swift @@ -56,15 +56,14 @@ public struct TestingObservability { self.collector.hasWarnings } - final class Collector: ObservabilityHandlerProvider, DiagnosticsHandler, CustomStringConvertible { + fileprivate final class Collector: ObservabilityHandlerProvider, DiagnosticsHandler, CustomStringConvertible { var diagnosticsHandler: DiagnosticsHandler { self } - let diagnostics: ThreadSafeArrayStore private let verbose: Bool + let diagnostics = ThreadSafeArrayStore() init(verbose: Bool) { self.verbose = verbose - self.diagnostics = .init() } // TODO: do something useful with scope @@ -98,6 +97,7 @@ public func XCTAssertNoDiagnostics( ) { let diagnostics = problemsOnly ? diagnostics.filter { $0.severity >= .warning } : diagnostics if diagnostics.isEmpty { return } + let description = diagnostics.map { "- " + $0.description }.joined(separator: "\n") XCTFail("Found unexpected diagnostics: \n\(description)", file: file, line: line) } diff --git a/Tests/XCBuildSupportTests/PIFBuilderTests.swift b/Tests/XCBuildSupportTests/PIFBuilderTests.swift index fc480b34e27..172d59f255b 100644 --- a/Tests/XCBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/XCBuildSupportTests/PIFBuilderTests.swift @@ -2952,7 +2952,7 @@ final class PIFBuilderTests: XCTestCase { "/Foo/Sources/qux/main.swift" ) - let observability = ObservabilitySystem.makeForTesting() + let observability = ObservabilitySystem.makeForTesting(verbose: false) // Don't print expected [error]s. let graph = try loadModulesGraph( fileSystem: fs, manifests: [ From 1befa527a52dd5fd1d4ceb70beb8fbc713f7eeff Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Tue, 15 Apr 2025 23:33:11 -0400 Subject: [PATCH 19/33] Docs: Refer to swiftly in CONTRIBUTING.md (#8504) The Swift toolchain installation on swift.org indicate to use swiftly to install the toolchain on macOS and Linux. Update the CONTRIBUTING.md file to refer to swiftly for toolchain management. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b565d970a89..a49877c8e8e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,7 +88,7 @@ $> swift --version Apple Swift version 5.3 ``` -Note: Alternatively use tools like [swiftenv](https://github.com/kylef/swiftenv) that help manage toolchains versions. +Note: Alternatively use tools like [swiftly](https://www.swift.org/swiftly/documentation/swiftlydocs/) that help manage toolchains versions. ## Local Development From 84322209c88cd590b7599d058e37cee478b20dc5 Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Wed, 16 Apr 2025 08:59:37 -0400 Subject: [PATCH 20/33] Test: Enable ConcurrencyHelpersTests on Windows (#8506) --- Tests/BasicsTests/ConcurrencyHelpersTests.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Tests/BasicsTests/ConcurrencyHelpersTests.swift b/Tests/BasicsTests/ConcurrencyHelpersTests.swift index fb47cfcd3dc..2efa891fded 100644 --- a/Tests/BasicsTests/ConcurrencyHelpersTests.swift +++ b/Tests/BasicsTests/ConcurrencyHelpersTests.swift @@ -14,15 +14,9 @@ import TSCTestSupport import XCTest -import _InternalTestSupport // for skipOnWindowsAsTestCurrentlyFails - final class ConcurrencyHelpersTest: XCTestCase { let queue = DispatchQueue(label: "ConcurrencyHelpersTest", attributes: .concurrent) - override func setUpWithError() throws { - try skipOnWindowsAsTestCurrentlyFails() - } - func testThreadSafeKeyValueStore() { for _ in 0 ..< 100 { let sync = DispatchGroup() From 6245fa90f5c64ac478ef98399d6199d3ee9586da Mon Sep 17 00:00:00 2001 From: Doug Schaefer <167107236+dschaefer2@users.noreply.github.com> Date: Wed, 16 Apr 2025 14:24:18 -0400 Subject: [PATCH 21/33] Really fix duplicate module maps this time. (#8498) I had mucked with the fix to finish some testing and forgot to put the fix back. And the test didn't properly catch that. Fixing both. --- Sources/Build/BuildPlan/BuildPlan+Swift.swift | 2 +- Tests/BuildTests/BuildPlanTests.swift | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index f0c0b256cbb..51a9ad4679d 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -20,7 +20,7 @@ extension BuildPlan { func plan(swiftTarget: SwiftModuleBuildDescription) throws { // We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target // builds against - for case .module(let dependency, let description) in swiftTarget.recursiveDependencies(using: self) { + for case .module(let dependency, let description) in swiftTarget.recursiveLinkDependencies(using: self) { switch dependency.underlying { case let underlyingTarget as ClangModule where underlyingTarget.type == .library: guard case let .clang(target)? = description else { diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 423afcd0e04..6d8741e7ad4 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -6972,8 +6972,7 @@ class BuildPlanTestCase: BuildSystemProviderTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let myLib = try XCTUnwrap(plan.targets.first(where: { $0.module.name == "MyLib" })).swift() - print(myLib.additionalFlags) - XCTAssertFalse(myLib.additionalFlags.contains(where: { $0.contains("-tool/include")}), "flags shouldn't contain tools items") + XCTAssertFalse(myLib.additionalFlags.contains(where: { $0.contains("-tool")}), "flags shouldn't contain tools items") } } From e842b0ed3feb04eb885b3c7a1fc5fbc86d51cd25 Mon Sep 17 00:00:00 2001 From: Dave Inglis Date: Wed, 16 Apr 2025 15:26:46 -0400 Subject: [PATCH 22/33] Implement --enable-parseable-module-interfaces for swift-build buildsystem (#8421) - this also fixes -enable-library-evolution when used as a unsafeFlags Closes: #8337 ### Modifications: sets SWIFT_EMIT_MODULE_INTERFACE build setting when option is set ### Result: the build option will include the .swftinertface files in the module folder --- .../SwiftBuildSupport/SwiftBuildSystem.swift | 5 + Tests/CommandsTests/BuildCommandTests.swift | 148 ++++++++++-------- 2 files changed, 88 insertions(+), 65 deletions(-) diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index fe774d54173..374a02cf517 100644 --- a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift +++ b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift @@ -478,6 +478,11 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { settings["ARCHS"] = architectures.joined(separator: " ") } + // support for --enable-parseable-module-interfaces + if buildParameters.driverParameters.enableParseableModuleInterfaces { + settings["SWIFT_EMIT_MODULE_INTERFACE"] = "YES" + } + // Generate the build parameters. var params = SwiftBuild.SWBBuildParameters() params.configurationName = buildParameters.configuration.swiftbuildName diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index bead37cf431..61db4261f59 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -38,7 +38,7 @@ class BuildCommandTestCases: CommandsBuildProviderTestCase { } @discardableResult - private func execute( + func execute( _ args: [String] = [], environment: Environment? = nil, packagePath: AbsolutePath? = nil @@ -70,7 +70,19 @@ class BuildCommandTestCases: CommandsBuildProviderTestCase { // is what `binContents` is meant to represent. return contents != ["output-file-map.json"] } - let moduleContents = (try? localFileSystem.getDirectoryContents(binPath.appending(component: "Modules"))) ?? [] + var moduleContents: [String] = [] + if buildSystemProvider == .native { + moduleContents = (try? localFileSystem.getDirectoryContents(binPath.appending(component: "Modules"))) ?? [] + } else { + let moduleDirs = (try? localFileSystem.getDirectoryContents(binPath).filter { + $0.hasSuffix(".swiftmodule") + }) ?? [] + for dir: String in moduleDirs { + moduleContents += + (try? localFileSystem.getDirectoryContents(binPath.appending(component: dir)).map { "\(dir)/\($0)" }) ?? [] + } + } + if cleanAfterward { try! await executeSwiftPackage( @@ -103,6 +115,10 @@ class BuildCommandTestCases: CommandsBuildProviderTestCase { XCTAssertMatch(stdout, .contains("USAGE: swift build")) } + func testBinSymlink() async throws { + XCTAssertTrue(false, "Must be implemented at build system test class.") + } + func testSeeAlso() async throws { let stdout = try await execute(["--help"]).stdout XCTAssertMatch(stdout, .contains("SEE ALSO: swift run, swift package, swift test")) @@ -190,48 +206,6 @@ class BuildCommandTestCases: CommandsBuildProviderTestCase { } } - func testBinSymlink() async throws { - try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in - let fullPath = try resolveSymlinks(fixturePath) - let targetPath = try fullPath.appending( - components: ".build", - UserToolchain.default.targetTriple.platformBuildPathComponent - ) - let xcbuildTargetPath = fullPath.appending(components: ".build", "apple") - try await XCTAssertAsyncEqual( - try await self.execute(["--show-bin-path"], packagePath: fullPath).stdout, - "\(targetPath.appending("debug").pathString)\n" - ) - try await XCTAssertAsyncEqual( - try await self.execute(["-c", "release", "--show-bin-path"], packagePath: fullPath).stdout, - "\(targetPath.appending("release").pathString)\n" - ) - - guard buildSystemProvider == .xcode || buildSystemProvider == .swiftbuild else { - // The remainder of this test only applies to XCBuild or Swift Build - return - } - - // Print correct path when building with XCBuild or Swift Build - let xcodeDebugOutput = try await execute( - ["-c", "debug", "--show-bin-path"], - packagePath: fullPath) - .stdout - let xcodeReleaseOutput = try await execute( - ["-c", "release", "--show-bin-path"], - packagePath: fullPath - ).stdout - XCTAssertEqual( - xcodeDebugOutput, - "\(xcbuildTargetPath.appending(components: "Products", "Debug").pathString)\n" - ) - XCTAssertEqual( - xcodeReleaseOutput, - "\(xcbuildTargetPath.appending(components: "Products", "Release").pathString)\n" - ) - } - } - func testSymlink() async throws { try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) @@ -792,6 +766,25 @@ class BuildCommandNativeTests: BuildCommandTestCases { override func testUsage() async throws { try await super.testUsage() } + + override func testBinSymlink() async throws { + try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in + let fullPath = try resolveSymlinks(fixturePath) + let targetPath = try fullPath.appending( + components: ".build", + UserToolchain.default.targetTriple.platformBuildPathComponent + ) + try await XCTAssertAsyncEqual( + try await self.execute(["--show-bin-path"], packagePath: fullPath).stdout, + "\(targetPath.appending("debug").pathString)\n" + ) + try await XCTAssertAsyncEqual( + try await self.execute(["-c", "release", "--show-bin-path"], packagePath: fullPath) + .stdout, + "\(targetPath.appending("release").pathString)\n" + ) + } + } } #if os(macOS) @@ -806,51 +799,51 @@ class BuildCommandXcodeTests: BuildCommandTestCases { } override func testAutomaticParseableInterfacesWithLibraryEvolution() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testNonReachableProductsAndTargetsFunctional() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testCodeCoverage() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testBuildStartMessage() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testBinSymlink() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testSymlink() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testSwiftGetVersion() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testParseableInterfaces() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testProductAndTarget() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testImportOfMissedDepWarning() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testGetTaskAllowEntitlement() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } override func testBuildCompleteMessage() async throws { - try XCTSkip("Test not implemented for xcode build system.") + throw XCTSkip("Test not implemented for xcode build system.") } } #endif @@ -866,9 +859,31 @@ class BuildCommandSwiftBuildTests: BuildCommandTestCases { } override func testParseableInterfaces() async throws { - throw XCTSkip("SWBINTTODO: Test failed with swiftbuild engine because the --enable-parseable-module-interfaces flag doesn't yet produce .swiftinterface files. This needs to be investigated") + try await fixture(name: "Miscellaneous/ParseableInterfaces") { fixturePath in + do { + let result = try await build(["--enable-parseable-module-interfaces"], packagePath: fixturePath) + XCTAssertMatch(result.moduleContents, [.regex(#"A\.swiftmodule\/.*\.swiftinterface"#)]) + XCTAssertMatch(result.moduleContents, [.regex(#"B\.swiftmodule\/.*\.swiftmodule"#)]) + } catch SwiftPMError.executionFailure(_, _, let stderr) { + XCTFail(stderr) + } + } } + override func testBinSymlink() async throws { + try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in + let fullPath = try resolveSymlinks(fixturePath) + let targetPath = try fullPath.appending( + components: ".build", + UserToolchain.default.targetTriple.platformBuildPathComponent + ) + let debugPath = try await self.execute(["--show-bin-path"], packagePath: fullPath).stdout + XCTAssertMatch(debugPath, .regex(targetPath.appending(components: "Products", "Debug").pathString + "(\\-linux|\\-Windows)?\\n")) + let releasePath = try await self.execute(["-c", "release", "--show-bin-path"], packagePath: fullPath).stdout + XCTAssertMatch(releasePath, .regex(targetPath.appending(components: "Products", "Release").pathString + "(\\-linux|\\-Windows)?\\n")) + } + } + override func testGetTaskAllowEntitlement() async throws { throw XCTSkip("SWBINTTODO: Test failed because swiftbuild doesn't output precis codesign commands. Once swift run works with swiftbuild the test can be investigated.") } @@ -880,13 +895,20 @@ class BuildCommandSwiftBuildTests: BuildCommandTestCases { override func testAtMainSupport() async throws { #if !os(macOS) throw XCTSkip("SWBINTTODO: File not found or missing libclang errors on non-macOS platforms. This needs to be investigated") - #endif - + #else try await super.testAtMainSupport() + #endif } override func testAutomaticParseableInterfacesWithLibraryEvolution() async throws { - throw XCTSkip("SWBINTTODO: The test fails because when the unsafe flag for a target is set to '-enable-library-evolution' it is not producing the correct .swiftinterface files. This needs to be investigated") + try await fixture(name: "Miscellaneous/LibraryEvolution") { fixturePath in + do { + let result = try await build([], packagePath: fixturePath) + XCTAssertMatch( + result.moduleContents, [.regex(#"A\.swiftmodule\/.*\.swiftinterface"#)]) + XCTAssertMatch(result.moduleContents, [.regex(#"B\.swiftmodule\/.*\.swiftmodule"#)]) + } + } } override func testImportOfMissedDepWarning() async throws { @@ -901,10 +923,6 @@ class BuildCommandSwiftBuildTests: BuildCommandTestCases { throw XCTSkip("SWBINTTODO: Test fails because the dummy-swiftc used in the test isn't accepted by swift-build. This needs to be investigated") } - override func testBinSymlink() async throws { - throw XCTSkip("SWBINTTODO: Test fails because of a difference in the build layout. This needs to be updated to the expected path") - } - override func testSymlink() async throws { throw XCTSkip("SWBINTTODO: Test fails because of a difference in the build layout. This needs to be updated to the expected path") } @@ -927,7 +945,7 @@ class BuildCommandSwiftBuildTests: BuildCommandTestCases { override func testBuildSystemDefaultSettings() async throws { #if os(Linux) - if FileManager.default.contents(atPath: "/etc/system-release").map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false { + if FileManager.default.contents(atPath: "/etc/system-release").map( { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ) ?? false { throw XCTSkip("Skipping SwiftBuild testing on Amazon Linux because of platform issues.") } #endif From 861a87105df840ffedf2ae0b2d235310f621a479 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 16 Apr 2025 16:05:01 -0400 Subject: [PATCH 23/33] Add `swift test --attachments-path`. (#8492) This PR adds support for the `--attachments-path` CLI argument on `swift test` as approved in [ST-0009](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0009-attachments.md). We will maintain support for the older `--experimental-attachments-path` version through Swift 6.2. The implementation of this option is held entirely in Swift Testing at this time, so no actual code is needed to support it, we just need to make sure Swift Argument Parser doesn't complain about it. Resolves rdar://147753584. @plemarquand has integration tests but they're blocked on a newer toolchain; unit testing of this argument exists in the Swift Testing repo. --- Sources/Commands/SwiftTestCommand.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index 0eaf6b8623c..c4c6b45bc62 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -124,6 +124,11 @@ struct TestEventStreamOptions: ParsableArguments { @Option(name: .customLong("experimental-attachments-path"), help: .private) var experimentalAttachmentsPath: AbsolutePath? + + /// Path for writing attachments (Swift Testing only.) + @Option(name: .customLong("attachments-path"), + help: "Path where attachments should be written (Swift Testing only). This path must be an existing directory the current user can write to. If not specified, any attachments created during testing are discarded.") + var attachmentsPath: AbsolutePath? } struct TestCommandOptions: ParsableArguments { From 0b230739a426b1edd5f61f627fce0c78401d0384 Mon Sep 17 00:00:00 2001 From: Paulo Mattos Date: Wed, 16 Apr 2025 17:34:12 -0700 Subject: [PATCH 24/33] Tests: Ensure we get clean test fixtures (#8507) ### Motivation: When running tests ensure the *test fixtures* we copy over to temporary directories don't carry over any previous build information. ### Modifications: When copying packages from `/Fixtures/**` we now ensure we delete any `.build` or `.swiftpm` directories, if any. --- Sources/_InternalTestSupport/misc.swift | 17 +++++++++++++---- Tests/CommandsTests/BuildCommandTests.swift | 4 ++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Sources/_InternalTestSupport/misc.swift b/Sources/_InternalTestSupport/misc.swift index b8bdf00999b..0cc73af222d 100644 --- a/Sources/_InternalTestSupport/misc.swift +++ b/Sources/_InternalTestSupport/misc.swift @@ -173,13 +173,22 @@ fileprivate func verifyFixtureExists(at fixtureSubpath: RelativePath, file: Stat return fixtureDir } -fileprivate func setup(fixtureDir: AbsolutePath, in tmpDirPath: AbsolutePath, copyName: String, createGitRepo: Bool = true) throws -> AbsolutePath { +fileprivate func setup( + fixtureDir: AbsolutePath, + in tmpDirPath: AbsolutePath, + copyName: String, + createGitRepo: Bool = true +) throws -> AbsolutePath { func copy(from srcDir: AbsolutePath, to dstDir: AbsolutePath) throws { -#if os(Windows) + #if os(Windows) try localFileSystem.copy(from: srcDir, to: dstDir) -#else + #else try systemQuietly("cp", "-R", "-H", srcDir.pathString, dstDir.pathString) -#endif + #endif + + // Ensure we get a clean test fixture. + try localFileSystem.removeFileTree(dstDir.appending(component: ".build")) + try localFileSystem.removeFileTree(dstDir.appending(component: ".swiftpm")) } // The fixture contains either a checkout or just a Git directory. diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index 61db4261f59..20cfedce6d8 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -209,8 +209,8 @@ class BuildCommandTestCases: CommandsBuildProviderTestCase { func testSymlink() async throws { try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in let fullPath = try resolveSymlinks(fixturePath) - let targetPath = try fullPath.appending( - components: ".build", + let targetPath = try fullPath.appending(components: + ".build", UserToolchain.default.targetTriple.platformBuildPathComponent ) // Test symlink. From f673bf6225fcd0c24bbfc324648bb1f7f78e201e Mon Sep 17 00:00:00 2001 From: Doug Schaefer <167107236+dschaefer2@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:35:47 -0400 Subject: [PATCH 25/33] Revert "Really fix duplicate module maps this time." (#8517) Reverts swiftlang/swift-package-manager#8498 Broke swift-foundation build. --- Sources/Build/BuildPlan/BuildPlan+Swift.swift | 2 +- Tests/BuildTests/BuildPlanTests.swift | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index 51a9ad4679d..f0c0b256cbb 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -20,7 +20,7 @@ extension BuildPlan { func plan(swiftTarget: SwiftModuleBuildDescription) throws { // We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target // builds against - for case .module(let dependency, let description) in swiftTarget.recursiveLinkDependencies(using: self) { + for case .module(let dependency, let description) in swiftTarget.recursiveDependencies(using: self) { switch dependency.underlying { case let underlyingTarget as ClangModule where underlyingTarget.type == .library: guard case let .clang(target)? = description else { diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 6d8741e7ad4..423afcd0e04 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -6972,7 +6972,8 @@ class BuildPlanTestCase: BuildSystemProviderTestCase { XCTAssertNoDiagnostics(observability.diagnostics) let myLib = try XCTUnwrap(plan.targets.first(where: { $0.module.name == "MyLib" })).swift() - XCTAssertFalse(myLib.additionalFlags.contains(where: { $0.contains("-tool")}), "flags shouldn't contain tools items") + print(myLib.additionalFlags) + XCTAssertFalse(myLib.additionalFlags.contains(where: { $0.contains("-tool/include")}), "flags shouldn't contain tools items") } } From 393a8f32da9f49203932400be4e4b04eefc7b1e8 Mon Sep 17 00:00:00 2001 From: Paulo Mattos Date: Thu, 17 Apr 2025 01:03:29 -0700 Subject: [PATCH 26/33] Adopt new PIF builder in SwiftBuildSupport (#8454) ### Motivation: Switch from the legacy PIF builder in `SwiftBuildSupport` to the new one, introduced by PR #8405. The new PIF builder (i.e., `SwiftBuildSupport/PackagePIFBuilder*.swift`) is the exact same we use in Xcode. ### Modifications: Replaces the old PIF builder (i.e., `SwiftBuildSupport/PIF.swift` and `SwiftBuildSupport/PIFBuilder.swift` ) with the new one (i.e., `SwiftBuildSupport/PackagePIFBuilder*.swift`). ### Result: The new PIF builder now fully replaces the legacy PIF builder. In particular, all these Swift Build tests are passing (i.e., same as before this PR): * BuildPlanSwiftBuildTests * APIDiffSwiftBuildTests * BuildCommandSwiftBuildTests * PackageCommandSwiftBuildTests * RunCommandSwiftBuildTests * TestCommandSwiftBuildTests I also improved the PIF logging too. Try the `--very-verbose` option to see what I mean :-) Tracked by rdar://147527170. --- .../Tests/IntegrationTests/SwiftPMTests.swift | 19 +- Sources/SwiftBuildSupport/BuildSystem.swift | 2 +- Sources/SwiftBuildSupport/PIF.swift | 1253 +--------- Sources/SwiftBuildSupport/PIFBuilder.swift | 2021 ++--------------- .../PackagePIFBuilder+Helpers.swift | 112 +- .../SwiftBuildSupport/PackagePIFBuilder.swift | 99 +- .../PackagePIFProjectBuilder+Modules.swift | 72 +- .../PackagePIFProjectBuilder+Products.swift | 80 +- .../PackagePIFProjectBuilder.swift | 17 +- Tests/BuildTests/BuildPlanTests.swift | 5 +- Tests/CommandsTests/BuildCommandTests.swift | 20 +- 11 files changed, 599 insertions(+), 3101 deletions(-) diff --git a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift index b35d0f6e274..770d046ee14 100644 --- a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift +++ b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift @@ -75,10 +75,15 @@ private struct SwiftPMTests { } @Test( + .requireHostOS(.windows, when: false), .requireThreadSafeWorkingDirectory, .bug( "https://github.com/swiftlang/swift-package-manager/issues/8416", - "swift run using --build-system swiftbuild fails to run executable" + "[Linux] swift run using --build-system swiftbuild fails to run executable" + ), + .bug( + "https://github.com/swiftlang/swift-package-manager/issues/8514", + "[Windows] Integration test SwiftPMTests.packageInitExecutable with --build-system swiftbuild is skipped" ), arguments: BuildSystemProvider.allCases ) @@ -119,12 +124,12 @@ private struct SwiftPMTests { try localFileSystem.createDirectory(packagePath) try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "library") try withKnownIssue( - """ - Linux: /lib/x86_64-linux-gnu/Scrt1.o:function _start: error: undefined reference to 'main' - Windows: lld-link: error: subsystem must be defined - MacOS: Could not find or use auto-linked library 'Testing': library 'Testing' not found - """, - isIntermittent: true + """ + Linux: /lib/x86_64-linux-gnu/Scrt1.o:function _start: error: undefined reference to 'main' + Windows: lld-link: error: subsystem must be defined + MacOS: Could not find or use auto-linked library 'Testing': library 'Testing' not found + """, + isIntermittent: true ) { try sh(swiftBuild, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue, "--vv") let (stdout, stderr) = try sh( diff --git a/Sources/SwiftBuildSupport/BuildSystem.swift b/Sources/SwiftBuildSupport/BuildSystem.swift index 0418c5d3ce9..41f955eb3b6 100644 --- a/Sources/SwiftBuildSupport/BuildSystem.swift +++ b/Sources/SwiftBuildSupport/BuildSystem.swift @@ -14,7 +14,7 @@ extension BuildSubset { var pifTargetName: String { switch self { case .product(let name, _): - _PackagePIFProjectBuilder.targetName(for: name) + targetName(forProductName: name) case .target(let name, _): name case .allExcludingTests: diff --git a/Sources/SwiftBuildSupport/PIF.swift b/Sources/SwiftBuildSupport/PIF.swift index fa295895139..8c74f90ffcb 100644 --- a/Sources/SwiftBuildSupport/PIF.swift +++ b/Sources/SwiftBuildSupport/PIF.swift @@ -17,6 +17,9 @@ import PackageModel import struct TSCBasic.ByteString +#if canImport(SwiftBuild) +import enum SwiftBuild.ProjectModel + /// The Project Interchange Format (PIF) is a structured representation of the /// project model created by clients to send to SwiftBuild. /// @@ -27,39 +30,40 @@ import struct TSCBasic.ByteString /// between builds which use different schemes or configurations), and can be /// incrementally updated by clients when something changes. public enum PIF { - /// This is used as part of the signature for the high-level PIF objects, to ensure that changes to the PIF schema - /// are represented by the objects which do not use a content-based signature scheme (workspaces and projects, - /// currently). - static let schemaVersion = 11 - /// The type used for identifying PIF objects. - public typealias GUID = String - + public typealias GUID = ProjectModel.GUID + /// The top-level PIF object. public struct TopLevelObject: Encodable { public let workspace: PIF.Workspace - + public init(workspace: PIF.Workspace) { self.workspace = workspace } - + public func encode(to encoder: Encoder) throws { var container = encoder.unkeyedContainer() - + // Encode the workspace. try container.encode(workspace) - + // Encode the projects and their targets. for project in workspace.projects { try container.encode(project) - - for target in project.targets { - try container.encode(target) + let targets = project.underlying.targets + + for target in targets where !target.id.hasSuffix(.dynamic) { + try container.encode(Target(wrapping: target)) + } + + // Add *dynamic variants* at the end just to have a clear split from other targets. + for target in targets where target.id.hasSuffix(.dynamic) { + try container.encode(Target(wrapping: target)) } } } } - + /// Represents a high-level PIF object. /// /// For instance, a JSON serialized *workspace* might look like this: @@ -82,1219 +86,202 @@ public enum PIF { class var type: String { fatalError("\(self) missing implementation") } - - let type: String? - + + let type: String + fileprivate init() { - type = Swift.type(of: self).type + type = Self.type } - + fileprivate enum CodingKeys: CodingKey { case type case signature, contents // Used by subclasses. } - + public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(Swift.type(of: self).type, forKey: .type) + var superContainer = encoder.container(keyedBy: CodingKeys.self) + try superContainer.encode(type, forKey: .type) } - + required public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - type = try container.decode(String.self, forKey: .type) + let superContainer = try decoder.container(keyedBy: CodingKeys.self) + self.type = try superContainer.decode(String.self, forKey: .type) + + guard self.type == Self.type else { + throw InternalError("Expected same type for high-level object: \(self.type)") + } } } - + + /// The high-level PIF *workspace* object. public final class Workspace: HighLevelObject { override class var type: String { "workspace" } - + public let guid: GUID public var name: String public var path: AbsolutePath public var projects: [Project] var signature: String? - public init(guid: GUID, name: String, path: AbsolutePath, projects: [Project]) { - precondition(!guid.isEmpty) + public init(guid: GUID, name: String, path: AbsolutePath, projects: [ProjectModel.Project]) { + precondition(!guid.value.isEmpty) precondition(!name.isEmpty) - precondition(Set(projects.map({ $0.guid })).count == projects.count) - + precondition(Set(projects.map(\.id)).count == projects.count) + self.guid = guid self.name = name self.path = path - self.projects = projects + self.projects = projects.map { Project(wrapping: $0) } super.init() } - + private enum CodingKeys: CodingKey { - case guid, name, path, projects, signature + case guid, name, path, projects } - + public override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) + var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self) var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid) + + try contents.encode("\(guid)", forKey: .guid) try contents.encode(name, forKey: .name) try contents.encode(path, forKey: .path) - + try contents.encode(projects.map(\.signature), forKey: .projects) + if encoder.userInfo.keys.contains(.encodeForSwiftBuild) { guard let signature else { - throw InternalError("Expected to have workspace signature when encoding for SwiftBuild") + throw InternalError("Expected to have workspace *signature* when encoding for SwiftBuild") } try superContainer.encode(signature, forKey: .signature) - try contents.encode(projects.map({ $0.signature }), forKey: .projects) - } else { - try contents.encode(projects, forKey: .projects) } } - + public required init(from decoder: Decoder) throws { let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self) let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - - let guidString = try contents.decode(GUID.self, forKey: .guid) - self.guid = String(guidString.dropLast("\(schemaVersion)".count + 1)) + + self.guid = try contents.decode(GUID.self, forKey: .guid) self.name = try contents.decode(String.self, forKey: .name) self.path = try contents.decode(AbsolutePath.self, forKey: .path) self.projects = try contents.decode([Project].self, forKey: .projects) + try super.init(from: decoder) } } - - /// A PIF project, consisting of a tree of groups and file references, a list of targets, and some additional - /// information. + + /// A high-level PIF *project* object. public final class Project: HighLevelObject { override class var type: String { "project" } - - public let guid: GUID - public var name: String - public var path: AbsolutePath - public var projectDirectory: AbsolutePath - public var developmentRegion: String - public var buildConfigurations: [BuildConfiguration] - public var targets: [BaseTarget] - public var groupTree: Group + + public var underlying: ProjectModel.Project var signature: String? - - public init( - guid: GUID, - name: String, - path: AbsolutePath, - projectDirectory: AbsolutePath, - developmentRegion: String, - buildConfigurations: [BuildConfiguration], - targets: [BaseTarget], - groupTree: Group - ) { - precondition(!guid.isEmpty) - precondition(!name.isEmpty) - precondition(!developmentRegion.isEmpty) - precondition(Set(targets.map({ $0.guid })).count == targets.count) - precondition(Set(buildConfigurations.map({ $0.guid })).count == buildConfigurations.count) - - self.guid = guid - self.name = name - self.path = path - self.projectDirectory = projectDirectory - self.developmentRegion = developmentRegion - self.buildConfigurations = buildConfigurations - self.targets = targets - self.groupTree = groupTree + var id: ProjectModel.GUID { underlying.id } + + public init(wrapping underlying: ProjectModel.Project) { + precondition(!underlying.name.isEmpty) + precondition(!underlying.id.value.isEmpty) + precondition(!underlying.path.isEmpty) + precondition(!underlying.projectDir.isEmpty) + + precondition(Set(underlying.targets.map(\.id)).count == underlying.targets.count) + precondition(Set(underlying.buildConfigs.map(\.id)).count == underlying.buildConfigs.count) + + self.underlying = underlying super.init() } - - private enum CodingKeys: CodingKey { - case guid, projectName, projectIsPackage, path, projectDirectory, developmentRegion, defaultConfigurationName, buildConfigurations, targets, groupTree, signature - } - - public override func encode(to encoder: Encoder) throws { + + public override func encode(to encoder: any Encoder) throws { try super.encode(to: encoder) var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self) - var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid) - try contents.encode(name, forKey: .projectName) - try contents.encode("true", forKey: .projectIsPackage) - try contents.encode(path, forKey: .path) - try contents.encode(projectDirectory, forKey: .projectDirectory) - try contents.encode(developmentRegion, forKey: .developmentRegion) - try contents.encode("Release", forKey: .defaultConfigurationName) - try contents.encode(buildConfigurations, forKey: .buildConfigurations) + try superContainer.encode(underlying, forKey: .contents) if encoder.userInfo.keys.contains(.encodeForSwiftBuild) { guard let signature else { - throw InternalError("Expected to have project signature when encoding for SwiftBuild") + throw InternalError("Expected to have project *signature* when encoding for SwiftBuild") } try superContainer.encode(signature, forKey: .signature) - try contents.encode(targets.map{ $0.signature }, forKey: .targets) - } else { - try contents.encode(targets, forKey: .targets) } - - try contents.encode(groupTree, forKey: .groupTree) } - + public required init(from decoder: Decoder) throws { let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self) - let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - - let guidString = try contents.decode(GUID.self, forKey: .guid) - self.guid = String(guidString.dropLast("\(schemaVersion)".count + 1)) - self.name = try contents.decode(String.self, forKey: .projectName) - self.path = try contents.decode(AbsolutePath.self, forKey: .path) - self.projectDirectory = try contents.decode(AbsolutePath.self, forKey: .projectDirectory) - self.developmentRegion = try contents.decode(String.self, forKey: .developmentRegion) - self.buildConfigurations = try contents.decode([BuildConfiguration].self, forKey: .buildConfigurations) - - let untypedTargets = try contents.decode([UntypedTarget].self, forKey: .targets) - var targetContainer = try contents.nestedUnkeyedContainer(forKey: .targets) - self.targets = try untypedTargets.map { target in - let type = target.contents.type - switch type { - case "aggregate": - return try targetContainer.decode(AggregateTarget.self) - case "standard", "packageProduct": - return try targetContainer.decode(Target.self) - default: - throw InternalError("unknown target type \(type)") - } - } - - self.groupTree = try contents.decode(Group.self, forKey: .groupTree) + self.underlying = try superContainer.decode(ProjectModel.Project.self, forKey: .contents) + try super.init(from: decoder) } } - - /// Abstract base class for all items in the group hierarchy. - public class Reference: HighLevelObject { - /// Determines the base path for a reference's relative path. - public enum SourceTree: String, Codable { - - /// Indicates that the path is relative to the source root (i.e. the "project directory"). - case sourceRoot = "SOURCE_ROOT" - - /// Indicates that the path is relative to the path of the parent group. - case group = "" - - /// Indicates that the path is relative to the effective build directory (which varies depending on active - /// scheme, active run destination, or even an overridden build setting. - case builtProductsDir = "BUILT_PRODUCTS_DIR" - - /// Indicates that the path is an absolute path. - case absolute = "" - } - - public let guid: GUID - - /// Relative path of the reference. It is usually a literal, but may in fact contain build settings. - public var path: String - - /// Determines the base path for the reference's relative path. - public var sourceTree: SourceTree - - /// Name of the reference, if different from the last path component (if not set, the last path component will - /// be used as the name). - public var name: String? - - fileprivate init( - guid: GUID, - path: String, - sourceTree: SourceTree, - name: String? - ) { - precondition(!guid.isEmpty) - precondition(!(name?.isEmpty ?? false)) - - self.guid = guid - self.path = path - self.sourceTree = sourceTree - self.name = name + + /// A high-level PIF *target* object. + private final class Target: HighLevelObject { + override class var type: String { "target" } + + public var underlying: ProjectModel.BaseTarget + var id: ProjectModel.GUID { underlying.id } + + public init(wrapping underlying: ProjectModel.BaseTarget) { + precondition(!underlying.id.value.isEmpty) + precondition(!underlying.common.name.isEmpty) + + self.underlying = underlying super.init() } - - private enum CodingKeys: CodingKey { - case guid, sourceTree, path, name, type - } - - public override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(guid, forKey: .guid) - try container.encode(sourceTree, forKey: .sourceTree) - try container.encode(path, forKey: .path) - try container.encode(name ?? path, forKey: .name) - } - - public required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.guid = try container.decode(String.self, forKey: .guid) - self.sourceTree = try container.decode(SourceTree.self, forKey: .sourceTree) - self.path = try container.decode(String.self, forKey: .path) - self.name = try container.decodeIfPresent(String.self, forKey: .name) - try super.init(from: decoder) - } - } - - /// A reference to a file system entity (a file, folder, etc). - public final class FileReference: Reference { - override class var type: String { "file" } - - public var fileType: String - - public init( - guid: GUID, - path: String, - sourceTree: SourceTree = .group, - name: String? = nil, - fileType: String? = nil - ) { - self.fileType = fileType ?? FileReference.fileTypeIdentifier(forPath: path) - super.init(guid: guid, path: path, sourceTree: sourceTree, name: name) - } - - private enum CodingKeys: CodingKey { - case fileType - } - - public override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(fileType, forKey: .fileType) - } - - public required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.fileType = try container.decode(String.self, forKey: .fileType) - try super.init(from: decoder) - } - } - - /// A group that can contain References (FileReferences and other Groups). The resolved path of a group is used as - /// the base path for any child references whose source tree type is GroupRelative. - public final class Group: Reference { - override class var type: String { "group" } - - public var children: [Reference] - - public init( - guid: GUID, - path: String, - sourceTree: SourceTree = .group, - name: String? = nil, - children: [Reference] - ) { - precondition( - Set(children.map({ $0.guid })).count == children.count, - "multiple group children with the same guid: \(children.map({ $0.guid }))" - ) - - self.children = children - - super.init(guid: guid, path: path, sourceTree: sourceTree, name: name) - } - - private enum CodingKeys: CodingKey { - case children, type - } - - public override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(children, forKey: .children) - } - - public required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - let untypedChildren = try container.decode([HighLevelObject].self, forKey: .children) - var childrenContainer = try container.nestedUnkeyedContainer(forKey: .children) - - self.children = try untypedChildren.map { child in - switch child.type { - case Group.type: - return try childrenContainer.decode(Group.self) - case FileReference.type: - return try childrenContainer.decode(FileReference.self) - default: - throw InternalError("unknown reference type \(child.type ?? "")") - } - } - - try super.init(from: decoder) - } - } - - /// Represents a dependency on another target (identified by its PIF GUID). - public struct TargetDependency: Codable { - /// Identifier of depended-upon target. - public var targetGUID: String - - /// The platform filters for this target dependency. - public var platformFilters: [PlatformFilter] - - public init(targetGUID: String, platformFilters: [PlatformFilter] = []) { - self.targetGUID = targetGUID - self.platformFilters = platformFilters - } - - private enum CodingKeys: CodingKey { - case guid, platformFilters - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode("\(targetGUID)@\(schemaVersion)", forKey: .guid) - - if !platformFilters.isEmpty { - try container.encode(platformFilters, forKey: .platformFilters) - } - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - let targetGUIDString = try container.decode(String.self, forKey: .guid) - self.targetGUID = String(targetGUIDString.dropLast("\(schemaVersion)".count + 1)) - platformFilters = try container.decodeIfPresent([PlatformFilter].self, forKey: .platformFilters) ?? [] - } - } - - public class BaseTarget: HighLevelObject { - class override var type: String { "target" } - public let guid: GUID - public var name: String - public var buildConfigurations: [BuildConfiguration] - public var buildPhases: [BuildPhase] - public var dependencies: [TargetDependency] - public var impartedBuildProperties: ImpartedBuildProperties - var signature: String? - - fileprivate init( - guid: GUID, - name: String, - buildConfigurations: [BuildConfiguration], - buildPhases: [BuildPhase], - dependencies: [TargetDependency], - impartedBuildSettings: PIF.BuildSettings, - signature: String? - ) { - self.guid = guid - self.name = name - self.buildConfigurations = buildConfigurations - self.buildPhases = buildPhases - self.dependencies = dependencies - impartedBuildProperties = ImpartedBuildProperties(settings: impartedBuildSettings) - self.signature = signature - super.init() - } - - public required init(from decoder: Decoder) throws { - throw InternalError("init(from:) has not been implemented") - } - } - - public final class AggregateTarget: BaseTarget { - public init( - guid: GUID, - name: String, - buildConfigurations: [BuildConfiguration], - buildPhases: [BuildPhase], - dependencies: [TargetDependency], - impartedBuildSettings: PIF.BuildSettings - ) { - super.init( - guid: guid, - name: name, - buildConfigurations: buildConfigurations, - buildPhases: buildPhases, - dependencies: dependencies, - impartedBuildSettings: impartedBuildSettings, - signature: nil - ) - } - - private enum CodingKeys: CodingKey { - case type, guid, name, buildConfigurations, buildPhases, dependencies, impartedBuildProperties, signature - } - - public override func encode(to encoder: Encoder) throws { + + public override func encode(to encoder: any Encoder) throws { try super.encode(to: encoder) var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self) - var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - try contents.encode("aggregate", forKey: .type) - try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid) - try contents.encode(name, forKey: .name) - try contents.encode(buildConfigurations, forKey: .buildConfigurations) - try contents.encode(buildPhases, forKey: .buildPhases) - try contents.encode(dependencies, forKey: .dependencies) - try contents.encode(impartedBuildProperties, forKey: .impartedBuildProperties) + try superContainer.encode(underlying, forKey: .contents) if encoder.userInfo.keys.contains(.encodeForSwiftBuild) { - guard let signature else { - throw InternalError("Expected to have \(Swift.type(of: self)) signature when encoding for SwiftBuild") + guard let signature = underlying.common.signature else { + throw InternalError("Expected to have target *signature* when encoding for SwiftBuild") } try superContainer.encode(signature, forKey: .signature) } } - + public required init(from decoder: Decoder) throws { + // FIXME: Remove all support for decoding PIF objects in SwiftBuildSupport? rdar://149003797 + fatalError("Decoding not implemented") + /* let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self) - let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - - let guidString = try contents.decode(GUID.self, forKey: .guid) - let guid = String(guidString.dropLast("\(schemaVersion)".count + 1)) - - let name = try contents.decode(String.self, forKey: .name) - let buildConfigurations = try contents.decode([BuildConfiguration].self, forKey: .buildConfigurations) - - let untypedBuildPhases = try contents.decode([HighLevelObject].self, forKey: .buildPhases) - var buildPhasesContainer = try contents.nestedUnkeyedContainer(forKey: .buildPhases) - - let buildPhases: [BuildPhase] = try untypedBuildPhases.map { - guard let type = $0.type else { - throw InternalError("Expected type in build phase \($0)") - } - return try BuildPhase.decode(container: &buildPhasesContainer, type: type) - } - - let dependencies = try contents.decode([TargetDependency].self, forKey: .dependencies) - let impartedBuildProperties = try contents.decode(BuildSettings.self, forKey: .impartedBuildProperties) - - super.init( - guid: guid, - name: name, - buildConfigurations: buildConfigurations, - buildPhases: buildPhases, - dependencies: dependencies, - impartedBuildSettings: impartedBuildProperties, - signature: nil - ) - } - } - - /// An Xcode target, representing a single entity to build. - public final class Target: BaseTarget { - public enum ProductType: String, Codable { - case application = "com.apple.product-type.application" - case staticArchive = "com.apple.product-type.library.static" - case objectFile = "com.apple.product-type.objfile" - case dynamicLibrary = "com.apple.product-type.library.dynamic" - case framework = "com.apple.product-type.framework" - case executable = "com.apple.product-type.tool" - case unitTest = "com.apple.product-type.bundle.unit-test" - case bundle = "com.apple.product-type.bundle" - case packageProduct = "packageProduct" - } - - public var productName: String - public var productType: ProductType - - public init( - guid: GUID, - name: String, - productType: ProductType, - productName: String, - buildConfigurations: [BuildConfiguration], - buildPhases: [BuildPhase], - dependencies: [TargetDependency], - impartedBuildSettings: PIF.BuildSettings - ) { - self.productType = productType - self.productName = productName - - super.init( - guid: guid, - name: name, - buildConfigurations: buildConfigurations, - buildPhases: buildPhases, - dependencies: dependencies, - impartedBuildSettings: impartedBuildSettings, - signature: nil - ) - } - - private enum CodingKeys: CodingKey { - case guid, name, dependencies, buildConfigurations, type, frameworksBuildPhase, productTypeIdentifier, productReference, buildRules, buildPhases, impartedBuildProperties, signature - } - - override public func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var superContainer = encoder.container(keyedBy: HighLevelObject.CodingKeys.self) - var contents = superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - try contents.encode("\(guid)@\(schemaVersion)", forKey: .guid) - try contents.encode(name, forKey: .name) - try contents.encode(dependencies, forKey: .dependencies) - try contents.encode(buildConfigurations, forKey: .buildConfigurations) - - if encoder.userInfo.keys.contains(.encodeForSwiftBuild) { - guard let signature else { - throw InternalError("Expected to have \(Swift.type(of: self)) signature when encoding for SwiftBuild") - } - try superContainer.encode(signature, forKey: .signature) - } - - if productType == .packageProduct { - try contents.encode("packageProduct", forKey: .type) - - // Add the framework build phase, if present. - if let phase = buildPhases.first as? PIF.FrameworksBuildPhase { - try contents.encode(phase, forKey: .frameworksBuildPhase) - } - } else { - try contents.encode("standard", forKey: .type) - try contents.encode(productType, forKey: .productTypeIdentifier) - - let productReference = [ - "type": "file", - "guid": "PRODUCTREF-\(guid)", - "name": productName, - ] - try contents.encode(productReference, forKey: .productReference) - - try contents.encode([String](), forKey: .buildRules) - try contents.encode(buildPhases, forKey: .buildPhases) - try contents.encode(impartedBuildProperties, forKey: .impartedBuildProperties) - } - } - - public required init(from decoder: Decoder) throws { - let superContainer = try decoder.container(keyedBy: HighLevelObject.CodingKeys.self) - let contents = try superContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .contents) - - let guidString = try contents.decode(GUID.self, forKey: .guid) - let guid = String(guidString.dropLast("\(schemaVersion)".count + 1)) - let name = try contents.decode(String.self, forKey: .name) - let buildConfigurations = try contents.decode([BuildConfiguration].self, forKey: .buildConfigurations) - let dependencies = try contents.decode([TargetDependency].self, forKey: .dependencies) - - let type = try contents.decode(String.self, forKey: .type) - - let buildPhases: [BuildPhase] - let impartedBuildProperties: ImpartedBuildProperties - - if type == "packageProduct" { - self.productType = .packageProduct - self.productName = "" - let fwkBuildPhase = try contents.decodeIfPresent(FrameworksBuildPhase.self, forKey: .frameworksBuildPhase) - buildPhases = fwkBuildPhase.map{ [$0] } ?? [] - impartedBuildProperties = ImpartedBuildProperties(settings: BuildSettings()) - } else if type == "standard" { - self.productType = try contents.decode(ProductType.self, forKey: .productTypeIdentifier) - - let productReference = try contents.decode([String: String].self, forKey: .productReference) - self.productName = productReference["name"]! - - let untypedBuildPhases = try contents.decodeIfPresent([HighLevelObject].self, forKey: .buildPhases) ?? [] - var buildPhasesContainer = try contents.nestedUnkeyedContainer(forKey: .buildPhases) - - buildPhases = try untypedBuildPhases.map { - guard let type = $0.type else { - throw InternalError("Expected type in build phase \($0)") - } - return try BuildPhase.decode(container: &buildPhasesContainer, type: type) - } - - impartedBuildProperties = try contents.decode(ImpartedBuildProperties.self, forKey: .impartedBuildProperties) - } else { - throw InternalError("Unhandled target type \(type)") - } - - super.init( - guid: guid, - name: name, - buildConfigurations: buildConfigurations, - buildPhases: buildPhases, - dependencies: dependencies, - impartedBuildSettings: impartedBuildProperties.buildSettings, - signature: nil - ) - } - } - - /// Abstract base class for all build phases in a target. - public class BuildPhase: HighLevelObject { - static func decode(container: inout UnkeyedDecodingContainer, type: String) throws -> BuildPhase { - switch type { - case HeadersBuildPhase.type: - return try container.decode(HeadersBuildPhase.self) - case SourcesBuildPhase.type: - return try container.decode(SourcesBuildPhase.self) - case FrameworksBuildPhase.type: - return try container.decode(FrameworksBuildPhase.self) - case ResourcesBuildPhase.type: - return try container.decode(ResourcesBuildPhase.self) - default: - throw InternalError("unknown build phase \(type)") - } - } - - public let guid: GUID - public var buildFiles: [BuildFile] - - public init(guid: GUID, buildFiles: [BuildFile]) { - precondition(!guid.isEmpty) - - self.guid = guid - self.buildFiles = buildFiles - super.init() - } - - private enum CodingKeys: CodingKey { - case guid, buildFiles - } - - public override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(guid, forKey: .guid) - try container.encode(buildFiles, forKey: .buildFiles) - } - - public required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.guid = try container.decode(GUID.self, forKey: .guid) - self.buildFiles = try container.decode([BuildFile].self, forKey: .buildFiles) + self.underlying = try superContainer.decode(ProjectModel.BaseTarget.self, forKey: .contents) + try super.init(from: decoder) + */ } } - - /// A "headers" build phase, i.e. one that copies headers into a directory of the product, after suitable - /// processing. - public final class HeadersBuildPhase: BuildPhase { - override class var type: String { "com.apple.buildphase.headers" } - } - - /// A "sources" build phase, i.e. one that compiles sources and provides them to be linked into the executable code - /// of the product. - public final class SourcesBuildPhase: BuildPhase { - override class var type: String { "com.apple.buildphase.sources" } - } - - /// A "frameworks" build phase, i.e. one that links compiled code and libraries into the executable of the product. - public final class FrameworksBuildPhase: BuildPhase { - override class var type: String { "com.apple.buildphase.frameworks" } - } - - public final class ResourcesBuildPhase: BuildPhase { - override class var type: String { "com.apple.buildphase.resources" } - } - - /// A build file, representing the membership of either a file or target product reference in a build phase. - public struct BuildFile: Codable { - public enum Reference { - case file(guid: PIF.GUID) - case target(guid: PIF.GUID) - } - - public enum HeaderVisibility: String, Codable { - case `public` = "public" - case `private` = "private" - } - - public let guid: GUID - public var reference: Reference - public var headerVisibility: HeaderVisibility? = nil - public var platformFilters: [PlatformFilter] - - public init(guid: GUID, file: FileReference, platformFilters: [PlatformFilter], headerVisibility: HeaderVisibility? = nil) { - self.guid = guid - self.reference = .file(guid: file.guid) - self.platformFilters = platformFilters - self.headerVisibility = headerVisibility - } - - public init(guid: GUID, fileGUID: PIF.GUID, platformFilters: [PlatformFilter], headerVisibility: HeaderVisibility? = nil) { - self.guid = guid - self.reference = .file(guid: fileGUID) - self.platformFilters = platformFilters - self.headerVisibility = headerVisibility - } - - public init(guid: GUID, target: PIF.BaseTarget, platformFilters: [PlatformFilter], headerVisibility: HeaderVisibility? = nil) { - self.guid = guid - self.reference = .target(guid: target.guid) - self.platformFilters = platformFilters - self.headerVisibility = headerVisibility - } - - public init(guid: GUID, targetGUID: PIF.GUID, platformFilters: [PlatformFilter], headerVisibility: HeaderVisibility? = nil) { - self.guid = guid - self.reference = .target(guid: targetGUID) - self.platformFilters = platformFilters - self.headerVisibility = headerVisibility - } - - public init(guid: GUID, reference: Reference, platformFilters: [PlatformFilter], headerVisibility: HeaderVisibility? = nil) { - self.guid = guid - self.reference = reference - self.platformFilters = platformFilters - self.headerVisibility = headerVisibility - } - - private enum CodingKeys: CodingKey { - case guid, platformFilters, fileReference, targetReference, headerVisibility - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(guid, forKey: .guid) - try container.encode(platformFilters, forKey: .platformFilters) - try container.encodeIfPresent(headerVisibility, forKey: .headerVisibility) - - switch self.reference { - case .file(let fileGUID): - try container.encode(fileGUID, forKey: .fileReference) - case .target(let targetGUID): - try container.encode("\(targetGUID)@\(schemaVersion)", forKey: .targetReference) - } - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - guid = try container.decode(GUID.self, forKey: .guid) - platformFilters = try container.decode([PlatformFilter].self, forKey: .platformFilters) - headerVisibility = try container.decodeIfPresent(HeaderVisibility.self, forKey: .headerVisibility) - - if container.allKeys.contains(.fileReference) { - reference = try .file(guid: container.decode(GUID.self, forKey: .fileReference)) - } else if container.allKeys.contains(.targetReference) { - let targetGUIDString = try container.decode(GUID.self, forKey: .targetReference) - let targetGUID = String(targetGUIDString.dropLast("\(schemaVersion)".count + 1)) - reference = .target(guid: targetGUID) - } else { - throw InternalError("Expected \(CodingKeys.fileReference) or \(CodingKeys.targetReference) in the keys") - } - } - } - - /// Represents a generic platform filter. - public struct PlatformFilter: Codable, Equatable { - /// The name of the platform (`LC_BUILD_VERSION`). - /// - /// Example: macos, ios, watchos, tvos. - public var platform: String - - /// The name of the environment (`LC_BUILD_VERSION`) - /// - /// Example: simulator, maccatalyst. - public var environment: String - - public init(platform: String, environment: String = "") { - self.platform = platform - self.environment = environment - } - } - - /// A build configuration, which is a named collection of build settings. - public struct BuildConfiguration: Codable { - public let guid: GUID - public var name: String - public var buildSettings: BuildSettings - public let impartedBuildProperties: ImpartedBuildProperties - - public init(guid: GUID, name: String, buildSettings: BuildSettings, impartedBuildProperties: ImpartedBuildProperties = ImpartedBuildProperties(settings: BuildSettings())) { - precondition(!guid.isEmpty) - precondition(!name.isEmpty) - - self.guid = guid - self.name = name - self.buildSettings = buildSettings - self.impartedBuildProperties = impartedBuildProperties - } - } - - public struct ImpartedBuildProperties: Codable { - public var buildSettings: BuildSettings - - public init(settings: BuildSettings) { - self.buildSettings = settings - } - } - - /// A set of build settings, which is represented as a struct of optional build settings. This is not optimally - /// efficient, but it is great for code completion and type-checking. - public struct BuildSettings: Codable { - public enum SingleValueSetting: String, Codable { - case APPLICATION_EXTENSION_API_ONLY - case BUILT_PRODUCTS_DIR - case CLANG_CXX_LANGUAGE_STANDARD - case CLANG_ENABLE_MODULES - case CLANG_ENABLE_OBJC_ARC - case CODE_SIGNING_REQUIRED - case CODE_SIGN_IDENTITY - case COMBINE_HIDPI_IMAGES - case COPY_PHASE_STRIP - case DEBUG_INFORMATION_FORMAT - case DEFINES_MODULE - case DRIVERKIT_DEPLOYMENT_TARGET - case DYLIB_INSTALL_NAME_BASE - case EMBEDDED_CONTENT_CONTAINS_SWIFT - case ENABLE_NS_ASSERTIONS - case ENABLE_TESTABILITY - case ENABLE_TESTING_SEARCH_PATHS - case ENTITLEMENTS_REQUIRED - case EXECUTABLE_PREFIX - case GENERATE_INFOPLIST_FILE - case GCC_C_LANGUAGE_STANDARD - case GCC_OPTIMIZATION_LEVEL - case GENERATE_MASTER_OBJECT_FILE - case INFOPLIST_FILE - case IPHONEOS_DEPLOYMENT_TARGET - case KEEP_PRIVATE_EXTERNS - case CLANG_COVERAGE_MAPPING_LINKER_ARGS - case MACH_O_TYPE - case MACOSX_DEPLOYMENT_TARGET - case MODULEMAP_FILE - case MODULEMAP_FILE_CONTENTS - case MODULEMAP_PATH - case MODULE_CACHE_DIR - case ONLY_ACTIVE_ARCH - case PACKAGE_RESOURCE_BUNDLE_NAME - case PACKAGE_RESOURCE_TARGET_KIND - case PRODUCT_BUNDLE_IDENTIFIER - case PRODUCT_MODULE_NAME - case PRODUCT_NAME - case PROJECT_NAME - case SDKROOT - case SDK_VARIANT - case SKIP_INSTALL - case INSTALL_PATH - case SUPPORTS_MACCATALYST - case SWIFT_SERIALIZE_DEBUGGING_OPTIONS - case SWIFT_INSTALL_OBJC_HEADER - case SWIFT_OBJC_INTERFACE_HEADER_NAME - case SWIFT_OBJC_INTERFACE_HEADER_DIR - case SWIFT_OPTIMIZATION_LEVEL - case SWIFT_VERSION - case TARGET_NAME - case TARGET_BUILD_DIR - case TVOS_DEPLOYMENT_TARGET - case USE_HEADERMAP - case USES_SWIFTPM_UNSAFE_FLAGS - case WATCHOS_DEPLOYMENT_TARGET - case XROS_DEPLOYMENT_TARGET - case MARKETING_VERSION - case CURRENT_PROJECT_VERSION - case SWIFT_EMIT_MODULE_INTERFACE - case GENERATE_RESOURCE_ACCESSORS - } - - public enum MultipleValueSetting: String, Codable { - case EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES - case FRAMEWORK_SEARCH_PATHS - case GCC_PREPROCESSOR_DEFINITIONS - case HEADER_SEARCH_PATHS - case LD_RUNPATH_SEARCH_PATHS - case LIBRARY_SEARCH_PATHS - case OTHER_CFLAGS - case OTHER_CPLUSPLUSFLAGS - case OTHER_LDFLAGS - case OTHER_LDRFLAGS - case OTHER_SWIFT_FLAGS - case PRELINK_FLAGS - case SPECIALIZATION_SDK_OPTIONS - case SUPPORTED_PLATFORMS - case SWIFT_ACTIVE_COMPILATION_CONDITIONS - case SWIFT_MODULE_ALIASES - } - - public enum Platform: String, CaseIterable, Codable { - case macOS = "macos" - case macCatalyst = "maccatalyst" - case iOS = "ios" - case tvOS = "tvos" - case watchOS = "watchos" - case driverKit = "driverkit" - case linux - - public var packageModelPlatform: PackageModel.Platform { - switch self { - case .macOS: return .macOS - case .macCatalyst: return .macCatalyst - case .iOS: return .iOS - case .tvOS: return .tvOS - case .watchOS: return .watchOS - case .driverKit: return .driverKit - case .linux: return .linux - } - } - - public var conditions: [String] { - let filters = [PackageCondition(platforms: [packageModelPlatform])].toPlatformFilters().map { filter in - if filter.environment.isEmpty { - return filter.platform - } else { - return "\(filter.platform)-\(filter.environment)" - } - }.sorted() - return ["__platform_filter=\(filters.joined(separator: ";"))"] - } - } - - public private(set) var platformSpecificSingleValueSettings = OrderedDictionary>() - public private(set) var platformSpecificMultipleValueSettings = OrderedDictionary>() - public private(set) var singleValueSettings: OrderedDictionary = [:] - public private(set) var multipleValueSettings: OrderedDictionary = [:] - - public subscript(_ setting: SingleValueSetting) -> String? { - get { singleValueSettings[setting] } - set { singleValueSettings[setting] = newValue } - } - - public subscript(_ setting: SingleValueSetting, for platform: Platform) -> String? { - get { platformSpecificSingleValueSettings[platform]?[setting] } - set { platformSpecificSingleValueSettings[platform, default: [:]][setting] = newValue } - } - - public subscript(_ setting: SingleValueSetting, default defaultValue: @autoclosure () -> String) -> String { - get { singleValueSettings[setting, default: defaultValue()] } - set { singleValueSettings[setting] = newValue } - } - - public subscript(_ setting: MultipleValueSetting) -> [String]? { - get { multipleValueSettings[setting] } - set { multipleValueSettings[setting] = newValue } - } - - public subscript(_ setting: MultipleValueSetting, for platform: Platform) -> [String]? { - get { platformSpecificMultipleValueSettings[platform]?[setting] } - set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue } - } - - public subscript( - _ setting: MultipleValueSetting, - default defaultValue: @autoclosure () -> [String] - ) -> [String] { - get { multipleValueSettings[setting, default: defaultValue()] } - set { multipleValueSettings[setting] = newValue } - } - - public subscript( - _ setting: MultipleValueSetting, - for platform: Platform, - default defaultValue: @autoclosure () -> [String] - ) -> [String] { - get { platformSpecificMultipleValueSettings[platform, default: [:]][setting, default: defaultValue()] } - set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue } - } - - public init() { - } - - private enum CodingKeys: CodingKey { - case platformSpecificSingleValueSettings, platformSpecificMultipleValueSettings, singleValueSettings, multipleValueSettings - } - - public func encode(to encoder: Encoder) throws { - if encoder.userInfo.keys.contains(.encodeForSwiftBuild) { - return try encodeForSwiftBuild(to: encoder) - } - - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(platformSpecificSingleValueSettings, forKey: .platformSpecificSingleValueSettings) - try container.encode(platformSpecificMultipleValueSettings, forKey: .platformSpecificMultipleValueSettings) - try container.encode(singleValueSettings, forKey: .singleValueSettings) - try container.encode(multipleValueSettings, forKey: .multipleValueSettings) - } - - private func encodeForSwiftBuild(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: StringKey.self) - - for (key, value) in singleValueSettings { - try container.encode(value, forKey: StringKey(key.rawValue)) - } - - for (key, value) in multipleValueSettings { - try container.encode(value, forKey: StringKey(key.rawValue)) - } - - for (platform, values) in platformSpecificSingleValueSettings { - for condition in platform.conditions { - for (key, value) in values { - try container.encode(value, forKey: "\(key.rawValue)[\(condition)]") - } - } - } - - for (platform, values) in platformSpecificMultipleValueSettings { - for condition in platform.conditions { - for (key, value) in values { - try container.encode(value, forKey: "\(key.rawValue)[\(condition)]") - } - } - } - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - platformSpecificSingleValueSettings = try container.decodeIfPresent(OrderedDictionary>.self, forKey: .platformSpecificSingleValueSettings) ?? .init() - platformSpecificMultipleValueSettings = try container.decodeIfPresent(OrderedDictionary>.self, forKey: .platformSpecificMultipleValueSettings) ?? .init() - singleValueSettings = try container.decodeIfPresent(OrderedDictionary.self, forKey: .singleValueSettings) ?? [:] - multipleValueSettings = try container.decodeIfPresent(OrderedDictionary.self, forKey: .multipleValueSettings) ?? [:] - } - } -} - -/// Represents a filetype recognized by the Xcode build system. -public struct SwiftBuildFileType: CaseIterable { - public static let xcassets: SwiftBuildFileType = SwiftBuildFileType( - fileType: "xcassets", - fileTypeIdentifier: "folder.abstractassetcatalog" - ) - - public static let xcdatamodeld: SwiftBuildFileType = SwiftBuildFileType( - fileType: "xcdatamodeld", - fileTypeIdentifier: "wrapper.xcdatamodeld" - ) - - public static let xcdatamodel: SwiftBuildFileType = SwiftBuildFileType( - fileType: "xcdatamodel", - fileTypeIdentifier: "wrapper.xcdatamodel" - ) - - public static let xcmappingmodel: SwiftBuildFileType = SwiftBuildFileType( - fileType: "xcmappingmodel", - fileTypeIdentifier: "wrapper.xcmappingmodel" - ) - - public static let allCases: [SwiftBuildFileType] = [ - .xcdatamodeld, - .xcdatamodel, - .xcmappingmodel, - ] - - public let fileTypes: Set - public let fileTypeIdentifier: String - - private init(fileTypes: Set, fileTypeIdentifier: String) { - self.fileTypes = fileTypes - self.fileTypeIdentifier = fileTypeIdentifier - } - - private init(fileType: String, fileTypeIdentifier: String) { - self.init(fileTypes: [fileType], fileTypeIdentifier: fileTypeIdentifier) - } -} - -fileprivate struct StringKey: CodingKey, ExpressibleByStringInterpolation { - var stringValue: String - var intValue: Int? - - init(stringLiteral stringValue: String) { - self.stringValue = stringValue - } - - init(stringValue value: String) { - self.stringValue = value - } - - init(_ value: String) { - self.stringValue = value - } - - init?(intValue: Int) { - assertionFailure("does not support integer keys") - return nil - } } -extension PIF.FileReference { - fileprivate static func fileTypeIdentifier(forPath path: String) -> String { - let pathExtension: String? - if let path = try? AbsolutePath(validating: path) { - pathExtension = path.extension - } else if let path = try? RelativePath(validating: path) { - pathExtension = path.extension - } else { - pathExtension = nil - } - - switch pathExtension { - case "a": - return "archive.ar" - case "s", "S": - return "sourcecode.asm" - case "c": - return "sourcecode.c.c" - case "cl": - return "sourcecode.opencl" - case "cpp", "cp", "cxx", "cc", "c++", "C", "tcc": - return "sourcecode.cpp.cpp" - case "d": - return "sourcecode.dtrace" - case "defs", "mig": - return "sourcecode.mig" - case "m": - return "sourcecode.c.objc" - case "mm", "M": - return "sourcecode.cpp.objcpp" - case "metal": - return "sourcecode.metal" - case "l", "lm", "lmm", "lpp", "lp", "lxx": - return "sourcecode.lex" - case "swift": - return "sourcecode.swift" - case "y", "ym", "ymm", "ypp", "yp", "yxx": - return "sourcecode.yacc" - - case "xcassets": - return "folder.assetcatalog" - case "xcstrings": - return "text.json.xcstrings" - case "storyboard": - return "file.storyboard" - case "xib": - return "file.xib" - - case "xcframework": - return "wrapper.xcframework" - - default: - return pathExtension.flatMap({ pathExtension in - SwiftBuildFileType.allCases.first(where:{ $0.fileTypes.contains(pathExtension) }) - })?.fileTypeIdentifier ?? "file" - } - } -} +// MARK: - PIF Signature Support extension CodingUserInfoKey { - public static let encodingPIFSignature: CodingUserInfoKey = CodingUserInfoKey(rawValue: "encodingPIFSignature")! - /// Perform the encoding for SwiftBuild consumption. public static let encodeForSwiftBuild: CodingUserInfoKey = CodingUserInfoKey(rawValue: "encodeForXCBuild")! } -private struct UntypedTarget: Decodable { - struct TargetContents: Decodable { - let type: String - } - let contents: TargetContents -} - -// MARK: - PIF Signature Support - -protocol PIFSignableObject: AnyObject { - var signature: String? { get set } -} -extension PIF.Workspace: PIFSignableObject {} -extension PIF.Project: PIFSignableObject {} -extension PIF.BaseTarget: PIFSignableObject {} - extension PIF { - /// Add signature to workspace and its subobjects. - public static func sign(_ workspace: PIF.Workspace) throws { + /// Add signature to workspace and its high-level subobjects. + static func sign(workspace: PIF.Workspace) throws { let encoder = JSONEncoder.makeWithDefaults() - func sign(_ obj: T) throws { + func signature(of obj: some Encodable) throws -> String { let signatureContent = try encoder.encode(obj) - let bytes = ByteString(signatureContent) - obj.signature = bytes.sha256Checksum + let signatureBytes = ByteString(signatureContent) + let signature = signatureBytes.sha256Checksum + return signature } - let projects = workspace.projects - try projects.flatMap{ $0.targets }.forEach(sign) - try projects.forEach(sign) - try sign(workspace) + for project in workspace.projects { + for targetIndex in project.underlying.targets.indices { + let targetSignature = try signature(of: project.underlying.targets[targetIndex]) + project.underlying.targets[targetIndex].common.signature = targetSignature + } + project.signature = try signature(of: project) + } + workspace.signature = try signature(of: workspace) } } + +#endif // SwiftBuild diff --git a/Sources/SwiftBuildSupport/PIFBuilder.swift b/Sources/SwiftBuildSupport/PIFBuilder.swift index e5a4762c926..8c21abad34c 100644 --- a/Sources/SwiftBuildSupport/PIFBuilder.swift +++ b/Sources/SwiftBuildSupport/PIFBuilder.swift @@ -22,6 +22,10 @@ import SPMBuildCore import func TSCBasic.memoize import func TSCBasic.topologicalSort +#if canImport(SwiftBuild) +import enum SwiftBuild.ProjectModel +#endif + /// The parameters required by `PIFBuilder`. struct PIFBuilderParameters { let triple: Triple @@ -50,10 +54,10 @@ struct PIFBuilderParameters { /// PIF object builder for a package graph. public final class PIFBuilder { - /// Name of the PIF target aggregating all targets (excluding tests). + /// Name of the PIF target aggregating all targets (*excluding* tests). public static let allExcludingTestsTargetName = "AllExcludingTests" - /// Name of the PIF target aggregating all targets (including tests). + /// Name of the PIF target aggregating all targets (*including* tests). public static let allIncludingTestsTargetName = "AllIncludingTests" /// The package graph to build from. @@ -68,8 +72,6 @@ public final class PIFBuilder { /// The file system to read from. let fileSystem: FileSystem - private var pif: PIF.TopLevelObject? - /// Creates a `PIFBuilder` instance. /// - Parameters: /// - graph: The package graph to build from. @@ -97,6 +99,7 @@ public final class PIFBuilder { prettyPrint: Bool = true, preservePIFModelStructure: Bool = false ) throws -> String { + #if canImport(SwiftBuild) let encoder = prettyPrint ? JSONEncoder.makeWithDefaults() : JSONEncoder() if !preservePIFModelStructure { @@ -105,41 +108,75 @@ public final class PIFBuilder { let topLevelObject = try self.construct() - // Sign the pif objects before encoding it for SwiftBuild. - try PIF.sign(topLevelObject.workspace) + // Sign the PIF objects before encoding it for Swift Build. + try PIF.sign(workspace: topLevelObject.workspace) let pifData = try encoder.encode(topLevelObject) - return String(decoding: pifData, as: UTF8.self) + let pifString = String(decoding: pifData, as: UTF8.self) + + return pifString + #else + fatalError("Swift Build support is not linked in.") + #endif } + + #if canImport(SwiftBuild) + + private var cachedPIF: PIF.TopLevelObject? /// Constructs a `PIF.TopLevelObject` representing the package graph. - public func construct() throws -> PIF.TopLevelObject { - try memoize(to: &self.pif) { - let rootPackage = self.graph.rootPackages[self.graph.rootPackages.startIndex] + private func construct() throws -> PIF.TopLevelObject { + try memoize(to: &self.cachedPIF) { + guard let rootPackage = self.graph.rootPackages.only else { + if self.graph.rootPackages.isEmpty { + throw PIFGenerationError.rootPackageNotFound + } else { + throw PIFGenerationError.multipleRootPackagesFound + } + } let sortedPackages = self.graph.packages .sorted { $0.manifest.displayName < $1.manifest.displayName } // TODO: use identity instead? - var projects: [PIFProjectBuilder] = try sortedPackages.map { package in - try _PackagePIFProjectBuilder( - package: package, - parameters: self.parameters, - fileSystem: self.fileSystem, + + let packagesAndProjects: [(ResolvedPackage, ProjectModel.Project)] = try sortedPackages.map { package in + let packagePIFBuilderDelegate = PackagePIFBuilderDelegate( + package: package + ) + let packagePIFBuilder = PackagePIFBuilder( + modulesGraph: self.graph, + resolvedPackage: package, + packageManifest: package.manifest, + delegate: packagePIFBuilderDelegate, + buildToolPluginResultsByTargetName: [:], + createDylibForDynamicProducts: self.parameters.shouldCreateDylibForDynamicProducts, + packageDisplayVersion: package.manifest.displayName, observabilityScope: self.observabilityScope ) + + try packagePIFBuilder.build() + return (package, packagePIFBuilder.pifProject) } + + var projects = packagesAndProjects.map(\.1) + projects.append( + try buildAggregateProject( + packagesAndProjects: packagesAndProjects, + observabilityScope: observabilityScope + ) + ) - projects.append(AggregatePIFProjectBuilder(projects: projects)) - - let workspace = try PIF.Workspace( + let workspace = PIF.Workspace( guid: "Workspace:\(rootPackage.path.pathString)", name: rootPackage.manifest.displayName, // TODO: use identity instead? path: rootPackage.path, - projects: projects.map { try $0.construct() } + projects: projects ) return PIF.TopLevelObject(workspace: workspace) } } + + #endif // Convenience method for generating PIF. public static func generatePIF( @@ -160,1811 +197,208 @@ public final class PIFBuilder { } } -class PIFProjectBuilder { - let groupTree: PIFGroupBuilder - private(set) var targets: [PIFBaseTargetBuilder] - private(set) var buildConfigurations: [PIFBuildConfigurationBuilder] - - @DelayedImmutable - var guid: PIF.GUID - @DelayedImmutable - var name: String - @DelayedImmutable - var path: AbsolutePath - @DelayedImmutable - var projectDirectory: AbsolutePath - @DelayedImmutable - var developmentRegion: String +#if canImport(SwiftBuild) - fileprivate init() { - self.groupTree = PIFGroupBuilder(path: "") - self.targets = [] - self.buildConfigurations = [] +fileprivate final class PackagePIFBuilderDelegate: PackagePIFBuilder.BuildDelegate { + let package: ResolvedPackage + + init(package: ResolvedPackage) { + self.package = package } - - /// Creates and adds a new empty build configuration, i.e. one that does not initially have any build settings. - /// The name must not be empty and must not be equal to the name of any existing build configuration in the target. - @discardableResult - func addBuildConfiguration( - name: String, - settings: PIF.BuildSettings = PIF.BuildSettings(), - impartedBuildProperties: PIF.ImpartedBuildProperties = PIF - .ImpartedBuildProperties(settings: PIF.BuildSettings()) - ) -> PIFBuildConfigurationBuilder { - let builder = PIFBuildConfigurationBuilder( - name: name, - settings: settings, - impartedBuildProperties: impartedBuildProperties - ) - self.buildConfigurations.append(builder) - return builder + + var isRootPackage: Bool { + self.package.manifest.packageKind.isRoot } - - /// Creates and adds a new empty target, i.e. one that does not initially have any build phases. If provided, - /// the ID must be non-empty and unique within the PIF workspace; if not provided, an arbitrary guaranteed-to-be- - /// unique identifier will be assigned. The name must not be empty and must not be equal to the name of any existing - /// target in the project. - @discardableResult - func addTarget( - guid: PIF.GUID, - name: String, - productType: PIF.Target.ProductType, - productName: String - ) -> PIFTargetBuilder { - let target = PIFTargetBuilder(guid: guid, name: name, productType: productType, productName: productName) - self.targets.append(target) - return target + + var hostsOnlyPackages: Bool { + false } - - @discardableResult - func addAggregateTarget(guid: PIF.GUID, name: String) -> PIFAggregateTargetBuilder { - let target = PIFAggregateTargetBuilder(guid: guid, name: name) - self.targets.append(target) - return target + + var isUserManaged: Bool { + true } - - func construct() throws -> PIF.Project { - let buildConfigurations = self.buildConfigurations.map { builder -> PIF.BuildConfiguration in - builder.guid = "\(self.guid)::BUILDCONFIG_\(builder.name)" - return builder.construct() - } - - // Construct group tree before targets to make sure file references have GUIDs. - groupTree.guid = "\(self.guid)::MAINGROUP" - let groupTree = self.groupTree.construct() as! PIF.Group - let targets = try self.targets.map { try $0.construct() } - - return PIF.Project( - guid: self.guid, - name: self.name, - path: self.path, - projectDirectory: self.projectDirectory, - developmentRegion: self.developmentRegion, - buildConfigurations: buildConfigurations, - targets: targets, - groupTree: groupTree - ) + + var isBranchOrRevisionBased: Bool { + false } -} - -final class _PackagePIFProjectBuilder: PIFProjectBuilder { - private let package: ResolvedPackage - private let parameters: PIFBuilderParameters - private let fileSystem: FileSystem - private let observabilityScope: ObservabilityScope - private var binaryGroup: PIFGroupBuilder! - private let executableTargetProductMap: [ResolvedModule.ID: ResolvedProduct] - - var isRootPackage: Bool { self.package.manifest.packageKind.isRoot } - - init( - package: ResolvedPackage, - parameters: PIFBuilderParameters, - fileSystem: FileSystem, - observabilityScope: ObservabilityScope - ) throws { - self.package = package - self.parameters = parameters - self.fileSystem = fileSystem - self.observabilityScope = observabilityScope.makeChildScope( - description: "Package PIF Builder", - metadata: package.underlying.diagnosticsMetadata - ) - - self.executableTargetProductMap = try Dictionary( - throwingUniqueKeysWithValues: package.products - .filter { $0.type == .executable } - .map { ($0.mainTarget.id, $0) } - ) - - super.init() - - self.guid = package.pifProjectGUID - self.name = package.manifest.displayName // TODO: use identity instead? - self.path = package.path - self.projectDirectory = package.path - self.developmentRegion = package.manifest.defaultLocalization ?? "en" - self.binaryGroup = groupTree.addGroup(path: "/", sourceTree: .absolute, name: "Binaries") - - // Configure the project-wide build settings. First we set those that are in common between the "Debug" and - // "Release" configurations, and then we set those that are different. - var settings = PIF.BuildSettings() - settings[.PRODUCT_NAME] = "$(TARGET_NAME)" - settings[.SUPPORTED_PLATFORMS] = ["$(AVAILABLE_PLATFORMS)"] - settings[.SDKROOT] = "auto" - settings[.SDK_VARIANT] = "auto" - settings[.SKIP_INSTALL] = "YES" - settings[.MACOSX_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .macOS) - settings[.IPHONEOS_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .iOS) - settings[.IPHONEOS_DEPLOYMENT_TARGET, for: .macCatalyst] = package.deploymentTarget(for: .macCatalyst) - settings[.TVOS_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .tvOS) - settings[.WATCHOS_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .watchOS) - settings[.XROS_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .visionOS) - settings[.DRIVERKIT_DEPLOYMENT_TARGET] = package.deploymentTarget(for: .driverKit) - settings[.DYLIB_INSTALL_NAME_BASE] = "@rpath" - settings[.USE_HEADERMAP] = "NO" - settings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS] = ["$(inherited)", "SWIFT_PACKAGE"] - settings[.GCC_PREPROCESSOR_DEFINITIONS] = ["$(inherited)", "SWIFT_PACKAGE"] - -#if os(macOS) - // Objective-C support only for macOS - settings[.CLANG_ENABLE_OBJC_ARC] = "YES" -#endif - - settings[.KEEP_PRIVATE_EXTERNS] = "NO" - // We currently deliberately do not support Swift ObjC interface headers. - settings[.SWIFT_INSTALL_OBJC_HEADER] = "NO" - settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME] = "" - settings[.OTHER_LDRFLAGS] = [] - - // This will add the XCTest related search paths automatically - // (including the Swift overlays). - settings[.ENABLE_TESTING_SEARCH_PATHS] = "YES" - - // XCTest search paths should only be specified for certain platforms (watchOS doesn't have XCTest). - for platform: PIF.BuildSettings.Platform in [.macOS, .iOS, .tvOS] { - settings[.FRAMEWORK_SEARCH_PATHS, for: platform, default: ["$(inherited)"]] - .append("$(PLATFORM_DIR)/Developer/Library/Frameworks") - } - - PlatformRegistry.default.knownPlatforms.forEach { - guard let platform = PIF.BuildSettings.Platform.from(platform: $0) else { return } - let supportedPlatform = package.getSupportedPlatform(for: $0, usingXCTest: false) - if !supportedPlatform.options.isEmpty { - settings[.SPECIALIZATION_SDK_OPTIONS, for: platform] = supportedPlatform.options - } - } - - // Disable signing for all the things since there is no way to configure - // signing information in packages right now. - settings[.ENTITLEMENTS_REQUIRED] = "NO" - settings[.CODE_SIGNING_REQUIRED] = "NO" - settings[.CODE_SIGN_IDENTITY] = "" - - var debugSettings = settings - debugSettings[.COPY_PHASE_STRIP] = "NO" - debugSettings[.DEBUG_INFORMATION_FORMAT] = "dwarf" - debugSettings[.ENABLE_NS_ASSERTIONS] = "YES" - debugSettings[.GCC_OPTIMIZATION_LEVEL] = "0" - debugSettings[.ONLY_ACTIVE_ARCH] = "YES" - debugSettings[.SWIFT_OPTIMIZATION_LEVEL] = "-Onone" - debugSettings[.ENABLE_TESTABILITY] = "YES" - debugSettings[.SWIFT_ACTIVE_COMPILATION_CONDITIONS, default: []].append("DEBUG") - debugSettings[.GCC_PREPROCESSOR_DEFINITIONS, default: ["$(inherited)"]].append("DEBUG=1") - addBuildConfiguration(name: "Debug", settings: debugSettings) - - var releaseSettings = settings - releaseSettings[.COPY_PHASE_STRIP] = "YES" - releaseSettings[.DEBUG_INFORMATION_FORMAT] = "dwarf-with-dsym" - releaseSettings[.GCC_OPTIMIZATION_LEVEL] = "s" - releaseSettings[.SWIFT_OPTIMIZATION_LEVEL] = "-Owholemodule" - - if parameters.enableTestability { - releaseSettings[.ENABLE_TESTABILITY] = "YES" - } - - addBuildConfiguration(name: "Release", settings: releaseSettings) - - for product in package.products.sorted(by: { $0.name < $1.name }) { - let productScope = observabilityScope.makeChildScope( - description: "Adding \(product.name) product", - metadata: package.underlying.diagnosticsMetadata - ) - - productScope.trap { try self.addTarget(for: product) } - } - - for target in package.modules.sorted(by: { $0.name < $1.name }) { - let targetScope = observabilityScope.makeChildScope( - description: "Adding \(target.name) module", - metadata: package.underlying.diagnosticsMetadata - ) - targetScope.trap { try self.addTarget(for: target) } - } - - if self.binaryGroup.children.isEmpty { - groupTree.removeChild(self.binaryGroup) - } + + func customProductType(forExecutable product: PackageModel.Product) -> ProjectModel.Target.ProductType? { + nil } - - private func addTarget(for product: ResolvedProduct) throws { - switch product.type { - case .executable, .snippet, .test: - try self.addMainModuleTarget(for: product) - case .library: - self.addLibraryTarget(for: product) - case .plugin, .macro: - return - } + + func deviceFamilyIDs() -> Set { + [] } - - private func addTarget(for target: ResolvedModule) throws { - switch target.type { - case .library: - try self.addLibraryTarget(for: target) - case .systemModule: - try self.addSystemTarget(for: target) - case .executable, .snippet, .test: - // Skip executable module targets and test module targets (they will have been dealt with as part of the - // products to which they belong). - return - case .binary: - // Binary target don't need to be built. - return - case .plugin: - // Package plugin modules. - return - case .macro: - // Macros are not supported when using SwiftBuild, similar to package plugins. - return - } + + var shouldiOSPackagesBuildForARM64e: Bool { + false } - - private func targetName(for product: ResolvedProduct) -> String { - Self.targetName(for: product.name) + + var isPluginExecutionSandboxingDisabled: Bool { + false } - - static func targetName(for productName: String) -> String { - "\(productName)_\(String(productName.hash, radix: 16, uppercase: true))_PackageProduct" + + func configureProjectBuildSettings(_ buildSettings: inout ProjectModel.BuildSettings) { + /* empty */ } - - private func addMainModuleTarget(for product: ResolvedProduct) throws { - let productType: PIF.Target.ProductType = product.type == .executable ? .executable : .unitTest - let pifTarget = self.addTarget( - guid: product.pifTargetGUID, - name: self.targetName(for: product), - productType: productType, - productName: "\(product.name)\(parameters.triple.executableExtension)" - ) - - // We'll be infusing the product's main module target into the one for the product itself. - let mainTarget = product.mainTarget - - self.addSources(mainTarget.sources, to: pifTarget) - - let dependencies = try! topologicalSort(mainTarget.dependencies) { $0.packageDependencies }.sorted() - for dependency in dependencies { - self.addDependency(to: dependency, in: pifTarget, linkProduct: true) - } - - // Configure the target-wide build settings. The details depend on the kind of product we're building, but are - // in general the ones that are suitable for end-product artifacts such as executables and test bundles. - var settings = PIF.BuildSettings() - settings[.TARGET_NAME] = product.name - settings[.PACKAGE_RESOURCE_TARGET_KIND] = "regular" - settings[.PRODUCT_NAME] = "$(TARGET_NAME)" - settings[.PRODUCT_MODULE_NAME] = mainTarget.c99name - settings[.PRODUCT_BUNDLE_IDENTIFIER] = product.name - settings[.CLANG_ENABLE_MODULES] = "YES" - settings[.DEFINES_MODULE] = "YES" - - if product.type == .executable || product.type == .test { - if let darwinPlatform = parameters.triple.darwinPlatform { - settings[.LIBRARY_SEARCH_PATHS] = [ - "$(inherited)", - "\(self.parameters.toolchainLibDir.pathString)/swift/\(darwinPlatform.platformName)", - ] - } - } - - // Tests can have a custom deployment target based on the minimum supported by XCTest. - if mainTarget.underlying.type == .test { - settings[.MACOSX_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .macOS, usingXCTest: true) - settings[.IPHONEOS_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .iOS, usingXCTest: true) - settings[.TVOS_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .tvOS, usingXCTest: true) - settings[.WATCHOS_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .watchOS, usingXCTest: true) - settings[.XROS_DEPLOYMENT_TARGET] = mainTarget.deploymentTarget(for: .visionOS, usingXCTest: true) - } - - if product.type == .executable { - // Setup install path for executables if it's in root of a pure Swift package. - if self.isRootPackage { - settings[.SKIP_INSTALL] = "NO" - settings[.INSTALL_PATH] = "/usr/local/bin" - settings[.LD_RUNPATH_SEARCH_PATHS, default: ["$(inherited)"]].append("@executable_path/../lib") - } - } else { - // FIXME: we shouldn't always include both the deep and shallow bundle paths here, but for that we'll need - // rdar://problem/31867023 - settings[.LD_RUNPATH_SEARCH_PATHS, default: ["$(inherited)"]] += - ["@loader_path/Frameworks", "@loader_path/../Frameworks"] - settings[.GENERATE_INFOPLIST_FILE] = "YES" - } - - if let clangTarget = mainTarget.underlying as? ClangModule { - // Let the target itself find its own headers. - settings[.HEADER_SEARCH_PATHS, default: ["$(inherited)"]].append(clangTarget.includeDir.pathString) - settings[.GCC_C_LANGUAGE_STANDARD] = clangTarget.cLanguageStandard - settings[.CLANG_CXX_LANGUAGE_STANDARD] = clangTarget.cxxLanguageStandard - } else if let swiftTarget = mainTarget.underlying as? SwiftModule { - try settings.addSwiftVersionSettings(target: swiftTarget, parameters: self.parameters) - settings.addCommonSwiftSettings(package: self.package, target: mainTarget, parameters: self.parameters) - } - - if let resourceBundle = addResourceBundle(for: mainTarget, in: pifTarget) { - settings[.PACKAGE_RESOURCE_BUNDLE_NAME] = resourceBundle - settings[.GENERATE_RESOURCE_ACCESSORS] = "YES" - } - - // For targets, we use the common build settings for both the "Debug" and the "Release" configurations (all - // differentiation is at the project level). - var debugSettings = settings - var releaseSettings = settings - - var impartedSettings = PIF.BuildSettings() - try self.addManifestBuildSettings( - from: mainTarget.underlying, - debugSettings: &debugSettings, - releaseSettings: &releaseSettings, - impartedSettings: &impartedSettings - ) - - let impartedBuildProperties = PIF.ImpartedBuildProperties(settings: impartedSettings) - pifTarget.addBuildConfiguration( - name: "Debug", - settings: debugSettings, - impartedBuildProperties: impartedBuildProperties - ) - pifTarget.addBuildConfiguration( - name: "Release", - settings: releaseSettings, - impartedBuildProperties: impartedBuildProperties - ) + + func configureSourceModuleBuildSettings(sourceModule: ResolvedModule, settings: inout ProjectModel.BuildSettings) { + /* empty */ } - - private func addLibraryTarget(for product: ResolvedProduct) { - // For the name of the product reference - let pifTargetProductName: String - let productType: PIF.Target.ProductType - if product.type == .library(.dynamic) { - if self.parameters.shouldCreateDylibForDynamicProducts { - pifTargetProductName = "\(parameters.triple.dynamicLibraryPrefix)\(product.name)\(parameters.triple.dynamicLibraryExtension)" - productType = .dynamicLibrary - } else { - pifTargetProductName = product.name + ".framework" - productType = .framework - } - } else { - pifTargetProductName = "lib\(product.name)\(parameters.triple.staticLibraryExtension)" - productType = .packageProduct - } - - // Create a special kind of .packageProduct PIF target that just "groups" a set of targets for clients to - // depend on. SwiftBuild will not produce a separate artifact for a package product, but will instead consider any - // dependency on the package product to be a dependency on the whole set of targets on which the package product - // depends. - let pifTarget = self.addTarget( - guid: product.pifTargetGUID, - name: self.targetName(for: product), - productType: productType, - productName: pifTargetProductName - ) - - // Handle the dependencies of the targets in the product (and link against them, which in the case of a package - // product, really just means that clients should link against them). - let dependencies = product.recursivePackageDependencies() - for dependency in dependencies { - switch dependency { - case .module(let target, let conditions): - if target.type != .systemModule { - self.addDependency(to: target, in: pifTarget, conditions: conditions, linkProduct: true) - } - case .product(let product, let conditions): - self.addDependency(to: product, in: pifTarget, conditions: conditions, linkProduct: true) - } - } - - var settings = PIF.BuildSettings() - let usesUnsafeFlags = dependencies.contains { $0.module?.underlying.usesUnsafeFlags == true } - settings[.USES_SWIFTPM_UNSAFE_FLAGS] = usesUnsafeFlags ? "YES" : "NO" - - // If there are no system modules in the dependency graph, mark the target as extension-safe. - let dependsOnAnySystemModules = dependencies.contains { $0.module?.type == .systemModule } - if !dependsOnAnySystemModules { - settings[.APPLICATION_EXTENSION_API_ONLY] = "YES" - } - - // Add other build settings when we're building an actual dylib. - if product.type == .library(.dynamic) { - settings[.TARGET_NAME] = product.name - settings[.PRODUCT_NAME] = "$(TARGET_NAME)" - settings[.PRODUCT_MODULE_NAME] = product.name - settings[.PRODUCT_BUNDLE_IDENTIFIER] = product.name - settings[.EXECUTABLE_PREFIX] = parameters.triple.dynamicLibraryPrefix - settings[.CLANG_ENABLE_MODULES] = "YES" - settings[.DEFINES_MODULE] = "YES" - settings[.SKIP_INSTALL] = "NO" - settings[.INSTALL_PATH] = "/usr/local/lib" - if let darwinPlatform = parameters.triple.darwinPlatform { - settings[.LIBRARY_SEARCH_PATHS] = [ - "$(inherited)", - "\(self.parameters.toolchainLibDir.pathString)/swift/\(darwinPlatform.platformName)", - ] - } - - if !self.parameters.shouldCreateDylibForDynamicProducts { - settings[.GENERATE_INFOPLIST_FILE] = "YES" - // If the built framework is named same as one of the target in the package, it can be picked up - // automatically during indexing since the build system always adds a -F flag to the built products dir. - // To avoid this problem, we build all package frameworks in a subdirectory. - settings[.BUILT_PRODUCTS_DIR] = "$(BUILT_PRODUCTS_DIR)/PackageFrameworks" - settings[.TARGET_BUILD_DIR] = "$(TARGET_BUILD_DIR)/PackageFrameworks" - - // Set the project and marketing version for the framework because the app store requires these to be - // present. The AppStore requires bumping the project version when ingesting new builds but that's for - // top-level apps and not frameworks embedded inside it. - settings[.MARKETING_VERSION] = "1.0" // Version - settings[.CURRENT_PROJECT_VERSION] = "1" // Build - } - - pifTarget.addSourcesBuildPhase() - } - - pifTarget.addBuildConfiguration(name: "Debug", settings: settings) - pifTarget.addBuildConfiguration(name: "Release", settings: settings) + + func customInstallPath(product: PackageModel.Product) -> String? { + nil } - - private func addLibraryTarget(for target: ResolvedModule) throws { - let pifTarget = self.addTarget( - guid: target.pifTargetGUID, - name: target.name, - productType: .objectFile, - productName: "\(target.name)_Module.o" - ) - - var settings = PIF.BuildSettings() - settings[.TARGET_NAME] = target.name + "_Module" - settings[.PACKAGE_RESOURCE_TARGET_KIND] = "regular" - settings[.PRODUCT_NAME] = "$(TARGET_NAME)" - settings[.PRODUCT_MODULE_NAME] = target.c99name - settings[.PRODUCT_BUNDLE_IDENTIFIER] = target.name - settings[.CLANG_ENABLE_MODULES] = "YES" - settings[.DEFINES_MODULE] = "YES" - settings[.MACH_O_TYPE] = "mh_object" - settings[.GENERATE_MASTER_OBJECT_FILE] = "NO" - // Disable code coverage linker flags since we're producing .o files. Otherwise, we will run into duplicated - // symbols when there are more than one targets that produce .o as their product. - settings[.CLANG_COVERAGE_MAPPING_LINKER_ARGS] = "NO" - if let aliases = target.moduleAliases { - settings[.SWIFT_MODULE_ALIASES] = aliases.map { $0.key + "=" + $0.value } - } - - // Create a set of build settings that will be imparted to any target that depends on this one. - var impartedSettings = PIF.BuildSettings() - - let generatedModuleMapDir = "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)" - let moduleMapFile = "\(generatedModuleMapDir)/\(target.name).modulemap" - let moduleMapFileContents: String? - let shouldImpartModuleMap: Bool - - if let clangTarget = target.underlying as? ClangModule { - // Let the target itself find its own headers. - settings[.HEADER_SEARCH_PATHS, default: ["$(inherited)"]].append(clangTarget.includeDir.pathString) - settings[.GCC_C_LANGUAGE_STANDARD] = clangTarget.cLanguageStandard - settings[.CLANG_CXX_LANGUAGE_STANDARD] = clangTarget.cxxLanguageStandard - - // Also propagate this search path to all direct and indirect clients. - impartedSettings[.HEADER_SEARCH_PATHS, default: ["$(inherited)"]].append(clangTarget.includeDir.pathString) - - if !self.fileSystem.exists(clangTarget.moduleMapPath) { - impartedSettings[.OTHER_SWIFT_FLAGS, default: ["$(inherited)"]] += - ["-Xcc", "-fmodule-map-file=\(moduleMapFile)"] - - moduleMapFileContents = """ - module \(target.c99name) { - umbrella "\(clangTarget.includeDir.pathString)" - export * - } - """ - - shouldImpartModuleMap = true - } else { - moduleMapFileContents = nil - shouldImpartModuleMap = false - } - } else if let swiftTarget = target.underlying as? SwiftModule { - try settings.addSwiftVersionSettings(target: swiftTarget, parameters: self.parameters) - - // Generate ObjC compatibility header for Swift library targets. - settings[.SWIFT_OBJC_INTERFACE_HEADER_DIR] = "$(OBJROOT)/GeneratedModuleMaps/$(PLATFORM_NAME)" - settings[.SWIFT_OBJC_INTERFACE_HEADER_NAME] = "\(target.name)-Swift.h" - - settings.addCommonSwiftSettings(package: self.package, target: target, parameters: self.parameters) - - moduleMapFileContents = """ - module \(target.c99name) { - header "\(target.name)-Swift.h" - export * - } - """ - - shouldImpartModuleMap = true - } else { - throw InternalError("unexpected target") - } - - if let moduleMapFileContents { - settings[.MODULEMAP_PATH] = moduleMapFile - settings[.MODULEMAP_FILE_CONTENTS] = moduleMapFileContents - } - - // Pass the path of the module map up to all direct and indirect clients. - if shouldImpartModuleMap { - impartedSettings[.OTHER_CFLAGS, default: ["$(inherited)"]].append("-fmodule-map-file=\(moduleMapFile)") - } - impartedSettings[.OTHER_LDRFLAGS] = [] - - if target.underlying.isCxx { - impartedSettings[.OTHER_LDFLAGS, default: ["$(inherited)"]].append("-lc++") - } - -#if os(macOS) - // radar://112671586 supress unnecessary warnings, only for macOS where the linker supports this flag - impartedSettings[.OTHER_LDFLAGS, default: ["$(inherited)"]].append("-Wl,-no_warn_duplicate_libraries") -#endif - - self.addSources(target.sources, to: pifTarget) - - // Handle the target's dependencies (but don't link against them). - let dependencies = try! topologicalSort(target.dependencies) { $0.packageDependencies }.sorted() - for dependency in dependencies { - self.addDependency(to: dependency, in: pifTarget, linkProduct: false) - } - - if let resourceBundle = addResourceBundle(for: target, in: pifTarget) { - settings[.PACKAGE_RESOURCE_BUNDLE_NAME] = resourceBundle - settings[.GENERATE_RESOURCE_ACCESSORS] = "YES" - impartedSettings[.EMBED_PACKAGE_RESOURCE_BUNDLE_NAMES, default: ["$(inherited)"]].append(resourceBundle) - } - - // For targets, we use the common build settings for both the "Debug" and the "Release" configurations (all - // differentiation is at the project level). - var debugSettings = settings - var releaseSettings = settings - - try addManifestBuildSettings( - from: target.underlying, - debugSettings: &debugSettings, - releaseSettings: &releaseSettings, - impartedSettings: &impartedSettings - ) - - let impartedBuildProperties = PIF.ImpartedBuildProperties(settings: impartedSettings) - pifTarget.addBuildConfiguration( - name: "Debug", - settings: debugSettings, - impartedBuildProperties: impartedBuildProperties - ) - pifTarget.addBuildConfiguration( - name: "Release", - settings: releaseSettings, - impartedBuildProperties: impartedBuildProperties - ) - pifTarget.impartedBuildSettings = impartedSettings + + func customExecutableName(product: PackageModel.Product) -> String? { + nil } - - private func addSystemTarget(for target: ResolvedModule) throws { - guard let systemTarget = target.underlying as? SystemLibraryModule else { - throw InternalError("unexpected target type") - } - - // Impart the header search path to all direct and indirect clients. - var impartedSettings = PIF.BuildSettings() - - var cFlags: [String] = [] - for result in try pkgConfigArgs( - for: systemTarget, - pkgConfigDirectories: self.parameters.pkgConfigDirectories, - sdkRootPath: self.parameters.sdkRootPath, - fileSystem: self.fileSystem, - observabilityScope: self.observabilityScope - ) { - if let error = result.error { - self.observabilityScope.emit( - warning: "\(error.interpolationDescription)", - metadata: .pkgConfig(pcFile: result.pkgConfigName, targetName: target.name) - ) - } else { - cFlags = result.cFlags - impartedSettings[.OTHER_LDFLAGS, default: ["$(inherited)"]] += result.libs - } - } - - impartedSettings[.OTHER_LDRFLAGS] = [] - impartedSettings[.OTHER_CFLAGS, default: ["$(inherited)"]] += - ["-fmodule-map-file=\(systemTarget.moduleMapPath)"] + cFlags - impartedSettings[.OTHER_SWIFT_FLAGS, default: ["$(inherited)"]] += - ["-Xcc", "-fmodule-map-file=\(systemTarget.moduleMapPath)"] + cFlags - let impartedBuildProperties = PIF.ImpartedBuildProperties(settings: impartedSettings) - - // Create an aggregate PIF target (which doesn't have an actual product). - let pifTarget = addAggregateTarget(guid: target.pifTargetGUID, name: target.name) - pifTarget.addBuildConfiguration( - name: "Debug", - settings: PIF.BuildSettings(), - impartedBuildProperties: impartedBuildProperties - ) - pifTarget.addBuildConfiguration( - name: "Release", - settings: PIF.BuildSettings(), - impartedBuildProperties: impartedBuildProperties - ) - pifTarget.impartedBuildSettings = impartedSettings + + func customLibraryType(product: PackageModel.Product) -> PackageModel.ProductType.LibraryType? { + nil } - - private func addSources(_ sources: Sources, to pifTarget: PIFTargetBuilder) { - // Create a group for the target's source files. For now we use an absolute path for it, but we should really - // make it be container-relative, since it's always inside the package directory. - let targetGroup = groupTree.addGroup( - path: sources.root.relative(to: self.package.path).pathString, - sourceTree: .group - ) - - // Add a source file reference for each of the source files, and also an indexable-file URL for each one. - for path in sources.relativePaths { - pifTarget.addSourceFile(targetGroup.addFileReference(path: path.pathString, sourceTree: .group)) - } + + func customSDKOptions(forPlatform: PackageModel.Platform) -> [String] { + [] } - - private func addDependency( - to dependency: ResolvedModule.Dependency, - in pifTarget: PIFTargetBuilder, - linkProduct: Bool + + func addCustomTargets(pifProject: SwiftBuild.ProjectModel.Project) throws -> [PackagePIFBuilder.ModuleOrProduct] { + return [] + } + + func shouldSuppressProductDependency(product: PackageModel.Product, buildSettings: inout SwiftBuild.ProjectModel.BuildSettings) -> Bool { + false + } + + func shouldSetInstallPathForDynamicLib(productName: String) -> Bool { + false + } + + func configureLibraryProduct( + product: PackageModel.Product, + target: WritableKeyPath, + additionalFiles: WritableKeyPath ) { - switch dependency { - case .module(let target, let conditions): - self.addDependency( - to: target, - in: pifTarget, - conditions: conditions, - linkProduct: linkProduct - ) - case .product(let product, let conditions): - self.addDependency( - to: product, - in: pifTarget, - conditions: conditions, - linkProduct: linkProduct - ) - } + /* empty */ + } + + func suggestAlignedPlatformVersionGiveniOSVersion(platform: PackageModel.Platform, iOSVersion: PackageModel.PlatformVersion) -> String? { + nil + } + + func validateMacroFingerprint(for macroModule: ResolvedModule) -> Bool { + true } +} - private func addDependency( - to target: ResolvedModule, - in pifTarget: PIFTargetBuilder, - conditions: [PackageCondition], - linkProduct: Bool +fileprivate func buildAggregateProject( + packagesAndProjects: [(package: ResolvedPackage, project: ProjectModel.Project)], + observabilityScope: ObservabilityScope +) throws -> ProjectModel.Project { + precondition(!packagesAndProjects.isEmpty) + + var aggregateProject = ProjectModel.Project( + id: "AGGREGATE", + path: packagesAndProjects[0].project.path, + projectDir: packagesAndProjects[0].project.projectDir, + name: "Aggregate", + developmentRegion: "en" + ) + observabilityScope.logPIF(.debug, "Created project '\(aggregateProject.id)' with name '\(aggregateProject.name)'") + + var settings = ProjectModel.BuildSettings() + settings[.PRODUCT_NAME] = "$(TARGET_NAME)" + settings[.SUPPORTED_PLATFORMS] = ["$(AVAILABLE_PLATFORMS)"] + settings[.SDKROOT] = "auto" + settings[.SDK_VARIANT] = "auto" + settings[.SKIP_INSTALL] = "YES" + + aggregateProject.addBuildConfig { id in BuildConfig(id: id, name: "Debug", settings: settings) } + aggregateProject.addBuildConfig { id in BuildConfig(id: id, name: "Release", settings: settings) } + + func addEmptyBuildConfig( + to targetKeyPath: WritableKeyPath, + name: String ) { - // Only add the binary target as a library when we want to link against the product. - if let binaryTarget = target.underlying as? BinaryModule { - let ref = self.binaryGroup.addFileReference(path: binaryTarget.artifactPath.pathString) - pifTarget.addLibrary(ref, platformFilters: conditions.toPlatformFilters()) - } else { - // If this is an executable target, the dependency should be to the PIF target created from the its - // product, as we don't have PIF targets corresponding to executable targets. - let targetGUID = self.executableTargetProductMap[target.id]?.pifTargetGUID ?? target.pifTargetGUID - let linkProduct = linkProduct && target.type != .systemModule && target.type != .executable - pifTarget.addDependency( - toTargetWithGUID: targetGUID, - platformFilters: conditions.toPlatformFilters(), - linkProduct: linkProduct - ) + let emptySettings = BuildSettings() + aggregateProject[keyPath: targetKeyPath].common.addBuildConfig { id in + BuildConfig(id: id, name: name, settings: emptySettings) } } - - private func addDependency( - to product: ResolvedProduct, - in pifTarget: PIFTargetBuilder, - conditions: [PackageCondition], - linkProduct: Bool - ) { - pifTarget.addDependency( - toTargetWithGUID: product.pifTargetGUID, - platformFilters: conditions.toPlatformFilters(), - linkProduct: linkProduct + + let allIncludingTestsTargetKeyPath = try aggregateProject.addAggregateTarget { _ in + ProjectModel.AggregateTarget( + id: "ALL-INCLUDING-TESTS", + name: PIFBuilder.allIncludingTestsTargetName ) } - - private func addResourceBundle(for target: ResolvedModule, in pifTarget: PIFTargetBuilder) -> String? { - guard !target.underlying.resources.isEmpty else { - return nil - } - - let bundleName = "\(package.manifest.displayName)_\(target.name)" // TODO: use identity instead? - let resourcesTarget = self.addTarget( - guid: target.pifResourceTargetGUID, - name: bundleName, - productType: .bundle, - productName: bundleName - ) - - pifTarget.addDependency( - toTargetWithGUID: resourcesTarget.guid, - platformFilters: [], - linkProduct: false + addEmptyBuildConfig(to: allIncludingTestsTargetKeyPath, name: "Debug") + addEmptyBuildConfig(to: allIncludingTestsTargetKeyPath, name: "Release") + + let allExcludingTestsTargetKeyPath = try aggregateProject.addAggregateTarget { _ in + ProjectModel.AggregateTarget( + id: "ALL-EXCLUDING-TESTS", + name: PIFBuilder.allExcludingTestsTargetName ) - - var settings = PIF.BuildSettings() - settings[.TARGET_NAME] = bundleName - settings[.PRODUCT_NAME] = "$(TARGET_NAME)" - settings[.PRODUCT_MODULE_NAME] = bundleName - let bundleIdentifier = "\(package.manifest.displayName).\(target.name).resources" - .spm_mangledToBundleIdentifier() // TODO: use identity instead? - settings[.PRODUCT_BUNDLE_IDENTIFIER] = bundleIdentifier - settings[.GENERATE_INFOPLIST_FILE] = "YES" - settings[.PACKAGE_RESOURCE_TARGET_KIND] = "resource" - - resourcesTarget.addBuildConfiguration(name: "Debug", settings: settings) - resourcesTarget.addBuildConfiguration(name: "Release", settings: settings) - - let coreDataFileTypes = [SwiftBuildFileType.xcdatamodeld, .xcdatamodel].flatMap(\.fileTypes) - for resource in target.underlying.resources { - // FIXME: Handle rules here. - let resourceFile = groupTree.addFileReference( - path: resource.path.pathString, - sourceTree: .absolute - ) - - // CoreData files should also be in the actual target because they can end up generating code during the - // build. The build system will only perform codegen tasks for the main target in this case. - if coreDataFileTypes.contains(resource.path.extension ?? "") { - pifTarget.addSourceFile(resourceFile) - } - - // Asset Catalogs need to be included in the sources target for generated asset symbols. - if SwiftBuildFileType.xcassets.fileTypes.contains(resource.path.extension ?? "") { - pifTarget.addSourceFile(resourceFile) - } - - resourcesTarget.addResourceFile(resourceFile) - } - - let targetGroup = groupTree.addGroup(path: "/", sourceTree: .group) - pifTarget.addResourceFile(targetGroup.addFileReference( - path: "\(bundleName)\(parameters.triple.nsbundleExtension)", - sourceTree: .builtProductsDir - )) - - return bundleName - } - - // Add inferred build settings for a particular value for a manifest setting and value. - private func addInferredBuildSettings( - for setting: PIF.BuildSettings.MultipleValueSetting, - value: [String], - platform: PIF.BuildSettings.Platform? = nil, - configuration: BuildConfiguration, - settings: inout PIF.BuildSettings - ) { - // Automatically set SWIFT_EMIT_MODULE_INTERFACE if the package author uses unsafe flags to enable - // library evolution (this is needed until there is a way to specify this in the package manifest). - if setting == .OTHER_SWIFT_FLAGS && value.contains("-enable-library-evolution") { - settings[.SWIFT_EMIT_MODULE_INTERFACE] = "YES" - } } - - // Apply target-specific build settings defined in the manifest. - private func addManifestBuildSettings( - from target: Module, - debugSettings: inout PIF.BuildSettings, - releaseSettings: inout PIF.BuildSettings, - impartedSettings: inout PIF.BuildSettings - ) throws { - for (setting, assignments) in target.buildSettings.pifAssignments { - for assignment in assignments { - var value = assignment.value - if setting == .HEADER_SEARCH_PATHS { - value = try value - .map { try AbsolutePath(validating: $0, relativeTo: target.sources.root).pathString } + addEmptyBuildConfig(to: allExcludingTestsTargetKeyPath, name: "Debug") + addEmptyBuildConfig(to: allExcludingTestsTargetKeyPath, name: "Release") + + for (package, packageProject) in packagesAndProjects where package.manifest.packageKind.isRoot { + for target in packageProject.targets { + switch target { + case .target(let target): + guard !target.id.hasSuffix(.dynamic) else { + // Otherwise we hit a bunch of "Unknown multiple commands produce: ..." errors, + // as the build artifacts from "PACKAGE-TARGET:Foo" + // conflicts with those from "PACKAGE-TARGET:Foo-dynamic". + continue } - - if let platforms = assignment.platforms { - for platform in platforms { - for configuration in assignment.configurations { - switch configuration { - case .debug: - debugSettings[setting, for: platform, default: ["$(inherited)"]] += value - self.addInferredBuildSettings( - for: setting, - value: value, - platform: platform, - configuration: .debug, - settings: &debugSettings - ) - case .release: - releaseSettings[setting, for: platform, default: ["$(inherited)"]] += value - self.addInferredBuildSettings( - for: setting, - value: value, - platform: platform, - configuration: .release, - settings: &releaseSettings - ) - } - } - - if setting == .OTHER_LDFLAGS { - impartedSettings[setting, for: platform, default: ["$(inherited)"]] += value - } - } - } else { - for configuration in assignment.configurations { - switch configuration { - case .debug: - debugSettings[setting, default: ["$(inherited)"]] += value - self.addInferredBuildSettings( - for: setting, - value: value, - configuration: .debug, - settings: &debugSettings - ) - case .release: - releaseSettings[setting, default: ["$(inherited)"]] += value - self.addInferredBuildSettings( - for: setting, - value: value, - configuration: .release, - settings: &releaseSettings - ) - } - } - - if setting == .OTHER_LDFLAGS { - impartedSettings[setting, default: ["$(inherited)"]] += value - } + + aggregateProject[keyPath: allIncludingTestsTargetKeyPath].common.addDependency( + on: target.id, + platformFilters: [], + linkProduct: false + ) + if target.productType != .unitTest { + aggregateProject[keyPath: allExcludingTestsTargetKeyPath].common.addDependency( + on: target.id, + platformFilters: [], + linkProduct: false + ) } + case .aggregate: + break } } } + + do { + let allIncludingTests = aggregateProject[keyPath: allIncludingTestsTargetKeyPath] + let allExcludingTests = aggregateProject[keyPath: allExcludingTestsTargetKeyPath] + + observabilityScope.logPIF( + .debug, + indent: 1, + "Created target '\(allIncludingTests.id)' with name '\(allIncludingTests.name)' " + + "and \(allIncludingTests.common.dependencies.count) (unlinked) dependencies" + ) + observabilityScope.logPIF( + .debug, + indent: 1, + "Created target '\(allExcludingTests.id)' with name '\(allExcludingTests.name)' " + + "and \(allExcludingTests.common.dependencies.count) (unlinked) dependencies" + ) + } + + return aggregateProject } -final class AggregatePIFProjectBuilder: PIFProjectBuilder { - init(projects: [PIFProjectBuilder]) { - super.init() - - guid = "AGGREGATE" - name = "Aggregate" - path = projects[0].path - projectDirectory = projects[0].projectDirectory - developmentRegion = "en" - - var settings = PIF.BuildSettings() - settings[.PRODUCT_NAME] = "$(TARGET_NAME)" - settings[.SUPPORTED_PLATFORMS] = ["$(AVAILABLE_PLATFORMS)"] - settings[.SDKROOT] = "auto" - settings[.SDK_VARIANT] = "auto" - settings[.SKIP_INSTALL] = "YES" - - addBuildConfiguration(name: "Debug", settings: settings) - addBuildConfiguration(name: "Release", settings: settings) - - let allExcludingTestsTarget = addAggregateTarget( - guid: "ALL-EXCLUDING-TESTS", - name: PIFBuilder.allExcludingTestsTargetName - ) - - allExcludingTestsTarget.addBuildConfiguration(name: "Debug") - allExcludingTestsTarget.addBuildConfiguration(name: "Release") - - let allIncludingTestsTarget = addAggregateTarget( - guid: "ALL-INCLUDING-TESTS", - name: PIFBuilder.allIncludingTestsTargetName - ) - - allIncludingTestsTarget.addBuildConfiguration(name: "Debug") - allIncludingTestsTarget.addBuildConfiguration(name: "Release") - - for case let project as _PackagePIFProjectBuilder in projects where project.isRootPackage { - for case let target as PIFTargetBuilder in project.targets { - if target.productType != .unitTest { - allExcludingTestsTarget.addDependency( - toTargetWithGUID: target.guid, - platformFilters: [], - linkProduct: false - ) - } - - allIncludingTestsTarget.addDependency( - toTargetWithGUID: target.guid, - platformFilters: [], - linkProduct: false - ) - } - } - } -} - -protocol PIFReferenceBuilder: AnyObject { - var guid: String { get set } - - func construct() -> PIF.Reference -} - -final class PIFFileReferenceBuilder: PIFReferenceBuilder { - let path: String - let sourceTree: PIF.Reference.SourceTree - let name: String? - let fileType: String? - - @DelayedImmutable - var guid: String - - init(path: String, sourceTree: PIF.Reference.SourceTree, name: String? = nil, fileType: String? = nil) { - self.path = path - self.sourceTree = sourceTree - self.name = name - self.fileType = fileType - } - - func construct() -> PIF.Reference { - PIF.FileReference( - guid: self.guid, - path: self.path, - sourceTree: self.sourceTree, - name: self.name, - fileType: self.fileType - ) - } -} - -final class PIFGroupBuilder: PIFReferenceBuilder { - let path: String - let sourceTree: PIF.Reference.SourceTree - let name: String? - private(set) var children: [PIFReferenceBuilder] - - @DelayedImmutable - var guid: PIF.GUID - - init(path: String, sourceTree: PIF.Reference.SourceTree = .group, name: String? = nil) { - self.path = path - self.sourceTree = sourceTree - self.name = name - self.children = [] - } - - /// Creates and appends a new Group to the list of children. The new group is returned so that it can be configured. - func addGroup( - path: String, - sourceTree: PIF.Reference.SourceTree = .group, - name: String? = nil - ) -> PIFGroupBuilder { - let group = PIFGroupBuilder(path: path, sourceTree: sourceTree, name: name) - self.children.append(group) - return group - } - - /// Creates and appends a new FileReference to the list of children. - func addFileReference( - path: String, - sourceTree: PIF.Reference.SourceTree = .group, - name: String? = nil, - fileType: String? = nil - ) -> PIFFileReferenceBuilder { - let file = PIFFileReferenceBuilder(path: path, sourceTree: sourceTree, name: name, fileType: fileType) - self.children.append(file) - return file - } - - func removeChild(_ reference: PIFReferenceBuilder) { - self.children.removeAll { $0 === reference } - } - - func construct() -> PIF.Reference { - let children = self.children.enumerated().map { kvp -> PIF.Reference in - let (index, builder) = kvp - builder.guid = "\(self.guid)::REF_\(index)" - return builder.construct() - } - - return PIF.Group( - guid: self.guid, - path: self.path, - sourceTree: self.sourceTree, - name: self.name, - children: children - ) - } -} - -class PIFBaseTargetBuilder { - public let guid: PIF.GUID - public let name: String - public fileprivate(set) var buildConfigurations: [PIFBuildConfigurationBuilder] - public fileprivate(set) var buildPhases: [PIFBuildPhaseBuilder] - public fileprivate(set) var dependencies: [PIF.TargetDependency] - public fileprivate(set) var impartedBuildSettings: PIF.BuildSettings - - fileprivate init(guid: PIF.GUID, name: String) { - self.guid = guid - self.name = name - self.buildConfigurations = [] - self.buildPhases = [] - self.dependencies = [] - self.impartedBuildSettings = PIF.BuildSettings() - } - - /// Creates and adds a new empty build configuration, i.e. one that does not initially have any build settings. - /// The name must not be empty and must not be equal to the name of any existing build configuration in the - /// target. - @discardableResult - public func addBuildConfiguration( - name: String, - settings: PIF.BuildSettings = PIF.BuildSettings(), - impartedBuildProperties: PIF.ImpartedBuildProperties = PIF - .ImpartedBuildProperties(settings: PIF.BuildSettings()) - ) -> PIFBuildConfigurationBuilder { - let builder = PIFBuildConfigurationBuilder( - name: name, - settings: settings, - impartedBuildProperties: impartedBuildProperties - ) - self.buildConfigurations.append(builder) - return builder - } - - func construct() throws -> PIF.BaseTarget { - throw InternalError("implement in subclass") - } - - /// Adds a "headers" build phase, i.e. one that copies headers into a directory of the product, after suitable - /// processing. - @discardableResult - func addHeadersBuildPhase() -> PIFHeadersBuildPhaseBuilder { - let buildPhase = PIFHeadersBuildPhaseBuilder() - self.buildPhases.append(buildPhase) - return buildPhase - } - - /// Adds a "sources" build phase, i.e. one that compiles sources and provides them to be linked into the - /// executable code of the product. - @discardableResult - func addSourcesBuildPhase() -> PIFSourcesBuildPhaseBuilder { - let buildPhase = PIFSourcesBuildPhaseBuilder() - self.buildPhases.append(buildPhase) - return buildPhase - } - - /// Adds a "frameworks" build phase, i.e. one that links compiled code and libraries into the executable of the - /// product. - @discardableResult - func addFrameworksBuildPhase() -> PIFFrameworksBuildPhaseBuilder { - let buildPhase = PIFFrameworksBuildPhaseBuilder() - self.buildPhases.append(buildPhase) - return buildPhase - } - - @discardableResult - func addResourcesBuildPhase() -> PIFResourcesBuildPhaseBuilder { - let buildPhase = PIFResourcesBuildPhaseBuilder() - self.buildPhases.append(buildPhase) - return buildPhase - } - - /// Adds a dependency on another target. It is the caller's responsibility to avoid creating dependency cycles. - /// A dependency of one target on another ensures that the other target is built first. If `linkProduct` is - /// true, the receiver will also be configured to link against the product produced by the other target (this - /// presumes that the product type is one that can be linked against). - func addDependency(toTargetWithGUID targetGUID: String, platformFilters: [PIF.PlatformFilter], linkProduct: Bool) { - self.dependencies.append(.init(targetGUID: targetGUID, platformFilters: platformFilters)) - if linkProduct { - let frameworksPhase = self.buildPhases.first { $0 is PIFFrameworksBuildPhaseBuilder } - ?? self.addFrameworksBuildPhase() - frameworksPhase.addBuildFile(toTargetWithGUID: targetGUID, platformFilters: platformFilters) - } - } - - /// Convenience function to add a file reference to the Headers build phase, after creating it if needed. - @discardableResult - public func addHeaderFile( - _ fileReference: PIFFileReferenceBuilder, - headerVisibility: PIF.BuildFile.HeaderVisibility - ) -> PIFBuildFileBuilder { - let headerPhase = self.buildPhases.first { $0 is PIFHeadersBuildPhaseBuilder } ?? self.addHeadersBuildPhase() - return headerPhase.addBuildFile(to: fileReference, platformFilters: [], headerVisibility: headerVisibility) - } - - /// Convenience function to add a file reference to the Sources build phase, after creating it if needed. - @discardableResult - public func addSourceFile(_ fileReference: PIFFileReferenceBuilder) -> PIFBuildFileBuilder { - let sourcesPhase = self.buildPhases.first { $0 is PIFSourcesBuildPhaseBuilder } ?? self.addSourcesBuildPhase() - return sourcesPhase.addBuildFile(to: fileReference, platformFilters: []) - } - - /// Convenience function to add a file reference to the Frameworks build phase, after creating it if needed. - @discardableResult - public func addLibrary( - _ fileReference: PIFFileReferenceBuilder, - platformFilters: [PIF.PlatformFilter] - ) -> PIFBuildFileBuilder { - let frameworksPhase = self.buildPhases.first { $0 is PIFFrameworksBuildPhaseBuilder } ?? self - .addFrameworksBuildPhase() - return frameworksPhase.addBuildFile(to: fileReference, platformFilters: platformFilters) - } - - @discardableResult - public func addResourceFile(_ fileReference: PIFFileReferenceBuilder) -> PIFBuildFileBuilder { - let resourcesPhase = self.buildPhases.first { $0 is PIFResourcesBuildPhaseBuilder } ?? self - .addResourcesBuildPhase() - return resourcesPhase.addBuildFile(to: fileReference, platformFilters: []) - } - - fileprivate func constructBuildConfigurations() -> [PIF.BuildConfiguration] { - self.buildConfigurations.map { builder -> PIF.BuildConfiguration in - builder.guid = "\(self.guid)::BUILDCONFIG_\(builder.name)" - return builder.construct() - } - } - - fileprivate func constructBuildPhases() throws -> [PIF.BuildPhase] { - try self.buildPhases.enumerated().map { kvp in - let (index, builder) = kvp - builder.guid = "\(self.guid)::BUILDPHASE_\(index)" - return try builder.construct() - } - } -} - -final class PIFAggregateTargetBuilder: PIFBaseTargetBuilder { - override func construct() throws -> PIF.BaseTarget { - try PIF.AggregateTarget( - guid: guid, - name: name, - buildConfigurations: constructBuildConfigurations(), - buildPhases: self.constructBuildPhases(), - dependencies: dependencies, - impartedBuildSettings: impartedBuildSettings - ) - } -} - -final class PIFTargetBuilder: PIFBaseTargetBuilder { - let productType: PIF.Target.ProductType - let productName: String - var productReference: PIF.FileReference? = nil - - public init(guid: PIF.GUID, name: String, productType: PIF.Target.ProductType, productName: String) { - self.productType = productType - self.productName = productName - super.init(guid: guid, name: name) - } - - override func construct() throws -> PIF.BaseTarget { - try PIF.Target( - guid: guid, - name: name, - productType: self.productType, - productName: self.productName, - buildConfigurations: constructBuildConfigurations(), - buildPhases: self.constructBuildPhases(), - dependencies: dependencies, - impartedBuildSettings: impartedBuildSettings - ) - } -} - -class PIFBuildPhaseBuilder { - public private(set) var buildFiles: [PIFBuildFileBuilder] - - @DelayedImmutable - var guid: PIF.GUID - - fileprivate init() { - self.buildFiles = [] - } - - /// Adds a new build file builder that refers to a file reference. - /// - Parameters: - /// - file: The builder for the file reference. - @discardableResult - func addBuildFile( - to file: PIFFileReferenceBuilder, - platformFilters: [PIF.PlatformFilter], - headerVisibility: PIF.BuildFile.HeaderVisibility? = nil - ) -> PIFBuildFileBuilder { - let builder = PIFBuildFileBuilder( - file: file, - platformFilters: platformFilters, - headerVisibility: headerVisibility - ) - self.buildFiles.append(builder) - return builder - } - - /// Adds a new build file builder that refers to a target GUID. - /// - Parameters: - /// - targetGUID: The GIUD referencing the target. - @discardableResult - func addBuildFile( - toTargetWithGUID targetGUID: PIF.GUID, - platformFilters: [PIF.PlatformFilter] - ) -> PIFBuildFileBuilder { - let builder = PIFBuildFileBuilder(targetGUID: targetGUID, platformFilters: platformFilters) - self.buildFiles.append(builder) - return builder - } - - func construct() throws -> PIF.BuildPhase { - throw InternalError("implement in subclass") - } - - fileprivate func constructBuildFiles() -> [PIF.BuildFile] { - self.buildFiles.enumerated().map { kvp -> PIF.BuildFile in - let (index, builder) = kvp - builder.guid = "\(self.guid)::\(index)" - return builder.construct() - } - } -} - -final class PIFHeadersBuildPhaseBuilder: PIFBuildPhaseBuilder { - override func construct() -> PIF.BuildPhase { - PIF.HeadersBuildPhase(guid: guid, buildFiles: constructBuildFiles()) - } -} - -final class PIFSourcesBuildPhaseBuilder: PIFBuildPhaseBuilder { - override func construct() -> PIF.BuildPhase { - PIF.SourcesBuildPhase(guid: guid, buildFiles: constructBuildFiles()) - } -} - -final class PIFFrameworksBuildPhaseBuilder: PIFBuildPhaseBuilder { - override func construct() -> PIF.BuildPhase { - PIF.FrameworksBuildPhase(guid: guid, buildFiles: constructBuildFiles()) - } -} - -final class PIFResourcesBuildPhaseBuilder: PIFBuildPhaseBuilder { - override func construct() -> PIF.BuildPhase { - PIF.ResourcesBuildPhase(guid: guid, buildFiles: constructBuildFiles()) - } -} - -final class PIFBuildFileBuilder { - private enum Reference { - case file(builder: PIFFileReferenceBuilder) - case target(guid: PIF.GUID) - - var pifReference: PIF.BuildFile.Reference { - switch self { - case .file(let builder): - .file(guid: builder.guid) - case .target(let guid): - .target(guid: guid) - } - } - } - - private let reference: Reference - - @DelayedImmutable - var guid: PIF.GUID - - let platformFilters: [PIF.PlatformFilter] - - let headerVisibility: PIF.BuildFile.HeaderVisibility? - - fileprivate init( - file: PIFFileReferenceBuilder, - platformFilters: [PIF.PlatformFilter], - headerVisibility: PIF.BuildFile.HeaderVisibility? = nil - ) { - self.reference = .file(builder: file) - self.platformFilters = platformFilters - self.headerVisibility = headerVisibility - } - - fileprivate init( - targetGUID: PIF.GUID, - platformFilters: [PIF.PlatformFilter], - headerVisibility: PIF.BuildFile.HeaderVisibility? = nil - ) { - self.reference = .target(guid: targetGUID) - self.platformFilters = platformFilters - self.headerVisibility = headerVisibility - } - - func construct() -> PIF.BuildFile { - PIF.BuildFile( - guid: self.guid, - reference: self.reference.pifReference, - platformFilters: self.platformFilters, - headerVisibility: self.headerVisibility - ) - } -} - -final class PIFBuildConfigurationBuilder { - let name: String - let settings: PIF.BuildSettings - let impartedBuildProperties: PIF.ImpartedBuildProperties - - @DelayedImmutable - var guid: PIF.GUID - - public init(name: String, settings: PIF.BuildSettings, impartedBuildProperties: PIF.ImpartedBuildProperties) { - precondition(!name.isEmpty) - self.name = name - self.settings = settings - self.impartedBuildProperties = impartedBuildProperties - } - - func construct() -> PIF.BuildConfiguration { - PIF.BuildConfiguration( - guid: self.guid, - name: self.name, - buildSettings: self.settings, - impartedBuildProperties: self.impartedBuildProperties - ) - } -} - -struct XCBBuildParameters: Encodable { - struct RunDestination: Encodable { - var platform: String - var sdk: String - var sdkVariant: String? - var targetArchitecture: String - var supportedArchitectures: [String] - var disableOnlyActiveArch: Bool - } - - struct XCBSettingsTable: Encodable { - var table: [String: String] - } - - struct SettingsOverride: Encodable { - var synthesized: XCBSettingsTable? = nil - } - - var configurationName: String - var overrides: SettingsOverride - var activeRunDestination: RunDestination -} - -// Helper functions to consistently generate a PIF target identifier string for a product/target/resource bundle in a -// package. This format helps make sure that there is no collision with any other PIF targets, and in particular that a -// PIF target and a PIF product can have the same name (as they often do). - -extension ResolvedPackage { - var pifProjectGUID: PIF.GUID { "PACKAGE:\(manifest.packageLocation)" } -} - -extension ResolvedProduct { - var pifTargetGUID: PIF.GUID { "PACKAGE-PRODUCT:\(name)" } - - var mainTarget: ResolvedModule { - modules.first { $0.type == underlying.type._targetType }! - } - - /// Returns the recursive dependencies, limited to the target's package, which satisfy the input build environment, - /// based on their conditions and in a stable order. - /// - Parameters: - /// - environment: The build environment to use to filter dependencies on. - public func recursivePackageDependencies() -> [ResolvedModule.Dependency] { - let initialDependencies = modules.map { ResolvedModule.Dependency.module($0, conditions: []) } - return try! topologicalSort(initialDependencies) { dependency in - dependency.packageDependencies - }.sorted() - } -} - -extension ResolvedModule { - var pifTargetGUID: PIF.GUID { "PACKAGE-TARGET:\(name)" } - var pifResourceTargetGUID: PIF.GUID { "PACKAGE-RESOURCE:\(name)" } -} - -extension [ResolvedModule.Dependency] { - /// Sorts to get products first, sorted by name, followed by targets, sorted by name. - func sorted() -> [ResolvedModule.Dependency] { - self.sorted { lhsDependency, rhsDependency in - switch (lhsDependency, rhsDependency) { - case (.product, .module): - true - case (.module, .product): - false - case (.product(let lhsProduct, _), .product(let rhsProduct, _)): - lhsProduct.name < rhsProduct.name - case (.module(let lhsTarget, _), .module(let rhsTarget, _)): - lhsTarget.name < rhsTarget.name - } - } - } -} - -extension ResolvedPackage { - func deploymentTarget(for platform: PackageModel.Platform, usingXCTest: Bool = false) -> String? { - self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString - } -} - -extension ResolvedModule { - func deploymentTarget(for platform: PackageModel.Platform, usingXCTest: Bool = false) -> String? { - self.getSupportedPlatform(for: platform, usingXCTest: usingXCTest).version.versionString - } -} - -extension Module { - var isCxx: Bool { - (self as? ClangModule)?.isCXX ?? false - } -} - -extension ProductType { - var _targetType: Module.Kind { - switch self { - case .executable: - .executable - case .snippet: - .snippet - case .test: - .test - case .library: - .library - case .plugin: - .plugin - case .macro: - .macro - } - } -} - -private struct PIFBuildSettingAssignment { - /// The assignment value. - let value: [String] - - /// The configurations this assignment applies to. - let configurations: [BuildConfiguration] - - /// The platforms this assignment is restrained to, or nil to apply to all platforms. - let platforms: [PIF.BuildSettings.Platform]? -} - -extension PackageModel.BuildSettings.AssignmentTable { - fileprivate var pifAssignments: [PIF.BuildSettings.MultipleValueSetting: [PIFBuildSettingAssignment]] { - var pifAssignments: [PIF.BuildSettings.MultipleValueSetting: [PIFBuildSettingAssignment]] = [:] - - for (declaration, assignments) in self.assignments { - for assignment in assignments { - let setting: PIF.BuildSettings.MultipleValueSetting - let value: [String] - - switch declaration { - case .LINK_LIBRARIES: - setting = .OTHER_LDFLAGS - value = assignment.values.map { "-l\($0)" } - case .LINK_FRAMEWORKS: - setting = .OTHER_LDFLAGS - value = assignment.values.flatMap { ["-framework", $0] } - default: - guard let parsedSetting = PIF.BuildSettings.MultipleValueSetting(rawValue: declaration.name) else { - continue - } - setting = parsedSetting - value = assignment.values - } - - let pifAssignment = PIFBuildSettingAssignment( - value: value, - configurations: assignment.configurations, - platforms: assignment.pifPlatforms - ) - - pifAssignments[setting, default: []].append(pifAssignment) - } - } - - return pifAssignments - } -} - -extension PackageModel.BuildSettings.Assignment { - fileprivate var configurations: [BuildConfiguration] { - if let configurationCondition = conditions.lazy.compactMap(\.configurationCondition).first { - [configurationCondition.configuration] - } else { - BuildConfiguration.allCases - } - } - - fileprivate var pifPlatforms: [PIF.BuildSettings.Platform]? { - if let platformsCondition = conditions.lazy.compactMap(\.platformsCondition).first { - platformsCondition.platforms.compactMap { PIF.BuildSettings.Platform(rawValue: $0.name) } - } else { - nil - } - } -} - -@propertyWrapper -public struct DelayedImmutable { - private var _value: Value? = nil - - public init() {} - - public var wrappedValue: Value { - get { - guard let value = _value else { - fatalError("property accessed before being initialized") - } - return value - } - set { - if self._value != nil { - fatalError("property initialized twice") - } - self._value = newValue - } - } -} - -extension [PackageCondition] { - func toPlatformFilters() -> [PIF.PlatformFilter] { - var result: [PIF.PlatformFilter] = [] - let platformConditions = self.compactMap(\.platformsCondition).flatMap(\.platforms) - - for condition in platformConditions { - switch condition { - case .macOS: - result += PIF.PlatformFilter.macOSFilters - - case .macCatalyst: - result += PIF.PlatformFilter.macCatalystFilters - - case .iOS: - result += PIF.PlatformFilter.iOSFilters - - case .tvOS: - result += PIF.PlatformFilter.tvOSFilters - - case .watchOS: - result += PIF.PlatformFilter.watchOSFilters - - case .visionOS: - result += PIF.PlatformFilter.visionOSFilters - - case .linux: - result += PIF.PlatformFilter.linuxFilters - - case .android: - result += PIF.PlatformFilter.androidFilters - - case .windows: - result += PIF.PlatformFilter.windowsFilters - - case .driverKit: - result += PIF.PlatformFilter.driverKitFilters - - case .wasi: - result += PIF.PlatformFilter.webAssemblyFilters - - case .openbsd: - result += PIF.PlatformFilter.openBSDFilters - - case .freebsd: - result += PIF.PlatformFilter.freeBSDFilters - - default: - assertionFailure("Unhandled platform condition: \(condition)") - } - } - return result - } -} - -extension PIF.PlatformFilter { - /// macOS platform filters. - public static let macOSFilters: [PIF.PlatformFilter] = [.init(platform: "macos")] - - /// Mac Catalyst platform filters. - public static let macCatalystFilters: [PIF.PlatformFilter] = [ - .init(platform: "ios", environment: "maccatalyst"), - ] - - /// iOS platform filters. - public static let iOSFilters: [PIF.PlatformFilter] = [ - .init(platform: "ios"), - .init(platform: "ios", environment: "simulator"), - ] - - /// tvOS platform filters. - public static let tvOSFilters: [PIF.PlatformFilter] = [ - .init(platform: "tvos"), - .init(platform: "tvos", environment: "simulator"), - ] - - /// watchOS platform filters. - public static let watchOSFilters: [PIF.PlatformFilter] = [ - .init(platform: "watchos"), - .init(platform: "watchos", environment: "simulator"), - ] - - /// DriverKit platform filters. - public static let driverKitFilters: [PIF.PlatformFilter] = [ - .init(platform: "driverkit"), - ] - - /// Windows platform filters. - public static let windowsFilters: [PIF.PlatformFilter] = [ - .init(platform: "windows", environment: "msvc"), - .init(platform: "windows", environment: "gnu"), - ] - - /// Android platform filters. - public static let androidFilters: [PIF.PlatformFilter] = [ - .init(platform: "linux", environment: "android"), - .init(platform: "linux", environment: "androideabi"), - ] - - /// Common Linux platform filters. - public static let linuxFilters: [PIF.PlatformFilter] = ["", "eabi", "gnu", "gnueabi", "gnueabihf"].map { - .init(platform: "linux", environment: $0) - } - - /// OpenBSD filters. - public static let openBSDFilters: [PIF.PlatformFilter] = [ - .init(platform: "openbsd"), - ] - - /// WebAssembly platform filters. - public static let webAssemblyFilters: [PIF.PlatformFilter] = [ - .init(platform: "wasi"), - ] - - /// VisionOS platform filters. - public static let visionOSFilters: [PIF.PlatformFilter] = [ - .init(platform: "xros"), - .init(platform: "xros", environment: "simulator"), - .init(platform: "visionos"), - .init(platform: "visionos", environment: "simulator"), - ] - - /// FreeBSD filters. - public static let freeBSDFilters: [PIF.PlatformFilter] = [ - .init(platform: "freebsd"), - ] -} - -extension PIF.BuildSettings { - fileprivate mutating func addSwiftVersionSettings( - target: SwiftModule, - parameters: PIFBuilderParameters - ) throws { - guard let versionAssignments = target.buildSettings.assignments[.SWIFT_VERSION] else { - // This should never happens in practice because there is always a default tools version based value. - return - } - - func isSupportedVersion(_ version: SwiftLanguageVersion) -> Bool { - parameters.supportedSwiftVersions.isEmpty || parameters.supportedSwiftVersions.contains(version) - } - - func computeEffectiveSwiftVersions(for versions: [SwiftLanguageVersion]) -> [String] { - versions - .filter { target.declaredSwiftVersions.contains($0) } - .filter { isSupportedVersion($0) }.map(\.description) - } - - func computeEffectiveTargetVersion(for assignment: PackageModel.BuildSettings.Assignment) throws -> String { - let versions = assignment.values.compactMap { SwiftLanguageVersion(string: $0) } - if let effectiveVersion = computeEffectiveSwiftVersions(for: versions).last { - return effectiveVersion - } - - throw PIFGenerationError.unsupportedSwiftLanguageVersions( - targetName: target.name, - versions: versions, - supportedVersions: parameters.supportedSwiftVersions - ) - } - - var toolsSwiftVersion: SwiftLanguageVersion? = nil - // First, check whether there are any target specific settings. - for assignment in versionAssignments { - if assignment.default { - toolsSwiftVersion = assignment.values.first.flatMap { .init(string: $0) } - continue - } - - if assignment.conditions.isEmpty { - self[.SWIFT_VERSION] = try computeEffectiveTargetVersion(for: assignment) - continue - } - - for condition in assignment.conditions { - if let platforms = condition.platformsCondition { - for platform: Platform in platforms.platforms.compactMap({ .init(rawValue: $0.name) }) { - self[.SWIFT_VERSION, for: platform] = try computeEffectiveTargetVersion(for: assignment) - } - } - } - } - - // If there were no target specific assignments, let's add a fallback tools version based value. - if let toolsSwiftVersion, self[.SWIFT_VERSION] == nil { - // Use tools based version if it's supported. - if isSupportedVersion(toolsSwiftVersion) { - self[.SWIFT_VERSION] = toolsSwiftVersion.description - return - } - - // Otherwise pick the newest supported tools version based value. - - // We have to normalize to two component strings to match the results from SwiftBuild w.r.t. to hashing of - // `SwiftLanguageVersion` instances. - let normalizedDeclaredVersions = Set(target.declaredSwiftVersions.compactMap { - SwiftLanguageVersion(string: "\($0.major).\($0.minor)") - }) - - let declaredSwiftVersions = Array( - normalizedDeclaredVersions - .intersection(parameters.supportedSwiftVersions) - ).sorted(by: >) - if let swiftVersion = declaredSwiftVersions.first { - self[.SWIFT_VERSION] = swiftVersion.description - return - } - - throw PIFGenerationError.unsupportedSwiftLanguageVersions( - targetName: target.name, - versions: Array(normalizedDeclaredVersions), - supportedVersions: parameters.supportedSwiftVersions - ) - } - } - - fileprivate mutating func addCommonSwiftSettings( - package: ResolvedPackage, - target: ResolvedModule, - parameters: PIFBuilderParameters - ) { - let packageOptions = package.packageNameArgument( - target: target, - isPackageNameSupported: parameters.isPackageAccessModifierSupported - ) - if !packageOptions.isEmpty { - self[.OTHER_SWIFT_FLAGS] = packageOptions - } - } -} - -extension PIF.BuildSettings.Platform { - fileprivate static func from(platform: PackageModel.Platform) -> PIF.BuildSettings.Platform? { - switch platform { - case .iOS: .iOS - case .linux: .linux - case .macCatalyst: .macCatalyst - case .macOS: .macOS - case .tvOS: .tvOS - case .watchOS: .watchOS - case .driverKit: .driverKit - default: nil - } - } -} +#endif public enum PIFGenerationError: Error { + case rootPackageNotFound, multipleRootPackagesFound + case unsupportedSwiftLanguageVersions( targetName: String, versions: [SwiftLanguageVersion], @@ -1975,12 +409,19 @@ public enum PIFGenerationError: Error { extension PIFGenerationError: CustomStringConvertible { public var description: String { switch self { + case .rootPackageNotFound: + "No root package was found" + + case .multipleRootPackagesFound: + "Multiple root packages were found, making the PIF generation (root packages) ordering sensitive" + case .unsupportedSwiftLanguageVersions( targetName: let target, versions: let given, supportedVersions: let supported ): - "None of the Swift language versions used in target '\(target)' settings are supported. (given: \(given), supported: \(supported))" + "None of the Swift language versions used in target '\(target)' settings are supported." + + " (given: \(given), supported: \(supported))" } } } diff --git a/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift b/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift index f8819eaf0cf..e1689c1b086 100644 --- a/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift +++ b/Sources/SwiftBuildSupport/PackagePIFBuilder+Helpers.swift @@ -55,17 +55,27 @@ import struct PackageGraph.ResolvedProduct import func PackageLoading.pkgConfigArgs +// TODO: Move this back to `PackagePIFBuilder` once we get rid of `#if canImport(SwiftBuild)`. +func targetName(forProductName name: String, suffix: String? = nil) -> String { + let suffix = suffix ?? "" + return "\(name)\(suffix)-product" +} + #if canImport(SwiftBuild) import enum SwiftBuild.ProjectModel // MARK: - PIF GUID Helpers -enum TargetGUIDSuffix: String { +enum TargetSuffix: String { case testable, dynamic + + func hasSuffix(id: GUID) -> Bool { + id.value.hasSuffix("-\(self.rawValue)") + } } -extension TargetGUIDSuffix? { +extension TargetSuffix? { func description(forName name: String) -> String { switch self { case .some(let suffix): @@ -76,34 +86,45 @@ extension TargetGUIDSuffix? { } } +extension GUID { + func hasSuffix(_ suffix: TargetSuffix) -> Bool { + self.value.hasSuffix("-\(suffix.rawValue)") + } +} + extension PackageModel.Module { - func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> GUID { + var pifTargetGUID: GUID { pifTargetGUID(suffix: nil) } + + func pifTargetGUID(suffix: TargetSuffix?) -> GUID { PackagePIFBuilder.targetGUID(forModuleName: self.name, suffix: suffix) } } extension PackageGraph.ResolvedModule { - func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> GUID { + var pifTargetGUID: GUID { pifTargetGUID(suffix: nil) } + + func pifTargetGUID(suffix: TargetSuffix?) -> GUID { self.underlying.pifTargetGUID(suffix: suffix) } } extension PackageModel.Product { - func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> GUID { + var pifTargetGUID: GUID { pifTargetGUID(suffix: nil) } + + func pifTargetGUID(suffix: TargetSuffix?) -> GUID { PackagePIFBuilder.targetGUID(forProductName: self.name, suffix: suffix) } } extension PackageGraph.ResolvedProduct { - func pifTargetGUID(suffix: TargetGUIDSuffix? = nil) -> GUID { + var pifTargetGUID: GUID { pifTargetGUID(suffix: nil) } + + func pifTargetGUID(suffix: TargetSuffix?) -> GUID { self.underlying.pifTargetGUID(suffix: suffix) } - /// Helper function to consistently generate a target name string for a product in a package. - /// This format helps make sure that targets and products with the same name (as they often have) have different - /// target names in the PIF. - func targetNameForProduct(suffix: String = "") -> String { - "\(name)\(suffix) product" + func targetName(suffix: TargetSuffix? = nil) -> String { + PackagePIFBuilder.targetName(forProductName: self.name, suffix: suffix) } } @@ -112,7 +133,7 @@ extension PackagePIFBuilder { /// /// This format helps make sure that there is no collision with any other PIF targets, /// and in particular that a PIF target and a PIF product can have the same name (as they often do). - static func targetGUID(forModuleName name: String, suffix: TargetGUIDSuffix? = nil) -> GUID { + static func targetGUID(forModuleName name: String, suffix: TargetSuffix? = nil) -> GUID { let suffixDescription = suffix.description(forName: name) return "PACKAGE-TARGET:\(name)\(suffixDescription)" } @@ -121,10 +142,17 @@ extension PackagePIFBuilder { /// /// This format helps make sure that there is no collision with any other PIF targets, /// and in particular that a PIF target and a PIF product can have the same name (as they often do). - static func targetGUID(forProductName name: String, suffix: TargetGUIDSuffix? = nil) -> GUID { + static func targetGUID(forProductName name: String, suffix: TargetSuffix? = nil) -> GUID { let suffixDescription = suffix.description(forName: name) return "PACKAGE-PRODUCT:\(name)\(suffixDescription)" } + + /// Helper function to consistently generate a target name string for a product in a package. + /// This format helps make sure that targets and products with the same name (as they often have) have different + /// target names in the PIF. + static func targetName(forProductName name: String, suffix: TargetSuffix? = nil) -> String { + return SwiftBuildSupport.targetName(forProductName: name, suffix: suffix?.rawValue) + } } // MARK: - SwiftPM PackageModel Helpers @@ -796,7 +824,14 @@ extension TSCUtility.Version { } } -// MARK: - Swift Build PIF Helpers +// MARK: - Swift Build ProjectModel Helpers + +/// Helpful for logging. +extension ProjectModel.GUID: @retroactive CustomStringConvertible { + public var description: String { + value + } +} extension ProjectModel.BuildSettings { subscript(_ setting: MultipleValueSetting, default defaultValue: [String]) -> [String] { @@ -1013,6 +1048,55 @@ extension ProjectModel.BuildSettings.Declaration { } } +// MARK: - ObservabilityScope Helpers + +extension ObservabilityScope { + /// Logs an informational PIF message (intended for developers, not end users). + func logPIF( + _ severity: Diagnostic.Severity = .debug, + indent: UInt = 0, + _ message: String, + sourceFile: StaticString = #fileID, + sourceLine: UInt = #line + ) { + var metadata = ObservabilityMetadata() + metadata.sourceLocation = SourceLocation(sourceFile, sourceLine) + + let indentation = String(repeating: " ", count: Int(indent)) + let message = "PIF: \(indentation)\(message)" + + let diagnostic = Diagnostic(severity: severity, message: message, metadata: metadata) + self.emit(diagnostic) + } +} + +extension ObservabilityMetadata { + public var sourceLocation: SourceLocation? { + get { + self[SourceLocationKey.self] + } + set { + self[SourceLocationKey.self] = newValue + } + } + + private enum SourceLocationKey: Key { + typealias Value = SourceLocation + } +} + +public struct SourceLocation: Sendable { + public let file: StaticString + public let line: UInt + + public init(_ file: StaticString, _ line: UInt) { + precondition(file.description.hasContent) + + self.file = file + self.line = line + } +} + // MARK: - General Helpers extension SourceControlURL { diff --git a/Sources/SwiftBuildSupport/PackagePIFBuilder.swift b/Sources/SwiftBuildSupport/PackagePIFBuilder.swift index c2582c5c8bd..88a2bee948f 100644 --- a/Sources/SwiftBuildSupport/PackagePIFBuilder.swift +++ b/Sources/SwiftBuildSupport/PackagePIFBuilder.swift @@ -59,18 +59,14 @@ public final class PackagePIFBuilder { /// Scope for logging informational debug messages (intended for developers, not end users). let observabilityScope: ObservabilityScope - /// Logs an informational debug message (intended for developers, not end users). + /// Logs an informational message (intended for developers, not end users). func log( _ severity: Diagnostic.Severity, _ message: String, sourceFile: StaticString = #fileID, sourceLine: UInt = #line ) { - var metadata = ObservabilityMetadata() - metadata.sourceLocation = SourceLocation(sourceFile, sourceLine) - - let diagnostic = Diagnostic(severity: severity, message: message, metadata: metadata) - self.observabilityScope.emit(diagnostic) + self.observabilityScope.logPIF(severity, message, sourceFile: sourceFile, sourceLine: sourceLine) } unowned let delegate: BuildDelegate @@ -84,15 +80,15 @@ public final class PackagePIFBuilder { /// If a pure Swift package is open in the workspace. var hostsOnlyPackages: Bool { get } - /// Returns `true` if the package is managed by the user (i.e., the user is allowed to modify its sources, - /// package structure, etc). + /// Returns `true` if the package is managed by the user + /// (i.e., the user is allowed to modify its sources, package structure, etc). var isUserManaged: Bool { get } /// Whether or not this package is required by *branch* or *revision*. var isBranchOrRevisionBased: Bool { get } - /// For executables — only executables for now — we check to see if there is a custom package product type - /// provider that can provide this information. + /// For executables — only executables for now — we check to see if there is a + /// custom package product type provider that can provide this information. func customProductType(forExecutable product: PackageModel.Product) -> ProjectModel.Target.ProductType? /// Returns all *device family* IDs for all SDK variants. @@ -373,25 +369,24 @@ public final class PackagePIFBuilder { /// Build the PIF. @discardableResult public func build() throws -> [ModuleOrProduct] { - self.log(.info, "Building PIF for package \(self.package.identity)") - - var builder = PackagePIFProjectBuilder(createForPackage: package, builder: self) - self.addProjectBuildSettings(&builder) + self.log( + .info, + "Building PIF project for package '\(self.package.identity)' " + + "(\(package.products.count) products, \(package.modules.count) modules)" + ) - self._pifProject = builder.project + var projectBuilder = PackagePIFProjectBuilder(createForPackage: package, builder: self) + self.addProjectBuildSettings(&projectBuilder) // - // Construct PIF *targets* (for modules, products, and test bundles) based on the contents of the parsed - // package. - // These PIF targets will be sent down to Swift Build. + // Construct PIF *targets* (for modules, products, and test bundles) based on the contents + // of the parsed package. These PIF targets will be sent down to Swift Build. // // We also track all constructed objects as `ModuleOrProduct` value for easy introspection by clients. // In SwiftPM a product is a codeless entity with a reference to the modules(s) that contains the - // implementation. - // In order to avoid creating two ModuleOrProducts for each product in the package, the logic below creates a - // single - // unified ModuleOrProduct from the combination of a product and the single target that contains its - // implementation. + // implementation. In order to avoid creating two ModuleOrProducts for each product in the package, + // the logic below creates a single unified ModuleOrProduct from the combination of a product + // and the single target that contains its implementation. // // Products. SwiftPM considers unit tests to be products, so in this discussion, the term *product* // refers to an *executable*, a *library*, or an *unit test*. @@ -402,54 +397,58 @@ public final class PackagePIFBuilder { // the structure of the client(s). // + self.log(.debug, "Processing \(package.products.count) products:") + // For each of the **products** in the package we create a corresponding `PIFTarget` of the appropriate type. for product in self.package.products { switch product.type { case .library(.static): let libraryType = self.delegate.customLibraryType(product: product.underlying) ?? .static - try builder.makeLibraryProduct(product, type: libraryType) + try projectBuilder.makeLibraryProduct(product, type: libraryType) case .library(.dynamic): let libraryType = self.delegate.customLibraryType(product: product.underlying) ?? .dynamic - try builder.makeLibraryProduct(product, type: libraryType) + try projectBuilder.makeLibraryProduct(product, type: libraryType) case .library(.automatic): // Check if this is a system library product. if product.isSystemLibraryProduct { - try builder.makeSystemLibraryProduct(product) + try projectBuilder.makeSystemLibraryProduct(product) } else { // Otherwise, it is a regular library product. let libraryType = self.delegate.customLibraryType(product: product.underlying) ?? .automatic - try builder.makeLibraryProduct(product, type: libraryType) + try projectBuilder.makeLibraryProduct(product, type: libraryType) } case .executable, .test: - try builder.makeMainModuleProduct(product) + try projectBuilder.makeMainModuleProduct(product) case .plugin: - try builder.makePluginProduct(product) + try projectBuilder.makePluginProduct(product) case .snippet, .macro: break // TODO: Double-check what's going on here as we skip snippet modules too (rdar://147705448) } } + self.log(.debug, "Processing \(package.modules.count) modules:") + // For each of the **modules** in the package other than those that are the *main* module of a product // —— which we've already dealt with above —— we create a corresponding `PIFTarget` of the appropriate type. for module in self.package.modules { switch module.type { case .executable: - try builder.makeTestableExecutableSourceModule(module) + try projectBuilder.makeTestableExecutableSourceModule(module) case .snippet: // Already handled as a product. Note that snippets don't need testable modules. break case .library: - try builder.makeLibraryModule(module) + try projectBuilder.makeLibraryModule(module) case .systemModule: - try builder.makeSystemLibraryModule(module) + try projectBuilder.makeSystemLibraryModule(module) case .test: // Skip test module targets. @@ -461,17 +460,18 @@ public final class PackagePIFBuilder { break case .plugin: - try builder.makePluginModule(module) + try projectBuilder.makePluginModule(module) case .macro: - try builder.makeMacroModule(module) + try projectBuilder.makeMacroModule(module) } } - let customModulesAndProducts = try delegate.addCustomTargets(pifProject: builder.project) - builder.builtModulesAndProducts.append(contentsOf: customModulesAndProducts) + let customModulesAndProducts = try delegate.addCustomTargets(pifProject: projectBuilder.project) + projectBuilder.builtModulesAndProducts.append(contentsOf: customModulesAndProducts) - return builder.builtModulesAndProducts + self._pifProject = projectBuilder.project + return projectBuilder.builtModulesAndProducts } /// Configure the project-wide build settings. @@ -661,29 +661,4 @@ extension PackagePIFBuilder.LinkedPackageBinary { } } -extension ObservabilityMetadata { - public var sourceLocation: SourceLocation? { - get { - self[SourceLocationKey.self] - } - set { - self[SourceLocationKey.self] = newValue - } - } - - private enum SourceLocationKey: Key { - typealias Value = SourceLocation - } -} - -public struct SourceLocation: Sendable { - public let file: StaticString - public let line: UInt - - public init(_ file: StaticString, _ line: UInt) { - self.file = file - self.line = line - } -} - #endif diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift index 89d7d1f7b16..bc17d22ee15 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift @@ -39,7 +39,7 @@ extension PackagePIFProjectBuilder { // Create an executable PIF target in order to get specialization. let pluginTargetKeyPath = try self.project.addTarget { _ in ProjectModel.Target( - id: pluginModule.pifTargetGUID(), + id: pluginModule.pifTargetGUID, productType: .executable, name: pluginModule.name, productName: pluginModule.name @@ -47,7 +47,11 @@ extension PackagePIFProjectBuilder { } do { let pluginTarget = self.project[keyPath: pluginTargetKeyPath] - log(.debug, "Created \(pluginTarget.productType) '\(pluginTarget.id)' with name '\(pluginTarget.name)'") + log( + .debug, + "Created target '\(pluginTarget.id)' of type " + + "\(pluginTarget.productType) and name '\(pluginTarget.name)'" + ) } var buildSettings: ProjectModel.BuildSettings = self.package.underlying.packageBaseBuildSettings @@ -73,20 +77,20 @@ extension PackagePIFProjectBuilder { if let productDependency { self.project[keyPath: pluginTargetKeyPath].common.addDependency( - on: productDependency.pifTargetGUID(), + on: productDependency.pifTargetGUID, platformFilters: dependencyPlatformFilters ) - log(.debug, indent: 1, "Added dependency on product '\(productDependency.pifTargetGUID())'") + log(.debug, indent: 1, "Added dependency on product '\(productDependency.pifTargetGUID)'") } else { log( .debug, indent: 1, - "Could not find a build plugin product to depend on for target '\(moduleDependency.pifTargetGUID())'" + "Could not find a build plugin product to depend on for target '\(moduleDependency.pifTargetGUID)'" ) } case .library, .systemModule, .test, .binary, .plugin, .macro: - let dependencyGUID = moduleDependency.pifTargetGUID() + let dependencyGUID = moduleDependency.pifTargetGUID self.project[keyPath: pluginTargetKeyPath].common.addDependency( on: dependencyGUID, platformFilters: dependencyPlatformFilters @@ -104,7 +108,7 @@ extension PackagePIFProjectBuilder { product: productDependency.underlying, buildSettings: &buildSettings ) { - let dependencyGUID = productDependency.pifTargetGUID() + let dependencyGUID = productDependency.pifTargetGUID let dependencyPlatformFilters = packageConditions .toPlatformFilter(toolsVersion: self.package.manifest.toolsVersion) @@ -215,7 +219,7 @@ extension PackagePIFProjectBuilder { // MARK: - Source Modules - enum SourceModuleType { + enum SourceModuleType: String { case dynamicLibrary case staticLibrary case executable @@ -228,36 +232,36 @@ extension PackagePIFProjectBuilder { private mutating func buildSourceModule( _ sourceModule: PackageGraph.ResolvedModule, type desiredModuleType: SourceModuleType, - targetSuffix: TargetGUIDSuffix? = nil, + targetSuffix: TargetSuffix? = nil, addBuildToolPluginCommands: Bool = true, inputResourceBundleName: String? = nil ) throws -> (PackagePIFBuilder.ModuleOrProduct, resourceBundleName: String?) { precondition(sourceModule.isSourceModule) - let pifTargetName: String + let pifProductName: String let executableName: String let productType: ProjectModel.Target.ProductType switch desiredModuleType { case .dynamicLibrary: if pifBuilder.createDylibForDynamicProducts { // We are re-using this default for dynamic targets as well. - pifTargetName = "lib\(sourceModule.name).dylib" - executableName = pifTargetName + pifProductName = "lib\(sourceModule.name).dylib" + executableName = pifProductName productType = .dynamicLibrary } else { - pifTargetName = sourceModule.name + ".framework" + pifProductName = sourceModule.name + ".framework" executableName = sourceModule.name productType = .framework } case .staticLibrary, .executable: - pifTargetName = "\(sourceModule.name).o" - executableName = pifTargetName + pifProductName = "\(sourceModule.name).o" + executableName = pifProductName productType = .objectFile case .macro: - pifTargetName = sourceModule.name - executableName = pifTargetName + pifProductName = sourceModule.name + executableName = pifProductName productType = .hostBuildTool } @@ -276,17 +280,17 @@ extension PackagePIFProjectBuilder { ProjectModel.Target( id: sourceModule.pifTargetGUID(suffix: targetSuffix), productType: productType, - name: sourceModule.name, - productName: pifTargetName, + name: "\(sourceModule.name)", + productName: pifProductName, approvedByUser: approvedByUser ) } do { - let sourceModuleTarget = self.project[keyPath: sourceModuleTargetKeyPath] + let sourceModule = self.project[keyPath: sourceModuleTargetKeyPath] log( .debug, - "Created \(sourceModuleTarget.productType) '\(sourceModuleTarget.id)' " + - "with name '\(sourceModuleTarget.name)' and product name '\(sourceModuleTarget.productName)'" + "Created target '\(sourceModule.id)' of type '\(sourceModule.productType)' " + + "with name '\(sourceModule.name)' and product name '\(sourceModule.productName)'" ) } @@ -507,7 +511,7 @@ extension PackagePIFProjectBuilder { if enableDuplicateLinkageCulling { baselineOTHER_LDFLAGS = [ "-Wl,-no_warn_duplicate_libraries", - "$(inherited)", + "$(inherited)" ] } else { baselineOTHER_LDFLAGS = ["$(inherited)"] @@ -517,7 +521,7 @@ extension PackagePIFProjectBuilder { log( .debug, indent: 1, - "Added '\(String(describing: impartedSettings[.OTHER_LDFLAGS]))' to imparted OTHER_LDFLAGS" + "Added '\(impartedSettings[.OTHER_LDFLAGS]!)' to imparted OTHER_LDFLAGS" ) // This should be only for dynamic targets, but that isn't possible today. @@ -527,7 +531,7 @@ extension PackagePIFProjectBuilder { log( .debug, indent: 1, - "Added '\(String(describing: impartedSettings[.FRAMEWORK_SEARCH_PATHS]))' to imparted FRAMEWORK_SEARCH_PATHS" + "Added '\(impartedSettings[.FRAMEWORK_SEARCH_PATHS]!)' to imparted FRAMEWORK_SEARCH_PATHS" ) // Set the appropriate language versions. @@ -625,16 +629,16 @@ extension PackagePIFProjectBuilder { .productRepresentingDependencyOfBuildPlugin(in: moduleMainProducts) { self.project[keyPath: sourceModuleTargetKeyPath].common.addDependency( - on: product.pifTargetGUID(), + on: product.pifTargetGUID, platformFilters: dependencyPlatformFilters, linkProduct: false ) - log(.debug, indent: 1, "Added dependency on product '\(product.pifTargetGUID())'") + log(.debug, indent: 1, "Added dependency on product '\(product.pifTargetGUID)'") } else { log( .debug, indent: 1, - "Could not find a build plugin product to depend on for target '\(moduleDependency.pifTargetGUID())'" + "Could not find a build plugin product to depend on for target '\(moduleDependency.pifTargetGUID)'" ) } @@ -666,7 +670,7 @@ extension PackagePIFProjectBuilder { log(.debug, indent: 1, "Added use of binary library '\(moduleDependency.path)'") case .plugin: - let dependencyGUID = moduleDependency.pifTargetGUID() + let dependencyGUID = moduleDependency.pifTargetGUID self.project[keyPath: sourceModuleTargetKeyPath].common.addDependency( on: dependencyGUID, platformFilters: dependencyPlatformFilters, @@ -676,14 +680,14 @@ extension PackagePIFProjectBuilder { case .library, .test, .macro, .systemModule: self.project[keyPath: sourceModuleTargetKeyPath].common.addDependency( - on: moduleDependency.pifTargetGUID(), + on: moduleDependency.pifTargetGUID, platformFilters: dependencyPlatformFilters, linkProduct: shouldLinkProduct ) log( .debug, indent: 1, - "Added \(shouldLinkProduct ? "linked " : "")dependency on target '\(moduleDependency.pifTargetGUID())'" + "Added \(shouldLinkProduct ? "linked " : "")dependency on target '\(moduleDependency.pifTargetGUID)'" ) } @@ -702,7 +706,7 @@ extension PackagePIFProjectBuilder { let shouldLinkProduct = shouldLinkProduct && productDependency.isLinkable self.project[keyPath: sourceModuleTargetKeyPath].common.addDependency( - on: productDependency.pifTargetGUID(), + on: productDependency.pifTargetGUID, platformFilters: dependencyPlatformFilters, linkProduct: shouldLinkProduct ) @@ -810,7 +814,7 @@ extension PackagePIFProjectBuilder { // Create an aggregate PIF target (which doesn't have an actual product). let systemLibraryTargetKeyPath = try self.project.addAggregateTarget { _ in ProjectModel.AggregateTarget( - id: resolvedSystemLibrary.pifTargetGUID(), + id: resolvedSystemLibrary.pifTargetGUID, name: resolvedSystemLibrary.name ) } @@ -818,7 +822,7 @@ extension PackagePIFProjectBuilder { let systemLibraryTarget = self.project[keyPath: systemLibraryTargetKeyPath] log( .debug, - "Created \(type(of: systemLibraryTarget)) '\(systemLibraryTarget.id)' with name '\(systemLibraryTarget.name)'" + "Created aggregate target '\(systemLibraryTarget.id)' with name '\(systemLibraryTarget.name)'" ) } diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift index e44f2b099ff..5bf30c1397a 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift @@ -74,9 +74,9 @@ extension PackagePIFProjectBuilder { // It's not a library product, so create a regular PIF target of the appropriate product type. let mainModuleTargetKeyPath = try self.project.addTarget { _ in ProjectModel.Target( - id: product.pifTargetGUID(), + id: product.pifTargetGUID, productType: pifProductType, - name: product.name, + name: product.targetName(), productName: product.name ) } @@ -84,8 +84,8 @@ extension PackagePIFProjectBuilder { let mainModuleTarget = self.project[keyPath: mainModuleTargetKeyPath] log( .debug, - "Created \(mainModuleTarget.productType)) '\(mainModuleTarget.id)' " + - "with name '\(mainModuleTarget.name)' and product name '\(mainModuleTarget.productName)'" + "Created target '\(mainModuleTarget.id)' of type '\(mainModuleTarget.productType)' " + + "with name '\(mainModuleTarget.name)' and product name '\(mainModuleTarget.productName)'" ) } @@ -93,7 +93,7 @@ extension PackagePIFProjectBuilder { // a main-module product but, for diagnostic purposes, we warn about any that we do come across. if product.otherModules.hasContent { let otherModuleNames = product.otherModules.map(\.name).joined(separator: ",") - log(.debug, ".. warning: ignored unexpected other module targets \(otherModuleNames)") + log(.debug, indent: 1, "Warning: ignored unexpected other module targets \(otherModuleNames)") } // Deal with any generated source files or resource files. @@ -124,7 +124,7 @@ extension PackagePIFProjectBuilder { settings[.LD_RUNPATH_SEARCH_PATHS] = [ "@loader_path/Frameworks", "@loader_path/../Frameworks", - "$(inherited)", + "$(inherited)" ] settings[.GENERATE_INFOPLIST_FILE] = "YES" settings[.SKIP_INSTALL] = "NO" @@ -342,7 +342,7 @@ extension PackagePIFProjectBuilder { log(.debug, indent: 1, "Added use of binary library '\(moduleDependency.path)'") case .plugin: - let dependencyId = moduleDependency.pifTargetGUID() + let dependencyId = moduleDependency.pifTargetGUID self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: dependencyId, platformFilters: packageConditions @@ -352,7 +352,7 @@ extension PackagePIFProjectBuilder { log(.debug, indent: 1, "Added use of plugin target '\(dependencyId)'") case .macro: - let dependencyId = moduleDependency.pifTargetGUID() + let dependencyId = moduleDependency.pifTargetGUID self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: dependencyId, platformFilters: packageConditions @@ -398,7 +398,7 @@ extension PackagePIFProjectBuilder { // (i.e., we infuse the product's main module target into the one for the product itself). let productDependency = modulesGraph.allProducts.only { $0.name == moduleDependency.name } if let productDependency { - let productDependencyGUID = productDependency.pifTargetGUID() + let productDependencyGUID = productDependency.pifTargetGUID self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: productDependencyGUID, platformFilters: packageConditions @@ -423,7 +423,7 @@ extension PackagePIFProjectBuilder { case .library, .systemModule, .test: let shouldLinkProduct = moduleDependency.type != .systemModule - let dependencyGUID = moduleDependency.pifTargetGUID() + let dependencyGUID = moduleDependency.pifTargetGUID self.project[keyPath: mainModuleTargetKeyPath].common.addDependency( on: dependencyGUID, platformFilters: packageConditions @@ -511,14 +511,14 @@ extension PackagePIFProjectBuilder { if !pifBuilder.delegate.shouldSuppressProductDependency(product: product.underlying, buildSettings: &settings) { let shouldLinkProduct = isLinkable self.project[keyPath: targetKeyPath].common.addDependency( - on: product.pifTargetGUID(), + on: product.pifTargetGUID, platformFilters: packageConditions.toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: shouldLinkProduct ) log( .debug, indent: 1, - "Added \(shouldLinkProduct ? "linked " : "")dependency on product '\(product.pifTargetGUID()))'" + "Added \(shouldLinkProduct ? "linked " : "")dependency on product '\(product.pifTargetGUID)'" ) } } @@ -570,21 +570,21 @@ extension PackagePIFProjectBuilder { private mutating func buildLibraryProduct( _ product: PackageGraph.ResolvedProduct, type desiredProductType: ProductType.LibraryType, - targetSuffix: TargetGUIDSuffix? = nil, + targetSuffix: TargetSuffix? = nil, embedResources: Bool ) throws -> PackagePIFBuilder.ModuleOrProduct { precondition(product.type.isLibrary) // FIXME: Cleanup this mess with - let pifTargetProductName: String + let pifProductName: String let executableName: String let productType: ProjectModel.Target.ProductType if desiredProductType == .dynamic { if pifBuilder.createDylibForDynamicProducts { - pifTargetProductName = "lib\(product.name).dylib" - executableName = pifTargetProductName + pifProductName = "lib\(product.name).dylib" + executableName = pifProductName productType = .dynamicLibrary } else { // If a product is explicitly declared dynamic, we preserve its name, @@ -600,12 +600,12 @@ extension PackagePIFProjectBuilder { } else { executableName = PackagePIFBuilder.computePackageProductFrameworkName(productName: product.name) } - pifTargetProductName = "\(executableName).framework" + pifProductName = "\(executableName).framework" productType = .framework } } else { - pifTargetProductName = "lib\(product.name).a" - executableName = pifTargetProductName + pifProductName = "lib\(product.name).a" + executableName = pifProductName productType = .packageProduct } @@ -617,8 +617,8 @@ extension PackagePIFProjectBuilder { ProjectModel.Target( id: product.pifTargetGUID(suffix: targetSuffix), productType: productType, - name: product.name, - productName: pifTargetProductName + name: product.targetName(suffix: targetSuffix), + productName: pifProductName ) } do { @@ -626,7 +626,7 @@ extension PackagePIFProjectBuilder { log( .debug, "Created target '\(librayTarget.id)' of type '\(librayTarget.productType)' with " + - "name '\(librayTarget.name)' and product name '\(librayTarget.productName)'" + "name '\(librayTarget.name)' and product name '\(librayTarget.productName)'" ) } @@ -647,11 +647,11 @@ extension PackagePIFProjectBuilder { // SwiftBuild won't actually link them, but will instead impart linkage to any clients that // link against the package product. self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( - on: module.pifTargetGUID(), + on: module.pifTargetGUID, platformFilters: [], linkProduct: true ) - log(.debug, indent: 1, "Added linked dependency on target '\(module.pifTargetGUID())'") + log(.debug, indent: 1, "Added linked dependency on target '\(module.pifTargetGUID)'") } for module in product.modules where module.underlying.isSourceModule && module.resources.hasContent { @@ -686,7 +686,7 @@ extension PackagePIFProjectBuilder { if desiredProductType == .dynamic { settings.configureDynamicSettings( productName: product.name, - targetName: product.targetNameForProduct(), + targetName: product.targetName(), executableName: executableName, packageIdentity: package.identity, packageName: package.identity.c99name, @@ -746,7 +746,7 @@ extension PackagePIFProjectBuilder { } if moduleDependency.type == .plugin { - let dependencyId = moduleDependency.pifTargetGUID() + let dependencyId = moduleDependency.pifTargetGUID self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( on: dependencyId, platformFilters: packageConditions @@ -769,28 +769,28 @@ extension PackagePIFProjectBuilder { .productRepresentingDependencyOfBuildPlugin(in: mainModuleProducts) { self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( - on: product.pifTargetGUID(), + on: product.pifTargetGUID, platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: false ) - log(.debug, indent: 1, "Added dependency on product '\(product.pifTargetGUID())'") + log(.debug, indent: 1, "Added dependency on product '\(product.pifTargetGUID)'") return } else { log( .debug, indent: 1, - "Could not find a build plugin product to depend on for target '\(product.pifTargetGUID()))'" + "Could not find a build plugin product to depend on for target '\(product.pifTargetGUID)'" ) } } self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( - on: moduleDependency.pifTargetGUID(), + on: moduleDependency.pifTargetGUID, platformFilters: packageConditions.toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: true ) - log(.debug, indent: 1, "Added linked dependency on target '\(moduleDependency.pifTargetGUID()))'") + log(.debug, indent: 1, "Added linked dependency on target '\(moduleDependency.pifTargetGUID)'") case .product(let productDependency, let packageConditions): // Do not add a dependency for binary-only executable products since they are not part of the build. @@ -804,7 +804,7 @@ extension PackagePIFProjectBuilder { ) { let shouldLinkProduct = productDependency.isLinkable self.project[keyPath: librayUmbrellaTargetKeyPath].common.addDependency( - on: productDependency.pifTargetGUID(), + on: productDependency.pifTargetGUID, platformFilters: packageConditions .toPlatformFilter(toolsVersion: package.manifest.toolsVersion), linkProduct: shouldLinkProduct @@ -812,7 +812,7 @@ extension PackagePIFProjectBuilder { log( .debug, indent: 1, - "Added \(shouldLinkProduct ? "linked" : "") dependency on product '\(productDependency.pifTargetGUID()))'" + "Added \(shouldLinkProduct ? "linked" : "") dependency on product '\(productDependency.pifTargetGUID)'" ) } } @@ -878,9 +878,9 @@ extension PackagePIFProjectBuilder { let systemLibraryTargetKeyPath = try self.project.addTarget { _ in ProjectModel.Target( - id: product.pifTargetGUID(), + id: product.pifTargetGUID, productType: .packageProduct, - name: product.name, + name: product.targetName(), productName: product.name ) } @@ -889,7 +889,7 @@ extension PackagePIFProjectBuilder { log( .debug, "Created target '\(systemLibraryTarget.id)' of type '\(systemLibraryTarget.productType)' " + - "with name '\(systemLibraryTarget.name)' and product name '\(systemLibraryTarget.productName)'" + "with name '\(systemLibraryTarget.name)' and product name '\(systemLibraryTarget.productName)'" ) } @@ -902,7 +902,7 @@ extension PackagePIFProjectBuilder { } self.project[keyPath: systemLibraryTargetKeyPath].common.addDependency( - on: product.systemModule!.pifTargetGUID(), + on: product.systemModule!.pifTargetGUID, platformFilters: [], linkProduct: false ) @@ -929,8 +929,8 @@ extension PackagePIFProjectBuilder { let pluginTargetKeyPath = try self.project.addAggregateTarget { _ in ProjectModel.AggregateTarget( - id: pluginProduct.pifTargetGUID(), - name: pluginProduct.name + id: pluginProduct.pifTargetGUID, + name: pluginProduct.targetName() ) } do { @@ -948,7 +948,7 @@ extension PackagePIFProjectBuilder { for pluginModule in pluginProduct.pluginModules! { self.project[keyPath: pluginTargetKeyPath].common.addDependency( - on: pluginModule.pifTargetGUID(), + on: pluginModule.pifTargetGUID, platformFilters: [] ) } diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift index 149d97e84ab..bd7a36b4895 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift @@ -71,13 +71,18 @@ struct PackagePIFProjectBuilder { func log( _ severity: Diagnostic.Severity, - indent: Int = 0, + indent: UInt = 0, _ message: String, sourceFile: StaticString = #fileID, sourceLine: UInt = #line ) { - let levelPrefix = String(repeating: " ", count: indent) - self.pifBuilder.log(severity, levelPrefix + message, sourceFile: sourceFile, sourceLine: sourceLine) + self.pifBuilder.observabilityScope.logPIF( + severity, + indent: indent, + message, + sourceFile: sourceFile, + sourceLine: sourceLine + ) } init(createForPackage package: PackageGraph.ResolvedPackage, builder: PackagePIFBuilder) { @@ -188,7 +193,7 @@ struct PackagePIFProjectBuilder { for pluginModule in module.pluginsAppliedToModule { self.project[keyPath: resourcesTargetKeyPath].common.addDependency( - on: pluginModule.pifTargetGUID(), + on: pluginModule.pifTargetGUID, platformFilters: [], linkProduct: false ) @@ -197,8 +202,8 @@ struct PackagePIFProjectBuilder { self.log( .debug, indent: 1, - "Created \(type(of: resourcesTarget)) '\(resourcesTarget.id)' of type '\(resourcesTarget.productType)' " + - "with name '\(resourcesTarget.name)' and product name '\(resourcesTarget.productName)'" + "Created target '\(resourcesTarget.id)' of type '\(resourcesTarget.productType)' " + + "with name '\(resourcesTarget.name)' and product name '\(resourcesTarget.productName)'" ) var settings: ProjectModel.BuildSettings = self.package.underlying.packageBaseBuildSettings diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 423afcd0e04..449cde066fc 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -7011,8 +7011,11 @@ class BuildPlanSwiftBuildTests: BuildPlanTestCase { #if os(Linux) if FileManager.default.contents(atPath: "/etc/system-release").map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false { - throw XCTSkip("Skipping SwiftBuild testing on Amazon Linux because of platform issues.") + throw XCTSkip("Skipping Swift Build testing on Amazon Linux because of platform issues.") } + // Linking error: "/usr/bin/ld.gold: fatal error: -pie and -static are incompatible". + // Tracked by GitHub issue: https://github.com/swiftlang/swift-package-manager/issues/8499 + throw XCTSkip("Skipping Swift Build testing on Linux because of linking issues.") #endif try await super.testPackageNameFlag() diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index 20cfedce6d8..e4c607eff96 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -862,13 +862,18 @@ class BuildCommandSwiftBuildTests: BuildCommandTestCases { try await fixture(name: "Miscellaneous/ParseableInterfaces") { fixturePath in do { let result = try await build(["--enable-parseable-module-interfaces"], packagePath: fixturePath) - XCTAssertMatch(result.moduleContents, [.regex(#"A\.swiftmodule\/.*\.swiftinterface"#)]) - XCTAssertMatch(result.moduleContents, [.regex(#"B\.swiftmodule\/.*\.swiftmodule"#)]) + XCTAssertMatch(result.moduleContents, [.regex(#"A[.]swiftmodule[/].*[.]swiftinterface"#)]) + XCTAssertMatch(result.moduleContents, [.regex(#"B[.]swiftmodule[/].*[.]swiftmodule"#)]) } catch SwiftPMError.executionFailure(_, _, let stderr) { XCTFail(stderr) } } } + + override func testAutomaticParseableInterfacesWithLibraryEvolution() async throws { + throw XCTSkip("SWBINTTODO: Test failed because of missing 'A.swiftmodule/*.swiftinterface' files") + // TODO: We still need to override this test just like we did for `testParseableInterfaces` above. + } override func testBinSymlink() async throws { try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in @@ -900,17 +905,6 @@ class BuildCommandSwiftBuildTests: BuildCommandTestCases { #endif } - override func testAutomaticParseableInterfacesWithLibraryEvolution() async throws { - try await fixture(name: "Miscellaneous/LibraryEvolution") { fixturePath in - do { - let result = try await build([], packagePath: fixturePath) - XCTAssertMatch( - result.moduleContents, [.regex(#"A\.swiftmodule\/.*\.swiftinterface"#)]) - XCTAssertMatch(result.moduleContents, [.regex(#"B\.swiftmodule\/.*\.swiftmodule"#)]) - } - } - } - override func testImportOfMissedDepWarning() async throws { throw XCTSkip("SWBINTTODO: Test fails because the warning message regarding missing imports is expected to be more verbose and actionable at the SwiftPM level with mention of the involved targets. This needs to be investigated. See case targetDiagnostic(TargetDiagnosticInfo) as a message type that may help.") } From 2c4c785d77cb38c40f4b000a8d46d4e6e6e0c981 Mon Sep 17 00:00:00 2001 From: Paulo Mattos Date: Thu, 17 Apr 2025 01:54:04 -0700 Subject: [PATCH 27/33] Tests: Fix 22 warning in BuildCommandTests (#8509) ### Motivation: Reduce noise from solvable warnings. ### Modifications: We should `throw XCTSkip("...")` but `try XCTSkipIf(true, "...")`. The BuildCommandTests was mixing both (i.e., `try XCTSkip("...")`), making the test a nop actually. From 9fb4efb7521e10254e5cb50c167d38576cdd61ba Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Thu, 17 Apr 2025 00:14:16 -0700 Subject: [PATCH 28/33] Add an .editorconfig file This encodes our indentation size and line ending preferences and such. This is an exact copy of the .editorconfig in swift-build, which uses the same conventions. --- .editorconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..ac8a0df1bb3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +xcode_trim_whitespace_on_empty_lines = true + +[*.{yml,yaml}] +indent_size = 2 From 95ce2a3b1757db1d1d584968efada23065e3f4c3 Mon Sep 17 00:00:00 2001 From: Shawn Hyam Date: Fri, 18 Apr 2025 01:37:32 -0400 Subject: [PATCH 29/33] Make sure the workspace state is saved after adding/removing artifacts. (#8475) --- Sources/Basics/CMakeLists.txt | 1 + .../Workspace/Workspace+BinaryArtifacts.swift | 65 ++++++++++++++----- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/Sources/Basics/CMakeLists.txt b/Sources/Basics/CMakeLists.txt index 1b56ad2d5e3..1ee88ac29d8 100644 --- a/Sources/Basics/CMakeLists.txt +++ b/Sources/Basics/CMakeLists.txt @@ -24,6 +24,7 @@ add_library(Basics Concurrency/ThreadSafeArrayStore.swift Concurrency/ThreadSafeBox.swift Concurrency/ThreadSafeKeyValueStore.swift + Concurrency/ThrowingDefer.swift Concurrency/TokenBucket.swift DispatchTimeInterval+Extensions.swift Environment/Environment.swift diff --git a/Sources/Workspace/Workspace+BinaryArtifacts.swift b/Sources/Workspace/Workspace+BinaryArtifacts.swift index 3fb72f3b984..a99f895d09d 100644 --- a/Sources/Workspace/Workspace+BinaryArtifacts.swift +++ b/Sources/Workspace/Workspace+BinaryArtifacts.swift @@ -155,7 +155,7 @@ extension Workspace { if !indexFiles.isEmpty { let errors = ThreadSafeArrayStore() - zipArtifacts.append(contentsOf: try await withThrowingTaskGroup( + try await zipArtifacts.append(contentsOf: withThrowingTaskGroup( of: RemoteArtifact?.self, returning: [RemoteArtifact].self ) { group in @@ -164,7 +164,8 @@ extension Workspace { group.addTask { var request = HTTPClient.Request(method: .get, url: indexFile.url) request.options.validResponseCodes = [200] - request.options.authorizationProvider = self.authorizationProvider?.httpAuthorizationHeader(for:) + request.options.authorizationProvider = self.authorizationProvider? + .httpAuthorizationHeader(for:) do { let response = try await self.httpClient.execute(request) guard let body = response.body else { @@ -227,7 +228,10 @@ extension Workspace { group.addTask { () -> ManagedArtifact? in let destinationDirectory = artifactsDirectory .appending(components: [artifact.packageRef.identity.description, artifact.targetName]) - guard observabilityScope.trap({ try fileSystem.createDirectory(destinationDirectory, recursive: true) }) + guard observabilityScope.trap({ try fileSystem.createDirectory( + destinationDirectory, + recursive: true + ) }) else { return nil } @@ -295,7 +299,8 @@ extension Workspace { return nil } - observabilityScope.emit(debug: "extracting \(archivePath) to \(tempExtractionDirectory)") + observabilityScope + .emit(debug: "extracting \(archivePath) to \(tempExtractionDirectory)") do { try await self.archiver.extract( from: archivePath, @@ -326,7 +331,8 @@ extension Workspace { debug: "no first level component stripping needed for \(tempExtractionDirectory)" ) } - let content = try self.fileSystem.getDirectoryContents(tempExtractionDirectory) + let content = try self.fileSystem + .getDirectoryContents(tempExtractionDirectory) // copy from temp location to actual location for file in content { let source = tempExtractionDirectory @@ -350,8 +356,8 @@ extension Workspace { observabilityScope: observabilityScope ) else { observabilityScope.emit(BinaryArtifactsManagerError.remoteArtifactNotFound( - artifactURL: artifact.url, - targetName: artifact.targetName + artifactURL: artifact.url, + targetName: artifact.targetName )) return nil } @@ -432,7 +438,7 @@ extension Workspace { artifactsDirectory: AbsolutePath, observabilityScope: ObservabilityScope ) async throws -> [ManagedArtifact] { - return try await withThrowingTaskGroup(of: ManagedArtifact?.self) { group in + try await withThrowingTaskGroup(of: ManagedArtifact?.self) { group in for artifact in artifacts { group.addTask { () -> ManagedArtifact? in let destinationDirectory = artifactsDirectory @@ -458,7 +464,9 @@ extension Workspace { acceptableExtensions: BinaryModule.Kind.allCases.map(\.fileExtension) ) { observabilityScope - .emit(debug: "stripping first level component from \(tempExtractionDirectory)") + .emit( + debug: "stripping first level component from \(tempExtractionDirectory)" + ) try self.fileSystem.stripFirstLevel(of: tempExtractionDirectory) } else { observabilityScope.emit( @@ -564,7 +572,7 @@ extension Workspace { artifact: RemoteArtifact, destination: AbsolutePath, observabilityScope: ObservabilityScope, - progress: @escaping @Sendable (Int64, Optional) -> Void + progress: @escaping @Sendable (Int64, Int64?) -> Void ) async throws -> Bool { // not using cache, download directly guard let cachePath = self.cachePath else { @@ -591,7 +599,8 @@ extension Workspace { let cachedArtifactPath = cachePath.appending(cacheKey) if self.fileSystem.exists(cachedArtifactPath) { - observabilityScope.emit(debug: "copying cached binary artifact for \(artifact.url) from \(cachedArtifactPath)") + observabilityScope + .emit(debug: "copying cached binary artifact for \(artifact.url) from \(cachedArtifactPath)") self.delegate?.willDownloadBinaryArtifact(from: artifact.url.absoluteString, fromCache: true) // copy from cache to destination @@ -600,7 +609,8 @@ extension Workspace { } // download to the cache - observabilityScope.emit(debug: "downloading binary artifact for \(artifact.url) to cache at \(cachedArtifactPath)") + observabilityScope + .emit(debug: "downloading binary artifact for \(artifact.url) to cache at \(cachedArtifactPath)") self.delegate?.willDownloadBinaryArtifact(from: artifact.url.absoluteString, fromCache: false) @@ -623,7 +633,7 @@ extension Workspace { artifact: RemoteArtifact, destination: AbsolutePath, observabilityScope: ObservabilityScope, - progress: @escaping @Sendable (Int64, Optional) -> Void + progress: @escaping @Sendable (Int64, Int64?) -> Void ) async throws { observabilityScope.emit(debug: "downloading \(artifact.url) to \(destination)") @@ -800,6 +810,26 @@ extension Workspace { manifests: DependencyManifests, addedOrUpdatedPackages: [PackageReference], observabilityScope: ObservabilityScope + ) async throws { + try await withAsyncThrowing { + try await self._updateBinaryArtifacts( + manifests: manifests, + addedOrUpdatedPackages: addedOrUpdatedPackages, + observabilityScope: observabilityScope + ) + } defer: { + // Make sure the workspace state is saved exactly once, even if the method exits early. + // Files may have been deleted, download, etc. and the state needs to reflect that. + await observabilityScope.trap { + try await self.state.save() + } + } + } + + private func _updateBinaryArtifacts( + manifests: DependencyManifests, + addedOrUpdatedPackages: [PackageReference], + observabilityScope: ObservabilityScope ) async throws { let manifestArtifacts = try self.binaryArtifactsManager.parseArtifacts( from: manifests, @@ -893,7 +923,10 @@ extension Workspace { // Remove the artifacts and directories which are not needed anymore. await observabilityScope.trap { for artifact in artifactsToRemove { - await state.artifacts.remove(packageIdentity: artifact.packageRef.identity, targetName: artifact.targetName) + await state.artifacts.remove( + packageIdentity: artifact.packageRef.identity, + targetName: artifact.targetName + ) if isAtArtifactsDirectory(artifact) { try fileSystem.removeFileTree(artifact.path) @@ -937,10 +970,6 @@ extension Workspace { throw Diagnostics.fatalError } - await observabilityScope.trap { - try await self.state.save() - } - func isAtArtifactsDirectory(_ artifact: ManagedArtifact) -> Bool { artifact.path.isDescendant(of: self.location.artifactsDirectory) } From b9383282a24670b1b35197965282c2f7e33e168e Mon Sep 17 00:00:00 2001 From: Doug Schaefer <167107236+dschaefer2@users.noreply.github.com> Date: Wed, 23 Apr 2025 02:42:05 -0400 Subject: [PATCH 30/33] build-using-self use a self build of swift-test instead (#8548) We are seeing hangs in swift test on Windows. Instead of build-using-self using the underlying toolchain, build swift-test and use that for the tests. When we make improvements to it to fix the underlying problem, we will be able to take advantage of that fix right away. --- .../IntegrationTestSupport/Helpers.swift | 2 +- Utilities/build-using-self | 39 +++++++++---------- Utilities/test-toolchain | 2 +- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/IntegrationTests/Sources/IntegrationTestSupport/Helpers.swift b/IntegrationTests/Sources/IntegrationTestSupport/Helpers.swift index 80f0963a563..eb59b855742 100644 --- a/IntegrationTests/Sources/IntegrationTestSupport/Helpers.swift +++ b/IntegrationTests/Sources/IntegrationTestSupport/Helpers.swift @@ -106,7 +106,7 @@ public let lldb: AbsolutePath = { }() public let swiftpmBinaryDirectory: AbsolutePath = { - if let environmentPath = ProcessInfo.processInfo.environment["SWIFTPM_BIN_DIR"] { + if let environmentPath = ProcessInfo.processInfo.environment["SWIFTPM_CUSTOM_BIN_DIR"] { return try! AbsolutePath(validating: environmentPath) } diff --git a/Utilities/build-using-self b/Utilities/build-using-self index 9ccf8706792..3b59152b9ba 100755 --- a/Utilities/build-using-self +++ b/Utilities/build-using-self @@ -102,8 +102,8 @@ def is_on_darwin() -> bool: def set_environment(*, swiftpm_bin_dir: pathlib.Path,) -> None: os.environ["SWIFTCI_IS_SELF_HOSTED"] = "1" - # Set the SWIFTPM_BIN_DIR path - os.environ["SWIFTPM_BIN_DIR"] = str(swiftpm_bin_dir) + # Set the SWIFTPM_CUSTOM_BIN_DIR path + os.environ["SWIFTPM_CUSTOM_BIN_DIR"] = str(swiftpm_bin_dir) # Ensure SDKROOT is configure if is_on_darwin(): @@ -133,6 +133,7 @@ def run_bootstrap(swiftpm_bin_dir: pathlib.Path) -> None: def main() -> None: args = get_arguments() + ignore = "-Xlinker /ignore:4217" if os.name == "nt" else "" logging.getLogger().setLevel(logging.DEBUG if args.is_verbose else logging.INFO) logging.debug("Args: %r", args) @@ -144,31 +145,27 @@ def main() -> None: shlex.split("swift --version"), ) - # call( - # shlex.split("swift package reset"), - # ) call( shlex.split("swift package update"), ) call( - shlex.split(f"swift build --configuration {args.config}"), - ) - swift_testing_arg= "--enable-swift-testing" if args.enable_swift_testing else "" - xctest_arg= "--enable-xctest" if args.enable_swift_testing else "" - call( - shlex.split(f"swift test --configuration {args.config} --parallel {swift_testing_arg} {xctest_arg}"), + shlex.split(f"swift build --configuration {args.config} {ignore}"), ) - with change_directory(REPO_ROOT_PATH / "IntegrationTests"): - call( - shlex.split("swift package update"), - ) - call( - shlex.split( - f"{swiftpm_bin_dir / 'swift-test'} --parallel", - posix=(os.name == "posix"), # must be set correctly, otherwhsie shlex.split("C:\\Foo\\bar") become ['CFoobar'] - ), - ) + if os.name != "nt": # turn off for Windows until we get the hang resolved + swift_testing_arg= "--enable-swift-testing" if args.enable_swift_testing else "" + xctest_arg= "--enable-xctest" if args.enable_swift_testing else "" + call( + shlex.split(f"swift run swift-test --configuration {args.config} --parallel {swift_testing_arg} {xctest_arg} --scratch-path .test {ignore}"), + ) + + integration_test_dir = REPO_ROOT_PATH / "IntegrationTests" + call( + shlex.split(f"swift package --package-path {integration_test_dir} update"), + ) + call( + shlex.split(f"swift run swift-test --package-path {integration_test_dir} --parallel {ignore}"), + ) if is_on_darwin(): run_bootstrap(swiftpm_bin_dir=swiftpm_bin_dir) diff --git a/Utilities/test-toolchain b/Utilities/test-toolchain index 2eb878cea3a..47eae638667 100755 --- a/Utilities/test-toolchain +++ b/Utilities/test-toolchain @@ -128,7 +128,7 @@ def get_env(args): if args.lldb_path: env['LLDB_PATH'] = args.lldb_path if args.swiftpm_bin_dir: - env["SWIFTPM_BIN_DIR"] = args.swiftpm_bin_dir + env["SWIFTPM_CUSTOM_BIN_DIR"] = args.swiftpm_bin_dir return env From 2bc3390f81e021646ab2cfcf3ceff330bc1b2333 Mon Sep 17 00:00:00 2001 From: "Bassam (Sam) Khouri" Date: Wed, 23 Apr 2025 03:03:52 -0400 Subject: [PATCH 31/33] Disable failing SwiftBuild test on AmazonLinux 2 (#8546) A SwiftBuild test is currently failing on Amazon Linux 2. Disable this test for now and link it to a GitHub issue. --- Tests/CommandsTests/BuildCommandTests.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index e4c607eff96..ca39fc6bbce 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -859,6 +859,12 @@ class BuildCommandSwiftBuildTests: BuildCommandTestCases { } override func testParseableInterfaces() async throws { + #if os(Linux) + if FileManager.default.contents(atPath: "/etc/system-release") + .map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false { + throw XCTSkip("https://github.com/swiftlang/swift-package-manager/issues/8545: Test currently fails on Amazon Linux 2") + } + #endif try await fixture(name: "Miscellaneous/ParseableInterfaces") { fixturePath in do { let result = try await build(["--enable-parseable-module-interfaces"], packagePath: fixturePath) From ffdc0c026c0541f5af8b391489705e24fba38788 Mon Sep 17 00:00:00 2001 From: Paulo Mattos Date: Wed, 23 Apr 2025 01:38:16 -0700 Subject: [PATCH 32/33] Tests: Run packageInitExecutable test on all platforms for native build system (#8527) ### Motivation: Increase code coverage for our integration tests. This is an issue I introduced in #8454 while ignoring `--build-system swiftbuild` issues on Windows (i.e., actually this specific issue #8514). I accidentally skipped the packageInitExecutable test for `--build-system native` as well (which should be green). This PR fixes that. ### Modifications: Swift Testing currently doesn't support combining *conditional traits* (e.g.,`skipHostOS(.windows)`) with *parametric tests*. So the 2nd best approach is to split the tests as I just did. --- .../Tests/IntegrationTests/SwiftPMTests.swift | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift index 770d046ee14..3f673e5509c 100644 --- a/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift +++ b/IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift @@ -75,7 +75,15 @@ private struct SwiftPMTests { } @Test( - .requireHostOS(.windows, when: false), + .requireThreadSafeWorkingDirectory, + arguments: [BuildSystemProvider.native] + ) + func packageInitExecutable(_ buildSystemProvider: BuildSystemProvider) throws { + try _packageInitExecutable(buildSystemProvider) + } + + @Test( + .skipHostOS(.windows), .requireThreadSafeWorkingDirectory, .bug( "https://github.com/swiftlang/swift-package-manager/issues/8416", @@ -85,30 +93,31 @@ private struct SwiftPMTests { "https://github.com/swiftlang/swift-package-manager/issues/8514", "[Windows] Integration test SwiftPMTests.packageInitExecutable with --build-system swiftbuild is skipped" ), - arguments: BuildSystemProvider.allCases + arguments: [BuildSystemProvider.swiftbuild] ) - func packageInitExecutable(_ buildSystemProvider: BuildSystemProvider) throws { - // Executable - do { - try withTemporaryDirectory { tmpDir in - let packagePath = tmpDir.appending(component: "foo") - try localFileSystem.createDirectory(packagePath) - try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "executable") - try sh(swiftBuild, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue) - - try withKnownIssue("Error while loading shared libraries: libswiftCore.so: cannot open shared object file: No such file or directory") { - // The 'native' build system uses 'swiftc' as the linker driver, which adds an RUNPATH to the swift runtime libraries in the SDK. - // 'swiftbuild' directly calls clang, which does not add the extra RUNPATH, so runtime libraries cannot be found. - let (stdout, stderr) = try sh( - swiftRun, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue - ) - #expect(!stderr.contains("error:")) - #expect(stdout.contains("Hello, world!")) - } when: { - buildSystemProvider == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux - } + func packageInitExecutablSkipWindows(_ buildSystemProvider: BuildSystemProvider) throws { + try _packageInitExecutable(buildSystemProvider) + } + + private func _packageInitExecutable(_ buildSystemProvider: BuildSystemProvider) throws { + try withTemporaryDirectory { tmpDir in + let packagePath = tmpDir.appending(component: "foo") + try localFileSystem.createDirectory(packagePath) + try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "executable") + try sh(swiftBuild, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue) + + try withKnownIssue("Error while loading shared libraries: libswiftCore.so: cannot open shared object file: No such file or directory") { + // The 'native' build system uses 'swiftc' as the linker driver, which adds an RUNPATH to the swift runtime libraries in the SDK. + // 'swiftbuild' directly calls clang, which does not add the extra RUNPATH, so runtime libraries cannot be found. + let (stdout, stderr) = try sh( + swiftRun, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue + ) + #expect(!stderr.contains("error:")) + #expect(stdout.contains("Hello, world!")) + } when: { + buildSystemProvider == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux + } } - } } @Test( From 6b256f36d86bc134575940f1aa2009133594d057 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Wed, 23 Apr 2025 11:30:55 -0400 Subject: [PATCH 33/33] Adopt MemberImportVisibility (#8525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopt the experimental feature MemberImportVisibility. ### Motivation: Due to a weakness in the existing Swift language implementation, a public extension declared in a library module can be accessible to source code that doesn’t directly import that module. ### Modifications: Adopt MemberImportVisibility on all targets and adjust imports to be explicit. There were a few places where ambiguities were exposed. The most apparent one is the ambiguity between Basics.AbsolutePath and TSCBasics.AbsolutePath. In this case they're functionally equivalent since the Basics version just type aliases the TSCBasics version. There were a few others where identically named methods were defined in both a SwiftPM module and an imported dependency. ### Result: SwiftPM compiles with no code included without a corresponding explicit import. --- Package.swift | 81 ++++++++----- Sources/Basics/AuthorizationProvider.swift | 1 + .../Collections/Dictionary+Extensions.swift | 2 + .../ThrottledProgressAnimation.swift | 1 + Sources/Basics/SQLite.swift | 1 + Sources/Basics/Triple+Basics.swift | 1 + .../ProductBuildDescription.swift | 3 +- .../SwiftModuleBuildDescription.swift | 1 + .../LLBuildManifestBuilder+Clang.swift | 1 + .../LLBuildManifestBuilder+Product.swift | 2 + .../LLBuildManifestBuilder+Resources.swift | 2 + .../LLBuildManifestBuilder+Swift.swift | 8 +- Sources/Build/BuildPlan/BuildPlan+Clang.swift | 5 + Sources/Build/BuildPlan/BuildPlan+Swift.swift | 4 + Sources/Build/BuildPlan/BuildPlan.swift | 21 ++-- Sources/Build/LLBuildDescription.swift | 1 + Sources/Build/SwiftCompilerOutputParser.swift | 1 + .../Commands/PackageCommands/APIDiff.swift | 2 + .../PackageCommands/AddDependency.swift | 2 + .../Commands/PackageCommands/AddProduct.swift | 2 + .../Commands/PackageCommands/AddTarget.swift | 2 + .../PackageCommands/AddTargetDependency.swift | 2 + .../PackageCommands/ArchiveSource.swift | 2 + .../PackageCommands/CompletionCommand.swift | 3 + Sources/Commands/PackageCommands/Config.swift | 1 + .../Commands/PackageCommands/Describe.swift | 2 + .../PackageCommands/DumpCommands.swift | 4 + .../PackageCommands/EditCommands.swift | 1 + Sources/Commands/PackageCommands/Format.swift | 5 +- Sources/Commands/PackageCommands/Init.swift | 1 + .../Commands/PackageCommands/Install.swift | 3 + Sources/Commands/PackageCommands/Learn.swift | 1 + .../PackageCommands/PluginCommand.swift | 6 +- .../PackageCommands/ResetCommands.swift | 1 + .../Commands/PackageCommands/Resolve.swift | 2 + .../PackageCommands/ShowExecutables.swift | 2 + Sources/Commands/PackageCommands/Update.swift | 1 + .../Commands/Snippets/Cards/SnippetCard.swift | 3 + .../Snippets/Cards/SnippetGroupCard.swift | 1 + Sources/Commands/Snippets/Cards/TopCard.swift | 1 + Sources/Commands/SwiftRunCommand.swift | 1 + Sources/Commands/SwiftTestCommand.swift | 1 + .../Utilities/DependenciesSerializer.swift | 2 + .../Commands/Utilities/MultiRootSupport.swift | 8 +- .../Utilities/SymbolGraphExtract.swift | 1 + .../Commands/Utilities/TestingSupport.swift | 1 + Sources/CoreCommands/BuildSystemSupport.swift | 1 + .../SwiftCommandObservabilityHandler.swift | 1 + .../DriverSupport/DriverSupportUtils.swift | 3 +- Sources/LLBuildManifest/LLBuildManifest.swift | 50 ++++---- .../PackageCollections+Validation.swift | 1 + .../PackageCollections.swift | 1 + .../JSONPackageCollectionProvider.swift | 3 +- ...FilePackageCollectionsSourcesStorage.swift | 5 +- .../SQLitePackageCollectionsStorage.swift | 1 + .../PackageCollectionSigning.swift | 1 + .../PackageDependency.swift | 5 + .../FilePackageFingerprintStorage.swift | 5 +- .../PackageFingerprintStorage.swift | 1 + .../PackageGraph/ModulesGraph+Loading.swift | 1 + Sources/PackageGraph/ModulesGraph.swift | 16 +-- .../PubGrub/ContainerProvider.swift | 1 + .../PubGrub/DiagnosticReportBuilder.swift | 4 + .../Resolution/ResolvedModule.swift | 1 + .../ManifestLoader+Validation.swift | 8 +- .../PackageLoading/ModuleMapGenerator.swift | 39 +++--- Sources/PackageLoading/PackageBuilder.swift | 2 + Sources/PackageLoading/PkgConfig.swift | 38 +++--- Sources/PackageLoading/Platform.swift | 7 +- ...RegistryReleaseMetadataSerialization.swift | 5 +- Sources/PackageLoading/Target+PkgConfig.swift | 1 + .../PackageLoading/TargetSourcesBuilder.swift | 57 ++++----- .../ArtifactsArchiveMetadata.swift | 7 +- .../MinimumDeploymentTarget.swift | 12 +- Sources/PackageModel/Module/ClangModule.swift | 15 +-- Sources/PackageModel/Module/Module.swift | 1 + .../PackageModel/Module/PluginModule.swift | 2 + Sources/PackageModel/PackageIdentity.swift | 5 +- .../PackageModel/Snippets/Model/Snippet.swift | 1 + Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 113 +++++++++--------- Sources/PackageModel/UserToolchain.swift | 17 +-- Sources/PackageModelSyntax/AddTarget.swift | 1 + .../ManifestSyntaxRepresentable.swift | 1 + .../PackageDependency+Syntax.swift | 1 + .../ProductDescription+Syntax.swift | 1 + .../PackageModelSyntax/SyntaxEditUtils.swift | 1 + .../TargetDescription+Syntax.swift | 2 +- Sources/PackagePlugin/Utilities.swift | 2 + Sources/PackageRegistry/ChecksumTOFU.swift | 1 + .../RegistryDownloadsManager.swift | 29 ++--- .../PackageRegistry/SignatureValidation.swift | 3 +- .../PackageRegistryCommand+Auth.swift | 3 + .../PackageRegistryCommand+Publish.swift | 1 + .../PackageRegistryCommand.swift | 1 + .../PackageSigning/CertificateStores.swift | 2 + .../FilePackageSigningEntityStorage.swift | 5 +- Sources/PackageSigning/SigningIdentity.swift | 1 + Sources/PackageSigning/X509Extensions.swift | 1 + .../BinaryTarget+Extensions.swift | 7 +- .../BuildParameters/BuildParameters.swift | 35 +++--- .../Plugins/PluginContextSerializer.swift | 10 +- .../Plugins/PluginScriptRunner.swift | 24 ++-- .../ResolvedPackage+Extensions.swift | 3 + Sources/SourceControl/RepositoryManager.swift | 29 ++--- Sources/SwiftBuildSupport/PIFBuilder.swift | 3 +- .../PackagePIFProjectBuilder+Modules.swift | 1 + .../PackagePIFProjectBuilder+Products.swift | 3 +- .../PackagePIFProjectBuilder.swift | 1 + Sources/SwiftSDKCommand/RemoveSwiftSDK.swift | 5 +- Sources/Workspace/InitPackage.swift | 1 + Sources/Workspace/ManagedArtifact.swift | 9 +- Sources/Workspace/ManagedDependency.swift | 21 ++-- Sources/Workspace/ManagedPrebuilt.swift | 3 +- .../RegistryPackageContainer.swift | 5 +- .../Workspace/Workspace+BinaryArtifacts.swift | 1 + Sources/Workspace/Workspace+Editing.swift | 3 + .../Workspace+PackageContainer.swift | 3 + Sources/Workspace/Workspace+Prebuilts.swift | 1 + .../Workspace+ResolvedPackages.swift | 2 + .../Workspace/Workspace+SourceControl.swift | 2 + Sources/Workspace/Workspace+State.swift | 39 +++--- Sources/XCBuildSupport/PIFBuilder.swift | 3 +- .../CrossCompilationPackageGraphTests.swift | 1 + .../PackageGraphTests/ModulesGraphTests.swift | 4 +- .../TopologicalSortTests.swift | 2 +- .../VersionSetSpecifierTests.swift | 1 + 126 files changed, 557 insertions(+), 352 deletions(-) diff --git a/Package.swift b/Package.swift index 93baca641c2..d55eba42c2b 100644 --- a/Package.swift +++ b/Package.swift @@ -35,6 +35,19 @@ if let resourceDirPath = ProcessInfo.processInfo.environment["SWIFTCI_INSTALL_RP packageLibraryLinkSettings = [] } +// Common experimental flags to be added to all targets. +let commonExperimentalFeatures: [SwiftSetting] = [ + .enableExperimentalFeature("MemberImportVisibility"), +] + +// Certain targets fail to compile with MemberImportVisibility enabled on 6.0.3 +// but work with >=6.1. These targets opt in to using `swift6CompatibleExperimentalFeatures`. +#if swift(>=6.1) +let swift6CompatibleExperimentalFeatures = commonExperimentalFeatures +#else +let swift6CompatibleExperimentalFeatures: [SwiftSetting] = [] +#endif + /** SwiftPMDataModel is the subset of SwiftPM product that includes just its data model. This allows some clients (such as IDEs) that use SwiftPM's data model but not its build system to not have to depend on SwiftDriver, SwiftLLBuild, etc. We should probably have better names here, @@ -152,7 +165,7 @@ let package = Package( .target( name: "PackageDescription", exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .define("USE_IMPL_ONLY_IMPORTS"), .unsafeFlags(["-package-description-version", "999.0"]), .unsafeFlags(["-enable-library-evolution"]), @@ -169,7 +182,7 @@ let package = Package( // AppleProductTypes library when they import it without further // messing with the manifest loader. dependencies: ["PackageDescription"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-package-description-version", "999.0"]), .unsafeFlags(["-enable-library-evolution"], .when(platforms: [.macOS])), .unsafeFlags(["-Xfrontend", "-module-link-name", "-Xfrontend", "AppleProductTypes"]) @@ -181,7 +194,7 @@ let package = Package( .target( name: "PackagePlugin", exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-package-description-version", "999.0"]), .unsafeFlags(["-enable-library-evolution"]), ], @@ -199,7 +212,7 @@ let package = Package( "SPMBuildCore", ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .enableExperimentalFeature("AccessLevelOnImport"), .unsafeFlags(["-static"]), ] @@ -215,7 +228,7 @@ let package = Package( .product(name: "SystemPackage", package: "swift-system"), ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .enableExperimentalFeature("StrictConcurrency"), .enableExperimentalFeature("AccessLevelOnImport"), .enableExperimentalFeature("InternalImportsByDefault"), @@ -235,7 +248,7 @@ let package = Package( .product(name: "SystemPackage", package: "swift-system"), ], exclude: ["CMakeLists.txt", "Vendor/README.md"], - swiftSettings: [ + swiftSettings: swift6CompatibleExperimentalFeatures + [ .enableExperimentalFeature("StrictConcurrency"), .enableExperimentalFeature("AccessLevelOnImport"), .unsafeFlags(["-static"]), @@ -247,7 +260,7 @@ let package = Package( name: "LLBuildManifest", dependencies: ["Basics"], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -263,7 +276,7 @@ let package = Package( "PackageSigning", ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -276,7 +289,7 @@ let package = Package( "PackageModel", ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -286,7 +299,7 @@ let package = Package( name: "SPMLLBuild", dependencies: ["Basics"], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -298,7 +311,7 @@ let package = Package( name: "PackageModel", dependencies: ["Basics"], exclude: ["CMakeLists.txt", "README.md"], - swiftSettings: [ + swiftSettings: swift6CompatibleExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -312,7 +325,7 @@ let package = Package( "PackageModel", ] + swiftSyntaxDependencies(["SwiftBasicFormat", "SwiftDiagnostics", "SwiftIDEUtils", "SwiftParser", "SwiftSyntax", "SwiftSyntaxBuilder"]), exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -326,7 +339,7 @@ let package = Package( "SourceControl", ], exclude: ["CMakeLists.txt", "README.md"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -343,7 +356,7 @@ let package = Package( .product(name: "OrderedCollections", package: "swift-collections"), ], exclude: ["CMakeLists.txt", "README.md"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -357,7 +370,7 @@ let package = Package( exclude: [ "Formats/v1.md", ], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -372,7 +385,7 @@ let package = Package( "PackageModel", "SourceControl", ], - swiftSettings: [ + swiftSettings: swift6CompatibleExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -385,7 +398,7 @@ let package = Package( "Basics", "PackageCollectionsModel", ], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -397,7 +410,7 @@ let package = Package( "PackageModel", ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -411,7 +424,7 @@ let package = Package( "PackageModel", ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -427,7 +440,7 @@ let package = Package( .product(name: "OrderedCollections", package: "swift-collections"), ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -445,7 +458,7 @@ let package = Package( "DriverSupport", ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -457,7 +470,7 @@ let package = Package( .product(name: "SwiftDriver", package: "swift-driver"), ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -470,7 +483,7 @@ let package = Package( .product(name: "OrderedCollections", package: "swift-collections"), ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -480,7 +493,8 @@ let package = Package( "SPMBuildCore", "PackageGraph", ], - exclude: ["CMakeLists.txt", "README.md"] + exclude: ["CMakeLists.txt", "README.md"], + swiftSettings: commonExperimentalFeatures ), .target( /** High level functionality */ @@ -497,7 +511,7 @@ let package = Package( .product(name: "OrderedCollections", package: "swift-collections"), ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -511,7 +525,7 @@ let package = Package( "PackageRegistry", "PackageSigning", ], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -533,7 +547,7 @@ let package = Package( "SwiftBuildSupport", ], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -555,7 +569,7 @@ let package = Package( "SwiftBuildSupport", ] + swiftSyntaxDependencies(["SwiftIDEUtils"]), exclude: ["CMakeLists.txt", "README.md"], - swiftSettings: [ + swiftSettings: swift6CompatibleExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -571,7 +585,7 @@ let package = Package( "PackageModel", ], exclude: ["CMakeLists.txt", "README.md"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -587,7 +601,7 @@ let package = Package( "PackageCollections", "PackageModel", ], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -609,7 +623,7 @@ let package = Package( "SPMBuildCore", "Workspace", ], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-static"]), ] ), @@ -717,7 +731,7 @@ let package = Package( name: "CompilerPluginSupport", dependencies: ["PackageDescription"], exclude: ["CMakeLists.txt"], - swiftSettings: [ + swiftSettings: commonExperimentalFeatures + [ .unsafeFlags(["-package-description-version", "999.0"]), .unsafeFlags(["-enable-library-evolution"]), ] @@ -840,7 +854,8 @@ let package = Package( ), .testTarget( name: "PackageGraphTests", - dependencies: ["PackageGraph", "_InternalTestSupport"] + dependencies: ["PackageGraph", "_InternalTestSupport"], + swiftSettings: commonExperimentalFeatures ), .testTarget( name: "PackageGraphPerformanceTests", diff --git a/Sources/Basics/AuthorizationProvider.swift b/Sources/Basics/AuthorizationProvider.swift index 5dcdbd3255d..85e7b1a7c51 100644 --- a/Sources/Basics/AuthorizationProvider.swift +++ b/Sources/Basics/AuthorizationProvider.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import TSCBasic import struct Foundation.Data import struct Foundation.Date import struct Foundation.URL diff --git a/Sources/Basics/Collections/Dictionary+Extensions.swift b/Sources/Basics/Collections/Dictionary+Extensions.swift index f31e0b57104..f08ff1d15ee 100644 --- a/Sources/Basics/Collections/Dictionary+Extensions.swift +++ b/Sources/Basics/Collections/Dictionary+Extensions.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import TSCBasic + extension Dictionary { @inlinable @discardableResult diff --git a/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift b/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift index 30f006c873a..b5b3597f15c 100644 --- a/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift +++ b/Sources/Basics/ProgressAnimation/ThrottledProgressAnimation.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import _Concurrency +import TSCUtility /// A progress animation wrapper that throttles updates to a given interval. final class ThrottledProgressAnimation: ProgressAnimationProtocol { diff --git a/Sources/Basics/SQLite.swift b/Sources/Basics/SQLite.swift index 803490b4865..cab2374f3c3 100644 --- a/Sources/Basics/SQLite.swift +++ b/Sources/Basics/SQLite.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import TSCBasic import Foundation #if SWIFT_PACKAGE && (os(Windows) || os(Android)) diff --git a/Sources/Basics/Triple+Basics.swift b/Sources/Basics/Triple+Basics.swift index 8247465475f..9773457033b 100644 --- a/Sources/Basics/Triple+Basics.swift +++ b/Sources/Basics/Triple+Basics.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import TSCUtility import enum TSCBasic.JSON extension Triple { diff --git a/Sources/Build/BuildDescription/ProductBuildDescription.swift b/Sources/Build/BuildDescription/ProductBuildDescription.swift index 742c75154db..a98633d91ad 100644 --- a/Sources/Build/BuildDescription/ProductBuildDescription.swift +++ b/Sources/Build/BuildDescription/ProductBuildDescription.swift @@ -18,6 +18,7 @@ import PackageModel import OrderedCollections import SPMBuildCore +import TSCUtility import struct TSCBasic.SortedArray @@ -434,7 +435,7 @@ extension SortedArray where Element == AbsolutePath { } } -extension Triple { +extension Basics.Triple { var supportsFrameworks: Bool { return self.isDarwin() } diff --git a/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift b/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift index 4eac7878727..347e4c9ca35 100644 --- a/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift @@ -15,6 +15,7 @@ import Basics import Foundation import PackageGraph import PackageLoading +import TSCUtility @_spi(SwiftPMInternal) import PackageModel diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift index 69962b11bc2..34476408fff 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift @@ -16,6 +16,7 @@ import struct Basics.InternalError import class Basics.ObservabilityScope import struct PackageGraph.ResolvedModule import PackageModel +import SPMBuildCore extension LLBuildManifestBuilder { /// Create a llbuild target for a Clang target description. diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift index 9817039a4fd..33eb6f4558f 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Product.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import PackageModel + import struct Basics.AbsolutePath import struct Basics.InternalError import struct LLBuildManifest.Node diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift index 4d72ff35076..1f185cb4ce8 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Resources.swift @@ -13,6 +13,8 @@ import struct LLBuildManifest.Node import struct Basics.RelativePath +import PackageModel + extension LLBuildManifestBuilder { /// Adds command for creating the resources bundle of the given target. /// diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index 11668bbe1b3..b2679e95b5a 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -24,10 +24,14 @@ import struct Basics.Environment #if USE_IMPL_ONLY_IMPORTS @_implementationOnly import class DriverSupport.SPMSwiftDriverExecutor +@_implementationOnly import Foundation @_implementationOnly import SwiftDriver +@_implementationOnly import TSCUtility #else import class DriverSupport.SPMSwiftDriverExecutor +import Foundation import SwiftDriver +import TSCUtility #endif import PackageModel @@ -81,7 +85,8 @@ extension LLBuildManifestBuilder { args: commandLine, diagnosticsOutput: .handler(self.observabilityScope.makeDiagnosticsHandler()), fileSystem: self.fileSystem, - executor: executor + executor: executor, + compilerIntegratedTooling: false ) try driver.checkLDPathOption(commandLine: commandLine) @@ -297,6 +302,7 @@ extension LLBuildManifestBuilder { args: commandLine, fileSystem: self.fileSystem, executor: executor, + compilerIntegratedTooling: false, externalTargetModuleDetailsMap: dependencyModuleDetailsMap, interModuleDependencyOracle: dependencyOracle ) diff --git a/Sources/Build/BuildPlan/BuildPlan+Clang.swift b/Sources/Build/BuildPlan/BuildPlan+Clang.swift index ca83e4b2e6a..eef1675a3e9 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Clang.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Clang.swift @@ -10,6 +10,11 @@ // //===----------------------------------------------------------------------===// +import Basics +import PackageGraph +import PackageLoading +import SPMBuildCore + import class PackageModel.BinaryModule import class PackageModel.ClangModule import class PackageModel.SwiftModule diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index f0c0b256cbb..d04c86e6033 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -16,6 +16,10 @@ import class PackageModel.BinaryModule import class PackageModel.ClangModule import class PackageModel.SystemLibraryModule +import PackageGraph +import PackageLoading +import SPMBuildCore + extension BuildPlan { func plan(swiftTarget: SwiftModuleBuildDescription) throws { // We need to iterate recursive dependencies because Swift compiler needs to see all the targets a target diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 01ffe8ec665..22a9727191f 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -19,6 +19,7 @@ import PackageGraph import PackageLoading import PackageModel import SPMBuildCore +import TSCBasic #if USE_IMPL_ONLY_IMPORTS @_implementationOnly import SwiftDriver @@ -90,7 +91,7 @@ extension [String] { extension BuildParameters { /// Returns the directory to be used for module cache. - public var moduleCache: AbsolutePath { + public var moduleCache: Basics.AbsolutePath { get throws { // FIXME: We use this hack to let swiftpm's functional test use shared // cache so it doesn't become painfully slow. @@ -168,9 +169,9 @@ public class BuildPlan: SPMBuildCore.BuildPlan { /// Return value of `inputs()` package enum Input { /// Any file in this directory affects the build plan - case directoryStructure(AbsolutePath) + case directoryStructure(Basics.AbsolutePath) /// The file at the given path affects the build plan - case file(AbsolutePath) + case file(Basics.AbsolutePath) } public enum Error: Swift.Error, CustomStringConvertible, Equatable { @@ -276,7 +277,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { pluginConfiguration: PluginConfiguration? = nil, pluginTools: [ResolvedModule.ID: [String: PluginTool]] = [:], additionalFileRules: [FileRuleDescription] = [], - pkgConfigDirectories: [AbsolutePath] = [], + pkgConfigDirectories: [Basics.AbsolutePath] = [], disableSandbox: Bool = false, fileSystem: any FileSystem, observabilityScope: ObservabilityScope @@ -698,7 +699,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { .map { .directoryStructure($0) } // Add the output paths of any prebuilds that were run, so that we redo the plan if they change. - var derivedSourceDirPaths: [AbsolutePath] = [] + var derivedSourceDirPaths: [Basics.AbsolutePath] = [] for result in self.prebuildCommandResults.values.flatMap({ $0 }) { derivedSourceDirPaths.append(contentsOf: result.outputDirectories) } @@ -768,7 +769,7 @@ extension BuildPlan { modulesGraph: ModulesGraph, tools: [ResolvedModule.ID: [String: PluginTool]], additionalFileRules: [FileRuleDescription], - pkgConfigDirectories: [AbsolutePath], + pkgConfigDirectories: [Basics.AbsolutePath], fileSystem: any FileSystem, observabilityScope: ObservabilityScope, surfaceDiagnostics: Bool = false @@ -895,8 +896,8 @@ extension BuildPlan { try pluginResults.map { pluginResult in // As we go we will collect a list of prebuild output directories whose contents should be input to the // build, and a list of the files in those directories after running the commands. - var derivedFiles: [AbsolutePath] = [] - var prebuildOutputDirs: [AbsolutePath] = [] + var derivedFiles: [Basics.AbsolutePath] = [] + var prebuildOutputDirs: [Basics.AbsolutePath] = [] for command in pluginResult.prebuildCommands { observabilityScope .emit( @@ -1248,7 +1249,7 @@ extension Basics.Diagnostic { extension BuildParameters { /// Returns a named bundle's path inside the build directory. - func bundlePath(named name: String) -> AbsolutePath { + func bundlePath(named name: String) -> Basics.AbsolutePath { self.buildPath.appending(component: name + self.triple.nsbundleExtension) } } @@ -1257,7 +1258,7 @@ extension BuildParameters { func generateResourceInfoPlist( fileSystem: FileSystem, target: ResolvedModule, - path: AbsolutePath + path: Basics.AbsolutePath ) throws -> Bool { guard let defaultLocalization = target.defaultLocalization else { return false diff --git a/Sources/Build/LLBuildDescription.swift b/Sources/Build/LLBuildDescription.swift index 81aeec613dd..8149a687129 100644 --- a/Sources/Build/LLBuildDescription.swift +++ b/Sources/Build/LLBuildDescription.swift @@ -15,6 +15,7 @@ import Foundation import LLBuildManifest import SPMBuildCore import PackageGraph +import PackageModel import struct TSCBasic.ByteString diff --git a/Sources/Build/SwiftCompilerOutputParser.swift b/Sources/Build/SwiftCompilerOutputParser.swift index 11eaf585d58..9f06aae52fe 100644 --- a/Sources/Build/SwiftCompilerOutputParser.swift +++ b/Sources/Build/SwiftCompilerOutputParser.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import Basics import Foundation import class TSCUtility.JSONMessageStreamingParser diff --git a/Sources/Commands/PackageCommands/APIDiff.swift b/Sources/Commands/PackageCommands/APIDiff.swift index 416c7a40057..3e114341fe8 100644 --- a/Sources/Commands/PackageCommands/APIDiff.swift +++ b/Sources/Commands/PackageCommands/APIDiff.swift @@ -17,6 +17,8 @@ import Dispatch import PackageGraph import PackageModel import SourceControl +import SPMBuildCore +import TSCUtility import _Concurrency struct DeprecatedAPIDiff: ParsableCommand { diff --git a/Sources/Commands/PackageCommands/AddDependency.swift b/Sources/Commands/PackageCommands/AddDependency.swift index 76b32169c9f..a968965b118 100644 --- a/Sources/Commands/PackageCommands/AddDependency.swift +++ b/Sources/Commands/PackageCommands/AddDependency.swift @@ -13,6 +13,8 @@ import ArgumentParser import Basics import CoreCommands +import Foundation +import PackageGraph import PackageModel import PackageModelSyntax import SwiftParser diff --git a/Sources/Commands/PackageCommands/AddProduct.swift b/Sources/Commands/PackageCommands/AddProduct.swift index 4a60bf1e6db..787dc84ea99 100644 --- a/Sources/Commands/PackageCommands/AddProduct.swift +++ b/Sources/Commands/PackageCommands/AddProduct.swift @@ -13,6 +13,8 @@ import ArgumentParser import Basics import CoreCommands +import Foundation +import PackageGraph import PackageModel import PackageModelSyntax import SwiftParser diff --git a/Sources/Commands/PackageCommands/AddTarget.swift b/Sources/Commands/PackageCommands/AddTarget.swift index 8d9bea9fe5a..a1fb583d15e 100644 --- a/Sources/Commands/PackageCommands/AddTarget.swift +++ b/Sources/Commands/PackageCommands/AddTarget.swift @@ -13,6 +13,8 @@ import ArgumentParser import Basics import CoreCommands +import Foundation +import PackageGraph import PackageModel import PackageModelSyntax import SwiftParser diff --git a/Sources/Commands/PackageCommands/AddTargetDependency.swift b/Sources/Commands/PackageCommands/AddTargetDependency.swift index d629469d4d7..9c888546b5b 100644 --- a/Sources/Commands/PackageCommands/AddTargetDependency.swift +++ b/Sources/Commands/PackageCommands/AddTargetDependency.swift @@ -13,6 +13,8 @@ import ArgumentParser import Basics import CoreCommands +import Foundation +import PackageGraph import PackageModel import PackageModelSyntax import SwiftParser diff --git a/Sources/Commands/PackageCommands/ArchiveSource.swift b/Sources/Commands/PackageCommands/ArchiveSource.swift index bf06cf70c9a..807a9f1a6c8 100644 --- a/Sources/Commands/PackageCommands/ArchiveSource.swift +++ b/Sources/Commands/PackageCommands/ArchiveSource.swift @@ -13,6 +13,8 @@ import ArgumentParser import Basics import CoreCommands +import PackageGraph +import PackageModel import SourceControl extension SwiftPackageCommand { diff --git a/Sources/Commands/PackageCommands/CompletionCommand.swift b/Sources/Commands/PackageCommands/CompletionCommand.swift index c73710aa8ae..10f51bd0676 100644 --- a/Sources/Commands/PackageCommands/CompletionCommand.swift +++ b/Sources/Commands/PackageCommands/CompletionCommand.swift @@ -11,7 +11,10 @@ //===----------------------------------------------------------------------===// import ArgumentParser +import Basics import CoreCommands +import PackageModel +import PackageGraph import var TSCBasic.stdoutStream diff --git a/Sources/Commands/PackageCommands/Config.swift b/Sources/Commands/PackageCommands/Config.swift index 0d61c59a449..6f4b14220b0 100644 --- a/Sources/Commands/PackageCommands/Config.swift +++ b/Sources/Commands/PackageCommands/Config.swift @@ -13,6 +13,7 @@ import ArgumentParser import Basics import CoreCommands +import PackageGraph import Workspace import var TSCBasic.stderrStream diff --git a/Sources/Commands/PackageCommands/Describe.swift b/Sources/Commands/PackageCommands/Describe.swift index 8edeb96b2dc..4392b0040fe 100644 --- a/Sources/Commands/PackageCommands/Describe.swift +++ b/Sources/Commands/PackageCommands/Describe.swift @@ -15,6 +15,8 @@ import Basics import CoreCommands import Foundation import PackageModel +import PackageGraph +import Workspace import struct TSCBasic.StringError diff --git a/Sources/Commands/PackageCommands/DumpCommands.swift b/Sources/Commands/PackageCommands/DumpCommands.swift index be0d9f18b19..3d40e42644e 100644 --- a/Sources/Commands/PackageCommands/DumpCommands.swift +++ b/Sources/Commands/PackageCommands/DumpCommands.swift @@ -15,6 +15,10 @@ import Basics import CoreCommands import Foundation import PackageModel +import PackageGraph +import SPMBuildCore +import TSCBasic +import Workspace import XCBuildSupport struct DumpSymbolGraph: AsyncSwiftCommand { diff --git a/Sources/Commands/PackageCommands/EditCommands.swift b/Sources/Commands/PackageCommands/EditCommands.swift index 6e3bbdc4a0c..f0966e2378d 100644 --- a/Sources/Commands/PackageCommands/EditCommands.swift +++ b/Sources/Commands/PackageCommands/EditCommands.swift @@ -14,6 +14,7 @@ import ArgumentParser import Basics import CoreCommands import SourceControl +import Workspace extension SwiftPackageCommand { struct Edit: AsyncSwiftCommand { diff --git a/Sources/Commands/PackageCommands/Format.swift b/Sources/Commands/PackageCommands/Format.swift index c114a5e25ca..29399d5ec8c 100644 --- a/Sources/Commands/PackageCommands/Format.swift +++ b/Sources/Commands/PackageCommands/Format.swift @@ -14,6 +14,9 @@ import ArgumentParser import Basics import CoreCommands import PackageModel +import PackageGraph +import TSCBasic +import Workspace import class Basics.AsyncProcess @@ -34,7 +37,7 @@ extension SwiftPackageCommand { func run(_ swiftCommandState: SwiftCommandState) async throws { // Look for swift-format binary. // FIXME: This should be moved to user toolchain. - let swiftFormatInEnv = lookupExecutablePath(filename: Environment.current["SWIFT_FORMAT"]) + let swiftFormatInEnv = Basics.lookupExecutablePath(filename: Environment.current["SWIFT_FORMAT"]) guard let swiftFormat = swiftFormatInEnv ?? AsyncProcess.findExecutable("swift-format") else { swiftCommandState.observabilityScope.emit(error: "Could not find swift-format in PATH or SWIFT_FORMAT") throw TSCUtility.Diagnostics.fatalError diff --git a/Sources/Commands/PackageCommands/Init.swift b/Sources/Commands/PackageCommands/Init.swift index 3cc7f87fa24..5eb8293ee2a 100644 --- a/Sources/Commands/PackageCommands/Init.swift +++ b/Sources/Commands/PackageCommands/Init.swift @@ -16,6 +16,7 @@ import Basics @_spi(SwiftPMInternal) import CoreCommands +import PackageModel import Workspace import SPMBuildCore diff --git a/Sources/Commands/PackageCommands/Install.swift b/Sources/Commands/PackageCommands/Install.swift index 1401819b47a..87a0a16c493 100644 --- a/Sources/Commands/PackageCommands/Install.swift +++ b/Sources/Commands/PackageCommands/Install.swift @@ -14,8 +14,11 @@ import ArgumentParser import struct Basics.Environment import CoreCommands import Foundation +import PackageGraph import PackageModel +import SPMBuildCore import TSCBasic +import Workspace extension SwiftPackageCommand { struct Install: AsyncSwiftCommand { diff --git a/Sources/Commands/PackageCommands/Learn.swift b/Sources/Commands/PackageCommands/Learn.swift index 72a9843abdb..176f7cd45fa 100644 --- a/Sources/Commands/PackageCommands/Learn.swift +++ b/Sources/Commands/PackageCommands/Learn.swift @@ -13,6 +13,7 @@ import ArgumentParser import Basics import CoreCommands +import Foundation import PackageGraph import PackageModel diff --git a/Sources/Commands/PackageCommands/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift index 5f05d421f42..fcc9b73192c 100644 --- a/Sources/Commands/PackageCommands/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -15,10 +15,12 @@ import Basics import _Concurrency import CoreCommands import Dispatch - +import SPMBuildCore import PackageGraph - import PackageModel +import TSCBasic +import TSCUtility +import Workspace struct PluginCommand: AsyncSwiftCommand { static let configuration = CommandConfiguration( diff --git a/Sources/Commands/PackageCommands/ResetCommands.swift b/Sources/Commands/PackageCommands/ResetCommands.swift index 7f010d0932d..2b8e5c02708 100644 --- a/Sources/Commands/PackageCommands/ResetCommands.swift +++ b/Sources/Commands/PackageCommands/ResetCommands.swift @@ -12,6 +12,7 @@ import ArgumentParser import CoreCommands +import Workspace extension SwiftPackageCommand { struct Clean: SwiftCommand { diff --git a/Sources/Commands/PackageCommands/Resolve.swift b/Sources/Commands/PackageCommands/Resolve.swift index cae63da126b..baf18eb9e7b 100644 --- a/Sources/Commands/PackageCommands/Resolve.swift +++ b/Sources/Commands/PackageCommands/Resolve.swift @@ -11,8 +11,10 @@ //===----------------------------------------------------------------------===// import ArgumentParser +import Basics import CoreCommands import TSCUtility +import Workspace import struct PackageGraph.TraitConfiguration diff --git a/Sources/Commands/PackageCommands/ShowExecutables.swift b/Sources/Commands/PackageCommands/ShowExecutables.swift index fb08f22ee90..c1e50248b19 100644 --- a/Sources/Commands/PackageCommands/ShowExecutables.swift +++ b/Sources/Commands/PackageCommands/ShowExecutables.swift @@ -14,6 +14,8 @@ import ArgumentParser import Basics import CoreCommands import Foundation +import PackageModel +import PackageGraph import Workspace struct ShowExecutables: AsyncSwiftCommand { diff --git a/Sources/Commands/PackageCommands/Update.swift b/Sources/Commands/PackageCommands/Update.swift index 31b3738af23..36a09a1aa75 100644 --- a/Sources/Commands/PackageCommands/Update.swift +++ b/Sources/Commands/PackageCommands/Update.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import ArgumentParser +import Basics import CoreCommands import Dispatch import PackageModel diff --git a/Sources/Commands/Snippets/Cards/SnippetCard.swift b/Sources/Commands/Snippets/Cards/SnippetCard.swift index f6ddd901137..d54b916ccf3 100644 --- a/Sources/Commands/Snippets/Cards/SnippetCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetCard.swift @@ -12,7 +12,10 @@ import Basics import CoreCommands +import Foundation import PackageModel +import PackageGraph +import SPMBuildCore import func TSCBasic.exec import enum TSCBasic.ProcessEnv diff --git a/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift b/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift index cd5e070f1b6..1a27442a5c9 100644 --- a/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetGroupCard.swift @@ -12,6 +12,7 @@ import CoreCommands import PackageModel +import TSCUtility /// A card showing the snippets in a ``SnippetGroup``. struct SnippetGroupCard: Card { diff --git a/Sources/Commands/Snippets/Cards/TopCard.swift b/Sources/Commands/Snippets/Cards/TopCard.swift index a1e24c39976..614a20b6080 100644 --- a/Sources/Commands/Snippets/Cards/TopCard.swift +++ b/Sources/Commands/Snippets/Cards/TopCard.swift @@ -14,6 +14,7 @@ import CoreCommands import Foundation import PackageGraph import PackageModel +import TSCUtility /// The top menu card for a package's help contents, including snippets. struct TopCard: Card { diff --git a/Sources/Commands/SwiftRunCommand.swift b/Sources/Commands/SwiftRunCommand.swift index 07d6b3b138f..21c455edd55 100644 --- a/Sources/Commands/SwiftRunCommand.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -16,6 +16,7 @@ import CoreCommands import Foundation import PackageGraph import PackageModel +import SPMBuildCore import enum TSCBasic.ProcessEnv import func TSCBasic.exec diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index c4c6b45bc62..aefa252850e 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -28,6 +28,7 @@ import PackageGraph import PackageModel import SPMBuildCore +import TSCUtility import func TSCLibc.exit import Workspace diff --git a/Sources/Commands/Utilities/DependenciesSerializer.swift b/Sources/Commands/Utilities/DependenciesSerializer.swift index 25190f011c4..d58e5c58162 100644 --- a/Sources/Commands/Utilities/DependenciesSerializer.swift +++ b/Sources/Commands/Utilities/DependenciesSerializer.swift @@ -10,8 +10,10 @@ // //===----------------------------------------------------------------------===// +import Basics import PackageModel import PackageGraph +import TSCUtility import enum TSCBasic.JSON import protocol TSCBasic.OutputByteStream diff --git a/Sources/Commands/Utilities/MultiRootSupport.swift b/Sources/Commands/Utilities/MultiRootSupport.swift index 7ee468a81f5..7cd42e877bc 100644 --- a/Sources/Commands/Utilities/MultiRootSupport.swift +++ b/Sources/Commands/Utilities/MultiRootSupport.swift @@ -13,6 +13,8 @@ import Basics import CoreCommands import Foundation +import TSCBasic + #if canImport(FoundationXML) import FoundationXML #endif @@ -44,7 +46,7 @@ public struct XcodeWorkspaceLoader: WorkspaceLoader { } /// Load the given workspace and return the file ref paths from it. - public func load(workspace: AbsolutePath) throws -> [AbsolutePath] { + public func load(workspace: Basics.AbsolutePath) throws -> [Basics.AbsolutePath] { let path = workspace.appending("contents.xcworkspacedata") let contents: Data = try self.fileSystem.readFileContents(path) @@ -56,9 +58,9 @@ public struct XcodeWorkspaceLoader: WorkspaceLoader { } /// Convert the parsed result into absolute paths. - var result: [AbsolutePath] = [] + var result: [Basics.AbsolutePath] = [] for location in delegate.locations { - let path: AbsolutePath + let path: Basics.AbsolutePath switch location.kind { case .absolute: diff --git a/Sources/Commands/Utilities/SymbolGraphExtract.swift b/Sources/Commands/Utilities/SymbolGraphExtract.swift index cadf1b2a4b4..f18f9aa1b76 100644 --- a/Sources/Commands/Utilities/SymbolGraphExtract.swift +++ b/Sources/Commands/Utilities/SymbolGraphExtract.swift @@ -12,6 +12,7 @@ import ArgumentParser import Basics +import Foundation import PackageGraph import PackageModel import SPMBuildCore diff --git a/Sources/Commands/Utilities/TestingSupport.swift b/Sources/Commands/Utilities/TestingSupport.swift index 84206c5e4dd..5eae29a06aa 100644 --- a/Sources/Commands/Utilities/TestingSupport.swift +++ b/Sources/Commands/Utilities/TestingSupport.swift @@ -14,6 +14,7 @@ import Basics import CoreCommands import PackageModel import SPMBuildCore +import TSCUtility import Workspace import struct TSCBasic.FileSystemError diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index 9407a10f8a4..28fa15d1407 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -16,6 +16,7 @@ import SPMBuildCore import XCBuildSupport import SwiftBuildSupport import PackageGraph +import Workspace import class Basics.ObservabilityScope import struct PackageGraph.ModulesGraph diff --git a/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift b/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift index 59b52d15d21..f555d2331d7 100644 --- a/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift +++ b/Sources/CoreCommands/SwiftCommandObservabilityHandler.swift @@ -13,6 +13,7 @@ @_spi(SwiftPMInternal) import Basics import Dispatch +import PackageModel import protocol TSCBasic.OutputByteStream import class TSCBasic.TerminalController diff --git a/Sources/DriverSupport/DriverSupportUtils.swift b/Sources/DriverSupport/DriverSupportUtils.swift index 497bd403373..1497ec1b94a 100644 --- a/Sources/DriverSupport/DriverSupportUtils.swift +++ b/Sources/DriverSupport/DriverSupportUtils.swift @@ -12,7 +12,7 @@ @_spi(SwiftPMInternal) import Basics - +import Foundation import PackageModel import SwiftDriver import class TSCBasic.Process @@ -42,6 +42,7 @@ public enum DriverSupport { let driver = try Driver( args: ["swiftc"], executor: executor, + compilerIntegratedTooling: false, compilerExecutableDir: TSCAbsolutePath(toolchain.swiftCompilerPath.parentDirectory) ) let supportedFlagSet = Set(driver.supportedFrontendFlags.map { $0.trimmingCharacters(in: ["-"]) }) diff --git a/Sources/LLBuildManifest/LLBuildManifest.swift b/Sources/LLBuildManifest/LLBuildManifest.swift index cc0cd4b74e0..520db084769 100644 --- a/Sources/LLBuildManifest/LLBuildManifest.swift +++ b/Sources/LLBuildManifest/LLBuildManifest.swift @@ -12,6 +12,8 @@ import Basics import Foundation +import TSCBasic +import TSCUtility import class Basics.AsyncProcess @@ -59,7 +61,7 @@ public enum WriteAuxiliary { public static let name = "link-file-list" // FIXME: We should extend the `InProcessTool` support to allow us to specify these in a typed way, but today we have to flatten all the inputs into a generic `Node` array (rdar://109844243). - public static func computeInputs(objects: [AbsolutePath]) -> [Node] { + public static func computeInputs(objects: [Basics.AbsolutePath]) -> [Node] { return [.virtual(Self.name)] + objects.map { Node.file($0) } } @@ -88,7 +90,7 @@ public enum WriteAuxiliary { public struct SourcesFileList: AuxiliaryFileType { public static let name = "sources-file-list" - public static func computeInputs(sources: [AbsolutePath]) -> [Node] { + public static func computeInputs(sources: [Basics.AbsolutePath]) -> [Node] { return [.virtual(Self.name)] + sources.map { Node.file($0) } } @@ -114,7 +116,7 @@ public enum WriteAuxiliary { public struct SwiftGetVersion: AuxiliaryFileType { public static let name = "swift-get-version" - public static func computeInputs(swiftCompilerPath: AbsolutePath) -> [Node] { + public static func computeInputs(swiftCompilerPath: Basics.AbsolutePath) -> [Node] { return [.virtual(Self.name), .file(swiftCompilerPath)] } @@ -122,7 +124,7 @@ public enum WriteAuxiliary { guard let swiftCompilerPathString = inputs.first(where: { $0.kind == .file })?.name else { throw Error.unknownSwiftCompilerPath } - let swiftCompilerPath = try AbsolutePath(validating: swiftCompilerPathString) + let swiftCompilerPath = try Basics.AbsolutePath(validating: swiftCompilerPathString) return try AsyncProcess.checkNonZeroExit(args: swiftCompilerPath.pathString, "-version") } @@ -164,7 +166,7 @@ public enum WriteAuxiliary { public struct EmbeddedResources: AuxiliaryFileType { public static let name = "embedded-resources" - public static func computeInputs(resources: [AbsolutePath]) -> [Node] { + public static func computeInputs(resources: [Basics.AbsolutePath]) -> [Node] { return [.virtual(Self.name)] + resources.map { Node.file($0) } } @@ -178,7 +180,7 @@ public enum WriteAuxiliary { """ for input in inputs where input.kind == .file { - let resourcePath = try AbsolutePath(validating: input.name) + let resourcePath = try Basics.AbsolutePath(validating: input.name) let variableName = resourcePath.basename.spm_mangledToC99ExtendedIdentifier() let fileContent = try Data(contentsOf: URL(fileURLWithPath: resourcePath.pathString)).map { String($0) }.joined(separator: ",") @@ -267,7 +269,7 @@ public struct LLBuildManifest { addCommand(name: name, tool: tool) } - public mutating func addEntitlementPlistCommand(entitlement: String, outputPath: AbsolutePath) { + public mutating func addEntitlementPlistCommand(entitlement: String, outputPath: Basics.AbsolutePath) { let inputs = WriteAuxiliary.EntitlementPlist.computeInputs(entitlement: entitlement) let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: outputPath) let name = outputPath.pathString @@ -275,8 +277,8 @@ public struct LLBuildManifest { } public mutating func addWriteLinkFileListCommand( - objects: [AbsolutePath], - linkFileListPath: AbsolutePath + objects: [Basics.AbsolutePath], + linkFileListPath: Basics.AbsolutePath ) { let inputs = WriteAuxiliary.LinkFileList.computeInputs(objects: objects) let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: linkFileListPath) @@ -285,8 +287,8 @@ public struct LLBuildManifest { } public mutating func addWriteSourcesFileListCommand( - sources: [AbsolutePath], - sourcesFileListPath: AbsolutePath + sources: [Basics.AbsolutePath], + sourcesFileListPath: Basics.AbsolutePath ) { let inputs = WriteAuxiliary.SourcesFileList.computeInputs(sources: sources) let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: sourcesFileListPath) @@ -295,8 +297,8 @@ public struct LLBuildManifest { } public mutating func addSwiftGetVersionCommand( - swiftCompilerPath: AbsolutePath, - swiftVersionFilePath: AbsolutePath + swiftCompilerPath: Basics.AbsolutePath, + swiftVersionFilePath: Basics.AbsolutePath ) { let inputs = WriteAuxiliary.SwiftGetVersion.computeInputs(swiftCompilerPath: swiftCompilerPath) let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: swiftVersionFilePath, alwaysOutOfDate: true) @@ -304,7 +306,7 @@ public struct LLBuildManifest { addCommand(name: name, tool: tool) } - public mutating func addWriteInfoPlistCommand(principalClass: String, outputPath: AbsolutePath) { + public mutating func addWriteInfoPlistCommand(principalClass: String, outputPath: Basics.AbsolutePath) { let inputs = WriteAuxiliary.XCTestInfoPlist.computeInputs(principalClass: principalClass) let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: outputPath) let name = outputPath.pathString @@ -312,8 +314,8 @@ public struct LLBuildManifest { } public mutating func addWriteEmbeddedResourcesCommand( - resources: [AbsolutePath], - outputPath: AbsolutePath + resources: [Basics.AbsolutePath], + outputPath: Basics.AbsolutePath ) { let inputs = WriteAuxiliary.EmbeddedResources.computeInputs(resources: resources) let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: outputPath) @@ -393,19 +395,19 @@ public struct LLBuildManifest { name: String, inputs: [Node], outputs: [Node], - executable: AbsolutePath, + executable: Basics.AbsolutePath, moduleName: String, moduleAliases: [String: String]?, - moduleOutputPath: AbsolutePath, - importPath: AbsolutePath, - tempsPath: AbsolutePath, - objects: [AbsolutePath], + moduleOutputPath: Basics.AbsolutePath, + importPath: Basics.AbsolutePath, + tempsPath: Basics.AbsolutePath, + objects: [Basics.AbsolutePath], otherArguments: [String], - sources: [AbsolutePath], - fileList: AbsolutePath, + sources: [Basics.AbsolutePath], + fileList: Basics.AbsolutePath, isLibrary: Bool, wholeModuleOptimization: Bool, - outputFileMapPath: AbsolutePath, + outputFileMapPath: Basics.AbsolutePath, prepareForIndexing: Bool ) { let tool = SwiftCompilerTool( diff --git a/Sources/PackageCollections/PackageCollections+Validation.swift b/Sources/PackageCollections/PackageCollections+Validation.swift index 3940021555d..c0e7bbd5c2e 100644 --- a/Sources/PackageCollections/PackageCollections+Validation.swift +++ b/Sources/PackageCollections/PackageCollections+Validation.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Basics +import Foundation import PackageCollectionsModel import PackageModel diff --git a/Sources/PackageCollections/PackageCollections.swift b/Sources/PackageCollections/PackageCollections.swift index b37eca329b2..17db7491bf1 100644 --- a/Sources/PackageCollections/PackageCollections.swift +++ b/Sources/PackageCollections/PackageCollections.swift @@ -13,6 +13,7 @@ import _Concurrency import Basics import PackageModel +import Foundation import protocol TSCBasic.Closable diff --git a/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift b/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift index 1df42244400..4e24bb717e0 100644 --- a/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift +++ b/Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift @@ -24,6 +24,7 @@ import PackageCollectionsModel import PackageCollectionsSigning import PackageModel import SourceControl +import TSCBasic import struct TSCUtility.Version @@ -64,7 +65,7 @@ struct JSONPackageCollectionProvider: PackageCollectionProvider { self.httpClient = customHTTPClient ?? Self.makeDefaultHTTPClient() self.signatureValidator = customSignatureValidator ?? PackageCollectionSigning( trustedRootCertsDir: configuration.trustedRootCertsDir ?? - (try? fileSystem.swiftPMConfigurationDirectory.appending("trust-root-certs").asURL) ?? AbsolutePath.root.asURL, + (try? fileSystem.swiftPMConfigurationDirectory.appending("trust-root-certs").asURL) ?? Basics.AbsolutePath.root.asURL, additionalTrustedRootCerts: sourceCertPolicy.allRootCerts.map { Array($0) }, observabilityScope: observabilityScope ) diff --git a/Sources/PackageCollections/Storage/FilePackageCollectionsSourcesStorage.swift b/Sources/PackageCollections/Storage/FilePackageCollectionsSourcesStorage.swift index b3760d3b246..ddc24c23f2b 100644 --- a/Sources/PackageCollections/Storage/FilePackageCollectionsSourcesStorage.swift +++ b/Sources/PackageCollections/Storage/FilePackageCollectionsSourcesStorage.swift @@ -13,6 +13,7 @@ import _Concurrency import Basics import Dispatch +import TSCBasic import struct Foundation.Data import class Foundation.JSONDecoder import class Foundation.JSONEncoder @@ -20,12 +21,12 @@ import struct Foundation.URL struct FilePackageCollectionsSourcesStorage: PackageCollectionsSourcesStorage { let fileSystem: FileSystem - let path: AbsolutePath + let path: Basics.AbsolutePath private let encoder: JSONEncoder private let decoder: JSONDecoder - init(fileSystem: FileSystem, path: AbsolutePath? = nil) { + init(fileSystem: FileSystem, path: Basics.AbsolutePath? = nil) { self.fileSystem = fileSystem self.path = path ?? (try? fileSystem.swiftPMConfigurationDirectory.appending("collections.json")) ?? .root diff --git a/Sources/PackageCollections/Storage/SQLitePackageCollectionsStorage.swift b/Sources/PackageCollections/Storage/SQLitePackageCollectionsStorage.swift index 30bd2589136..295a496deb7 100644 --- a/Sources/PackageCollections/Storage/SQLitePackageCollectionsStorage.swift +++ b/Sources/PackageCollections/Storage/SQLitePackageCollectionsStorage.swift @@ -20,6 +20,7 @@ import class Foundation.JSONEncoder import class Foundation.NSLock import struct Foundation.URL import PackageModel +import TSCUtility import protocol TSCBasic.Closable diff --git a/Sources/PackageCollectionsSigning/PackageCollectionSigning.swift b/Sources/PackageCollectionsSigning/PackageCollectionSigning.swift index 0903c0fccad..0dbc770acbe 100644 --- a/Sources/PackageCollectionsSigning/PackageCollectionSigning.swift +++ b/Sources/PackageCollectionsSigning/PackageCollectionSigning.swift @@ -15,6 +15,7 @@ import Basics import Dispatch import Foundation import PackageCollectionsModel +import SwiftASN1 #if USE_IMPL_ONLY_IMPORTS @_implementationOnly import _CryptoExtras diff --git a/Sources/PackageDescription/PackageDependency.swift b/Sources/PackageDescription/PackageDependency.swift index 60dbe1f426d..594736e558c 100644 --- a/Sources/PackageDescription/PackageDependency.swift +++ b/Sources/PackageDescription/PackageDependency.swift @@ -10,6 +10,11 @@ // //===----------------------------------------------------------------------===// +#if USE_IMPL_ONLY_IMPORTS +@_implementationOnly import Foundation +#else +import Foundation +#endif extension Package { /// A package dependency of a Swift package. diff --git a/Sources/PackageFingerprint/FilePackageFingerprintStorage.swift b/Sources/PackageFingerprint/FilePackageFingerprintStorage.swift index 54f3c11af01..a0af231bd6c 100644 --- a/Sources/PackageFingerprint/FilePackageFingerprintStorage.swift +++ b/Sources/PackageFingerprint/FilePackageFingerprintStorage.swift @@ -14,17 +14,18 @@ import Basics import Dispatch import Foundation import PackageModel +import TSCBasic import struct TSCUtility.Version public struct FilePackageFingerprintStorage: PackageFingerprintStorage { let fileSystem: FileSystem - let directoryPath: AbsolutePath + let directoryPath: Basics.AbsolutePath private let encoder: JSONEncoder private let decoder: JSONDecoder - public init(fileSystem: FileSystem, directoryPath: AbsolutePath) { + public init(fileSystem: FileSystem, directoryPath: Basics.AbsolutePath) { self.fileSystem = fileSystem self.directoryPath = directoryPath diff --git a/Sources/PackageFingerprint/PackageFingerprintStorage.swift b/Sources/PackageFingerprint/PackageFingerprintStorage.swift index 94355c02a0c..397a8e3f308 100644 --- a/Sources/PackageFingerprint/PackageFingerprintStorage.swift +++ b/Sources/PackageFingerprint/PackageFingerprintStorage.swift @@ -13,6 +13,7 @@ import Basics import Dispatch import PackageModel +import TSCBasic import struct TSCUtility.Version diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index e91f2d8f0a2..03b8ebd1e23 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -14,6 +14,7 @@ import Basics import OrderedCollections import PackageLoading import PackageModel +import Foundation import func TSCBasic.bestMatch import func TSCBasic.findCycle diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index d0ed47cfdb4..928f3c9d405 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -10,12 +10,14 @@ // //===----------------------------------------------------------------------===// -import protocol Basics.FileSystem -import class Basics.ObservabilityScope -import struct Basics.IdentifiableSet import OrderedCollections import PackageLoading import PackageModel +import TSCBasic + +import protocol Basics.FileSystem +import class Basics.ObservabilityScope +import struct Basics.IdentifiableSet enum PackageGraphError: Swift.Error { /// Indicates a non-root package with no modules. @@ -268,7 +270,7 @@ public struct ModulesGraph { for module in rootModules where module.type == .executable { // Find all dependencies of this module within its package. Note that we do not traverse plugin usages. - let dependencies = try topologicalSort(module.dependencies, successors: { + let dependencies = try topologicalSortIdentifiable(module.dependencies, successors: { $0.dependencies.compactMap{ $0.module }.filter{ $0.type != .plugin }.map{ .module($0, conditions: []) } }).compactMap({ $0.module }) @@ -399,12 +401,12 @@ enum GraphError: Error { /// /// - Complexity: O(v + e) where (v, e) are the number of vertices and edges /// reachable from the input nodes via the relation. -func topologicalSort( +func topologicalSortIdentifiable( _ nodes: [T], successors: (T) throws -> [T] ) throws -> [T] { // Implements a topological sort via recursion and reverse postorder DFS. func visit(_ node: T, - _ stack: inout OrderedSet, _ visited: inout Set, _ result: inout [T], + _ stack: inout OrderedCollections.OrderedSet, _ visited: inout Set, _ result: inout [T], _ successors: (T) throws -> [T]) throws { // Mark this node as visited -- we are done if it already was. if !visited.insert(node.id).inserted { @@ -431,7 +433,7 @@ func topologicalSort( // FIXME: This should use a stack not recursion. var visited = Set() var result = [T]() - var stack = OrderedSet() + var stack = OrderedCollections.OrderedSet() for node in nodes { precondition(stack.isEmpty) stack.append(node.id) diff --git a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift index 8352c146820..6508e5e145d 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift @@ -13,6 +13,7 @@ import Basics import Dispatch import PackageModel +import TSCBasic /// An utility class around PackageContainerProvider that allows "prefetching" the containers /// in parallel. The basic idea is to kick off container fetching before starting the resolution diff --git a/Sources/PackageGraph/Resolution/PubGrub/DiagnosticReportBuilder.swift b/Sources/PackageGraph/Resolution/PubGrub/DiagnosticReportBuilder.swift index 7792390d1eb..c1b6275dcee 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/DiagnosticReportBuilder.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/DiagnosticReportBuilder.swift @@ -10,6 +10,10 @@ // //===----------------------------------------------------------------------===// +import Foundation +import PackageModel +import TSCUtility + struct DiagnosticReportBuilder { let rootNode: DependencyResolutionNode let incompatibilities: [DependencyResolutionNode: [Incompatibility]] diff --git a/Sources/PackageGraph/Resolution/ResolvedModule.swift b/Sources/PackageGraph/Resolution/ResolvedModule.swift index c37e82c26a6..5f4aec5625c 100644 --- a/Sources/PackageGraph/Resolution/ResolvedModule.swift +++ b/Sources/PackageGraph/Resolution/ResolvedModule.swift @@ -12,6 +12,7 @@ import PackageModel +import func TSCBasic.topologicalSort import struct Basics.IdentifiableSet @available(*, deprecated, renamed: "ResolvedModule") diff --git a/Sources/PackageLoading/ManifestLoader+Validation.swift b/Sources/PackageLoading/ManifestLoader+Validation.swift index 285736c1198..a0213af9239 100644 --- a/Sources/PackageLoading/ManifestLoader+Validation.swift +++ b/Sources/PackageLoading/ManifestLoader+Validation.swift @@ -13,6 +13,8 @@ import Basics import Foundation import PackageModel +import TSCBasic +import TSCUtility public struct ManifestValidator { static var supportedLocalBinaryDependencyExtensions: [String] { @@ -160,7 +162,7 @@ public struct ManifestValidator { continue } - guard let relativePath = try? RelativePath(validating: path) else { + guard let relativePath = try? Basics.RelativePath(validating: path) else { diagnostics.append(.invalidLocalBinaryPath(path: path, targetName: target.name)) continue } @@ -274,7 +276,7 @@ public struct ManifestValidator { } public protocol ManifestSourceControlValidator { - func isValidDirectory(_ path: AbsolutePath) throws -> Bool + func isValidDirectory(_ path: Basics.AbsolutePath) throws -> Bool } extension Basics.Diagnostic { @@ -344,7 +346,7 @@ extension Basics.Diagnostic { } } - static func invalidSourceControlDirectory(_ path: AbsolutePath, underlyingError: Error? = nil) -> Self { + static func invalidSourceControlDirectory(_ path: Basics.AbsolutePath, underlyingError: Error? = nil) -> Self { .error("cannot clone from local directory \(path)\nPlease git init or use \"path:\" for \(path)\(errorSuffix(underlyingError))") } diff --git a/Sources/PackageLoading/ModuleMapGenerator.swift b/Sources/PackageLoading/ModuleMapGenerator.swift index 3009970eb3a..5f9cb4cb09d 100644 --- a/Sources/PackageLoading/ModuleMapGenerator.swift +++ b/Sources/PackageLoading/ModuleMapGenerator.swift @@ -13,11 +13,12 @@ import Basics import Foundation import PackageModel +import TSCBasic /// Name of the module map file recognized by the Clang and Swift compilers. public let moduleMapFilename = "module.modulemap" -extension AbsolutePath { +extension Basics.AbsolutePath { fileprivate var moduleEscapedPathString: String { return self.pathString.replacing("\\", with: "\\\\") } @@ -25,27 +26,27 @@ extension AbsolutePath { /// A protocol for targets which might have a modulemap. protocol ModuleMapProtocol { - var moduleMapPath: AbsolutePath { get } + var moduleMapPath: Basics.AbsolutePath { get } - var moduleMapDirectory: AbsolutePath { get } + var moduleMapDirectory: Basics.AbsolutePath { get } } extension SystemLibraryModule: ModuleMapProtocol { - var moduleMapDirectory: AbsolutePath { + var moduleMapDirectory: Basics.AbsolutePath { return path } - public var moduleMapPath: AbsolutePath { + public var moduleMapPath: Basics.AbsolutePath { return moduleMapDirectory.appending(component: moduleMapFilename) } } extension ClangModule: ModuleMapProtocol { - var moduleMapDirectory: AbsolutePath { + var moduleMapDirectory: Basics.AbsolutePath { return includeDir } - public var moduleMapPath: AbsolutePath { + public var moduleMapPath: Basics.AbsolutePath { return moduleMapDirectory.appending(component: moduleMapFilename) } } @@ -75,12 +76,12 @@ public struct ModuleMapGenerator { private let moduleName: String /// The target's public-headers directory. - private let publicHeadersDir: AbsolutePath + private let publicHeadersDir: Basics.AbsolutePath /// The file system to be used. private let fileSystem: FileSystem - public init(targetName: String, moduleName: String, publicHeadersDir: AbsolutePath, fileSystem: FileSystem) { + public init(targetName: String, moduleName: String, publicHeadersDir: Basics.AbsolutePath, fileSystem: FileSystem) { self.targetName = targetName self.moduleName = moduleName self.publicHeadersDir = publicHeadersDir @@ -110,7 +111,7 @@ public struct ModuleMapGenerator { } // Next try to get the entries in the public-headers directory. - let entries: Set + let entries: Set do { entries = try Set(fileSystem.getDirectoryContents(publicHeadersDir).map({ publicHeadersDir.appending(component: $0) })) } @@ -174,7 +175,7 @@ public struct ModuleMapGenerator { } /// Generates a module map based of the specified type, throwing an error if anything goes wrong. Any diagnostics are added to the receiver's diagnostics engine. - public func generateModuleMap(type: GeneratedModuleMapType, at path: AbsolutePath) throws { + public func generateModuleMap(type: GeneratedModuleMapType, at path: Basics.AbsolutePath) throws { var moduleMap = "module \(moduleName) {\n" switch type { case .umbrellaHeader(let hdr): @@ -205,8 +206,8 @@ public struct ModuleMapGenerator { /// A type of module map to generate. public enum GeneratedModuleMapType { - case umbrellaHeader(AbsolutePath) - case umbrellaDirectory(AbsolutePath) + case umbrellaHeader(Basics.AbsolutePath) + case umbrellaDirectory(Basics.AbsolutePath) } public extension ModuleMapType { @@ -223,32 +224,32 @@ public extension ModuleMapType { private extension Basics.Diagnostic { /// Warning emitted if the public-headers directory is missing. - static func missingPublicHeadersDirectory(targetName: String, publicHeadersDir: AbsolutePath) -> Self { + static func missingPublicHeadersDirectory(targetName: String, publicHeadersDir: Basics.AbsolutePath) -> Self { .warning("no include directory found for target '\(targetName)'; libraries cannot be imported without public headers") } /// Error emitted if the public-headers directory is inaccessible. - static func inaccessiblePublicHeadersDirectory(targetName: String, publicHeadersDir: AbsolutePath, fileSystemError: Error) -> Self { + static func inaccessiblePublicHeadersDirectory(targetName: String, publicHeadersDir: Basics.AbsolutePath, fileSystemError: Error) -> Self { .error("cannot access public-headers directory for target '\(targetName)': \(String(describing: fileSystemError))") } /// Warning emitted if a misnamed umbrella header was found. - static func misnamedUmbrellaHeader(misnamedUmbrellaHeader: AbsolutePath, umbrellaHeader: AbsolutePath) -> Self { + static func misnamedUmbrellaHeader(misnamedUmbrellaHeader: Basics.AbsolutePath, umbrellaHeader: Basics.AbsolutePath) -> Self { .warning("\(misnamedUmbrellaHeader) should be renamed to \(umbrellaHeader) to be used as an umbrella header") } /// Error emitted if there are directories next to a top-level umbrella header. - static func umbrellaHeaderHasSiblingDirectories(targetName: String, umbrellaHeader: AbsolutePath, siblingDirs: Set) -> Self { + static func umbrellaHeaderHasSiblingDirectories(targetName: String, umbrellaHeader: Basics.AbsolutePath, siblingDirs: Set) -> Self { .error("target '\(targetName)' has invalid header layout: umbrella header found at '\(umbrellaHeader)', but directories exist next to it: \(siblingDirs.map({ String(describing: $0) }).sorted().joined(separator: ", ")); consider removing them") } /// Error emitted if there are other directories next to the parent directory of a nested umbrella header. - static func umbrellaHeaderParentDirHasSiblingDirectories(targetName: String, umbrellaHeader: AbsolutePath, siblingDirs: Set) -> Self { + static func umbrellaHeaderParentDirHasSiblingDirectories(targetName: String, umbrellaHeader: Basics.AbsolutePath, siblingDirs: Set) -> Self { .error("target '\(targetName)' has invalid header layout: umbrella header found at '\(umbrellaHeader)', but more than one directory exists next to its parent directory: \(siblingDirs.map({ String(describing: $0) }).sorted().joined(separator: ", ")); consider reducing them to one") } /// Error emitted if there are other headers next to the parent directory of a nested umbrella header. - static func umbrellaHeaderParentDirHasSiblingHeaders(targetName: String, umbrellaHeader: AbsolutePath, siblingHeaders: Set) -> Self { + static func umbrellaHeaderParentDirHasSiblingHeaders(targetName: String, umbrellaHeader: Basics.AbsolutePath, siblingHeaders: Set) -> Self { .error("target '\(targetName)' has invalid header layout: umbrella header found at '\(umbrellaHeader)', but additional header files exist: \((siblingHeaders.map({ String(describing: $0) }).sorted().joined(separator: ", "))); consider reducing them to one") } } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 5e8f0c81122..8524e8ff707 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -14,6 +14,8 @@ import Basics import Dispatch import OrderedCollections import PackageModel +import Foundation +import TSCUtility import func TSCBasic.findCycle import struct TSCBasic.KeyedPair diff --git a/Sources/PackageLoading/PkgConfig.swift b/Sources/PackageLoading/PkgConfig.swift index b98d01932d5..86509d0f5cd 100644 --- a/Sources/PackageLoading/PkgConfig.swift +++ b/Sources/PackageLoading/PkgConfig.swift @@ -13,6 +13,8 @@ import Basics import Foundation import OrderedCollections +import TSCUtility +import TSCBasic import class Basics.AsyncProcess @@ -22,7 +24,7 @@ public struct PkgConfig { public let name: String /// The path to the definition file. - public let pcFile: AbsolutePath + public let pcFile: Basics.AbsolutePath /// The list of C compiler flags in the definition. public let cFlags: [String] @@ -44,9 +46,9 @@ public struct PkgConfig { /// - throws: PkgConfigError public init( name: String, - additionalSearchPaths: [AbsolutePath]? = .none, - brewPrefix: AbsolutePath? = .none, - sysrootDir: AbsolutePath? = .none, + additionalSearchPaths: [Basics.AbsolutePath]? = .none, + brewPrefix: Basics.AbsolutePath? = .none, + sysrootDir: Basics.AbsolutePath? = .none, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws { @@ -63,16 +65,16 @@ public struct PkgConfig { private init( name: String, - additionalSearchPaths: [AbsolutePath], - brewPrefix: AbsolutePath?, - sysrootDir: AbsolutePath?, + additionalSearchPaths: [Basics.AbsolutePath], + brewPrefix: Basics.AbsolutePath?, + sysrootDir: Basics.AbsolutePath?, loadingContext: LoadingContext, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws { loadingContext.pkgConfigStack.append(name) - if let path = try? AbsolutePath(validating: name) { + if let path = try? Basics.AbsolutePath(validating: name) { guard fileSystem.isFile(path) else { throw PkgConfigError.couldNotFindConfigFile(name: name) } self.name = path.basenameWithoutExt self.pcFile = path @@ -127,7 +129,7 @@ public struct PkgConfig { loadingContext.pkgConfigStack.removeLast(); } - private static var envSearchPaths: [AbsolutePath] { + private static var envSearchPaths: [Basics.AbsolutePath] { get throws { if let configPath = Environment.current["PKG_CONFIG_PATH"] { #if os(Windows) @@ -158,7 +160,7 @@ extension PkgConfig { /// See: https://www.freedesktop.org/wiki/Software/pkg-config/ // This is only internal so it can be unit tested. internal struct PkgConfigParser { - public let pcFile: AbsolutePath + public let pcFile: Basics.AbsolutePath private let fileSystem: FileSystem public private(set) var variables = [String: String]() public private(set) var dependencies = [String]() @@ -167,7 +169,7 @@ internal struct PkgConfigParser { public private(set) var libs = [String]() public private(set) var sysrootDir: String? - public init(pcFile: AbsolutePath, fileSystem: FileSystem, sysrootDir: String?) throws { + public init(pcFile: Basics.AbsolutePath, fileSystem: FileSystem, sysrootDir: String?) throws { guard fileSystem.isFile(pcFile) else { throw StringError("invalid pcfile \(pcFile)") } @@ -268,7 +270,7 @@ internal struct PkgConfigParser { // Add pc_sysrootdir variable. This is the path of the sysroot directory for pc files. // pkgconf does not define pc_sysrootdir if the path of the .pc file is outside sysrootdir. // SwiftPM does not currently make that check. - variables["pc_sysrootdir"] = sysrootDir ?? AbsolutePath.root.pathString + variables["pc_sysrootdir"] = sysrootDir ?? Basics.AbsolutePath.root.pathString let fileContents: String = try fileSystem.readFileContents(pcFile) for line in fileContents.components(separatedBy: "\n") { @@ -461,7 +463,7 @@ internal struct PCFileFinder { /// FIXME: This shouldn't use a static variable, since the first lookup /// will cache the result of whatever `brewPrefix` was passed in. It is /// also not threadsafe. - public private(set) static var pkgConfigPaths: [AbsolutePath]? // FIXME: @testable(internal) + public private(set) static var pkgConfigPaths: [Basics.AbsolutePath]? // FIXME: @testable(internal) private static var shouldEmitPkgConfigPathsDiagnostic = false /// The built-in search path list. @@ -469,7 +471,7 @@ internal struct PCFileFinder { /// By default, this is combined with the search paths inferred from /// `pkg-config` itself. static let searchPaths = [ - try? AbsolutePath(validating: "/usr/local/lib/pkgconfig"), + try? Basics.AbsolutePath(validating: "/usr/local/lib/pkgconfig"), try? AbsolutePath(validating: "/usr/local/share/pkgconfig"), try? AbsolutePath(validating: "/usr/lib/pkgconfig"), try? AbsolutePath(validating: "/usr/share/pkgconfig"), @@ -498,11 +500,11 @@ internal struct PCFileFinder { } } - public init(brewPrefix: AbsolutePath?) { + public init(brewPrefix: Basics.AbsolutePath?) { self.init(pkgConfigPath: brewPrefix?.appending(components: "bin", "pkg-config").pathString ?? "pkg-config") } - public init(pkgConfig: AbsolutePath? = .none) { + public init(pkgConfig: Basics.AbsolutePath? = .none) { self.init(pkgConfigPath: pkgConfig?.pathString ?? "pkg-config") } @@ -516,10 +518,10 @@ internal struct PCFileFinder { public func locatePCFile( name: String, - customSearchPaths: [AbsolutePath], + customSearchPaths: [Basics.AbsolutePath], fileSystem: FileSystem, observabilityScope: ObservabilityScope - ) throws -> AbsolutePath { + ) throws -> Basics.AbsolutePath { // FIXME: We should consider building a registry for all items in the // search paths, which is likely to be substantially more efficient if // we end up searching for a reasonably sized number of packages. diff --git a/Sources/PackageLoading/Platform.swift b/Sources/PackageLoading/Platform.swift index b98a3848cf4..cb7ddeecca8 100644 --- a/Sources/PackageLoading/Platform.swift +++ b/Sources/PackageLoading/Platform.swift @@ -12,12 +12,13 @@ import Basics import Foundation +import TSCBasic import class Basics.AsyncProcess private func isAndroid() -> Bool { - (try? localFileSystem.isFile(AbsolutePath(validating: "/system/bin/toolchain"))) ?? false || - (try? localFileSystem.isFile(AbsolutePath(validating: "/system/bin/toybox"))) ?? false + (try? Basics.localFileSystem.isFile(AbsolutePath(validating: "/system/bin/toolchain"))) ?? false || + (try? Basics.localFileSystem.isFile(AbsolutePath(validating: "/system/bin/toybox"))) ?? false } public enum Platform: Equatable, Sendable { @@ -48,7 +49,7 @@ extension Platform { case "freebsd"?: return .freebsd case "linux"?: - return Platform.findCurrentPlatformLinux(localFileSystem) + return Platform.findCurrentPlatformLinux(Basics.localFileSystem) default: return nil } diff --git a/Sources/PackageLoading/RegistryReleaseMetadataSerialization.swift b/Sources/PackageLoading/RegistryReleaseMetadataSerialization.swift index cf60cd9a211..31dd959ea30 100644 --- a/Sources/PackageLoading/RegistryReleaseMetadataSerialization.swift +++ b/Sources/PackageLoading/RegistryReleaseMetadataSerialization.swift @@ -13,6 +13,7 @@ import Basics import Foundation import PackageModel +import TSCBasic public enum RegistryReleaseMetadataStorage { public static let fileName = ".registry-metadata" @@ -20,13 +21,13 @@ public enum RegistryReleaseMetadataStorage { private static let encoder = JSONEncoder.makeWithDefaults() private static let decoder = JSONDecoder.makeWithDefaults() - public static func save(_ metadata: RegistryReleaseMetadata, to path: AbsolutePath, fileSystem: FileSystem) throws { + public static func save(_ metadata: RegistryReleaseMetadata, to path: Basics.AbsolutePath, fileSystem: FileSystem) throws { let codableMetadata = CodableRegistryReleaseMetadata(metadata) let data = try Self.encoder.encode(codableMetadata) try fileSystem.writeFileContents(path, data: data) } - public static func load(from path: AbsolutePath, fileSystem: FileSystem) throws -> RegistryReleaseMetadata { + public static func load(from path: Basics.AbsolutePath, fileSystem: FileSystem) throws -> RegistryReleaseMetadata { let codableMetadata = try Self.decoder.decode( path: path, fileSystem: fileSystem, diff --git a/Sources/PackageLoading/Target+PkgConfig.swift b/Sources/PackageLoading/Target+PkgConfig.swift index 8ed3d9db588..49b6fc30be7 100644 --- a/Sources/PackageLoading/Target+PkgConfig.swift +++ b/Sources/PackageLoading/Target+PkgConfig.swift @@ -12,6 +12,7 @@ import Basics import PackageModel +import Foundation import class Basics.AsyncProcess import struct TSCBasic.RegEx diff --git a/Sources/PackageLoading/TargetSourcesBuilder.swift b/Sources/PackageLoading/TargetSourcesBuilder.swift index 0d22e06101e..17f36fb2cbe 100644 --- a/Sources/PackageLoading/TargetSourcesBuilder.swift +++ b/Sources/PackageLoading/TargetSourcesBuilder.swift @@ -13,6 +13,7 @@ import Basics import Foundation import PackageModel +import TSCBasic /// A utility to compute the source/resource files of a target. public struct TargetSourcesBuilder { @@ -23,19 +24,19 @@ public struct TargetSourcesBuilder { public let packageKind: PackageReference.Kind /// The package path. - public let packagePath: AbsolutePath + public let packagePath: Basics.AbsolutePath /// The target for which we're computing source/resource files. public let target: TargetDescription /// The path of the target. - public let targetPath: AbsolutePath + public let targetPath: Basics.AbsolutePath /// The list of declared sources in the package manifest. - public let declaredSources: [AbsolutePath]? + public let declaredSources: [Basics.AbsolutePath]? /// The list of declared resources in the package manifest. - public let declaredResources: [(path: AbsolutePath, rule: TargetDescription.Resource.Rule)] + public let declaredResources: [(path: Basics.AbsolutePath, rule: TargetDescription.Resource.Rule)] /// The default localization. public let defaultLocalization: String? @@ -47,7 +48,7 @@ public struct TargetSourcesBuilder { public let toolsVersion: ToolsVersion /// The set of paths that should be excluded from any consideration. - public let excludedPaths: Set + public let excludedPaths: Set /// The set of opaque directories extensions (should not be treated as source) private let opaqueDirectoriesExtensions: Set @@ -62,9 +63,9 @@ public struct TargetSourcesBuilder { public init( packageIdentity: PackageIdentity, packageKind: PackageReference.Kind, - packagePath: AbsolutePath, + packagePath: Basics.AbsolutePath, target: TargetDescription, - path: AbsolutePath, + path: Basics.AbsolutePath, defaultLocalization: String?, additionalFileRules: [FileRuleDescription], toolsVersion: ToolsVersion, @@ -136,7 +137,7 @@ public struct TargetSourcesBuilder { } @discardableResult - private func validTargetPath(at: AbsolutePath) -> Error? { + private func validTargetPath(at: Basics.AbsolutePath) -> Error? { // Check if paths that are enumerated in targets: [] exist guard self.fileSystem.exists(at) else { return StringError("File not found") @@ -165,11 +166,11 @@ public struct TargetSourcesBuilder { } /// Run the builder to produce the sources of the target. - public func run() throws -> (sources: Sources, resources: [Resource], headers: [AbsolutePath], ignored: [AbsolutePath], others: [AbsolutePath]) { + public func run() throws -> (sources: Sources, resources: [Resource], headers: [Basics.AbsolutePath], ignored: [Basics.AbsolutePath], others: [Basics.AbsolutePath]) { let contents = self.computeContents() - var pathToRule: [AbsolutePath: FileRuleDescription.Rule] = [:] + var pathToRule: [Basics.AbsolutePath: FileRuleDescription.Rule] = [:] - var handledResources = [AbsolutePath]() + var handledResources = [Basics.AbsolutePath]() for path in contents { pathToRule[path] = Self.computeRule( for: path, @@ -222,7 +223,7 @@ public struct TargetSourcesBuilder { } /// Compute the rule for the given path. - private static func computeRule(for path: AbsolutePath, + private static func computeRule(for path: Basics.AbsolutePath, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription], observabilityScope: ObservabilityScope) -> FileRuleDescription.Rule { @@ -232,12 +233,12 @@ public struct TargetSourcesBuilder { } private static func computeRule( - for path: AbsolutePath, + for path: Basics.AbsolutePath, toolsVersion: ToolsVersion, rules: [FileRuleDescription], - declaredResources: [(path: AbsolutePath, rule: TargetDescription.Resource.Rule)], - declaredSources: [AbsolutePath]?, - matchingResourceRuleHandler: (AbsolutePath) -> () = { _ in }, + declaredResources: [(path: Basics.AbsolutePath, rule: TargetDescription.Resource.Rule)], + declaredSources: [Basics.AbsolutePath]?, + matchingResourceRuleHandler: (Basics.AbsolutePath) -> () = { _ in }, observabilityScope: ObservabilityScope ) -> FileRuleDescription.Rule { var matchedRule: FileRuleDescription.Rule = .none @@ -303,7 +304,7 @@ public struct TargetSourcesBuilder { } /// Returns the `Resource` file associated with a file and a particular rule, if there is one. - private static func resource(for path: AbsolutePath, with rule: FileRuleDescription.Rule, defaultLocalization: String?, targetName: String, targetPath: AbsolutePath, observabilityScope: ObservabilityScope) -> Resource? { + private static func resource(for path: Basics.AbsolutePath, with rule: FileRuleDescription.Rule, defaultLocalization: String?, targetName: String, targetPath: Basics.AbsolutePath, observabilityScope: ObservabilityScope) -> Resource? { switch rule { case .compile, .header, .none, .modulemap, .ignored: return nil @@ -342,7 +343,7 @@ public struct TargetSourcesBuilder { } } - private func resource(for path: AbsolutePath, with rule: FileRuleDescription.Rule) -> Resource? { + private func resource(for path: Basics.AbsolutePath, with rule: FileRuleDescription.Rule) -> Resource? { return Self.resource(for: path, with: rule, defaultLocalization: defaultLocalization, targetName: target.name, targetPath: targetPath, observabilityScope: observabilityScope) } @@ -406,7 +407,7 @@ public struct TargetSourcesBuilder { } /// Returns true if the given path is a declared source. - func isDeclaredSource(_ path: AbsolutePath) -> Bool { + func isDeclaredSource(_ path: Basics.AbsolutePath) -> Bool { return path == targetPath || declaredSources?.contains(path) == true } @@ -414,9 +415,9 @@ public struct TargetSourcesBuilder { /// /// This avoids recursing into certain directories like exclude or the /// ones that should be copied as-is. - public func computeContents() -> [AbsolutePath] { - var contents: [AbsolutePath] = [] - var queue: [AbsolutePath] = [targetPath] + public func computeContents() -> [Basics.AbsolutePath] { + var contents: [Basics.AbsolutePath] = [] + var queue: [Basics.AbsolutePath] = [targetPath] // Ignore xcodeproj and playground directories. var ignoredDirectoryExtensions = ["xcodeproj", "playground", "xcworkspace"] @@ -518,8 +519,8 @@ public struct TargetSourcesBuilder { return contents } - public static func computeContents(for generatedFiles: [AbsolutePath], toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription], defaultLocalization: String?, targetName: String, targetPath: AbsolutePath, observabilityScope: ObservabilityScope) -> (sources: [AbsolutePath], resources: [Resource]) { - var sources = [AbsolutePath]() + public static func computeContents(for generatedFiles: [Basics.AbsolutePath], toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription], defaultLocalization: String?, targetName: String, targetPath: Basics.AbsolutePath, observabilityScope: ObservabilityScope) -> (sources: [Basics.AbsolutePath], resources: [Resource]) { + var sources = [Basics.AbsolutePath]() var resources = [Resource]() generatedFiles.forEach { absPath in @@ -613,7 +614,7 @@ public struct FileRuleDescription: Sendable { } /// Match the given path to the rule. - public func match(path: AbsolutePath, toolsVersion: ToolsVersion) -> Bool { + public func match(path: Basics.AbsolutePath, toolsVersion: ToolsVersion) -> Bool { if toolsVersion < self.toolsVersion { return false } @@ -800,12 +801,12 @@ extension Resource { } extension Basics.Diagnostic { - static func symlinkInSources(symlink: RelativePath, targetName: String) -> Self { + static func symlinkInSources(symlink: Basics.RelativePath, targetName: String) -> Self { .warning("ignoring symlink at '\(symlink)' in target '\(targetName)'") } static func localizationDirectoryContainsSubDirectories( - localizationDirectory: RelativePath, + localizationDirectory: Basics.RelativePath, targetName: String ) -> Self { .error("localization directory '\(localizationDirectory)' in target '\(targetName)' contains sub-directories, which is forbidden") @@ -839,7 +840,7 @@ extension PackageReference.Kind { } extension PackageModel.Resource { - fileprivate var destinationForGrouping: RelativePath? { + fileprivate var destinationForGrouping: Basics.RelativePath? { do { return try self.destination } catch { diff --git a/Sources/PackageModel/ArtifactsArchiveMetadata.swift b/Sources/PackageModel/ArtifactsArchiveMetadata.swift index b76333ae4a0..1c07c2ed035 100644 --- a/Sources/PackageModel/ArtifactsArchiveMetadata.swift +++ b/Sources/PackageModel/ArtifactsArchiveMetadata.swift @@ -12,6 +12,7 @@ import Basics import Foundation +import TSCBasic import struct TSCUtility.Version @@ -51,10 +52,10 @@ public struct ArtifactsArchiveMetadata: Equatable { } public struct Variant: Equatable { - public let path: RelativePath + public let path: Basics.RelativePath public let supportedTriples: [Triple]? - public init(path: RelativePath, supportedTriples: [Triple]?) { + public init(path: Basics.RelativePath, supportedTriples: [Triple]?) { self.path = path self.supportedTriples = supportedTriples } @@ -62,7 +63,7 @@ public struct ArtifactsArchiveMetadata: Equatable { } extension ArtifactsArchiveMetadata { - public static func parse(fileSystem: FileSystem, rootPath: AbsolutePath) throws -> ArtifactsArchiveMetadata { + public static func parse(fileSystem: FileSystem, rootPath: Basics.AbsolutePath) throws -> ArtifactsArchiveMetadata { let path = rootPath.appending("info.json") guard fileSystem.exists(path) else { throw StringError("ArtifactsArchive info.json not found at '\(rootPath)'") diff --git a/Sources/PackageModel/MinimumDeploymentTarget.swift b/Sources/PackageModel/MinimumDeploymentTarget.swift index 77f507f0345..d2510e323ce 100644 --- a/Sources/PackageModel/MinimumDeploymentTarget.swift +++ b/Sources/PackageModel/MinimumDeploymentTarget.swift @@ -11,12 +11,14 @@ //===----------------------------------------------------------------------===// import Basics +import Foundation +import TSCUtility import class Basics.AsyncProcess public struct MinimumDeploymentTarget { private struct MinimumDeploymentTargetKey: Hashable { - let binaryPath: AbsolutePath + let binaryPath: Basics.AbsolutePath let platform: PackageModel.Platform } @@ -28,7 +30,7 @@ public struct MinimumDeploymentTarget { private init() { } - public func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath, platform: PackageModel.Platform) throws -> PlatformVersion { + public func computeMinimumDeploymentTarget(of binaryPath: Basics.AbsolutePath, platform: PackageModel.Platform) throws -> PlatformVersion { try self.minimumDeploymentTargets.memoize(MinimumDeploymentTargetKey(binaryPath: binaryPath, platform: platform)) { return try Self.computeMinimumDeploymentTarget(of: binaryPath, platform: platform) ?? platform.oldestSupportedVersion } @@ -40,7 +42,7 @@ public struct MinimumDeploymentTarget { } } - static func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath, platform: PackageModel.Platform) throws -> PlatformVersion? { + static func computeMinimumDeploymentTarget(of binaryPath: Basics.AbsolutePath, platform: PackageModel.Platform) throws -> PlatformVersion? { guard let (_, platformName) = platform.sdkNameAndPlatform else { return nil } @@ -58,8 +60,8 @@ public struct MinimumDeploymentTarget { static func computeXCTestMinimumDeploymentTarget(with runResult: AsyncProcessResult, platform: PackageModel.Platform) throws -> PlatformVersion? { guard let output = try runResult.utf8Output().spm_chuzzle() else { return nil } - let sdkPath = try AbsolutePath(validating: output) - let xcTestPath = try AbsolutePath(validating: "Developer/Library/Frameworks/XCTest.framework/XCTest", relativeTo: sdkPath) + let sdkPath = try Basics.AbsolutePath(validating: output) + let xcTestPath = try Basics.AbsolutePath(validating: "Developer/Library/Frameworks/XCTest.framework/XCTest", relativeTo: sdkPath) return try computeMinimumDeploymentTarget(of: xcTestPath, platform: platform) } diff --git a/Sources/PackageModel/Module/ClangModule.swift b/Sources/PackageModel/Module/ClangModule.swift index f1506aeb15e..bf066a6ecea 100644 --- a/Sources/PackageModel/Module/ClangModule.swift +++ b/Sources/PackageModel/Module/ClangModule.swift @@ -12,6 +12,7 @@ import struct Basics.AbsolutePath import struct Basics.StringError +import TSCBasic @available(*, deprecated, renamed: "ClangModule") public typealias ClangTarget = ClangModule @@ -24,7 +25,7 @@ public final class ClangModule: Module { public static let defaultPublicHeadersComponent = "include" /// The path to include directory. - public let includeDir: AbsolutePath + public let includeDir: Basics.AbsolutePath /// The target's module map type, which determines whether this target vends a custom module map, a generated module map, or no module map at all. public let moduleMapType: ModuleMapType @@ -32,7 +33,7 @@ public final class ClangModule: Module { /// The headers present in the target. /// /// Note that this contains both public and non-public headers. - public let headers: [AbsolutePath] + public let headers: [Basics.AbsolutePath] /// True if this is a C++ target. public let isCXX: Bool @@ -48,15 +49,15 @@ public final class ClangModule: Module { potentialBundleName: String? = nil, cLanguageStandard: String?, cxxLanguageStandard: String?, - includeDir: AbsolutePath, + includeDir: Basics.AbsolutePath, moduleMapType: ModuleMapType, - headers: [AbsolutePath] = [], + headers: [Basics.AbsolutePath] = [], type: Kind, - path: AbsolutePath, + path: Basics.AbsolutePath, sources: Sources, resources: [Resource] = [], - ignored: [AbsolutePath] = [], - others: [AbsolutePath] = [], + ignored: [Basics.AbsolutePath] = [], + others: [Basics.AbsolutePath] = [], dependencies: [Module.Dependency] = [], buildSettings: BuildSettings.AssignmentTable = .init(), buildSettingsDescription: [TargetBuildSettingDescription.Setting] = [], diff --git a/Sources/PackageModel/Module/Module.swift b/Sources/PackageModel/Module/Module.swift index 023033344f2..786e99de9f3 100644 --- a/Sources/PackageModel/Module/Module.swift +++ b/Sources/PackageModel/Module/Module.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Basics +import TSCUtility @available(*, deprecated, renamed: "Module") public typealias Target = Module diff --git a/Sources/PackageModel/Module/PluginModule.swift b/Sources/PackageModel/Module/PluginModule.swift index 374d7be036b..94c5d8eed1c 100644 --- a/Sources/PackageModel/Module/PluginModule.swift +++ b/Sources/PackageModel/Module/PluginModule.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import Basics + @available(*, deprecated, renamed: "PluginModule") public typealias PluginTarget = PluginModule diff --git a/Sources/PackageModel/PackageIdentity.swift b/Sources/PackageModel/PackageIdentity.swift index eee8b9538a4..bbc1b248694 100644 --- a/Sources/PackageModel/PackageIdentity.swift +++ b/Sources/PackageModel/PackageIdentity.swift @@ -12,6 +12,7 @@ import Basics import Foundation +import TSCBasic /// The canonical identifier for a package, based on its source location. public struct PackageIdentity: CustomStringConvertible, Sendable { @@ -39,7 +40,7 @@ public struct PackageIdentity: CustomStringConvertible, Sendable { /// Creates a package identity from a file path. /// - Parameter path: An absolute path to the package. - public init(path: AbsolutePath) { + public init(path: Basics.AbsolutePath) { self.description = PackageIdentityParser(path.pathString).description } @@ -310,7 +311,7 @@ struct PackageIdentityParser { } /// Compute the default name of a package given its path. - public static func computeDefaultName(fromPath path: AbsolutePath) -> String { + public static func computeDefaultName(fromPath path: Basics.AbsolutePath) -> String { Self.computeDefaultName(fromLocation: path.pathString) } diff --git a/Sources/PackageModel/Snippets/Model/Snippet.swift b/Sources/PackageModel/Snippets/Model/Snippet.swift index d44e8753d31..a74e3d92650 100644 --- a/Sources/PackageModel/Snippets/Model/Snippet.swift +++ b/Sources/PackageModel/Snippets/Model/Snippet.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Basics +import Foundation public struct Snippet { public var path: AbsolutePath diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index 066d29c1147..aacfad57428 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -12,6 +12,7 @@ import Basics import Foundation +import TSCBasic import class Basics.AsyncProcess @@ -20,7 +21,7 @@ import struct TSCUtility.Version /// Errors related to Swift SDKs. public enum SwiftSDKError: Swift.Error { /// A bundle archive should contain at least one directory with the `.artifactbundle` extension. - case invalidBundleArchive(AbsolutePath) + case invalidBundleArchive(Basics.AbsolutePath) /// A passed argument is neither a valid file system path nor a URL. case invalidPathOrURL(String) @@ -41,10 +42,10 @@ public enum SwiftSDKError: Swift.Error { case invalidBundleName(String) /// No valid Swift SDKs were decoded from a metadata file. - case noSwiftSDKDecoded(AbsolutePath) + case noSwiftSDKDecoded(Basics.AbsolutePath) /// Path used for storing Swift SDK configuration data is not a directory. - case pathIsNotDirectory(AbsolutePath) + case pathIsNotDirectory(Basics.AbsolutePath) /// Swift SDK metadata couldn't be serialized with the latest serialization schema, potentially because it /// was deserialized from an earlier incompatible schema version or initialized manually with properties @@ -63,7 +64,7 @@ public enum SwiftSDKError: Swift.Error { #if os(macOS) /// Quarantine attribute should be removed by the `xattr` command from an installed bundle. - case quarantineAttributePresent(bundlePath: AbsolutePath) + case quarantineAttributePresent(bundlePath: Basics.AbsolutePath) #endif } @@ -191,7 +192,7 @@ public struct SwiftSDK: Equatable { /// Root directory path of the SDK used to compile for the target triple. @available(*, deprecated, message: "use `pathsConfiguration.sdkRootPath` instead") - public var sdk: AbsolutePath? { + public var sdk: Basics.AbsolutePath? { get { sdkRootDir } @@ -202,7 +203,7 @@ public struct SwiftSDK: Equatable { /// Root directory path of the SDK used to compile for the target triple. @available(*, deprecated, message: "use `pathsConfiguration.sdkRootPath` instead") - public var sdkRootDir: AbsolutePath? { + public var sdkRootDir: Basics.AbsolutePath? { get { pathsConfiguration.sdkRootPath } @@ -213,13 +214,13 @@ public struct SwiftSDK: Equatable { /// Path to a directory containing the toolchain (compilers/linker) to be used for the compilation. @available(*, deprecated, message: "use `toolset.rootPaths` instead") - public var binDir: AbsolutePath { + public var binDir: Basics.AbsolutePath { toolchainBinDir } /// Path to a directory containing the toolchain (compilers/linker) to be used for the compilation. @available(*, deprecated, message: "use `toolset.rootPaths` instead") - public var toolchainBinDir: AbsolutePath { + public var toolchainBinDir: Basics.AbsolutePath { toolset.rootPaths[0] } @@ -260,12 +261,12 @@ public struct SwiftSDK: Equatable { public struct PathsConfiguration: Equatable { public init( - sdkRootPath: AbsolutePath?, - swiftResourcesPath: AbsolutePath? = nil, - swiftStaticResourcesPath: AbsolutePath? = nil, - includeSearchPaths: [AbsolutePath]? = nil, - librarySearchPaths: [AbsolutePath]? = nil, - toolsetPaths: [AbsolutePath]? = nil + sdkRootPath: Basics.AbsolutePath?, + swiftResourcesPath: Basics.AbsolutePath? = nil, + swiftStaticResourcesPath: Basics.AbsolutePath? = nil, + includeSearchPaths: [Basics.AbsolutePath]? = nil, + librarySearchPaths: [Basics.AbsolutePath]? = nil, + toolsetPaths: [Basics.AbsolutePath]? = nil ) { self.sdkRootPath = sdkRootPath self.swiftResourcesPath = swiftResourcesPath @@ -276,22 +277,22 @@ public struct SwiftSDK: Equatable { } /// Root directory path of the SDK used to compile for the target triple. - public var sdkRootPath: AbsolutePath? + public var sdkRootPath: Basics.AbsolutePath? /// Path containing Swift resources for dynamic linking. - public var swiftResourcesPath: AbsolutePath? + public var swiftResourcesPath: Basics.AbsolutePath? /// Path containing Swift resources for static linking. - public var swiftStaticResourcesPath: AbsolutePath? + public var swiftStaticResourcesPath: Basics.AbsolutePath? /// Array of paths containing headers. - public var includeSearchPaths: [AbsolutePath]? + public var includeSearchPaths: [Basics.AbsolutePath]? /// Array of paths containing libraries. - public var librarySearchPaths: [AbsolutePath]? + public var librarySearchPaths: [Basics.AbsolutePath]? /// Array of paths containing toolset files. - public var toolsetPaths: [AbsolutePath]? + public var toolsetPaths: [Basics.AbsolutePath]? /// Initialize paths configuration from values deserialized using v3 schema. /// - Parameters: @@ -299,7 +300,7 @@ public struct SwiftSDK: Equatable { /// - swiftSDKDirectory: directory used for converting relative paths in `properties` to absolute paths. fileprivate init( _ properties: SerializedDestinationV3.TripleProperties, - swiftSDKDirectory: AbsolutePath? = nil + swiftSDKDirectory: Basics.AbsolutePath? = nil ) throws { if let swiftSDKDirectory { self.init( @@ -346,7 +347,7 @@ public struct SwiftSDK: Equatable { /// - Parameters: /// - properties: properties of a Swift SDK for the given triple. /// - swiftSDKDirectory: directory used for converting relative paths in `properties` to absolute paths. - fileprivate init(_ properties: SwiftSDKMetadataV4.TripleProperties, swiftSDKDirectory: AbsolutePath? = nil) throws { + fileprivate init(_ properties: SwiftSDKMetadataV4.TripleProperties, swiftSDKDirectory: Basics.AbsolutePath? = nil) throws { if let swiftSDKDirectory { self.init( sdkRootPath: try AbsolutePath(validating: properties.sdkRootPath, relativeTo: swiftSDKDirectory), @@ -422,8 +423,8 @@ public struct SwiftSDK: Equatable { @available(*, deprecated, message: "use `init(targetTriple:sdkRootDir:toolset:)` instead") public init( target: Triple? = nil, - sdk: AbsolutePath?, - binDir: AbsolutePath, + sdk: Basics.AbsolutePath?, + binDir: Basics.AbsolutePath, extraCCFlags: [String] = [], extraSwiftCFlags: [String] = [], extraCPPFlags: [String] = [] @@ -445,8 +446,8 @@ public struct SwiftSDK: Equatable { public init( hostTriple: Triple? = nil, targetTriple: Triple? = nil, - sdkRootDir: AbsolutePath?, - toolchainBinDir: AbsolutePath, + sdkRootDir: Basics.AbsolutePath?, + toolchainBinDir: Basics.AbsolutePath, extraFlags: BuildFlags = BuildFlags() ) { self.init( @@ -501,7 +502,7 @@ public struct SwiftSDK: Equatable { /// Returns the bin directory for the host. private static func hostBinDir( fileSystem: FileSystem - ) throws -> AbsolutePath { + ) throws -> Basics.AbsolutePath { guard let cwd = fileSystem.currentWorkingDirectory else { return try AbsolutePath(validating: CommandLine.arguments[0]).parentDirectory } @@ -511,8 +512,8 @@ public struct SwiftSDK: Equatable { /// The Swift SDK describing the host platform. @available(*, deprecated, renamed: "hostSwiftSDK") public static func hostDestination( - _ binDir: AbsolutePath? = nil, - originalWorkingDirectory: AbsolutePath? = nil, + _ binDir: Basics.AbsolutePath? = nil, + originalWorkingDirectory: Basics.AbsolutePath? = nil, environment: Environment ) throws -> SwiftSDK { try self.hostSwiftSDK(binDir, environment: environment) @@ -520,10 +521,10 @@ public struct SwiftSDK: Equatable { /// The Swift SDK for the host platform. public static func hostSwiftSDK( - _ binDir: AbsolutePath? = nil, + _ binDir: Basics.AbsolutePath? = nil, environment: Environment = .current, observabilityScope: ObservabilityScope? = nil, - fileSystem: any FileSystem = localFileSystem + fileSystem: any FileSystem = Basics.localFileSystem ) throws -> SwiftSDK { try self.systemSwiftSDK( binDir, @@ -538,10 +539,10 @@ public struct SwiftSDK: Equatable { /// Equivalent to `hostSwiftSDK`, except on macOS, where passing a non-nil `darwinPlatformOverride` /// will result in the SDK for the corresponding Darwin platform. private static func systemSwiftSDK( - _ binDir: AbsolutePath? = nil, + _ binDir: Basics.AbsolutePath? = nil, environment: Environment = .current, observabilityScope: ObservabilityScope? = nil, - fileSystem: any FileSystem = localFileSystem, + fileSystem: any FileSystem = Basics.localFileSystem, darwinPlatformOverride: DarwinPlatform? = nil ) throws -> SwiftSDK { // Select the correct binDir. @@ -549,10 +550,10 @@ public struct SwiftSDK: Equatable { print("SWIFTPM_CUSTOM_BINDIR was deprecated in favor of SWIFTPM_CUSTOM_BIN_DIR") } let customBinDir = (environment["SWIFTPM_CUSTOM_BIN_DIR"] ?? environment["SWIFTPM_CUSTOM_BINDIR"]) - .flatMap { try? AbsolutePath(validating: $0) } + .flatMap { try? Basics.AbsolutePath(validating: $0) } let binDir = try customBinDir ?? binDir ?? SwiftSDK.hostBinDir(fileSystem: fileSystem) - let sdkPath: AbsolutePath? + let sdkPath: Basics.AbsolutePath? #if os(macOS) let darwinPlatform = darwinPlatformOverride ?? .macOS // Get the SDK. @@ -618,17 +619,17 @@ public struct SwiftSDK: Equatable { /// - SeeAlso: ``sdkPlatformPaths(for:environment:)`` public struct PlatformPaths { /// Paths of directories containing auxiliary platform frameworks. - public var frameworks: [AbsolutePath] + public var frameworks: [Basics.AbsolutePath] /// Paths of directories containing auxiliary platform libraries. - public var libraries: [AbsolutePath] + public var libraries: [Basics.AbsolutePath] } /// Returns `macosx` sdk platform framework path. @available(*, deprecated, message: "use sdkPlatformPaths(for:) instead") public static func sdkPlatformFrameworkPaths( environment: Environment = .current - ) throws -> (fwk: AbsolutePath, lib: AbsolutePath) { + ) throws -> (fwk: Basics.AbsolutePath, lib: Basics.AbsolutePath) { let paths = try sdkPlatformPaths(for: .macOS, environment: environment) guard let frameworkPath = paths.frameworks.first else { throw StringError("could not determine SDK platform framework path") @@ -659,15 +660,15 @@ public struct SwiftSDK: Equatable { } // For testing frameworks. - let frameworksPath = try AbsolutePath(validating: platformPath).appending( + let frameworksPath = try Basics.AbsolutePath(validating: platformPath).appending( components: "Developer", "Library", "Frameworks" ) - let privateFrameworksPath = try AbsolutePath(validating: platformPath).appending( + let privateFrameworksPath = try Basics.AbsolutePath(validating: platformPath).appending( components: "Developer", "Library", "PrivateFrameworks" ) // For testing libraries. - let librariesPath = try AbsolutePath(validating: platformPath).appending( + let librariesPath = try Basics.AbsolutePath(validating: platformPath).appending( components: "Developer", "usr", "lib" ) @@ -711,11 +712,11 @@ public struct SwiftSDK: Equatable { public static func deriveTargetSwiftSDK( hostSwiftSDK: SwiftSDK, hostTriple: Triple, - customToolsets: [AbsolutePath] = [], - customCompileDestination: AbsolutePath? = nil, + customToolsets: [Basics.AbsolutePath] = [], + customCompileDestination: Basics.AbsolutePath? = nil, customCompileTriple: Triple? = nil, - customCompileToolchain: AbsolutePath? = nil, - customCompileSDK: AbsolutePath? = nil, + customCompileToolchain: Basics.AbsolutePath? = nil, + customCompileSDK: Basics.AbsolutePath? = nil, swiftSDKSelector: String? = nil, architectures: [String] = [], store: SwiftSDKBundleStore, @@ -833,7 +834,7 @@ public struct SwiftSDK: Equatable { /// Note: Use this operation if you want new root path to take priority over existing paths. /// /// - Parameter toolsetRootPath: new path to add to Swift SDK's toolset. - public mutating func prepend(toolsetRootPath path: AbsolutePath) { + public mutating func prepend(toolsetRootPath path: Basics.AbsolutePath) { self.toolset.rootPaths.insert(path, at: 0) } @@ -843,7 +844,7 @@ public struct SwiftSDK: Equatable { /// have a lower priority vs. existing paths. /// /// - Parameter toolsetRootPath: new path to add to Swift SDK's toolset. - public mutating func append(toolsetRootPath: AbsolutePath) { + public mutating func append(toolsetRootPath: Basics.AbsolutePath) { self.toolset.rootPaths.append(toolsetRootPath) } } @@ -851,7 +852,7 @@ public struct SwiftSDK: Equatable { extension SwiftSDK { /// Load a ``SwiftSDK`` description from a JSON representation from disk. public static func decode( - fromFile path: AbsolutePath, + fromFile path: Basics.AbsolutePath, fileSystem: FileSystem, observabilityScope: ObservabilityScope ) throws -> [SwiftSDK] { @@ -874,7 +875,7 @@ extension SwiftSDK { /// Load a ``SwiftSDK`` description from a semantically versioned JSON representation from disk. private static func decode( semanticVersion: SemanticVersionInfo, - fromFile path: AbsolutePath, + fromFile path: Basics.AbsolutePath, fileSystem: FileSystem, decoder: JSONDecoder, observabilityScope: ObservabilityScope @@ -946,7 +947,7 @@ extension SwiftSDK { targetTriple: Triple, properties: SwiftSDKMetadataV4.TripleProperties, toolset: Toolset = .init(), - swiftSDKDirectory: AbsolutePath? = nil + swiftSDKDirectory: Basics.AbsolutePath? = nil ) throws { self.init( targetTriple: targetTriple, @@ -965,7 +966,7 @@ extension SwiftSDK { targetTriple: Triple, properties: SerializedDestinationV3.TripleProperties, toolset: Toolset = .init(), - swiftSDKDirectory: AbsolutePath? = nil + swiftSDKDirectory: Basics.AbsolutePath? = nil ) throws { self.init( targetTriple: targetTriple, @@ -977,7 +978,7 @@ extension SwiftSDK { /// Load a ``SwiftSDK`` description from a legacy JSON representation from disk. private init( legacy version: VersionInfo, - fromFile path: AbsolutePath, + fromFile path: Basics.AbsolutePath, fileSystem: FileSystem, decoder: JSONDecoder ) throws { @@ -1089,8 +1090,8 @@ private struct SemanticVersionInfo: Decodable { /// Represents v1 schema of `destination.json` files used for cross-compilation. private struct SerializedDestinationV1: Codable { let target: String? - let sdk: AbsolutePath? - let binDir: AbsolutePath + let sdk: Basics.AbsolutePath? + let binDir: Basics.AbsolutePath let extraCCFlags: [String] let extraSwiftCFlags: [String] let extraCPPFlags: [String] @@ -1169,13 +1170,13 @@ struct SwiftSDKMetadataV4: Decodable { let targetTriples: [String: TripleProperties] } -extension Optional where Wrapped == AbsolutePath { +extension Optional where Wrapped == Basics.AbsolutePath { fileprivate var configurationString: String { self?.pathString ?? "not set" } } -extension Optional where Wrapped == [AbsolutePath] { +extension Optional where Wrapped == [Basics.AbsolutePath] { fileprivate var configurationString: String { self?.map(\.pathString).description ?? "not set" } diff --git a/Sources/PackageModel/UserToolchain.swift b/Sources/PackageModel/UserToolchain.swift index 5a38f747171..7b97b2d8c64 100644 --- a/Sources/PackageModel/UserToolchain.swift +++ b/Sources/PackageModel/UserToolchain.swift @@ -12,6 +12,7 @@ import Basics import Foundation +import TSCUtility import class Basics.AsyncProcess @@ -69,9 +70,9 @@ public final class UserToolchain: Toolchain { /// The target triple that should be used for compilation. @available(*, deprecated, renamed: "targetTriple") - public var triple: Triple { targetTriple } + public var triple: Basics.Triple { targetTriple } - public let targetTriple: Triple + public let targetTriple: Basics.Triple /// The list of CPU architectures to build for. public let architectures: [String]? @@ -162,7 +163,7 @@ public final class UserToolchain: Toolchain { // MARK: - public API public static func determineLibrarian( - triple: Triple, + triple: Basics.Triple, binDirectories: [AbsolutePath], useXcrun: Bool, environment: Environment, @@ -405,7 +406,7 @@ public final class UserToolchain: Toolchain { #endif internal static func deriveSwiftCFlags( - triple: Triple, + triple: Basics.Triple, swiftSDK: SwiftSDK, environment: Environment, fileSystem: any FileSystem @@ -839,7 +840,7 @@ public final class UserToolchain: Toolchain { return .init(swiftCompilerPath: swiftCompilerPath) } - private static func derivePluginServerPath(triple: Triple) throws -> AbsolutePath? { + private static func derivePluginServerPath(triple: Basics.Triple) throws -> AbsolutePath? { if triple.isDarwin() { let pluginServerPathFindArgs = ["/usr/bin/xcrun", "--find", "swift-plugin-server"] if let path = try? AsyncProcess.checkNonZeroExit(arguments: pluginServerPathFindArgs, environment: [:]) @@ -889,7 +890,7 @@ public final class UserToolchain: Toolchain { // TODO: We should have some general utility to find tools. private static func deriveXCTestPath( swiftSDK: SwiftSDK, - triple: Triple, + triple: Basics.Triple, environment: Environment, fileSystem: any FileSystem ) throws -> AbsolutePath? { @@ -970,9 +971,9 @@ public final class UserToolchain: Toolchain { /// Find the swift-testing path if it is within a path that will need extra search paths. private static func deriveSwiftTestingPath( - derivedSwiftCompiler: AbsolutePath, + derivedSwiftCompiler: Basics.AbsolutePath, swiftSDK: SwiftSDK, - triple: Triple, + triple: Basics.Triple, environment: Environment, fileSystem: any FileSystem ) throws -> AbsolutePath? { diff --git a/Sources/PackageModelSyntax/AddTarget.swift b/Sources/PackageModelSyntax/AddTarget.swift index addb4e4886a..b6a081a7d67 100644 --- a/Sources/PackageModelSyntax/AddTarget.swift +++ b/Sources/PackageModelSyntax/AddTarget.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Basics +import Foundation import PackageModel import SwiftParser import SwiftSyntax diff --git a/Sources/PackageModelSyntax/ManifestSyntaxRepresentable.swift b/Sources/PackageModelSyntax/ManifestSyntaxRepresentable.swift index 1b40b2d592a..146bc6e3b74 100644 --- a/Sources/PackageModelSyntax/ManifestSyntaxRepresentable.swift +++ b/Sources/PackageModelSyntax/ManifestSyntaxRepresentable.swift @@ -12,6 +12,7 @@ import Basics import SwiftSyntax +import SwiftSyntaxBuilder /// Describes an entity in the package model that can be represented as /// a syntax node. diff --git a/Sources/PackageModelSyntax/PackageDependency+Syntax.swift b/Sources/PackageModelSyntax/PackageDependency+Syntax.swift index b8a7445938e..6133b9d7344 100644 --- a/Sources/PackageModelSyntax/PackageDependency+Syntax.swift +++ b/Sources/PackageModelSyntax/PackageDependency+Syntax.swift @@ -14,6 +14,7 @@ import Basics import PackageModel import SwiftParser import SwiftSyntax +import SwiftSyntaxBuilder import struct TSCUtility.Version extension MappablePackageDependency.Kind: ManifestSyntaxRepresentable { diff --git a/Sources/PackageModelSyntax/ProductDescription+Syntax.swift b/Sources/PackageModelSyntax/ProductDescription+Syntax.swift index eed6650dfce..614d5db8bf4 100644 --- a/Sources/PackageModelSyntax/ProductDescription+Syntax.swift +++ b/Sources/PackageModelSyntax/ProductDescription+Syntax.swift @@ -13,6 +13,7 @@ import Basics import PackageModel import SwiftSyntax +import SwiftSyntaxBuilder import SwiftParser extension ProductDescription: ManifestSyntaxRepresentable { diff --git a/Sources/PackageModelSyntax/SyntaxEditUtils.swift b/Sources/PackageModelSyntax/SyntaxEditUtils.swift index df64aa4c2d3..6e3c2a2a518 100644 --- a/Sources/PackageModelSyntax/SyntaxEditUtils.swift +++ b/Sources/PackageModelSyntax/SyntaxEditUtils.swift @@ -15,6 +15,7 @@ import PackageModel import SwiftBasicFormat import SwiftSyntax import SwiftParser +import SwiftSyntaxBuilder /// Default indent when we have to introduce indentation but have no context /// to get it right. diff --git a/Sources/PackageModelSyntax/TargetDescription+Syntax.swift b/Sources/PackageModelSyntax/TargetDescription+Syntax.swift index f47f6590f06..eed59e5fcae 100644 --- a/Sources/PackageModelSyntax/TargetDescription+Syntax.swift +++ b/Sources/PackageModelSyntax/TargetDescription+Syntax.swift @@ -13,9 +13,9 @@ import Basics import PackageModel import SwiftSyntax +import SwiftSyntaxBuilder import SwiftParser - extension TargetDescription: ManifestSyntaxRepresentable { /// The function name in the package manifest. private var functionName: String { diff --git a/Sources/PackagePlugin/Utilities.swift b/Sources/PackagePlugin/Utilities.swift index 9c4e4d0c35d..d84382d8139 100644 --- a/Sources/PackagePlugin/Utilities.swift +++ b/Sources/PackagePlugin/Utilities.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import Foundation + extension Package { /// The list of targets matching the given names. Throws an error if any of /// the targets cannot be found. diff --git a/Sources/PackageRegistry/ChecksumTOFU.swift b/Sources/PackageRegistry/ChecksumTOFU.swift index 81eef2d4ec4..137d3326bc9 100644 --- a/Sources/PackageRegistry/ChecksumTOFU.swift +++ b/Sources/PackageRegistry/ChecksumTOFU.swift @@ -16,6 +16,7 @@ import Dispatch import Basics import PackageFingerprint import PackageModel +import Foundation import struct TSCUtility.Version diff --git a/Sources/PackageRegistry/RegistryDownloadsManager.swift b/Sources/PackageRegistry/RegistryDownloadsManager.swift index 307d9c0c0c2..cbe33d03aa3 100644 --- a/Sources/PackageRegistry/RegistryDownloadsManager.swift +++ b/Sources/PackageRegistry/RegistryDownloadsManager.swift @@ -16,6 +16,7 @@ import Dispatch import Foundation import PackageLoading import PackageModel +import TSCBasic import struct TSCUtility.Version @@ -23,8 +24,8 @@ public class RegistryDownloadsManager: AsyncCancellable { public typealias Delegate = RegistryDownloadsManagerDelegate private let fileSystem: FileSystem - private let path: AbsolutePath - private let cachePath: AbsolutePath? + private let path: Basics.AbsolutePath + private let cachePath: Basics.AbsolutePath? private let registryClient: RegistryClient private let delegate: Delegate? @@ -33,13 +34,13 @@ public class RegistryDownloadsManager: AsyncCancellable { let version: Version } - private var pendingLookups = [PackageLookup: Task]() + private var pendingLookups = [PackageLookup: Task]() private var pendingLookupsLock = NSLock() public init( fileSystem: FileSystem, - path: AbsolutePath, - cachePath: AbsolutePath?, + path: Basics.AbsolutePath, + cachePath: Basics.AbsolutePath?, registryClient: RegistryClient, delegate: Delegate? ) { @@ -55,9 +56,9 @@ public class RegistryDownloadsManager: AsyncCancellable { version: Version, observabilityScope: ObservabilityScope, delegateQueue: DispatchQueue - ) async throws -> AbsolutePath { - let packageRelativePath: RelativePath - let packagePath: AbsolutePath + ) async throws -> Basics.AbsolutePath { + let packageRelativePath: Basics.RelativePath + let packagePath: Basics.AbsolutePath packageRelativePath = try package.downloadPath(version: version) packagePath = self.path.appending(packageRelativePath) @@ -127,7 +128,7 @@ public class RegistryDownloadsManager: AsyncCancellable { observabilityScope: ObservabilityScope, delegateQueue: DispatchQueue, callbackQueue: DispatchQueue, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { callbackQueue.asyncResult(completion) { try await self.lookup( @@ -147,7 +148,7 @@ public class RegistryDownloadsManager: AsyncCancellable { private func downloadAndPopulateCache( package: PackageIdentity, version: Version, - packagePath: AbsolutePath, + packagePath: Basics.AbsolutePath, observabilityScope: ObservabilityScope, delegateQueue: DispatchQueue ) async throws -> FetchDetails { @@ -302,7 +303,7 @@ public class RegistryDownloadsManager: AsyncCancellable { } } - private func initializeCacheIfNeeded(cachePath: AbsolutePath) throws { + private func initializeCacheIfNeeded(cachePath: Basics.AbsolutePath) throws { if !self.fileSystem.exists(cachePath) { try self.fileSystem.createDirectory(cachePath, recursive: true) } @@ -345,7 +346,7 @@ extension RegistryDownloadsManager { } extension FileSystem { - func validPackageDirectory(_ path: AbsolutePath) throws -> Bool { + func validPackageDirectory(_ path: Basics.AbsolutePath) throws -> Bool { if !self.exists(path) { return false } @@ -354,14 +355,14 @@ extension FileSystem { } extension PackageIdentity { - internal func downloadPath() throws -> RelativePath { + internal func downloadPath() throws -> Basics.RelativePath { guard let registryIdentity = self.registry else { throw StringError("invalid package identifier \(self), expected registry scope and name") } return try RelativePath(validating: registryIdentity.scope.description).appending(component: registryIdentity.name.description) } - internal func downloadPath(version: Version) throws -> RelativePath { + internal func downloadPath(version: Version) throws -> Basics.RelativePath { try self.downloadPath().appending(component: version.description) } } diff --git a/Sources/PackageRegistry/SignatureValidation.swift b/Sources/PackageRegistry/SignatureValidation.swift index 8e1631dd53c..bf5169ff138 100644 --- a/Sources/PackageRegistry/SignatureValidation.swift +++ b/Sources/PackageRegistry/SignatureValidation.swift @@ -18,6 +18,7 @@ import Basics import PackageLoading import PackageModel import PackageSigning +import TSCBasic import struct TSCUtility.Version @@ -590,7 +591,7 @@ extension VerifierConfiguration { // Load trusted roots from configured directory if let trustedRootsDirectoryPath = configuration.trustedRootCertificatesPath { - let trustedRootsDirectory: AbsolutePath + let trustedRootsDirectory: Basics.AbsolutePath do { trustedRootsDirectory = try AbsolutePath(validating: trustedRootsDirectoryPath) } catch { diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift index 690253a4ea3..d5658857f10 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Auth.swift @@ -15,8 +15,11 @@ import Basics import Commands import CoreCommands import Foundation +import PackageFingerprint import PackageModel import PackageRegistry +import PackageSigning +import Workspace import struct TSCBasic.SHA256 diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift index e2f4550ac60..1b1ffc700fb 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift @@ -16,6 +16,7 @@ import Commands import CoreCommands import Foundation import PackageModel +import PackageFingerprint import PackageRegistry import PackageSigning import Workspace diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand.swift index 2861b53e336..0b7cd4b88e7 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand.swift @@ -12,6 +12,7 @@ import ArgumentParser import Basics +import Commands import CoreCommands import Foundation import PackageModel diff --git a/Sources/PackageSigning/CertificateStores.swift b/Sources/PackageSigning/CertificateStores.swift index 48d3bf22e90..7d001f8484f 100644 --- a/Sources/PackageSigning/CertificateStores.swift +++ b/Sources/PackageSigning/CertificateStores.swift @@ -12,8 +12,10 @@ #if USE_IMPL_ONLY_IMPORTS @_implementationOnly import X509 +@_implementationOnly import SwiftASN1 #else import X509 +import SwiftASN1 #endif enum Certificates { diff --git a/Sources/PackageSigning/SigningEntity/FilePackageSigningEntityStorage.swift b/Sources/PackageSigning/SigningEntity/FilePackageSigningEntityStorage.swift index 91eb7287cbe..28fa216ecbc 100644 --- a/Sources/PackageSigning/SigningEntity/FilePackageSigningEntityStorage.swift +++ b/Sources/PackageSigning/SigningEntity/FilePackageSigningEntityStorage.swift @@ -14,17 +14,18 @@ import Basics import Dispatch import Foundation import PackageModel +import TSCBasic import struct TSCUtility.Version public struct FilePackageSigningEntityStorage: PackageSigningEntityStorage { let fileSystem: FileSystem - let directoryPath: AbsolutePath + let directoryPath: Basics.AbsolutePath private let encoder: JSONEncoder private let decoder: JSONDecoder - public init(fileSystem: FileSystem, directoryPath: AbsolutePath) { + public init(fileSystem: FileSystem, directoryPath: Basics.AbsolutePath) { self.fileSystem = fileSystem self.directoryPath = directoryPath diff --git a/Sources/PackageSigning/SigningIdentity.swift b/Sources/PackageSigning/SigningIdentity.swift index 084bc67d70d..ded10b2a483 100644 --- a/Sources/PackageSigning/SigningIdentity.swift +++ b/Sources/PackageSigning/SigningIdentity.swift @@ -27,6 +27,7 @@ import X509 #endif import Basics +import TSCBasic public protocol SigningIdentity {} diff --git a/Sources/PackageSigning/X509Extensions.swift b/Sources/PackageSigning/X509Extensions.swift index 7045a22ac81..c6f40cbb957 100644 --- a/Sources/PackageSigning/X509Extensions.swift +++ b/Sources/PackageSigning/X509Extensions.swift @@ -29,6 +29,7 @@ import X509 #endif import Basics +import TSCBasic #if canImport(Security) extension Certificate { diff --git a/Sources/SPMBuildCore/BinaryTarget+Extensions.swift b/Sources/SPMBuildCore/BinaryTarget+Extensions.swift index 088e7616325..ee91e7ed2b2 100644 --- a/Sources/SPMBuildCore/BinaryTarget+Extensions.swift +++ b/Sources/SPMBuildCore/BinaryTarget+Extensions.swift @@ -14,14 +14,15 @@ import Basics import Foundation import PackageGraph import PackageModel +import TSCBasic /// Information about a library from a binary dependency. public struct LibraryInfo: Equatable { /// The path to the binary. - public let libraryPath: AbsolutePath + public let libraryPath: Basics.AbsolutePath /// The paths to the headers directories. - public let headersPaths: [AbsolutePath] + public let headersPaths: [Basics.AbsolutePath] } /// Information about an executable from a binary dependency. @@ -30,7 +31,7 @@ public struct ExecutableInfo: Equatable { public let name: String /// The path to the executable. - public let executablePath: AbsolutePath + public let executablePath: Basics.AbsolutePath /// Supported triples, e.g. `x86_64-apple-macosx` public let supportedTriples: [Triple] diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift index e4650254a14..a7e926d2c2c 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift @@ -14,6 +14,7 @@ import Basics import class Foundation.ProcessInfo import PackageModel import PackageGraph +import TSCBasic public struct BuildParameters: Encodable { public enum PrepareForIndexingMode: Encodable { @@ -50,7 +51,7 @@ public struct BuildParameters: Encodable { public var destination: Destination /// The path to the data directory. - public var dataPath: AbsolutePath + public var dataPath: Basics.AbsolutePath /// The build configuration. public var configuration: BuildConfiguration @@ -69,7 +70,7 @@ public struct BuildParameters: Encodable { public var flags: BuildFlags /// An array of paths to search for pkg-config `.pc` files. - public var pkgConfigDirectories: [AbsolutePath] + public var pkgConfigDirectories: [Basics.AbsolutePath] /// The architectures to build for. // FIXME: this may be inconsistent with `targetTriple`. @@ -146,13 +147,13 @@ public struct BuildParameters: Encodable { public init( destination: Destination, - dataPath: AbsolutePath, + dataPath: Basics.AbsolutePath, configuration: BuildConfiguration, toolchain: Toolchain, triple: Triple? = nil, flags: BuildFlags, buildSystemKind: BuildSystemProvider.Kind = .native, - pkgConfigDirectories: [AbsolutePath] = [], + pkgConfigDirectories: [Basics.AbsolutePath] = [], architectures: [String]? = nil, workers: UInt32 = UInt32(ProcessInfo.processInfo.activeProcessorCount), shouldCreateDylibForDynamicProducts: Bool = true, @@ -223,7 +224,7 @@ public struct BuildParameters: Encodable { } /// The path to the build directory (inside the data directory). - public var buildPath: AbsolutePath { + public var buildPath: Basics.AbsolutePath { // TODO: query the build system for this. switch buildSystemKind { case .xcode, .swiftbuild: @@ -240,47 +241,47 @@ public struct BuildParameters: Encodable { } /// The path to the index store directory. - public var indexStore: AbsolutePath { + public var indexStore: Basics.AbsolutePath { assert(indexStoreMode != .off, "index store is disabled") return buildPath.appending(components: "index", "store") } /// The path to the code coverage directory. - public var codeCovPath: AbsolutePath { + public var codeCovPath: Basics.AbsolutePath { return buildPath.appending("codecov") } /// The path to the code coverage profdata file. - public var codeCovDataFile: AbsolutePath { + public var codeCovDataFile: Basics.AbsolutePath { return codeCovPath.appending("default.profdata") } - public var llbuildManifest: AbsolutePath { + public var llbuildManifest: Basics.AbsolutePath { // FIXME: this path isn't specific to `BuildParameters` due to its use of `..` // FIXME: it should be calculated in a different place return dataPath.appending(components: "..", configuration.dirname + ".yaml") } - public var pifManifest: AbsolutePath { + public var pifManifest: Basics.AbsolutePath { // FIXME: this path isn't specific to `BuildParameters` due to its use of `..` // FIXME: it should be calculated in a different place return dataPath.appending(components: "..", "manifest.pif") } - public var buildDescriptionPath: AbsolutePath { + public var buildDescriptionPath: Basics.AbsolutePath { // FIXME: this path isn't specific to `BuildParameters`, should be moved one directory level higher return buildPath.appending(components: "description.json") } - public var testOutputPath: AbsolutePath { + public var testOutputPath: Basics.AbsolutePath { return buildPath.appending(component: "testOutput.txt") } /// Returns the path to the binary of a product for the current build parameters. - public func binaryPath(for product: ResolvedProduct) throws -> AbsolutePath { + public func binaryPath(for product: ResolvedProduct) throws -> Basics.AbsolutePath { return try buildPath.appending(binaryRelativePath(for: product)) } - public func macroBinaryPath(_ module: ResolvedModule) throws -> AbsolutePath { + public func macroBinaryPath(_ module: ResolvedModule) throws -> Basics.AbsolutePath { assert(module.type == .macro) #if BUILD_MACROS_AS_DYLIBS return buildPath.appending(try dynamicLibraryPath(for: module.name)) @@ -290,17 +291,17 @@ public struct BuildParameters: Encodable { } /// Returns the path to the dynamic library of a product for the current build parameters. - private func dynamicLibraryPath(for name: String) throws -> RelativePath { + private func dynamicLibraryPath(for name: String) throws -> Basics.RelativePath { try RelativePath(validating: "\(self.triple.dynamicLibraryPrefix)\(name)\(self.suffix)\(self.triple.dynamicLibraryExtension)") } /// Returns the path to the executable of a product for the current build parameters. - package func executablePath(for name: String) throws -> RelativePath { + package func executablePath(for name: String) throws -> Basics.RelativePath { try RelativePath(validating: "\(name)\(self.suffix)\(self.triple.executableExtension)") } /// Returns the path to the binary of a product for the current build parameters, relative to the build directory. - public func binaryRelativePath(for product: ResolvedProduct) throws -> RelativePath { + public func binaryRelativePath(for product: ResolvedProduct) throws -> Basics.RelativePath { switch product.type { case .executable, .snippet: return try executablePath(for: product.name) diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index abb2f6d67c5..83b3b90321d 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -15,6 +15,8 @@ import Foundation import PackageGraph import PackageLoading import PackageModel +import TSCBasic +import TSCUtility typealias WireInput = HostToPluginMessage.InputContext @@ -24,10 +26,10 @@ internal struct PluginContextSerializer { let fileSystem: FileSystem let modulesGraph: ModulesGraph let buildEnvironment: BuildEnvironment - let pkgConfigDirectories: [AbsolutePath] - let sdkRootPath: AbsolutePath? + let pkgConfigDirectories: [Basics.AbsolutePath] + let sdkRootPath: Basics.AbsolutePath? var paths: [WireInput.URL] = [] - var pathsToIds: [AbsolutePath: WireInput.URL.Id] = [:] + var pathsToIds: [Basics.AbsolutePath: WireInput.URL.Id] = [:] var targets: [WireInput.Target] = [] var targetsToWireIDs: [ResolvedModule.ID: WireInput.Target.Id] = [:] var products: [WireInput.Product] = [] @@ -42,7 +44,7 @@ internal struct PluginContextSerializer { /// Adds a path to the serialized structure, if it isn't already there. /// Either way, this function returns the path's wire ID. - mutating func serialize(path: AbsolutePath) throws -> WireInput.URL.Id { + mutating func serialize(path: Basics.AbsolutePath) throws -> WireInput.URL.Id { // If we've already seen the path, just return the wire ID we already assigned to it. if let id = pathsToIds[path] { return id } diff --git a/Sources/SPMBuildCore/Plugins/PluginScriptRunner.swift b/Sources/SPMBuildCore/Plugins/PluginScriptRunner.swift index f8d6b569062..fda28b919a0 100644 --- a/Sources/SPMBuildCore/Plugins/PluginScriptRunner.swift +++ b/Sources/SPMBuildCore/Plugins/PluginScriptRunner.swift @@ -16,6 +16,8 @@ import Foundation import PackageModel import PackageLoading import PackageGraph +import TSCBasic +import TSCUtility /// Implements the mechanics of running and communicating with a plugin (implemented as a set of Swift source files). In most environments this is done by compiling the code to an executable, invoking it as a sandboxed subprocess, and communicating with it using pipes. Specific implementations are free to implement things differently, however. public protocol PluginScriptRunner { @@ -23,7 +25,7 @@ public protocol PluginScriptRunner { /// Public protocol function that starts compiling the plugin script to an executable. The name is used as the basename for the executable and auxiliary files. The tools version controls the availability of APIs in PackagePlugin, and should be set to the tools version of the package that defines the plugin (not of the target to which it is being applied). This function returns immediately and then calls the completion handler on the callback queue when compilation ends. @available(*, noasync, message: "Use the async alternative") func compilePluginScript( - sourceFiles: [AbsolutePath], + sourceFiles: [Basics.AbsolutePath], pluginName: String, toolsVersion: ToolsVersion, observabilityScope: ObservabilityScope, @@ -43,13 +45,13 @@ public protocol PluginScriptRunner { /// /// Every concrete implementation should cache any intermediates as necessary to avoid redundant work. func runPluginScript( - sourceFiles: [AbsolutePath], + sourceFiles: [Basics.AbsolutePath], pluginName: String, initialMessage: Data, toolsVersion: ToolsVersion, - workingDirectory: AbsolutePath, - writableDirectories: [AbsolutePath], - readOnlyDirectories: [AbsolutePath], + workingDirectory: Basics.AbsolutePath, + writableDirectories: [Basics.AbsolutePath], + readOnlyDirectories: [Basics.AbsolutePath], allowNetworkConnections: [SandboxNetworkPermission], fileSystem: FileSystem, observabilityScope: ObservabilityScope, @@ -60,12 +62,12 @@ public protocol PluginScriptRunner { /// Returns the Triple that represents the host for which plugin script tools should be built, or for which binary /// tools should be selected. - var hostTriple: Triple { get throws } + var hostTriple: Basics.Triple { get throws } } public extension PluginScriptRunner { func compilePluginScript( - sourceFiles: [AbsolutePath], + sourceFiles: [Basics.AbsolutePath], pluginName: String, toolsVersion: ToolsVersion, observabilityScope: ObservabilityScope, @@ -118,10 +120,10 @@ public struct PluginCompilationResult: Equatable { public var commandLine: [String] /// Path of the compiled executable. - public var executableFile: AbsolutePath + public var executableFile: Basics.AbsolutePath /// Path of the libClang diagnostics file emitted by the compiler. - public var diagnosticsFile: AbsolutePath + public var diagnosticsFile: Basics.AbsolutePath /// Any output emitted by the compiler (stdout and stderr combined). public var rawCompilerOutput: String @@ -132,8 +134,8 @@ public struct PluginCompilationResult: Equatable { public init( succeeded: Bool, commandLine: [String], - executableFile: AbsolutePath, - diagnosticsFile: AbsolutePath, + executableFile: Basics.AbsolutePath, + diagnosticsFile: Basics.AbsolutePath, compilerOutput rawCompilerOutput: String, cached: Bool ) { diff --git a/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift b/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift index 509a377a236..e70e3bcdbbc 100644 --- a/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift +++ b/Sources/SPMBuildCore/ResolvedPackage+Extensions.swift @@ -10,6 +10,9 @@ // //===----------------------------------------------------------------------===// +import PackageModel +import TSCUtility + import struct PackageGraph.ResolvedPackage import struct PackageGraph.ResolvedModule diff --git a/Sources/SourceControl/RepositoryManager.swift b/Sources/SourceControl/RepositoryManager.swift index d408d044b62..ae5a6d26fa2 100644 --- a/Sources/SourceControl/RepositoryManager.swift +++ b/Sources/SourceControl/RepositoryManager.swift @@ -15,16 +15,17 @@ import _Concurrency import Dispatch import Foundation import PackageModel +import TSCBasic /// Manages a collection of bare repositories. public class RepositoryManager: Cancellable { public typealias Delegate = RepositoryManagerDelegate /// The path under which repositories are stored. - public let path: AbsolutePath + public let path: Basics.AbsolutePath /// The path to the directory where all cached git repositories are stored. - private let cachePath: AbsolutePath? + private let cachePath: Basics.AbsolutePath? // used in tests to disable skipping of local packages. private let cacheLocalPackages: Bool @@ -67,9 +68,9 @@ public class RepositoryManager: Cancellable { /// - delegate: The repository manager delegate. public init( fileSystem: FileSystem, - path: AbsolutePath, + path: Basics.AbsolutePath, provider: RepositoryProvider, - cachePath: AbsolutePath? = .none, + cachePath: Basics.AbsolutePath? = .none, cacheLocalPackages: Bool = false, maxConcurrentOperations: Int? = .none, initializationWarningHandler: (String) -> Void, @@ -306,7 +307,7 @@ public class RepositoryManager: Cancellable { private func fetchAndPopulateCache( package: PackageIdentity, handle: RepositoryHandle, - repositoryPath: AbsolutePath, + repositoryPath: Basics.AbsolutePath, updateStrategy: RepositoryUpdateStrategy, observabilityScope: ObservabilityScope, delegateQueue: DispatchQueue @@ -408,7 +409,7 @@ public class RepositoryManager: Cancellable { } /// Open a working copy checkout at a path - public func openWorkingCopy(at path: AbsolutePath) throws -> WorkingCheckout { + public func openWorkingCopy(at path: Basics.AbsolutePath) throws -> WorkingCheckout { try self.provider.openWorkingCopy(at: path) } @@ -430,7 +431,7 @@ public class RepositoryManager: Cancellable { /// Create a working copy of the repository from a handle. private func createWorkingCopy( _ handle: RepositoryHandle, - at destinationPath: AbsolutePath, + at destinationPath: Basics.AbsolutePath, editable: Bool ) throws -> WorkingCheckout { try self.provider.createWorkingCopy( @@ -448,12 +449,12 @@ public class RepositoryManager: Cancellable { } /// Returns true if the directory is valid git location. - public func isValidDirectory(_ directory: AbsolutePath) throws -> Bool { + public func isValidDirectory(_ directory: Basics.AbsolutePath) throws -> Bool { try self.provider.isValidDirectory(directory) } /// Returns true if the directory is valid git location for the specified repository - public func isValidDirectory(_ directory: AbsolutePath, for repository: RepositorySpecifier) throws -> Bool { + public func isValidDirectory(_ directory: Basics.AbsolutePath, for repository: RepositorySpecifier) throws -> Bool { try self.provider.isValidDirectory(directory, for: repository) } @@ -472,7 +473,7 @@ public class RepositoryManager: Cancellable { } /// Sets up the cache directories if they don't already exist. - private func initializeCacheIfNeeded(cachePath: AbsolutePath) throws { + private func initializeCacheIfNeeded(cachePath: Basics.AbsolutePath) throws { // Create the supplied cache directory. if !self.fileSystem.exists(cachePath) { try self.fileSystem.createDirectory(cachePath, recursive: true) @@ -526,10 +527,10 @@ extension RepositoryManager { /// /// This is intentionally hidden from the clients so that the manager is /// allowed to move repositories transparently. - fileprivate let subpath: RelativePath + fileprivate let subpath: Basics.RelativePath /// Create a handle. - fileprivate init(manager: RepositoryManager, repository: RepositorySpecifier, subpath: RelativePath) { + fileprivate init(manager: RepositoryManager, repository: RepositorySpecifier, subpath: Basics.RelativePath) { self.manager = manager self.repository = repository self.subpath = subpath @@ -547,7 +548,7 @@ extension RepositoryManager { /// expected to be non-existent when called. /// /// - editable: The clone is expected to be edited by user. - public func createWorkingCopy(at path: AbsolutePath, editable: Bool) throws -> WorkingCheckout { + public func createWorkingCopy(at path: Basics.AbsolutePath, editable: Bool) throws -> WorkingCheckout { return try self.manager.createWorkingCopy(self, at: path, editable: editable) } } @@ -596,7 +597,7 @@ extension RepositoryManager.RepositoryHandle: CustomStringConvertible { extension RepositorySpecifier { // relative path where the repository should be stored - internal func storagePath() throws -> RelativePath { + internal func storagePath() throws -> Basics.RelativePath { return try RelativePath(validating: self.fileSystemIdentifier) } diff --git a/Sources/SwiftBuildSupport/PIFBuilder.swift b/Sources/SwiftBuildSupport/PIFBuilder.swift index 8c21abad34c..56f91274912 100644 --- a/Sources/SwiftBuildSupport/PIFBuilder.swift +++ b/Sources/SwiftBuildSupport/PIFBuilder.swift @@ -15,6 +15,7 @@ import Foundation import PackageGraph import PackageLoading import PackageModel +import TSCUtility @_spi(SwiftPMInternal) import SPMBuildCore @@ -28,7 +29,7 @@ import enum SwiftBuild.ProjectModel /// The parameters required by `PIFBuilder`. struct PIFBuilderParameters { - let triple: Triple + let triple: Basics.Triple /// Whether the toolchain supports `-package-name` option. let isPackageAccessModifierSupported: Bool diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift index bc17d22ee15..ca71fa6f7f3 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Modules.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Foundation +import TSCUtility import struct Basics.AbsolutePath import class Basics.ObservabilitySystem diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift index 5bf30c1397a..d9cb5d42b7f 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder+Products.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Foundation +import TSCUtility import struct Basics.AbsolutePath import class Basics.ObservabilitySystem @@ -989,7 +990,7 @@ extension PackagePIFProjectBuilder { private struct PackageRegistrySignature: Encodable { enum Source: Encodable { - case registry(url: URL) + case registry(url: Foundation.URL) } let packageIdentity: String diff --git a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift index bd7a36b4895..1b8648f1622 100644 --- a/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift +++ b/Sources/SwiftBuildSupport/PackagePIFProjectBuilder.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Foundation +import TSCUtility import struct Basics.AbsolutePath import struct Basics.Diagnostic diff --git a/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift b/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift index 78bc3248a0d..acb75dff6a3 100644 --- a/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/RemoveSwiftSDK.swift @@ -14,6 +14,7 @@ import ArgumentParser import Basics import CoreCommands import PackageModel +import TSCBasic package struct RemoveSwiftSDK: SwiftSDKSubcommand { package static let configuration = CommandConfiguration( @@ -33,12 +34,12 @@ package struct RemoveSwiftSDK: SwiftSDKSubcommand { func run( hostTriple: Triple, - _ swiftSDKsDirectory: AbsolutePath, + _ swiftSDKsDirectory: Basics.AbsolutePath, _ observabilityScope: ObservabilityScope ) async throws { let artifactBundleDirectory = swiftSDKsDirectory.appending(component: self.sdkIDOrBundleName) - let removedBundleDirectory: AbsolutePath + let removedBundleDirectory: Basics.AbsolutePath if fileSystem.exists(artifactBundleDirectory) { try fileSystem.removeFileTree(artifactBundleDirectory) diff --git a/Sources/Workspace/InitPackage.swift b/Sources/Workspace/InitPackage.swift index 1eb4596918d..1c1fc6b8953 100644 --- a/Sources/Workspace/InitPackage.swift +++ b/Sources/Workspace/InitPackage.swift @@ -13,6 +13,7 @@ import Basics import PackageModel import SPMBuildCore +import TSCUtility import protocol TSCBasic.OutputByteStream diff --git a/Sources/Workspace/ManagedArtifact.swift b/Sources/Workspace/ManagedArtifact.swift index 013d938c3a4..3919250eae0 100644 --- a/Sources/Workspace/ManagedArtifact.swift +++ b/Sources/Workspace/ManagedArtifact.swift @@ -14,6 +14,7 @@ import Basics import PackageGraph import PackageModel import SourceControl +import TSCBasic extension Workspace { /// A downloaded artifact managed by the workspace. @@ -28,7 +29,7 @@ extension Workspace { public let source: Source /// The path of the artifact on disk - public let path: AbsolutePath + public let path: Basics.AbsolutePath public let kind: BinaryModule.Kind @@ -36,7 +37,7 @@ extension Workspace { packageRef: PackageReference, targetName: String, source: Source, - path: AbsolutePath, + path: Basics.AbsolutePath, kind: BinaryModule.Kind ) { self.packageRef = packageRef @@ -52,7 +53,7 @@ extension Workspace { targetName: String, url: String, checksum: String, - path: AbsolutePath, + path: Basics.AbsolutePath, kind: BinaryModule.Kind ) -> ManagedArtifact { return ManagedArtifact( @@ -68,7 +69,7 @@ extension Workspace { public static func local( packageRef: PackageReference, targetName: String, - path: AbsolutePath, + path: Basics.AbsolutePath, kind: BinaryModule.Kind, checksum: String? = nil ) -> ManagedArtifact { diff --git a/Sources/Workspace/ManagedDependency.swift b/Sources/Workspace/ManagedDependency.swift index 30d810c88c2..dfbe53dd111 100644 --- a/Sources/Workspace/ManagedDependency.swift +++ b/Sources/Workspace/ManagedDependency.swift @@ -14,6 +14,7 @@ import Basics import PackageGraph import PackageModel import SourceControl +import TSCBasic import struct TSCUtility.Version @@ -26,7 +27,7 @@ extension Workspace { /// Represents the state of the managed dependency. public indirect enum State: Equatable, CustomStringConvertible { /// The dependency is a local package on the file system. - case fileSystem(AbsolutePath) + case fileSystem(Basics.AbsolutePath) /// The dependency is a managed source control checkout. case sourceControlCheckout(CheckoutState) @@ -39,9 +40,9 @@ extension Workspace { /// If the path is non-nil, the dependency is managed by a user and is /// located at the path. In other words, this dependency is being used /// for top of the tree style development. - case edited(basedOn: ManagedDependency?, unmanagedPath: AbsolutePath?) + case edited(basedOn: ManagedDependency?, unmanagedPath: Basics.AbsolutePath?) - case custom(version: Version, path: AbsolutePath) + case custom(version: Version, path: Basics.AbsolutePath) public var description: String { switch self { @@ -66,12 +67,12 @@ extension Workspace { public let state: State /// The checked out path of the dependency on disk, relative to the workspace checkouts path. - public let subpath: RelativePath + public let subpath: Basics.RelativePath internal init( packageRef: PackageReference, state: State, - subpath: RelativePath + subpath: Basics.RelativePath ) { self.packageRef = packageRef self.subpath = subpath @@ -84,7 +85,7 @@ extension Workspace { /// - Parameters: /// - subpath: The subpath inside the editable directory. /// - unmanagedPath: A custom absolute path instead of the subpath. - public func edited(subpath: RelativePath, unmanagedPath: AbsolutePath?) throws -> ManagedDependency { + public func edited(subpath: Basics.RelativePath, unmanagedPath: Basics.AbsolutePath?) throws -> ManagedDependency { guard case .sourceControlCheckout = self.state else { throw InternalError("invalid dependency state: \(self.state)") } @@ -116,7 +117,7 @@ extension Workspace { public static func sourceControlCheckout( packageRef: PackageReference, state: CheckoutState, - subpath: RelativePath + subpath: Basics.RelativePath ) throws -> ManagedDependency { switch packageRef.kind { case .localSourceControl, .remoteSourceControl: @@ -134,7 +135,7 @@ extension Workspace { public static func registryDownload( packageRef: PackageReference, version: Version, - subpath: RelativePath + subpath: Basics.RelativePath ) throws -> ManagedDependency { guard case .registry = packageRef.kind else { throw InternalError("invalid package type: \(packageRef.kind)") @@ -149,9 +150,9 @@ extension Workspace { /// Create an edited dependency public static func edited( packageRef: PackageReference, - subpath: RelativePath, + subpath: Basics.RelativePath, basedOn: ManagedDependency?, - unmanagedPath: AbsolutePath? + unmanagedPath: Basics.AbsolutePath? ) -> ManagedDependency { return ManagedDependency( packageRef: packageRef, diff --git a/Sources/Workspace/ManagedPrebuilt.swift b/Sources/Workspace/ManagedPrebuilt.swift index 108402e3b26..9c8aafc2343 100644 --- a/Sources/Workspace/ManagedPrebuilt.swift +++ b/Sources/Workspace/ManagedPrebuilt.swift @@ -12,6 +12,7 @@ import Basics import PackageModel +import TSCBasic extension Workspace { /// A downloaded prebuilt managed by the workspace. @@ -23,7 +24,7 @@ extension Workspace { public let libraryName: String /// The path to the extracted prebuilt artifacts - public let path: AbsolutePath + public let path: Basics.AbsolutePath /// The products in the library public let products: [String] diff --git a/Sources/Workspace/PackageContainer/RegistryPackageContainer.swift b/Sources/Workspace/PackageContainer/RegistryPackageContainer.swift index 1a618257df2..208b05dd2e0 100644 --- a/Sources/Workspace/PackageContainer/RegistryPackageContainer.swift +++ b/Sources/Workspace/PackageContainer/RegistryPackageContainer.swift @@ -17,6 +17,7 @@ import PackageGraph import PackageLoading import PackageModel import PackageRegistry +import TSCBasic import struct TSCUtility.Version @@ -178,7 +179,7 @@ public class RegistryPackageContainer: PackageContainer { throw StringError("failed locating placeholder manifest for \(preferredToolsVersion)") } // replace the fake manifest with the real manifest content - let manifestPath = AbsolutePath.root.appending(component: placeholderManifestFileName) + let manifestPath = Basics.AbsolutePath.root.appending(component: placeholderManifestFileName) try fileSystem.removeFileTree(manifestPath) try fileSystem.writeFileContents(manifestPath, string: manifestContent) // finally, load the manifest @@ -202,7 +203,7 @@ public class RegistryPackageContainer: PackageContainer { // ToolsVersionLoader is designed to scan files to decide which is the best tools-version // as such, this writes a fake manifest based on the information returned by the registry // with only the header line which is all that is needed by ToolsVersionLoader - let fileSystem = InMemoryFileSystem() + let fileSystem = Basics.InMemoryFileSystem() for manifest in manifests { let content = manifest.value.content ?? "// swift-tools-version:\(manifest.value.toolsVersion)" try fileSystem.writeFileContents(AbsolutePath.root.appending(component: manifest.key), string: content) diff --git a/Sources/Workspace/Workspace+BinaryArtifacts.swift b/Sources/Workspace/Workspace+BinaryArtifacts.swift index a99f895d09d..334dfbc9094 100644 --- a/Sources/Workspace/Workspace+BinaryArtifacts.swift +++ b/Sources/Workspace/Workspace+BinaryArtifacts.swift @@ -13,6 +13,7 @@ import Basics import Foundation import PackageLoading +import PackageGraph import PackageModel import SPMBuildCore diff --git a/Sources/Workspace/Workspace+Editing.swift b/Sources/Workspace/Workspace+Editing.swift index c38036fc04c..a403ca26931 100644 --- a/Sources/Workspace/Workspace+Editing.swift +++ b/Sources/Workspace/Workspace+Editing.swift @@ -11,6 +11,9 @@ //===----------------------------------------------------------------------===// import _Concurrency +import PackageModel +import TSCBasic + import struct Basics.AbsolutePath import class Basics.InMemoryFileSystem import class Basics.ObservabilityScope diff --git a/Sources/Workspace/Workspace+PackageContainer.swift b/Sources/Workspace/Workspace+PackageContainer.swift index 4787e3adef4..0b0a824f8fd 100644 --- a/Sources/Workspace/Workspace+PackageContainer.swift +++ b/Sources/Workspace/Workspace+PackageContainer.swift @@ -10,6 +10,9 @@ // //===----------------------------------------------------------------------===// +import SourceControl +import TSCBasic + import class Basics.ObservabilityScope import func Dispatch.dispatchPrecondition import class Dispatch.DispatchQueue diff --git a/Sources/Workspace/Workspace+Prebuilts.swift b/Sources/Workspace/Workspace+Prebuilts.swift index 7b58f142e98..8cca15bde2c 100644 --- a/Sources/Workspace/Workspace+Prebuilts.swift +++ b/Sources/Workspace/Workspace+Prebuilts.swift @@ -12,6 +12,7 @@ import Basics import Foundation +import OrderedCollections import PackageModel import protocol TSCBasic.HashAlgorithm diff --git a/Sources/Workspace/Workspace+ResolvedPackages.swift b/Sources/Workspace/Workspace+ResolvedPackages.swift index 4be96b417a1..3586926fba6 100644 --- a/Sources/Workspace/Workspace+ResolvedPackages.swift +++ b/Sources/Workspace/Workspace+ResolvedPackages.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import SourceControl + import class Basics.ObservabilityScope import class PackageGraph.ResolvedPackagesStore import struct PackageModel.PackageReference diff --git a/Sources/Workspace/Workspace+SourceControl.swift b/Sources/Workspace/Workspace+SourceControl.swift index c2afe146c51..773c856e17a 100644 --- a/Sources/Workspace/Workspace+SourceControl.swift +++ b/Sources/Workspace/Workspace+SourceControl.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import TSCBasic + import struct Basics.AbsolutePath import struct Basics.InternalError import class Basics.ObservabilityScope diff --git a/Sources/Workspace/Workspace+State.swift b/Sources/Workspace/Workspace+State.swift index adee2633287..3c395133031 100644 --- a/Sources/Workspace/Workspace+State.swift +++ b/Sources/Workspace/Workspace+State.swift @@ -15,6 +15,7 @@ import Foundation import PackageGraph import PackageModel import SourceControl +import TSCBasic import struct TSCUtility.Version @@ -30,14 +31,14 @@ public actor WorkspaceState { public private(set) var prebuilts: Workspace.ManagedPrebuilts /// Path to the state file. - public let storagePath: AbsolutePath + public let storagePath: Basics.AbsolutePath /// storage private let storage: WorkspaceStateStorage init( fileSystem: FileSystem, - storageDirectory: AbsolutePath, + storageDirectory: Basics.AbsolutePath, initializationWarningHandler: (String) -> Void ) { self.storagePath = storageDirectory.appending("workspace-state.json") @@ -101,12 +102,12 @@ public actor WorkspaceState { // MARK: - Serialization private struct WorkspaceStateStorage { - private let path: AbsolutePath + private let path: Basics.AbsolutePath private let fileSystem: FileSystem private let encoder = JSONEncoder.makeWithDefaults() private let decoder = JSONDecoder.makeWithDefaults() - init(path: AbsolutePath, fileSystem: FileSystem) { + init(path: Basics.AbsolutePath, fileSystem: FileSystem) { self.path = path self.fileSystem = fileSystem } @@ -285,7 +286,7 @@ extension WorkspaceStateStorage { let kind = try container.decode(String.self, forKey: .name) switch kind { case "local", "fileSystem": - let path = try container.decode(AbsolutePath.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath.self, forKey: .path) return self.init(underlying: .fileSystem(path)) case "checkout", "sourceControlCheckout": let checkout = try container.decode(CheckoutInfo.self, forKey: .checkoutState) @@ -295,14 +296,14 @@ extension WorkspaceStateStorage { return try self .init(underlying: .registryDownload(version: TSCUtility.Version(versionString: version))) case "edited": - let path = try container.decode(AbsolutePath?.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath?.self, forKey: .path) return try self.init(underlying: .edited( basedOn: basedOn.map { try .init($0) }, unmanagedPath: path )) case "custom": let version = try container.decode(String.self, forKey: .version) - let path = try container.decode(AbsolutePath.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath.self, forKey: .path) return try self.init(underlying: .custom( version: TSCUtility.Version(versionString: version), path: path @@ -456,7 +457,7 @@ extension WorkspaceStateStorage { struct Prebuilt: Codable { let packageRef: PackageReference let libraryName: String - let path: AbsolutePath + let path: Basics.AbsolutePath let products: [String] let cModules: [String] @@ -669,7 +670,7 @@ extension WorkspaceStateStorage { let kind = try container.decode(String.self, forKey: .name) switch kind { case "local", "fileSystem": - let path = try container.decode(AbsolutePath.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath.self, forKey: .path) return self.init(underlying: .fileSystem(path)) case "checkout", "sourceControlCheckout": let checkout = try container.decode(CheckoutInfo.self, forKey: .checkoutState) @@ -679,14 +680,14 @@ extension WorkspaceStateStorage { return try self .init(underlying: .registryDownload(version: TSCUtility.Version(versionString: version))) case "edited": - let path = try container.decode(AbsolutePath?.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath?.self, forKey: .path) return try self.init(underlying: .edited( basedOn: basedOn.map { try .init($0) }, unmanagedPath: path )) case "custom": let version = try container.decode(String.self, forKey: .version) - let path = try container.decode(AbsolutePath.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath.self, forKey: .path) return try self.init(underlying: .custom( version: TSCUtility.Version(versionString: version), path: path @@ -1025,7 +1026,7 @@ extension WorkspaceStateStorage { let kind = try container.decode(String.self, forKey: .name) switch kind { case "local", "fileSystem": - let path = try container.decode(AbsolutePath.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath.self, forKey: .path) return self.init(underlying: .fileSystem(path)) case "checkout", "sourceControlCheckout": let checkout = try container.decode(CheckoutInfo.self, forKey: .checkoutState) @@ -1035,14 +1036,14 @@ extension WorkspaceStateStorage { return try self .init(underlying: .registryDownload(version: TSCUtility.Version(versionString: version))) case "edited": - let path = try container.decode(AbsolutePath?.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath?.self, forKey: .path) return try self.init(underlying: .edited( basedOn: basedOn.map { try .init($0) }, unmanagedPath: path )) case "custom": let version = try container.decode(String.self, forKey: .version) - let path = try container.decode(AbsolutePath.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath.self, forKey: .path) return try self.init(underlying: .custom( version: TSCUtility.Version(versionString: version), path: path @@ -1215,7 +1216,7 @@ extension Workspace.ManagedDependency { extension Workspace.ManagedArtifact { fileprivate init(_ artifact: WorkspaceStateStorage.V5.Artifact) throws { - let path = try AbsolutePath(validating: artifact.path) + let path = try Basics.AbsolutePath(validating: artifact.path) try self.init( packageRef: .init(artifact.packageRef), targetName: artifact.targetName, @@ -1337,7 +1338,7 @@ extension WorkspaceStateStorage { let checkout = try container.decode(CheckoutInfo.self, forKey: .checkoutState) return try self.init(underlying: .sourceControlCheckout(.init(checkout))) case "edited": - let path = try container.decode(AbsolutePath?.self, forKey: .path) + let path = try container.decode(Basics.AbsolutePath?.self, forKey: .path) return try self.init(underlying: .edited( basedOn: basedOn.map { try .init($0) }, unmanagedPath: path @@ -1458,7 +1459,7 @@ extension Workspace.ManagedDependency { extension Workspace.ManagedArtifact { fileprivate init(_ artifact: WorkspaceStateStorage.V4.Artifact) throws { - let path = try AbsolutePath(validating: artifact.path) + let path = try Basics.AbsolutePath(validating: artifact.path) try self.init( packageRef: .init(artifact.packageRef), targetName: artifact.targetName, @@ -1479,7 +1480,7 @@ extension PackageModel.PackageReference { case "local": kind = try .fileSystem(.init(validating: reference.location)) case "remote": - if let path = try? AbsolutePath(validating: reference.location) { + if let path = try? Basics.AbsolutePath(validating: reference.location) { kind = .localSourceControl(path) } else { kind = .remoteSourceControl(SourceControlURL(reference.location)) @@ -1512,7 +1513,7 @@ extension CheckoutState { // backwards compatibility for older formats extension BinaryModule.Kind { - fileprivate static func forPath(_ path: AbsolutePath) -> Self { + fileprivate static func forPath(_ path: Basics.AbsolutePath) -> Self { if let kind = allCases.first(where: { $0.fileExtension == path.extension }) { return kind } diff --git a/Sources/XCBuildSupport/PIFBuilder.swift b/Sources/XCBuildSupport/PIFBuilder.swift index 93b88286edc..3d020614783 100644 --- a/Sources/XCBuildSupport/PIFBuilder.swift +++ b/Sources/XCBuildSupport/PIFBuilder.swift @@ -15,6 +15,7 @@ import Foundation import PackageGraph import PackageLoading import PackageModel +import TSCUtility @_spi(SwiftPMInternal) import SPMBuildCore @@ -24,7 +25,7 @@ import func TSCBasic.topologicalSort /// The parameters required by `PIFBuilder`. struct PIFBuilderParameters { - let triple: Triple + let triple: Basics.Triple /// Whether the toolchain supports `-package-name` option. let isPackageAccessModifierSupported: Bool diff --git a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift index 5e7119d5b45..88385d33868 100644 --- a/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift +++ b/Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift @@ -17,6 +17,7 @@ import _InternalTestSupport @testable import PackageGraph +import PackageModel import XCTest final class CrossCompilationPackageGraphTests: XCTestCase { diff --git a/Tests/PackageGraphTests/ModulesGraphTests.swift b/Tests/PackageGraphTests/ModulesGraphTests.swift index fa128410127..d840f8e189a 100644 --- a/Tests/PackageGraphTests/ModulesGraphTests.swift +++ b/Tests/PackageGraphTests/ModulesGraphTests.swift @@ -11,6 +11,8 @@ //===----------------------------------------------------------------------===// import Basics +import PackageLoading +import TSCUtility @_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly) @testable import PackageGraph @@ -2921,7 +2923,7 @@ final class ModulesGraphTests: XCTestCase { ] let expectedPlatformsForTests = customXCTestMinimumDeploymentTargets - .reduce(into: [Platform: PlatformVersion]()) { partialResult, entry in + .reduce(into: [PackageModel.Platform: PlatformVersion]()) { partialResult, entry in if entry.value > entry.key.oldestSupportedVersion { partialResult[entry.key] = entry.value } else { diff --git a/Tests/PackageGraphTests/TopologicalSortTests.swift b/Tests/PackageGraphTests/TopologicalSortTests.swift index ce357f1b5a3..650c340167b 100644 --- a/Tests/PackageGraphTests/TopologicalSortTests.swift +++ b/Tests/PackageGraphTests/TopologicalSortTests.swift @@ -37,7 +37,7 @@ extension Int { extension Int: @retroactive Identifiable {} private func topologicalSort(_ nodes: [Int], _ successors: [Int: [Int]]) throws -> [Int] { - return try topologicalSort(nodes, successors: { successors[$0] ?? [] }) + return try topologicalSortIdentifiable(nodes, successors: { successors[$0] ?? [] }) } private func topologicalSort(_ node: Int, _ successors: [Int: [Int]]) throws -> [Int] { return try topologicalSort([node], successors) diff --git a/Tests/PackageGraphTests/VersionSetSpecifierTests.swift b/Tests/PackageGraphTests/VersionSetSpecifierTests.swift index a79e635b02e..6fa280c8815 100644 --- a/Tests/PackageGraphTests/VersionSetSpecifierTests.swift +++ b/Tests/PackageGraphTests/VersionSetSpecifierTests.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Foundation +import TSCUtility import XCTest import PackageGraph