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),