diff --git a/Sources/Commands/PackageCommands/APIDiff.swift b/Sources/Commands/PackageCommands/APIDiff.swift index f24bfd5633c..85242d7821e 100644 --- a/Sources/Commands/PackageCommands/APIDiff.swift +++ b/Sources/Commands/PackageCommands/APIDiff.swift @@ -70,9 +70,6 @@ struct APIDiff: AsyncSwiftCommand { help: "One or more targets to include in the API comparison. If present, only the specified targets (and any products specified using `--products`) will be compared.") var targets: [String] = [] - @OptionGroup(visibility: .hidden) - package var traits: TraitOptions - @Option(name: .customLong("baseline-dir"), help: "The path to a directory used to store API baseline files. If unspecified, a temporary directory will be used.") var overrideBaselineDir: AbsolutePath? @@ -91,7 +88,6 @@ struct APIDiff: AsyncSwiftCommand { // We turn build manifest caching off because we need the build plan. let buildSystem = try await swiftCommandState.createBuildSystem( explicitBuildSystem: .native, - traitConfiguration: .init(traitOptions: self.traits), cacheBuildManifest: false ) diff --git a/Sources/Commands/PackageCommands/DumpCommands.swift b/Sources/Commands/PackageCommands/DumpCommands.swift index e5ccf3873fe..0f7e009a461 100644 --- a/Sources/Commands/PackageCommands/DumpCommands.swift +++ b/Sources/Commands/PackageCommands/DumpCommands.swift @@ -54,7 +54,7 @@ struct DumpSymbolGraph: AsyncSwiftCommand { let buildSystem = try await swiftCommandState.createBuildSystem( explicitBuildSystem: .native, // We are enabling all traits for dumping the symbol graph. - traitConfiguration: .init(enableAllTraits: true), + enableAllTraits: true, cacheBuildManifest: false ) try await buildSystem.build() diff --git a/Sources/Commands/PackageCommands/Install.swift b/Sources/Commands/PackageCommands/Install.swift index 9572c402559..839f4f089d2 100644 --- a/Sources/Commands/PackageCommands/Install.swift +++ b/Sources/Commands/PackageCommands/Install.swift @@ -88,7 +88,7 @@ extension SwiftPackageCommand { commandState.preferredBuildConfiguration = .release } - try await commandState.createBuildSystem(explicitProduct: productToInstall.name, traitConfiguration: .init()) + try await commandState.createBuildSystem(explicitProduct: productToInstall.name) .build(subset: .product(productToInstall.name)) let binPath = try commandState.productsBuildParameters.buildPath.appending(component: productToInstall.name) diff --git a/Sources/Commands/PackageCommands/Migrate.swift b/Sources/Commands/PackageCommands/Migrate.swift index 2a79720b50a..70f2a9e5e27 100644 --- a/Sources/Commands/PackageCommands/Migrate.swift +++ b/Sources/Commands/PackageCommands/Migrate.swift @@ -216,7 +216,6 @@ extension SwiftPackageCommand { } return try await swiftCommandState.createBuildSystem( - traitConfiguration: .init(), // Don't attempt to cache manifests with temporary // feature flags added just for migration purposes. cacheBuildManifest: false, diff --git a/Sources/Commands/PackageCommands/PluginCommand.swift b/Sources/Commands/PackageCommands/PluginCommand.swift index 891e154a1c6..fb1cc2c37f1 100644 --- a/Sources/Commands/PackageCommands/PluginCommand.swift +++ b/Sources/Commands/PackageCommands/PluginCommand.swift @@ -331,7 +331,6 @@ struct PluginCommand: AsyncSwiftCommand { // Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map. let buildSystem = try await swiftCommandState.createBuildSystem( explicitBuildSystem: buildSystemKind, - traitConfiguration: .init(), cacheBuildManifest: false, productsBuildParameters: swiftCommandState.productsBuildParameters, toolsBuildParameters: buildParameters, diff --git a/Sources/Commands/PackageCommands/Resolve.swift b/Sources/Commands/PackageCommands/Resolve.swift index 263184c385b..10c5dff9039 100644 --- a/Sources/Commands/PackageCommands/Resolve.swift +++ b/Sources/Commands/PackageCommands/Resolve.swift @@ -31,10 +31,6 @@ extension SwiftPackageCommand { @Argument(help: "The name of the package to resolve.") var packageName: String? - - /// Specifies the traits to build. - @OptionGroup(visibility: .hidden) - package var traits: TraitOptions } struct Resolve: AsyncSwiftCommand { @@ -50,10 +46,10 @@ extension SwiftPackageCommand { func run(_ swiftCommandState: SwiftCommandState) async throws { // If a package is provided, use that to resolve the dependencies. if let packageName = resolveOptions.packageName { - let workspace = try swiftCommandState.getActiveWorkspace(traitConfiguration: .init(traitOptions: resolveOptions.traits)) + let workspace = try swiftCommandState.getActiveWorkspace() try await workspace.resolve( packageName: packageName, - root: swiftCommandState.getWorkspaceRoot(traitConfiguration: .init(traitOptions: resolveOptions.traits)), + root: swiftCommandState.getWorkspaceRoot(), version: resolveOptions.version, branch: resolveOptions.branch, revision: resolveOptions.revision, @@ -64,7 +60,7 @@ extension SwiftPackageCommand { } } else { // Otherwise, run a normal resolve. - try await swiftCommandState.resolve(.init(traitOptions: resolveOptions.traits)) + try await swiftCommandState.resolve() } } } diff --git a/Sources/Commands/Snippets/Cards/SnippetCard.swift b/Sources/Commands/Snippets/Cards/SnippetCard.swift index d54b916ccf3..1e575b869ab 100644 --- a/Sources/Commands/Snippets/Cards/SnippetCard.swift +++ b/Sources/Commands/Snippets/Cards/SnippetCard.swift @@ -112,7 +112,7 @@ struct SnippetCard: Card { func runExample() async throws { print("Building '\(snippet.path)'\n") - let buildSystem = try await swiftCommandState.createBuildSystem(explicitProduct: snippet.name, traitConfiguration: .init()) + let buildSystem = try await swiftCommandState.createBuildSystem(explicitProduct: snippet.name) try await buildSystem.build(subset: .product(snippet.name)) let executablePath = try swiftCommandState.productsBuildParameters.buildPath.appending(component: snippet.name) if let exampleTarget = try await buildSystem.getPackageGraph().module(for: snippet.name) { diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index 8bc8e1aaccb..b2e14fd0da5 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -110,10 +110,6 @@ struct BuildCommandOptions: ParsableArguments { @OptionGroup(visibility: .private) var testLibraryOptions: TestLibraryOptions - /// Specifies the traits to build. - @OptionGroup(visibility: .hidden) - package var traits: TraitOptions - /// If should link the Swift stdlib statically. @Flag(name: .customLong("static-swift-stdlib"), inversion: .prefixedNo, help: "Link Swift stdlib statically.") public var shouldLinkStaticSwiftStdlib: Bool = false @@ -144,7 +140,6 @@ public struct SwiftBuildCommand: AsyncSwiftCommand { // FIXME: Doesn't seem ideal that we need an explicit build operation, but this concretely uses the `LLBuildManifest`. guard let buildOperation = try await swiftCommandState.createBuildSystem( explicitBuildSystem: .native, - traitConfiguration: .init(traitOptions: self.options.traits) ) as? BuildOperation else { throw StringError("asked for native build system but did not get it") } @@ -194,7 +189,6 @@ public struct SwiftBuildCommand: AsyncSwiftCommand { ) async throws { let buildSystem = try await swiftCommandState.createBuildSystem( explicitProduct: options.product, - traitConfiguration: .init(traitOptions: self.options.traits), shouldLinkStaticSwiftStdlib: options.shouldLinkStaticSwiftStdlib, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters, diff --git a/Sources/Commands/SwiftRunCommand.swift b/Sources/Commands/SwiftRunCommand.swift index ac08161af22..d343b5a04aa 100644 --- a/Sources/Commands/SwiftRunCommand.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -88,10 +88,6 @@ struct RunCommandOptions: ParsableArguments { @Argument(help: "The executable to run.", completion: .shellCommand("swift package completion-tool list-executables")) var executable: String? - /// Specifies the traits to build the product with. - @OptionGroup(visibility: .hidden) - package var traits: TraitOptions - /// The arguments to pass to the executable. @Argument(parsing: .captureForPassthrough, help: "The arguments to pass to the executable.") @@ -139,7 +135,6 @@ public struct SwiftRunCommand: AsyncSwiftCommand { // FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with the REPL. rdar://86112934 let buildSystem = try await swiftCommandState.createBuildSystem( explicitBuildSystem: .native, - traitConfiguration: .init(traitOptions: self.options.traits), cacheBuildManifest: false, packageGraphLoader: asyncUnsafeGraphLoader ) @@ -161,7 +156,6 @@ public struct SwiftRunCommand: AsyncSwiftCommand { do { let buildSystem = try await swiftCommandState.createBuildSystem( explicitProduct: options.executable, - traitConfiguration: .init(traitOptions: self.options.traits) ) let productName = try await findProductName(in: buildSystem.getPackageGraph()) if options.shouldBuildTests { @@ -217,9 +211,9 @@ public struct SwiftRunCommand: AsyncSwiftCommand { do { let buildSystem = try await swiftCommandState.createBuildSystem( explicitProduct: options.executable, - traitConfiguration: .init(traitOptions: self.options.traits) ) - let productName = try await findProductName(in: buildSystem.getPackageGraph()) + let modulesGraph = try await buildSystem.getPackageGraph() + let productName = try findProductName(in: modulesGraph) if options.shouldBuildTests { try await buildSystem.build(subset: .allIncludingTests) } else if options.shouldBuild { diff --git a/Sources/Commands/SwiftTestCommand.swift b/Sources/Commands/SwiftTestCommand.swift index a5b1e2cd919..c2ae2ada153 100644 --- a/Sources/Commands/SwiftTestCommand.swift +++ b/Sources/Commands/SwiftTestCommand.swift @@ -220,9 +220,6 @@ struct TestCommandOptions: ParsableArguments { var enableExperimentalTestOutput: Bool { return testOutput == .experimentalSummary } - - @OptionGroup(visibility: .hidden) - package var traits: TraitOptions } /// Tests filtering specifier, which is used to filter tests to run. @@ -659,7 +656,7 @@ public struct SwiftTestCommand: AsyncSwiftCommand { productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters, testProduct: self.options.sharedOptions.testProduct, - traitConfiguration: .init(traitOptions: self.options.traits) + traitConfiguration: .init(traitOptions: self.globalOptions.traits) ) } @@ -741,9 +738,6 @@ extension SwiftTestCommand { @OptionGroup() var testEventStreamOptions: TestEventStreamOptions - @OptionGroup(visibility: .hidden) - package var traits: TraitOptions - // for deprecated passthrough from SwiftTestTool (parse will fail otherwise) @Flag(name: [.customLong("list-tests"), .customShort("l")], help: .hidden) var _deprecated_passthrough: Bool = false @@ -850,7 +844,7 @@ extension SwiftTestCommand { productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters, testProduct: self.sharedOptions.testProduct, - traitConfiguration: .init(traitOptions: self.traits) + traitConfiguration: .init(traitOptions: self.globalOptions.traits) ) } } @@ -1561,7 +1555,6 @@ private func buildTestsIfNeeded( traitConfiguration: TraitConfiguration ) async throws -> [BuiltTestProduct] { let buildSystem = try await swiftCommandState.createBuildSystem( - traitConfiguration: traitConfiguration, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters ) diff --git a/Sources/Commands/Utilities/APIDigester.swift b/Sources/Commands/Utilities/APIDigester.swift index 208d78adbea..62a7d15a031 100644 --- a/Sources/Commands/Utilities/APIDigester.swift +++ b/Sources/Commands/Utilities/APIDigester.swift @@ -140,7 +140,6 @@ struct APIDigesterBaselineDumper { // FIXME: We need to implement the build tool invocation closure here so that build tool plugins work with the APIDigester. rdar://86112934 let buildSystem = try await swiftCommandState.createBuildSystem( explicitBuildSystem: .native, - traitConfiguration: .init(), cacheBuildManifest: false, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters, diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index ae75b3ea919..be788e59797 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -171,7 +171,6 @@ final class PluginDelegate: PluginInvocationDelegate { let buildSystem = try await swiftCommandState.createBuildSystem( explicitBuildSystem: buildSystem, explicitProduct: explicitProduct, - traitConfiguration: .init(), cacheBuildManifest: false, productsBuildParameters: buildParameters, outputStream: outputStream, @@ -237,7 +236,6 @@ final class PluginDelegate: PluginInvocationDelegate { toolsBuildParameters.testingParameters.explicitlyEnabledTestability = true toolsBuildParameters.testingParameters.enableCodeCoverage = parameters.enableCodeCoverage let buildSystem = try await swiftCommandState.createBuildSystem( - traitConfiguration: .init(), toolsBuildParameters: toolsBuildParameters ) try await buildSystem.build(subset: .allIncludingTests) @@ -401,7 +399,7 @@ final class PluginDelegate: PluginInvocationDelegate { // Create a build system for building the target., skipping the the cache because we need the build plan. let buildSystem = try await swiftCommandState.createBuildSystem( explicitBuildSystem: buildSystem, - traitConfiguration: TraitConfiguration(enableAllTraits: true), + enableAllTraits: true, cacheBuildManifest: false ) diff --git a/Sources/CoreCommands/BuildSystemSupport.swift b/Sources/CoreCommands/BuildSystemSupport.swift index ef0e744b4cf..03100ac6a2f 100644 --- a/Sources/CoreCommands/BuildSystemSupport.swift +++ b/Sources/CoreCommands/BuildSystemSupport.swift @@ -29,7 +29,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { func makeBuildSystem( explicitProduct: String?, - traitConfiguration: TraitConfiguration, + enableAllTraits: Bool, cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, @@ -38,7 +38,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { logLevel: Diagnostic.Severity?, observabilityScope: ObservabilityScope? ) async throws -> any BuildSystem { - _ = try await swiftCommandState.getRootPackageInformation(traitConfiguration: traitConfiguration) + _ = try await swiftCommandState.getRootPackageInformation(enableAllTraits) let testEntryPointPath = productsBuildParameters?.testProductStyle.explicitlySpecifiedEntryPointPath let cacheBuildManifest = if cacheBuildManifest { try await self.swiftCommandState.canUseCachedBuildManifest() @@ -52,7 +52,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { packageGraphLoader: packageGraphLoader ?? { try await self.swiftCommandState.loadPackageGraph( explicitProduct: explicitProduct, - traitConfiguration: traitConfiguration, + enableAllTraits: enableAllTraits, testEntryPointPath: testEntryPointPath ) }, @@ -62,7 +62,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory { disableSandbox: self.swiftCommandState.shouldDisableSandbox ), scratchDirectory: self.swiftCommandState.scratchDirectory, - traitConfiguration: traitConfiguration, + traitConfiguration: enableAllTraits ? .enableAllTraits : self.swiftCommandState.traitConfiguration, additionalFileRules: FileRuleDescription.swiftpmFileTypes, pkgConfigDirectories: self.swiftCommandState.options.locations.pkgConfigDirectories, outputStream: outputStream ?? self.swiftCommandState.outputStream, @@ -77,7 +77,7 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory { func makeBuildSystem( explicitProduct: String?, - traitConfiguration: TraitConfiguration, + enableAllTraits: Bool, cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, @@ -91,7 +91,7 @@ private struct XcodeBuildSystemFactory: BuildSystemFactory { packageGraphLoader: packageGraphLoader ?? { try await self.swiftCommandState.loadPackageGraph( explicitProduct: explicitProduct, - traitConfiguration: traitConfiguration + enableAllTraits: enableAllTraits ) }, outputStream: outputStream ?? self.swiftCommandState.outputStream, @@ -107,7 +107,7 @@ private struct SwiftBuildSystemFactory: BuildSystemFactory { func makeBuildSystem( explicitProduct: String?, - traitConfiguration: TraitConfiguration, + enableAllTraits: Bool, cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, @@ -120,7 +120,8 @@ private struct SwiftBuildSystemFactory: BuildSystemFactory { buildParameters: productsBuildParameters ?? self.swiftCommandState.productsBuildParameters, packageGraphLoader: packageGraphLoader ?? { try await self.swiftCommandState.loadPackageGraph( - explicitProduct: explicitProduct + explicitProduct: explicitProduct, + enableAllTraits: enableAllTraits, ) }, packageManagerResourcesDirectory: swiftCommandState.packageManagerResourcesDirectory, diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index 9dddd1f9c58..9330f43d92d 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -63,6 +63,9 @@ public struct GlobalOptions: ParsableArguments { @OptionGroup(title: "Build Options") public var linker: LinkerOptions + + @OptionGroup(title: "Trait Options") + public var traits: TraitOptions } public struct LocationOptions: ParsableArguments { @@ -683,8 +686,8 @@ public struct TestLibraryOptions: ParsableArguments { } } -package struct TraitOptions: ParsableArguments { - package init() {} +public struct TraitOptions: ParsableArguments { + public init() {} /// The traits to enable for the package. @Option( @@ -694,7 +697,7 @@ package struct TraitOptions: ParsableArguments { package var _enabledTraits: String? /// The set of enabled traits for the package. - package var enabledTraits: Set? { + public var enabledTraits: Set? { self._enabledTraits.flatMap { Set($0.components(separatedBy: ",")) } } @@ -703,7 +706,7 @@ package struct TraitOptions: ParsableArguments { name: .customLong("enable-all-traits"), help: "Enables all traits of the package." ) - package var enableAllTraits: Bool = false + public var enableAllTraits: Bool = false /// Disables all default traits of the package. @Flag( @@ -714,7 +717,7 @@ package struct TraitOptions: ParsableArguments { } extension TraitConfiguration { - package init(traitOptions: TraitOptions) { + public init(traitOptions: TraitOptions) { var enabledTraits = traitOptions.enabledTraits if traitOptions.disableDefaultTraits { // If there are no enabled traits specified we can disable the diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index f30c5103afa..38b6f849225 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -215,7 +215,7 @@ public final class SwiftCommandState { } /// Get the current workspace root object. - public func getWorkspaceRoot(traitConfiguration: TraitConfiguration = .default) throws -> PackageGraphRootInput { + public func getWorkspaceRoot() throws -> PackageGraphRootInput { let packages: [AbsolutePath] if let workspace = options.locations.multirootPackageDataFile { @@ -225,7 +225,7 @@ public final class SwiftCommandState { packages = try [self.getPackageRoot()] } - return PackageGraphRootInput(packages: packages, traitConfiguration: traitConfiguration) + return PackageGraphRootInput(packages: packages, traitConfiguration: self.traitConfiguration) } /// Scratch space (.build) directory. @@ -292,6 +292,8 @@ public final class SwiftCommandState { package var preferredBuildConfiguration = BuildConfiguration.debug + package let traitConfiguration: TraitConfiguration + /// Create an instance of this tool. /// /// - parameter options: The command line options to be passed to this tool. @@ -413,6 +415,9 @@ public final class SwiftCommandState { explicitDirectory: options.locations.swiftSDKsDirectory ?? options.locations.deprecatedSwiftSDKsDirectory ) + // Set the trait configuration from user-passed trait options. + self.traitConfiguration = .init(traitOptions: options.traits) + // set global process logging handler AsyncProcess.loggingHandler = { self.observabilityScope.emit(debug: $0) } } @@ -457,8 +462,13 @@ public final class SwiftCommandState { } /// Returns the currently active workspace. - public func getActiveWorkspace(emitDeprecatedConfigurationWarning: Bool = false, traitConfiguration: TraitConfiguration = .default) throws -> Workspace { - if let workspace = _workspace { + public func getActiveWorkspace(emitDeprecatedConfigurationWarning: Bool = false, enableAllTraits: Bool = false) throws -> Workspace { + if var workspace = _workspace { + // if we decide to override the trait configuration, we can resolve accordingly for + // calls like createSymbolGraphForPlugin. + if enableAllTraits { + workspace = workspace.updateConfiguration(with: .enableAllTraits) + } return workspace } @@ -511,7 +521,7 @@ public final class SwiftCommandState { prebuiltsDownloadURL: options.caching.prebuiltsDownloadURL, prebuiltsRootCertPath: options.caching.prebuiltsRootCertPath, pruneDependencies: self.options.resolver.pruneDependencies, - traitConfiguration: traitConfiguration + traitConfiguration: self.traitConfiguration ), cancellator: self.cancellator, initializationWarningHandler: { self.observabilityScope.emit(warning: $0) }, @@ -524,9 +534,9 @@ public final class SwiftCommandState { return workspace } - public func getRootPackageInformation(traitConfiguration: TraitConfiguration = .default) async throws -> (dependencies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) { - let workspace = try self.getActiveWorkspace(traitConfiguration: traitConfiguration) - let root = try self.getWorkspaceRoot(traitConfiguration: traitConfiguration) + public func getRootPackageInformation(_ enableAllTraits: Bool = false) async throws -> (dependencies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) { + let workspace = try self.getActiveWorkspace(enableAllTraits: enableAllTraits) + let root = try self.getWorkspaceRoot() let rootManifests = try await workspace.loadRootManifests( packages: root.packages, observabilityScope: self.observabilityScope @@ -649,9 +659,9 @@ public final class SwiftCommandState { } /// Resolve the dependencies. - public func resolve(_ traitConfiguration: TraitConfiguration = .default) async throws { - let workspace = try getActiveWorkspace(traitConfiguration: traitConfiguration) - let root = try getWorkspaceRoot(traitConfiguration: traitConfiguration) + public func resolve() async throws { + let workspace = try getActiveWorkspace() + let root = try getWorkspaceRoot() try await workspace.resolve( root: root, @@ -679,7 +689,7 @@ public final class SwiftCommandState { ) async throws -> ModulesGraph { try await self.loadPackageGraph( explicitProduct: explicitProduct, - traitConfiguration: .default, + enableAllTraits: false, testEntryPointPath: testEntryPointPath ) } @@ -692,15 +702,15 @@ public final class SwiftCommandState { @discardableResult package func loadPackageGraph( explicitProduct: String? = nil, - traitConfiguration: TraitConfiguration = .default, + enableAllTraits: Bool = false, testEntryPointPath: AbsolutePath? = nil ) async throws -> ModulesGraph { do { - let workspace = try getActiveWorkspace(traitConfiguration: traitConfiguration) + let workspace = try getActiveWorkspace(enableAllTraits: enableAllTraits) // Fetch and load the package graph. let graph = try await workspace.loadPackageGraph( - rootInput: self.getWorkspaceRoot(traitConfiguration: traitConfiguration), + rootInput: self.getWorkspaceRoot(), explicitProduct: explicitProduct, forceResolvedVersions: self.options.resolver.forceResolvedVersions, testEntryPointPath: testEntryPointPath, @@ -764,7 +774,7 @@ public final class SwiftCommandState { // Perform steps for build manifest caching if we can enabled it. // // FIXME: We don't add edited packages in the package structure command yet (SR-11254). - let hasEditedPackages = try await self.getActiveWorkspace(traitConfiguration: traitConfiguration).state.dependencies.contains(where: \.isEdited) + let hasEditedPackages = try await self.getActiveWorkspace().state.dependencies.contains(where: \.isEdited) if hasEditedPackages { return false } @@ -779,7 +789,7 @@ public final class SwiftCommandState { public func createBuildSystem( explicitBuildSystem: BuildSystemProvider.Kind? = .none, explicitProduct: String? = .none, - traitConfiguration: TraitConfiguration, + enableAllTraits: Bool = false, cacheBuildManifest: Bool = true, shouldLinkStaticSwiftStdlib: Bool = false, productsBuildParameters: BuildParameters? = .none, @@ -792,14 +802,12 @@ public final class SwiftCommandState { guard let buildSystemProvider else { fatalError("build system provider not initialized") } - var productsParameters = try productsBuildParameters ?? self.productsBuildParameters productsParameters.linkingParameters.shouldLinkStaticSwiftStdlib = shouldLinkStaticSwiftStdlib - let buildSystem = try await buildSystemProvider.createBuildSystem( kind: explicitBuildSystem ?? self.options.build.buildSystem, explicitProduct: explicitProduct, - traitConfiguration: traitConfiguration, + enableAllTraits: enableAllTraits, cacheBuildManifest: cacheBuildManifest, productsBuildParameters: productsParameters, toolsBuildParameters: toolsBuildParameters, diff --git a/Sources/PackageDescription/PackageDependency.swift b/Sources/PackageDescription/PackageDependency.swift index dd654c5b37a..a71e44dffbc 100644 --- a/Sources/PackageDescription/PackageDependency.swift +++ b/Sources/PackageDescription/PackageDependency.swift @@ -56,7 +56,7 @@ extension Package { public let kind: Kind /// The dependencies traits configuration. - @available(_PackageDescription, introduced: 999.0) + @available(_PackageDescription, introduced: 6.1) public let traits: Set /// The name of the dependency. diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index 3e7b4632129..2ea14951129 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -39,7 +39,8 @@ extension ModulesGraph { fileSystem: FileSystem, observabilityScope: ObservabilityScope, productsFilter: ((Product) -> Bool)? = nil, - modulesFilter: ((Module) -> Bool)? = nil + modulesFilter: ((Module) -> Bool)? = nil, + enabledTraitsMap: EnabledTraitsMap ) throws -> ModulesGraph { let observabilityScope = observabilityScope.makeChildScope(description: "Loading Package Graph") @@ -58,17 +59,11 @@ extension ModulesGraph { let rootManifestNodes = try root.packages.map { identity, package in // If we have enabled traits passed then we start with those. If there are no enabled // traits passed then the default traits will be used. - let enabledTraits = root.enabledTraits[identity] return try GraphLoadingNode( identity: identity, manifest: package.manifest, productFilter: .everything, - enabledTraits: calculateEnabledTraits( - parentPackage: nil, - identity: identity, - manifest: package.manifest, - explictlyEnabledTraits: enabledTraits - ) + enabledTraits: enabledTraitsMap[identity] ) } let rootDependencyNodes = try root.dependencies.lazy.filter { requiredDependencies.contains($0.packageRef) } @@ -78,7 +73,7 @@ extension ModulesGraph { identity: dependency.identity, manifest: $0.manifest, productFilter: dependency.productFilter, - enabledTraits: [] + enabledTraits: enabledTraitsMap[dependency.identity] ) } } @@ -100,26 +95,13 @@ extension ModulesGraph { // We are going to check the conditionally enabled traits here and enable them if // required. This checks the current node and then enables the conditional // dependencies of the dependency node. - let explictlyEnabledTraits = dependency.traits?.filter { - guard let conditionTraits = $0.condition?.traits else { - return true - } - return !conditionTraits.intersection(node.item.enabledTraits).isEmpty - }.map(\.name) - - let calculatedTraits = try calculateEnabledTraits( - parentPackage: node.item.identity, - identity: dependency.identity, - manifest: manifest, - explictlyEnabledTraits: explictlyEnabledTraits.flatMap { Set($0) } - ) return try KeyedPair( GraphLoadingNode( identity: dependency.identity, manifest: manifest, productFilter: dependency.productFilter, - enabledTraits: calculatedTraits + enabledTraits: enabledTraitsMap[manifest.packageIdentity] ), key: dependency.identity ) @@ -151,9 +133,8 @@ extension ModulesGraph { successors: nodeSuccessorProvider ) { allNodes[$0.key] = $0.item - } onDuplicate: { first, second in - // We are unifying the enabled traits on duplicate - allNodes[first.key]?.enabledTraits.formUnion(second.item.enabledTraits) + } onDuplicate: { _, _ in + // Nothing we need to compute here. } // Create the packages. @@ -171,6 +152,14 @@ extension ModulesGraph { let packagePath = manifest.path.parentDirectory nodeObservabilityScope.trap { // Create a package from the manifest and sources. + + // Special case to handle: if the traits enabled for this node is simply ["default"], + // this means that we don't have any defined traits for this package and should there + // flatten the set to be empty for the PackageBuilder. + var enabledTraits = node.enabledTraits + if enabledTraits == ["default"] { + enabledTraits = [] + } let builder = PackageBuilder( identity: node.identity, manifest: manifest, @@ -184,7 +173,7 @@ extension ModulesGraph { createREPLProduct: manifest.packageKind.isRoot ? createREPLProduct : false, fileSystem: fileSystem, observabilityScope: nodeObservabilityScope, - enabledTraits: node.enabledTraits + enabledTraits: enabledTraits ) let package = try builder.construct() manifestToPackage[manifest] = package @@ -304,7 +293,7 @@ private func checkAllDependenciesAreUsed( // that can be configured by enabling traits e.g. the depdency has a trait for its logging // behaviour. This allows the root package to configure traits of transitive dependencies // without emitting an unused dependency warning. - if !dependency.enabledTraits.isEmpty { + if dependency.manifest.supportsTraits { continue } @@ -410,7 +399,7 @@ private func createResolvedPackages( return ResolvedPackageBuilder( package, productFilter: node.productFilter, - enabledTraits: node.enabledTraits, + enabledTraits: node.enabledTraits /*?? []*/, isAllowedToVendUnsafeProducts: isAllowedToVendUnsafeProducts, allowedToOverride: allowedToOverride, platformVersionProvider: platformVersionProvider @@ -1449,7 +1438,7 @@ private final class ResolvedPackageBuilder: ResolvedBuilder { var products: [ResolvedProductBuilder] = [] /// The enabled traits of this package. - var enabledTraits: Set = [] + var enabledTraits: Set /// The dependencies of this package. var dependencies: [ResolvedPackageBuilder] = [] diff --git a/Sources/PackageGraph/ModulesGraph.swift b/Sources/PackageGraph/ModulesGraph.swift index 7c7e9800be5..5c9fe3ced47 100644 --- a/Sources/PackageGraph/ModulesGraph.swift +++ b/Sources/PackageGraph/ModulesGraph.swift @@ -470,12 +470,90 @@ public func loadModulesGraph( } let packages = Array(rootManifests.keys) + + let manifestMap = manifests.reduce(into: [PackageIdentity: Manifest]()) { manifestMap, manifest in + manifestMap[manifest.packageIdentity] = manifest + } + + // Note: The following is a copy of the existing `Workspace.precomputeTraits` method + func precomputeTraits( + _ enabledTraitsMap: EnabledTraitsMap, + _ topLevelManifests: [Manifest], + _ manifestMap: [PackageIdentity: Manifest] + ) throws -> [PackageIdentity: Set] { + var visited: Set = [] + var enabledTraitsMap = enabledTraitsMap + + func dependencies(of parent: Manifest, _ productFilter: ProductFilter = .everything) throws { + let parentTraits = enabledTraitsMap[parent.packageIdentity] + let requiredDependencies = try parent.dependenciesRequired(for: productFilter, parentTraits) + let guardedDependencies = parent.dependenciesTraitGuarded(withEnabledTraits: parentTraits) + + _ = try (requiredDependencies + guardedDependencies).compactMap({ dependency in + return try manifestMap[dependency.identity].flatMap({ manifest in + + let explicitlyEnabledTraits = dependency.traits?.filter { + guard let condition = $0.condition else { return true } + return condition.isSatisfied(by: parentTraits) + }.map(\.name) + + var enabledTraitsSet = explicitlyEnabledTraits.flatMap { Set($0) } + let precomputedTraits = enabledTraitsMap[dependency.identity] + + if precomputedTraits == ["default"], + let enabledTraitsSet { + enabledTraitsMap[dependency.identity] = enabledTraitsSet + } else { + // unify traits + enabledTraitsSet?.formUnion(precomputedTraits) + if let enabledTraitsSet { + enabledTraitsMap[dependency.identity] = enabledTraitsSet + } + } + + let calculatedTraits = try manifest.enabledTraits( + using: enabledTraitsSet ?? ["default"], + .init(parent) + ) + + enabledTraitsMap[dependency.identity] = calculatedTraits + let result = visited.insert(dependency.identity) + if result.inserted { + try dependencies(of: manifest, dependency.productFilter) + } + + return manifest + }) + }) + } + + for manifest in topLevelManifests { + // Track already-visited manifests to avoid cycles + let result = visited.insert(manifest.packageIdentity) + if result.inserted { + try dependencies(of: manifest) + } + } + + return enabledTraitsMap.dictionaryLiteral + } + + + // Precompute enabled traits for roots. + var enabledTraitsMap: EnabledTraitsMap = [:] + for root in rootManifests.values { + let enabledTraits = try root.enabledTraits(using: traitConfiguration) + enabledTraitsMap[root.packageIdentity] = enabledTraits + } + enabledTraitsMap = .init(try precomputeTraits(enabledTraitsMap, manifests, manifestMap)) + let input = PackageGraphRootInput(packages: packages, traitConfiguration: traitConfiguration) let graphRoot = try PackageGraphRoot( input: input, manifests: rootManifests, explicitProduct: explicitProduct, - observabilityScope: observabilityScope + observabilityScope: observabilityScope, + enabledTraitsMap: enabledTraitsMap ) return try ModulesGraph.load( @@ -492,6 +570,7 @@ public func loadModulesGraph( fileSystem: fileSystem, observabilityScope: observabilityScope, productsFilter: nil, - modulesFilter: nil + modulesFilter: nil, + enabledTraitsMap: enabledTraitsMap ) } diff --git a/Sources/PackageGraph/PackageContainer.swift b/Sources/PackageGraph/PackageContainer.swift index 515dfdbb3f4..031799a22da 100644 --- a/Sources/PackageGraph/PackageContainer.swift +++ b/Sources/PackageGraph/PackageContainer.swift @@ -75,7 +75,7 @@ public protocol PackageContainer { /// - Precondition: `versions.contains(version)` /// - Throws: If the version could not be resolved; this will abort /// dependency resolution completely. - func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set?) async throws -> [PackageContainerConstraint] + func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set) async throws -> [PackageContainerConstraint] /// Fetch the declared dependencies for a particular revision. /// @@ -84,12 +84,12 @@ public protocol PackageContainer { /// /// - Throws: If the revision could not be resolved; this will abort /// dependency resolution completely. - func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set?) async throws -> [PackageContainerConstraint] + func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set) async throws -> [PackageContainerConstraint] /// Fetch the dependencies of an unversioned package container. /// /// NOTE: This method should not be called on a versioned container. - func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set?) async throws -> [PackageContainerConstraint] + func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set) async throws -> [PackageContainerConstraint] /// Get the updated identifier at a bound version. /// @@ -97,12 +97,6 @@ public protocol PackageContainer { /// after the container is available. The updated identifier is returned in result of the /// dependency resolution. func loadPackageReference(at boundVersion: BoundVersion) async throws -> PackageReference - - - /// Fetch the enabled traits of a package container. - /// - /// NOTE: This method should only be called on root packages. - func getEnabledTraits(traitConfiguration: TraitConfiguration, version: Version?) async throws -> Set } extension PackageContainer { @@ -118,7 +112,7 @@ extension PackageContainer { return true } - public func getEnabledTraits(traitConfiguration: TraitConfiguration, version: Version? = nil) async throws -> Set { + func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) async throws -> [PackageContainerConstraint] { return [] } } @@ -156,11 +150,11 @@ public struct PackageContainerConstraint: Equatable, Hashable { public let products: ProductFilter /// The traits that have been enabled for the package. - public let enabledTraits: Set? + public let enabledTraits: Set /// Create a constraint requiring the given `container` satisfying the /// `requirement`. - public init(package: PackageReference, requirement: PackageRequirement, products: ProductFilter, enabledTraits: Set? = nil) { + public init(package: PackageReference, requirement: PackageRequirement, products: ProductFilter, enabledTraits: Set = ["default"]) { self.package = package self.requirement = requirement self.products = products @@ -169,7 +163,7 @@ public struct PackageContainerConstraint: Equatable, Hashable { /// Create a constraint requiring the given `container` satisfying the /// `versionRequirement`. - public init(package: PackageReference, versionRequirement: VersionSetSpecifier, products: ProductFilter, enabledTraits: Set? = nil) { + public init(package: PackageReference, versionRequirement: VersionSetSpecifier, products: ProductFilter, enabledTraits: Set = ["default"]) { self.init(package: package, requirement: .versionSet(versionRequirement), products: products, enabledTraits: enabledTraits) } @@ -188,7 +182,7 @@ public struct PackageContainerConstraint: Equatable, Hashable { extension PackageContainerConstraint: CustomStringConvertible { public var description: String { - return "Constraint(\(self.package), \(requirement), \(products), \(enabledTraits ?? [])" + return "Constraint(\(self.package), \(requirement), \(products), \(enabledTraits)" } } diff --git a/Sources/PackageGraph/PackageGraphRoot.swift b/Sources/PackageGraph/PackageGraphRoot.swift index 8091ba65656..7f7f3b56521 100644 --- a/Sources/PackageGraph/PackageGraphRoot.swift +++ b/Sources/PackageGraph/PackageGraphRoot.swift @@ -49,9 +49,6 @@ public struct PackageGraphRoot { return self.packages.compactMapValues { $0.manifest } } - /// The root manifest(s)'s enabled traits (and their transitively enabled traits). - public var enabledTraits: [PackageIdentity: Set] - /// The root package references. public var packageReferences: [PackageReference] { return self.packages.values.map { $0.reference } @@ -94,7 +91,8 @@ public struct PackageGraphRoot { manifests: [AbsolutePath: Manifest], explicitProduct: String? = nil, dependencyMapper: DependencyMapper? = nil, - observabilityScope: ObservabilityScope + observabilityScope: ObservabilityScope, + enabledTraitsMap: EnabledTraitsMap = .init() ) throws { self.packages = input.packages.reduce(into: .init(), { partial, inputPath in if let manifest = manifests[inputPath] { @@ -104,27 +102,6 @@ public struct PackageGraphRoot { } }) - // Calculate the enabled traits for root. - var enableTraitsMap: [PackageIdentity: Set] = [:] - enableTraitsMap = try packages.reduce(into: [PackageIdentity: Set]()) { traitsMap, package in - let manifest = package.value.manifest - let traitConfiguration = input.traitConfiguration - - // Should only ever have to use trait configuration here for roots. - let enabledTraits = try manifest.enabledTraits(using: traitConfiguration) - traitsMap[package.key] = enabledTraits - - // Calculate the enabled traits for each dependency of this root: - manifest.dependencies.forEach { dependency in - if let traits = dependency.traits { - let traitNames = traits.map(\.name) - traitsMap[dependency.identity, default: []].formUnion(Set(traitNames)) - } - } - } - - self.enabledTraits = enableTraitsMap - // FIXME: Deprecate special casing once the manifest supports declaring used executable products. // Special casing explicit products like this is necessary to pass the test suite and satisfy backwards compatibility. // However, changing the dependencies based on the command line arguments may force `Package.resolved` to temporarily change, @@ -138,8 +115,7 @@ public struct PackageGraphRoot { // If not, then we can omit this dependency if pruning unused dependencies // is enabled. return manifests.values.reduce(false) { result, manifest in - guard manifest.pruneDependencies else { return true } - let enabledTraits: Set? = enableTraitsMap[manifest.packageIdentity] + let enabledTraits: Set = enabledTraitsMap[manifest.packageIdentity] if let isUsed = try? manifest.isPackageDependencyUsed(dep, enabledTraits: enabledTraits) { return result || isUsed } @@ -152,7 +128,7 @@ public struct PackageGraphRoot { // FIXME: `dependenciesRequired` modifies manifests and prevents conversion of `Manifest` to a value type let deps = try? manifests.values.lazy .map({ manifest -> [PackageDependency] in - let enabledTraits: Set? = enableTraitsMap[manifest.packageIdentity] + let enabledTraits: Set = enabledTraitsMap[manifest.packageIdentity] return try manifest.dependenciesRequired(for: .everything, enabledTraits) }) .flatMap({ $0 }) @@ -168,10 +144,11 @@ public struct PackageGraphRoot { } /// Returns the constraints imposed by root manifests + dependencies. - public func constraints() throws -> [PackageContainerConstraint] { + public func constraints(_ enabledTraitsMap: EnabledTraitsMap) throws -> [PackageContainerConstraint] { + var rootEnabledTraits: Set = [] let constraints = self.packages.map { (identity, package) in - // Since these are root packages, can apply trait configuration as this is a root package concept. - let enabledTraits = self.enabledTraits[identity] + let enabledTraits = enabledTraitsMap[identity] + rootEnabledTraits.formUnion(enabledTraits) return PackageContainerConstraint( package: package.reference, requirement: .unversioned, @@ -182,16 +159,19 @@ public struct PackageGraphRoot { let depend = try dependencies .map { dep in - var enabledTraits: Set? - if let traits = dep.traits { - enabledTraits = Set(traits.map(\.name)) - } + let enabledTraits = dep.traits?.filter { + guard let condition = $0.condition else { return true } + return condition.isSatisfied(by: rootEnabledTraits) + }.map(\.name) + + var enabledTraitsSet = enabledTraits.flatMap { Set($0) } ?? ["default"] + enabledTraitsSet.formUnion(enabledTraitsMap[dep.identity]) return PackageContainerConstraint( package: dep.packageRef, requirement: try dep.toConstraintRequirement(), products: dep.productFilter, - enabledTraits: enabledTraits + enabledTraits: enabledTraitsSet ) } diff --git a/Sources/PackageGraph/PackageModel+Extensions.swift b/Sources/PackageGraph/PackageModel+Extensions.swift index 0fd06072ff3..a32b352fa6a 100644 --- a/Sources/PackageGraph/PackageModel+Extensions.swift +++ b/Sources/PackageGraph/PackageModel+Extensions.swift @@ -35,17 +35,20 @@ extension PackageDependency { extension Manifest { /// Constructs constraints of the dependencies in the raw package. - public func dependencyConstraints(productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + public func dependencyConstraints(productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { return try self.dependenciesRequired(for: productFilter, enabledTraits).map({ - var explicitlyEnabledTraits: Set? - if let traits = $0.traits { - explicitlyEnabledTraits = Set(traits.map(\.name)) - } + let explicitlyEnabledTraits = $0.traits?.filter { + guard let condition = $0.condition else { return true } + return condition.isSatisfied(by: enabledTraits) + }.map(\.name) + + var enabledTraitsSet = explicitlyEnabledTraits.flatMap({ Set($0) }) ?? ["default"] + return PackageContainerConstraint( package: $0.packageRef, requirement: try $0.toConstraintRequirement(), products: $0.productFilter, - enabledTraits: explicitlyEnabledTraits + enabledTraits: enabledTraitsSet ) }) } diff --git a/Sources/PackageGraph/Resolution/DependencyResolutionNode.swift b/Sources/PackageGraph/Resolution/DependencyResolutionNode.swift index 8865707b651..11693a109c7 100644 --- a/Sources/PackageGraph/Resolution/DependencyResolutionNode.swift +++ b/Sources/PackageGraph/Resolution/DependencyResolutionNode.swift @@ -44,7 +44,7 @@ public enum DependencyResolutionNode { /// Since a non‐existent product ends up with only its implicit dependency on its own package, /// only whichever package contains the product will end up adding additional constraints. /// See `ProductFilter` and `Manifest.register(...)`. - case product(String, package: PackageReference, enabledTraits: Set? = nil) + case product(String, package: PackageReference, enabledTraits: Set = ["default"]) /// A root node. /// @@ -58,7 +58,7 @@ public enum DependencyResolutionNode { /// It is a warning condition, and builds do not actually need these dependencies. /// However, forcing the graph to resolve and fetch them anyway allows the diagnostics passes access /// to the information needed in order to provide actionable suggestions to help the user stitch up the dependency declarations properly. - case root(package: PackageReference, traitConfiguration: TraitConfiguration = .default) + case root(package: PackageReference, enabledTraits: Set = ["default"]) /// The package. public var package: PackageReference { @@ -91,25 +91,12 @@ public enum DependencyResolutionNode { } /// Returns the enabled traits for this node's manifest. - public var traits: Set? { + public var enabledTraits: Set { switch self { - case .root(_, let config): - return config.enabledTraits - case .product(_, _, let enabledTraits): + case .root(_, let enabledTraits), .product(_, _, let enabledTraits): return enabledTraits default: - return nil - } - } - - public var traitConfiguration: TraitConfiguration { - switch self { - case .root(_, let config): - return config - case .product(_, _, let enabledTraits): - return .init(enabledTraits: enabledTraits) - case .empty: - return .default + return ["default"] } } @@ -123,7 +110,7 @@ public enum DependencyResolutionNode { package: self.package, versionRequirement: .exact(version), products: .specific([]), - enabledTraits: traits + enabledTraits: self.enabledTraits ) } @@ -137,7 +124,7 @@ public enum DependencyResolutionNode { package: self.package, requirement: .revision(revision), products: .specific([]), - enabledTraits: traits + enabledTraits: self.enabledTraits ) } } diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index 71e39debc1e..801162636d8 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -165,12 +165,12 @@ public struct PubGrubDependencyResolver { } /// Execute the resolution algorithm to find a valid assignment of versions. - public func solve(constraints: [Constraint], traitConfiguration: TraitConfiguration = .default) async -> Result<[DependencyResolverBinding], Error> { + public func solve(constraints: [Constraint]) async -> Result<[DependencyResolverBinding], Error> { // the graph resolution root let root: DependencyResolutionNode if constraints.count == 1, let constraint = constraints.first, constraint.package.kind.isRoot { // root level package, use it as our resolution root - root = .root(package: constraint.package, traitConfiguration: traitConfiguration) + root = .root(package: constraint.package, enabledTraits: constraint.enabledTraits) } else { // more complex setup requires a synthesized root root = .root( @@ -178,7 +178,7 @@ public struct PubGrubDependencyResolver { identity: .plain(""), path: .root ), - traitConfiguration: traitConfiguration + enabledTraits: ["default"] ) } diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubPackageContainer.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubPackageContainer.swift index 37893363a04..f82fa5d6d81 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubPackageContainer.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubPackageContainer.swift @@ -167,11 +167,10 @@ final class PubGrubPackageContainer { )] } - let enabledTraits = node.package.kind.isRoot ? try await self.underlying.getEnabledTraits(traitConfiguration: node.traitConfiguration) : node.traits var unprocessedDependencies = try await self.underlying.getDependencies( at: version, productFilter: node.productFilter, - enabledTraits + node.enabledTraits ) if let sharedVersion = node.versionLock(version: version) { unprocessedDependencies.append(sharedVersion) diff --git a/Sources/PackageGraph/Resolution/ResolvedPackage.swift b/Sources/PackageGraph/Resolution/ResolvedPackage.swift index 8534d9de2aa..5ace11d7207 100644 --- a/Sources/PackageGraph/Resolution/ResolvedPackage.swift +++ b/Sources/PackageGraph/Resolution/ResolvedPackage.swift @@ -40,7 +40,7 @@ public struct ResolvedPackage { public let products: [ResolvedProduct] /// The enabled traits of this package. - public let enabledTraits: Set + public let enabledTraits: Set? /// The dependencies of the package. public let dependencies: [PackageIdentity] @@ -62,7 +62,7 @@ public struct ResolvedPackage { defaultLocalization: String?, supportedPlatforms: [SupportedPlatform], dependencies: [PackageIdentity], - enabledTraits: Set, + enabledTraits: Set?, modules: IdentifiableSet, products: [ResolvedProduct], registryMetadata: RegistryReleaseMetadata?, diff --git a/Sources/PackageModel/CMakeLists.txt b/Sources/PackageModel/CMakeLists.txt index 3d5f2f509d3..aa8848b4155 100644 --- a/Sources/PackageModel/CMakeLists.txt +++ b/Sources/PackageModel/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(PackageModel BuildSettings.swift DependencyMapper.swift Diagnostics.swift + EnabledTraitsMap.swift IdentityResolver.swift InstalledSwiftPMConfiguration.swift Manifest/Manifest.swift diff --git a/Sources/PackageModel/EnabledTraitsMap.swift b/Sources/PackageModel/EnabledTraitsMap.swift new file mode 100644 index 00000000000..958aeb15219 --- /dev/null +++ b/Sources/PackageModel/EnabledTraitsMap.swift @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +/// A wrapper for a dictionary that stores the transitively enabled traits for each package. +public struct EnabledTraitsMap: ExpressibleByDictionaryLiteral { + public typealias Key = PackageIdentity + public typealias Value = Set + + var storage: [PackageIdentity: Set] = [:] + + public init() { } + + public init(dictionaryLiteral elements: (Key, Value)...) { + for (key, value) in elements { + storage[key] = value + } + } + + public init(_ dictionary: [Key: Value]) { + self.storage = dictionary + } + + public subscript(key: PackageIdentity) -> Set { + get { storage[key] ?? ["default"] } + set { storage[key] = newValue } + } + + public var dictionaryLiteral: [PackageIdentity: Set] { + return storage + } +} diff --git a/Sources/PackageModel/Manifest/Manifest+Traits.swift b/Sources/PackageModel/Manifest/Manifest+Traits.swift index 5f9260cc858..9f9364ea3a9 100644 --- a/Sources/PackageModel/Manifest/Manifest+Traits.swift +++ b/Sources/PackageModel/Manifest/Manifest+Traits.swift @@ -17,6 +17,29 @@ import Foundation /// Validator methods that check the correctness of traits and their support as defined in the manifest. extension Manifest { + public struct PackageIdentifier: Hashable, CustomStringConvertible { + public var identity: String + public var name: String? + + public init(identity: String, name: String? = nil) { + self.identity = identity + self.name = name + } + + public init(_ parent: Manifest) { + self.identity = parent.packageIdentity.description + self.name = parent.displayName + } + + public var description: String { + var result = "'\(identity)'" + if let name { + result.append(" (\(name))") + } + return result + } + } + /// Determines whether traits are supported for this Manifest. public var supportsTraits: Bool { !self.traits.isEmpty @@ -27,7 +50,7 @@ extension Manifest { guard !trait.isDefault else { if !supportsTraits { throw TraitError.invalidTrait( - package: self.displayName, + package: .init(self), trait: trait.name, availableTraits: traits.map({ $0.name }) ) @@ -40,11 +63,11 @@ extension Manifest { } /// Validates a trait by checking that it is defined in the manifest; if not, an error is thrown. - private func validateTrait(_ trait: String, parentPackage: String? = nil) throws { + private func validateTrait(_ trait: String, parentPackage: PackageIdentifier? = nil) throws { guard trait != "default" else { if !supportsTraits { throw TraitError.invalidTrait( - package: self.displayName, + package: .init(self), trait: trait, availableTraits: traits.map({ $0.name }) ) @@ -56,10 +79,10 @@ extension Manifest { // Check if the passed trait is a valid trait. if self.traits.first(where: { $0.name == trait }) == nil { throw TraitError.invalidTrait( - package: self.displayName, + package: .init(self), trait: trait, availableTraits: self.traits.map({ $0.name }), - parentPackage: parentPackage + parent: parentPackage ) } } @@ -68,14 +91,14 @@ extension Manifest { /// set of enabled traits and whether the manifest defines these traits (or if it defines any traits at all), then an /// error indicating the issue will be thrown. private func validateEnabledTraits( - _ explicitlyEnabledTraits: Set?, - _ parentPackage: String? = nil + _ explicitlyEnabledTraits: Set, + _ parentPackage: PackageIdentifier? = nil ) throws { guard supportsTraits else { - if let explicitlyEnabledTraits, !explicitlyEnabledTraits.contains("default") { + if explicitlyEnabledTraits != ["default"] /*!explicitlyEnabledTraits.contains("default")*/ { throw TraitError.traitsNotSupported( - parentPackage: parentPackage, - package: self.displayName, + parent: parentPackage, + package: .init(self), explicitlyEnabledTraits: explicitlyEnabledTraits.map({ $0 }) ) } @@ -83,7 +106,7 @@ extension Manifest { return } - let enabledTraits = explicitlyEnabledTraits ?? [] + let enabledTraits = explicitlyEnabledTraits // Validate each trait to assure it's defined in the current package. for trait in enabledTraits { @@ -97,8 +120,8 @@ extension Manifest { // We throw an error when default traits are disabled for a package without any traits // This allows packages to initially move new API behind traits once. throw TraitError.traitsNotSupported( - parentPackage: parentPackage, - package: displayName, + parent: parentPackage, + package: .init(self), explicitlyEnabledTraits: enabledTraits.map({ $0 }) ) } @@ -109,14 +132,14 @@ extension Manifest { switch traitConfiguration { case .disableAllTraits: throw TraitError.traitsNotSupported( - parentPackage: nil, - package: displayName, + parent: nil, + package: .init(self), explicitlyEnabledTraits: [] ) case .enabledTraits(let traits): throw TraitError.traitsNotSupported( - parentPackage: nil, - package: displayName, + parent: nil, + package: .init(self), explicitlyEnabledTraits: traits.map({ $0 }) ) case .enableAllTraits, .default: @@ -154,13 +177,13 @@ extension Manifest { /// Calculates the set of all transitive traits that are enabled for this manifest using the passed trait configuration. /// Since a trait configuration is only used for root packages, this method is intended for use with root packages only. - public func enabledTraits(using traitConfiguration: TraitConfiguration) throws -> Set? { + public func enabledTraits(using traitConfiguration: TraitConfiguration) throws -> Set { // If this manifest does not support traits, but the passed configuration either // disables default traits or enables non-default traits (i.e. traits that would // not exist for this manifest) then we must throw an error. try validateTraitConfiguration(traitConfiguration) guard supportsTraits, packageKind.isRoot else { - return nil + return ["default"] } var enabledTraits: Set = [] @@ -188,13 +211,13 @@ extension Manifest { /// Calculates the set of all transitive traits that are enabled for this manifest using the passed set of /// explicitly enabled traits, and the parent package that defines the enabled traits for this package. /// This method is intended for use with non-root packages. - public func enabledTraits(using explicitlyEnabledTraits: Set?, _ parentPackage: String?) throws -> Set? { + public func enabledTraits(using explicitlyEnabledTraits: Set = ["default"], _ parentPackage: PackageIdentifier?) throws -> Set { // If this manifest does not support traits, but the passed configuration either // disables default traits or enables non-default traits (i.e. traits that would // not exist for this manifest) then we must throw an error. try validateEnabledTraits(explicitlyEnabledTraits, parentPackage) guard supportsTraits else { - return nil + return ["default"] } var enabledTraits: Set = [] @@ -204,11 +227,10 @@ extension Manifest { } return enabledTraits - } /// Determines if a trait is enabled with a given set of enabled traits. - public func isTraitEnabled(_ trait: TraitDescription, _ enabledTraits: Set?) throws -> Bool { + public func isTraitEnabled(_ trait: TraitDescription, _ enabledTraits: Set) throws -> Bool { // First, check that the queried trait is valid. try validateTrait(trait) // Then, check that the list of enabled traits is valid. @@ -224,9 +246,9 @@ extension Manifest { // - If there is no existing list of enabled traits (nil), and we know that the // manifest has defined default traits, then just return true. // - If none of these conditions are met, then defaults aren't enabled and we return false. - if let enabledTraits, enabledTraits.contains(trait.name) { + if enabledTraits.contains(trait.name) { return true - } else if enabledTraits == nil { + } else if enabledTraits.isEmpty { return true } else { return false @@ -235,7 +257,7 @@ extension Manifest { // If manifest does not define default traits, then throw an invalid trait error. throw TraitError.invalidTrait( - package: self.displayName, + package: .init(self), trait: trait.name, availableTraits: self.traits.map(\.name) ) @@ -257,9 +279,9 @@ extension Manifest { // - If there is no existing list of enabled traits (nil), and we know that the // manifest has defined default traits, then just return true. // - If none of these conditions are met, then defaults aren't enabled and we return false. - if let enabledTraits, enabledTraits.contains(trait.name) { + if enabledTraits.contains(trait.name) { return true - } else if enabledTraits == nil { + } else if enabledTraits.isEmpty { return true } else { return false @@ -268,7 +290,7 @@ extension Manifest { // If manifest does not define default traits, then throw an invalid trait error. throw TraitError.invalidTrait( - package: self.displayName, + package: .init(self), trait: trait.name, availableTraits: self.traits.map(\.name) ) @@ -282,16 +304,16 @@ extension Manifest { /// Calculates and returns a set of all enabled traits, beginning with a set of explicitly enabled traits (which can either be the default traits of a manifest, or a configuration of enabled traits determined from a user-generated trait configuration) and determines which traits are transitively enabled. private func calculateAllEnabledTraits( - explictlyEnabledTraits: Set?, - _ parentPackage: String? = nil + explictlyEnabledTraits: Set, + _ parentPackage: PackageIdentifier? = nil ) throws -> Set { try validateEnabledTraits(explictlyEnabledTraits, parentPackage) // This the point where we flatten the enabled traits and resolve the recursive traits - var enabledTraits = explictlyEnabledTraits ?? [] + var enabledTraits = explictlyEnabledTraits let areDefaultsEnabled = enabledTraits.remove("default") != nil // We have to enable all default traits if no traits are enabled or the defaults are explicitly enabled - if explictlyEnabledTraits == nil || areDefaultsEnabled { + if /*explictlyEnabledTraits == nil*//*enabledTraits.isEmpty && */explictlyEnabledTraits == ["default"] || areDefaultsEnabled { if let defaultTraits { enabledTraits.formUnion(defaultTraits.flatMap(\.enabledTraits)) } @@ -306,9 +328,9 @@ extension Manifest { .flatMap { trait in guard let traitDescription = traitsMap[trait] else { throw TraitError.invalidTrait( - package: self.displayName, + package: .init(self), trait: trait, - parentPackage: parentPackage + parent: parentPackage ) } return traitDescription.enabledTraits @@ -327,7 +349,7 @@ extension Manifest { } /// Computes the dependencies that are in use per target in this manifest. - public func usedTargetDependencies(withTraits enabledTraits: Set?) throws -> [String: Set] { + public func usedTargetDependencies(withTraits enabledTraits: Set) throws -> [String: Set] { try self.targets.reduce(into: [String: Set]()) { depMap, target in let nonTraitDeps = target.dependencies.filter { $0.condition?.traits?.isEmpty ?? true @@ -336,6 +358,10 @@ extension Manifest { let traitGuardedDeps = try target.dependencies.filter { dep in let traits = dep.condition?.traits ?? [] + // If traits is empty, then we must manually validate the explicitly enabled traits. + if traits.isEmpty { + try validateEnabledTraits(enabledTraits) + } // For each trait that is a condition on this target dependency, assure that // each one is enabled in the manifest. return try traits.allSatisfy({ try isTraitEnabled(.init(stringLiteral: $0), enabledTraits) }) @@ -347,7 +373,7 @@ extension Manifest { } /// Computes the set of package dependencies that are used by targets of this manifest. - public func usedDependencies(withTraits enabledTraits: Set?) throws -> (knownPackage: Set, unknownPackage: Set) { + public func usedDependencies(withTraits enabledTraits: Set) throws -> (knownPackage: Set, unknownPackage: Set) { let deps = try self.usedTargetDependencies(withTraits: enabledTraits) .values .flatMap { $0 } @@ -411,10 +437,9 @@ extension Manifest { public func isTargetDependencyEnabled( target: String, _ dependency: TargetDescription.Dependency, - enabledTraits: Set?, - enableAllTraits: Bool = false + enabledTraits: Set, ) throws -> Bool { - guard self.supportsTraits, !enableAllTraits else { return true } + guard self.supportsTraits else { return true } guard let target = self.targetMap[target] else { return false } guard target.dependencies.contains(where: { $0 == dependency }) else { throw InternalError( @@ -432,15 +457,35 @@ extension Manifest { return traitsToEnable.isEmpty || isEnabled } /// Determines whether a given package dependency is used by this manifest given a set of enabled traits. - public func isPackageDependencyUsed(_ dependency: PackageDependency, enabledTraits: Set?) throws -> Bool { - let usedDependencies = try self.usedDependencies(withTraits: enabledTraits) - let foundKnownPackage = usedDependencies.knownPackage.contains(where: { - $0.caseInsensitiveCompare(dependency.identity.description) == .orderedSame - }) - - // if there is a target dependency referenced by name and the package it originates from is unknown, default to - // tentatively marking the package dependency as used. to be resolved later on. - return foundKnownPackage || (!foundKnownPackage && !usedDependencies.unknownPackage.isEmpty) + public func isPackageDependencyUsed(_ dependency: PackageDependency, enabledTraits: Set/* = ["default"]*/) throws -> Bool { + if self.pruneDependencies { + let usedDependencies = try self.usedDependencies(withTraits: enabledTraits) + let foundKnownPackage = usedDependencies.knownPackage.contains(where: { + $0.caseInsensitiveCompare(dependency.identity.description) == .orderedSame + }) + + // if there is a target dependency referenced by name and the package it originates from is unknown, default to + // tentatively marking the package dependency as used. to be resolved later on. + return foundKnownPackage || (!foundKnownPackage && !usedDependencies.unknownPackage.isEmpty) + } else { + // alternate path to compute trait-guarded package dependencies if the prune deps feature is not enabled + try validateEnabledTraits(enabledTraits) + + let targetDependenciesForPackageDependency = self.targets.flatMap({ $0.dependencies }) + .filter({ + $0.package?.caseInsensitiveCompare(dependency.identity.description) == .orderedSame + }) + + // if target deps is empty, default to returning true here. + let isTraitGuarded = targetDependenciesForPackageDependency.isEmpty ? false : targetDependenciesForPackageDependency.compactMap({ $0.condition?.traits }).allSatisfy({ + let condition = $0.subtracting(enabledTraits) + return !condition.isEmpty + }) + + let isUsedWithoutTraitGuarding = !targetDependenciesForPackageDependency.filter({ $0.condition?.traits == nil }).isEmpty + + return isUsedWithoutTraitGuarding || !isTraitGuarded + } } } @@ -449,17 +494,17 @@ extension Manifest { public enum TraitError: Swift.Error { /// Indicates that an invalid trait was enabled. case invalidTrait( - package: String, + package: Manifest.PackageIdentifier, trait: String, availableTraits: [String] = [], - parentPackage: String? = nil + parent: Manifest.PackageIdentifier? = nil ) /// Indicates that the manifest does not support traits, yet a method was called with a configuration of enabled /// traits. case traitsNotSupported( - parentPackage: String?, - package: String, + parent: Manifest.PackageIdentifier? = nil, + package: Manifest.PackageIdentifier, explicitlyEnabledTraits: [String] ) } @@ -471,9 +516,9 @@ extension TraitError: CustomStringConvertible { availableTraits = availableTraits.sorted() var errorMsg = "Trait '\(trait)'" if let parentPackage { - errorMsg += " enabled by parent package '\(parentPackage)'" + errorMsg += " enabled by parent package \(parentPackage)" } - errorMsg += " is not declared by package '\(package)'." + errorMsg += " is not declared by package \(package)." if availableTraits.isEmpty { errorMsg += " There are no available traits declared by this package." } else { @@ -486,21 +531,21 @@ extension TraitError: CustomStringConvertible { if explicitlyEnabledTraits.isEmpty { if let parentPackage { return """ - Disabled default traits by package '\(parentPackage)' on package '\(package)' that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. + Disabled default traits by package \(parentPackage) on package \(package) that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. """ } else { return """ - Disabled default traits on package '\(package)' that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. + Disabled default traits on package \(package) that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. """ } } else { if let parentPackage { return """ - Package '\(parentPackage)' enables traits [\(explicitlyEnabledTraits.joined(separator: ", "))] on package '\(package)' that declares no traits. + Package \(parentPackage) enables traits [\(explicitlyEnabledTraits.joined(separator: ", "))] on package \(package) that declares no traits. """ } else { return """ - Traits [\(explicitlyEnabledTraits.joined(separator: ", "))] have been enabled on package '\(package)' that declares no traits. + Traits [\(explicitlyEnabledTraits.joined(separator: ", "))] have been enabled on package \(package) that declares no traits. """ } } diff --git a/Sources/PackageModel/Manifest/Manifest.swift b/Sources/PackageModel/Manifest/Manifest.swift index 798a4f6543b..17cc309b1c1 100644 --- a/Sources/PackageModel/Manifest/Manifest.swift +++ b/Sources/PackageModel/Manifest/Manifest.swift @@ -199,7 +199,7 @@ public final class Manifest: Sendable { /// /// If we set the `enabledTraits` to be `["Trait1"]`, then the list of dependencies guarded by traits would be `[]`. /// Otherwise, if `enabledTraits` were `nil`, then the dependencies guarded by traits would be `["Bar"]`. - public func dependenciesTraitGuarded(withEnabledTraits enabledTraits: Set?) -> [PackageDependency] { + public func dependenciesTraitGuarded(withEnabledTraits enabledTraits: Set) -> [PackageDependency] { guard supportsTraits else { return [] } @@ -249,8 +249,7 @@ public final class Manifest: Sendable { continue } - if let enabledTraits, - guardingTraits.intersection(enabledTraits) != guardingTraits + if guardingTraits.intersection(enabledTraits) != guardingTraits { guardedDependencies.insert(dependency.identity) } @@ -267,7 +266,7 @@ public final class Manifest: Sendable { /// Returns the package dependencies required for a particular products filter and trait configuration. public func dependenciesRequired( for productFilter: ProductFilter, - _ enabledTraits: Set? + _ enabledTraits: Set = ["default"] ) throws -> [PackageDependency] { #if ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION // If we have already calculated it, returned the cached value. @@ -287,21 +286,18 @@ public final class Manifest: Sendable { guard self.toolsVersion >= .v5_2 && !self.packageKind.isRoot else { var dependencies = self.dependencies - if pruneDependencies { dependencies = try dependencies.filter({ - return try self.isPackageDependencyUsed($0, enabledTraits: enabledTraits) + let isUsed = try self.isPackageDependencyUsed($0, enabledTraits: enabledTraits) + return isUsed }) - } return dependencies } // using .nothing as cache key while ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION is false if var dependencies = self._requiredDependencies[.nothing] { - if self.pruneDependencies { dependencies = try dependencies.filter({ return try self.isPackageDependencyUsed($0, enabledTraits: enabledTraits) }) - } return dependencies } else { var requiredDependencies: Set = [] diff --git a/Sources/PackageModel/Manifest/PackageDependencyDescription.swift b/Sources/PackageModel/Manifest/PackageDependencyDescription.swift index 8cc316b3e81..2d24b6ac37d 100644 --- a/Sources/PackageModel/Manifest/PackageDependencyDescription.swift +++ b/Sources/PackageModel/Manifest/PackageDependencyDescription.swift @@ -24,11 +24,16 @@ public enum PackageDependency: Equatable, Hashable, Sendable { /// A condition that limits the application of a dependencies trait. package struct Condition: Hashable, Sendable, Codable { /// The set of traits of this package that enable the dependency's trait. - package let traits: Set? + private let traits: Set? public init(traits: Set?) { self.traits = traits } + + public func isSatisfied(by enabledTraits: Set) -> Bool { + guard let traits else { return true } + return !traits.intersection(enabledTraits).isEmpty + } } /// The name of the enabled trait. diff --git a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift index 992d3f9af29..690877fc9f4 100644 --- a/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift +++ b/Sources/SPMBuildCore/BuildSystem/BuildSystem.swift @@ -121,7 +121,7 @@ public protocol BuildPlan { public protocol BuildSystemFactory { func makeBuildSystem( explicitProduct: String?, - traitConfiguration: TraitConfiguration, + enableAllTraits: Bool, cacheBuildManifest: Bool, productsBuildParameters: BuildParameters?, toolsBuildParameters: BuildParameters?, @@ -149,7 +149,7 @@ public struct BuildSystemProvider { public func createBuildSystem( kind: Kind, explicitProduct: String? = .none, - traitConfiguration: TraitConfiguration, + enableAllTraits: Bool = false, cacheBuildManifest: Bool = true, productsBuildParameters: BuildParameters? = .none, toolsBuildParameters: BuildParameters? = .none, @@ -163,7 +163,7 @@ public struct BuildSystemProvider { } return try await buildSystemFactory.makeBuildSystem( explicitProduct: explicitProduct, - traitConfiguration: traitConfiguration, + enableAllTraits: enableAllTraits, cacheBuildManifest: cacheBuildManifest, productsBuildParameters: productsBuildParameters, toolsBuildParameters: toolsBuildParameters, diff --git a/Sources/Workspace/PackageContainer/FileSystemPackageContainer.swift b/Sources/Workspace/PackageContainer/FileSystemPackageContainer.swift index 473da456bdd..47a19c0bb73 100644 --- a/Sources/Workspace/PackageContainer/FileSystemPackageContainer.swift +++ b/Sources/Workspace/PackageContainer/FileSystemPackageContainer.swift @@ -94,7 +94,7 @@ public struct FileSystemPackageContainer: PackageContainer { } } - public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set?) async throws -> [PackageContainerConstraint] { + public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) async throws -> [PackageContainerConstraint] { let manifest = try await self.loadManifest() return try manifest.dependencyConstraints(productFilter: productFilter, enabledTraits) } @@ -121,22 +121,13 @@ public struct FileSystemPackageContainer: PackageContainer { fatalError("This should never be called") } - public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { fatalError("This should never be called") } - public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { fatalError("This should never be called") } - - public func getEnabledTraits(traitConfiguration: TraitConfiguration, at version: Version? = nil) async throws -> Set { - guard version == nil else { - throw InternalError("File system package container does not support versioning.") - } - let manifest = try await loadManifest() - let enabledTraits = try manifest.enabledTraits(using: traitConfiguration) - return enabledTraits ?? [] - } } extension FileSystemPackageContainer: CustomStringConvertible { diff --git a/Sources/Workspace/PackageContainer/RegistryPackageContainer.swift b/Sources/Workspace/PackageContainer/RegistryPackageContainer.swift index 4884dbdb562..630ba49a280 100644 --- a/Sources/Workspace/PackageContainer/RegistryPackageContainer.swift +++ b/Sources/Workspace/PackageContainer/RegistryPackageContainer.swift @@ -104,16 +104,16 @@ public class RegistryPackageContainer: PackageContainer { return results } - public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set?) async throws -> [PackageContainerConstraint] { + public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) async throws -> [PackageContainerConstraint] { let manifest = try await self.loadManifest(version: version) return try manifest.dependencyConstraints(productFilter: productFilter, enabledTraits) } - public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { throw InternalError("getDependencies for revision not supported by RegistryPackageContainer") } - public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { throw InternalError("getUnversionedDependencies not supported by RegistryPackageContainer") } @@ -210,18 +210,6 @@ public class RegistryPackageContainer: PackageContainer { self.availableManifestsCache[version] = (manifests: manifests, fileSystem: fileSystem) return (manifests: manifests, fileSystem: fileSystem) } - - public func getEnabledTraits(traitConfiguration: TraitConfiguration, at version: Version?) async throws -> Set { - guard let version else { - throw InternalError("Version needed to compute enabled traits for registry package \(self.package.identity.description)") - } - let manifest = try await loadManifest(version: version) - guard manifest.packageKind.isRoot else { - return [] - } - let enabledTraits = try manifest.enabledTraits(using: traitConfiguration) - return enabledTraits ?? [] - } } // MARK: - CustomStringConvertible diff --git a/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift b/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift index d2c9dc52fa9..2444ec5bfae 100644 --- a/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift +++ b/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift @@ -241,7 +241,7 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri } } - public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set?) async throws -> [Constraint] { + public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) async throws -> [Constraint] { do { return try await self.getCachedDependencies(forIdentifier: version.description, productFilter: productFilter) { guard let tag = try self.knownVersions()[version] else { @@ -259,7 +259,7 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri } } - public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set?) async throws -> [Constraint] { + public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) async throws -> [Constraint] { do { return try await self.getCachedDependencies(forIdentifier: revision, productFilter: productFilter) { // resolve the revision identifier and return its dependencies. @@ -323,7 +323,7 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri tag: String, version: Version? = nil, productFilter: ProductFilter, - enabledTraits: Set? + enabledTraits: Set ) async throws -> (Manifest, [Constraint]) { let manifest = try await self.loadManifest(tag: tag, version: version) return (manifest, try manifest.dependencyConstraints(productFilter: productFilter, enabledTraits)) @@ -334,13 +334,13 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri at revision: Revision, version: Version? = nil, productFilter: ProductFilter, - enabledTraits: Set? + enabledTraits: Set ) async throws -> (Manifest, [Constraint]) { let manifest = try await self.loadManifest(at: revision, version: version) return (manifest, try manifest.dependencyConstraints(productFilter: productFilter, enabledTraits)) } - public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [Constraint] { + public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [Constraint] { // We just return an empty array if requested for unversioned dependencies. return [] } @@ -412,14 +412,6 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri ) } - public func getEnabledTraits(traitConfiguration: TraitConfiguration, at revision: String?, version: Version?) async throws -> Set { - guard let version, let tag = getTag(for: version) else { - return [] - } - let manifest = try await self.loadManifest(tag: tag, version: version) - return try manifest.enabledTraits(using: traitConfiguration) ?? [] - } - public var isRemoteContainer: Bool? { return true } diff --git a/Sources/Workspace/ResolverPrecomputationProvider.swift b/Sources/Workspace/ResolverPrecomputationProvider.swift index 630c1dc56ae..4ae40119dce 100644 --- a/Sources/Workspace/ResolverPrecomputationProvider.swift +++ b/Sources/Workspace/ResolverPrecomputationProvider.swift @@ -122,7 +122,7 @@ private struct LocalPackageContainer: PackageContainer { try await self.versionsDescending() } - func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { // Because of the implementation of `reversedVersions`, we should only get the exact same version. switch dependency?.state { case .sourceControlCheckout(.version(version, revision: _)): @@ -134,7 +134,7 @@ private struct LocalPackageContainer: PackageContainer { } } - func getDependencies(at revisionString: String, productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + func getDependencies(at revisionString: String, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { let revision = Revision(identifier: revisionString) switch dependency?.state { case .sourceControlCheckout(.branch(_, revision: revision)), .sourceControlCheckout(.revision(revision)): @@ -150,7 +150,7 @@ private struct LocalPackageContainer: PackageContainer { } } - func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { switch dependency?.state { case .none, .fileSystem, .edited: return try manifest.dependencyConstraints(productFilter: productFilter, enabledTraits) @@ -172,23 +172,4 @@ private struct LocalPackageContainer: PackageContainer { return .root(identity: self.package.identity, path: self.manifest.path) } } - - func getEnabledTraits(traitConfiguration: TraitConfiguration, at version: Version? = nil) async throws -> Set { - guard manifest.packageKind.isRoot else { - return [] - } - - if let version { - switch dependency?.state { - case .sourceControlCheckout(.version(version, revision: _)): - return try manifest.enabledTraits(using: traitConfiguration) ?? [] - case .registryDownload(version: version): - return try manifest.enabledTraits(using: traitConfiguration) ?? [] - default: - throw InternalError("expected version based state, but state was \(String(describing: dependency?.state))") - } - } else { - return try manifest.enabledTraits(using: traitConfiguration) ?? [] - } - } } diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 0dcacd84e2a..64eb37227b8 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -85,7 +85,8 @@ extension Workspace { input: root, manifests: rootManifests, dependencyMapper: self.dependencyMapper, - observabilityScope: observabilityScope + observabilityScope: observabilityScope, + enabledTraitsMap: self.enabledTraitsMap ) let currentManifests = try await self.loadDependencyManifests( root: graphRoot, @@ -104,7 +105,7 @@ extension Workspace { var updateConstraints = currentManifests.editedPackagesConstraints // Create constraints based on root manifest and `Package.resolved` for the update resolution. - updateConstraints += try graphRoot.constraints() + updateConstraints += try graphRoot.constraints(self.enabledTraitsMap) let resolvedPackages: ResolvedPackagesStore.ResolvedPackages if packages.isEmpty { @@ -353,18 +354,20 @@ extension Workspace { manifests: rootManifests, explicitProduct: explicitProduct, dependencyMapper: self.dependencyMapper, - observabilityScope: observabilityScope + observabilityScope: observabilityScope, + enabledTraitsMap: self.enabledTraitsMap ) // Load the `Package.resolved` store or abort now. guard let resolvedPackagesStore = observabilityScope.trap({ try self.resolvedPackagesStore.load() }), !observabilityScope.errorsReported else { - return try await ( - self.loadDependencyManifests( - root: graphRoot, - observabilityScope: observabilityScope - ), + let dependencyManifests = try await self.loadDependencyManifests( + root: graphRoot, + observabilityScope: observabilityScope + ) + + return (dependencyManifests, .notRequired ) } @@ -462,7 +465,7 @@ extension Workspace { automaticallyAddManagedDependencies: true, observabilityScope: observabilityScope ) - + try await self.updateBinaryArtifacts( manifests: currentManifests, addedOrUpdatedPackages: [], @@ -518,7 +521,8 @@ extension Workspace { manifests: rootManifests, explicitProduct: explicitProduct, dependencyMapper: self.dependencyMapper, - observabilityScope: observabilityScope + observabilityScope: observabilityScope, + enabledTraitsMap: self.enabledTraitsMap ) // Of the enabled dependencies of targets, only consider these for dependency resolution @@ -526,6 +530,7 @@ extension Workspace { root: graphRoot, observabilityScope: observabilityScope ) + guard !observabilityScope.errorsReported else { return currentManifests } @@ -591,7 +596,7 @@ extension Workspace { // Create the constraints; filter unused dependencies. var computedConstraints = [PackageContainerConstraint]() computedConstraints += currentManifests.editedPackagesConstraints - computedConstraints += try graphRoot.constraints() + constraints + computedConstraints += try graphRoot.constraints(self.enabledTraitsMap) + constraints // Perform dependency resolution. let resolver = try self.createResolver(resolvedPackages: resolvedPackagesStore.resolvedPackages, observabilityScope: observabilityScope) @@ -854,9 +859,9 @@ extension Workspace { observabilityScope: ObservabilityScope ) async throws -> ResolutionPrecomputationResult { let computedConstraints = - try root.constraints() + + try root.constraints(self.enabledTraitsMap) + // Include constraints from the manifests in the graph root. - root.manifests.values.flatMap { try $0.dependencyConstraints(productFilter: .everything, nil) } + + root.manifests.values.flatMap { try $0.dependencyConstraints(productFilter: .everything, self.enabledTraitsMap[$0.packageIdentity]) } + dependencyManifests.dependencyConstraints + constraints @@ -1167,7 +1172,7 @@ extension Workspace { observabilityScope: ObservabilityScope ) async -> [DependencyResolverBinding] { os_signpost(.begin, name: SignpostName.pubgrub) - let result = await resolver.solve(constraints: constraints, traitConfiguration: configuration.traitConfiguration) + let result = await resolver.solve(constraints: constraints) os_signpost(.end, name: SignpostName.pubgrub) // Take an action based on the result. diff --git a/Sources/Workspace/Workspace+Manifests.swift b/Sources/Workspace/Workspace+Manifests.swift index 6a23983e8a5..334983cbd33 100644 --- a/Sources/Workspace/Workspace+Manifests.swift +++ b/Sources/Workspace/Workspace+Manifests.swift @@ -51,7 +51,7 @@ extension Workspace { /// A struct representing all the current manifests (root + external) in a package graph. public struct DependencyManifests { /// The package graph root. - let root: PackageGraphRoot + var root: PackageGraphRoot /// The dependency manifests in the transitive closure of root manifest. let dependencies: [( @@ -202,50 +202,29 @@ extension Workspace { } } - let rootEnabledTraitsMap: [PackageIdentity: Set] = root.manifests - .reduce(into: [PackageIdentity: Set]()) { traitMap, manifest in - traitMap[manifest.key] = root.enabledTraits[manifest.key] - } - - let allRootEnabledTraits = rootEnabledTraitsMap.values.flatMap { $0 } - - let rootDependenciesEnabledTraitsMap = root.dependencies - .reduce(into: [PackageIdentity: Set]()) { traitMap, dependency in - let explicitlyEnabledTraits = dependency.traits?.filter { - guard let conditionTraits = $0.condition?.traits else { - return true - } - return !conditionTraits.intersection(allRootEnabledTraits).isEmpty - }.map(\.name) ?? [] - - traitMap[dependency.identity] = Set(explicitlyEnabledTraits) - } - var unusedIdentities: OrderedCollections.OrderedSet = [] var inputIdentities: OrderedCollections.OrderedSet = [] let inputNodes: [GraphLoadingNode] = try root.packages.map { identity, package in inputIdentities.append(package.reference) - let traits: Set? = rootEnabledTraitsMap[package.reference.identity] ?? [] let node = try GraphLoadingNode( identity: identity, manifest: package.manifest, productFilter: .everything, - enabledTraits: traits ?? [] + enabledTraits: workspace.enabledTraitsMap[package.reference.identity] ) return node } + root.dependencies.compactMap { dependency in let package = dependency.packageRef inputIdentities.append(package) return try manifestsMap[dependency.identity].map { manifest in - let traits: Set? = rootDependenciesEnabledTraitsMap[dependency.identity] ?? [] return try GraphLoadingNode( identity: dependency.identity, manifest: manifest, productFilter: dependency.productFilter, - enabledTraits: traits ?? [] + enabledTraits: workspace.enabledTraitsMap[dependency.identity] ) } } @@ -265,8 +244,7 @@ extension Workspace { var requiredIdentities: OrderedCollections.OrderedSet = [] _ = try transitiveClosure(inputNodes) { node in - - try node.manifest.dependenciesRequired(for: node.productFilter, node.enabledTraits) + return try node.manifest.dependenciesRequired(for: node.productFilter, node.enabledTraits) .compactMap { dependency in let package = dependency.packageRef @@ -277,11 +255,18 @@ extension Workspace { enabledTraits: node.enabledTraits ) if !isDepUsed && workspace.configuration.pruneDependencies { - observabilityScope.emit(debug: """ + if !node.enabledTraits.isEmpty { + observabilityScope.emit(debug: """ '\(package.identity)' from '\(package.locationString)' was omitted \ from required dependencies because it is being guarded by the following traits:' \ \(node.enabledTraits.joined(separator: ", ")) """) + } else { + observabilityScope.emit(debug: """ + '\(package.identity)' from '\(package.locationString)' was omitted \ + from required dependencies because it is unused + """) + } } else { unusedDepsPerPackage[node.identity, default: []] = unusedDepsPerPackage[ node.identity, @@ -330,16 +315,14 @@ extension Workspace { // should calculate enabled traits here. let explicitlyEnabledTraits = dependency.traits?.filter { - guard let conditionTraits = $0.condition?.traits else { - return true - } - return !conditionTraits.intersection(node.enabledTraits).isEmpty + guard let condition = $0.condition else { return true } + return condition.isSatisfied(by: node.enabledTraits) }.map(\.name) return try manifestsMap[dependency.identity].map { manifest in // Calculate all transitively enabled traits for this manifest. - var allEnabledTraits: Set = [] + var allEnabledTraits: Set = ["default"] if let explicitlyEnabledTraits { allEnabledTraits = Set(explicitlyEnabledTraits) @@ -417,19 +400,6 @@ extension Workspace { ) throws -> [PackageContainerConstraint] { var allConstraints = [PackageContainerConstraint]() - let rootDependenciesEnabledTraitsMap = root.dependencies - .reduce(into: [PackageIdentity: Set]()) { traitMap, dependency in - let explicitlyEnabledTraits = dependency.traits?.filter { - guard let conditionTraits = $0.condition?.traits else { - return true - } - return !conditionTraits - .intersection(workspace.configuration.traitConfiguration.enabledTraits ?? []).isEmpty - }.map(\.name) ?? [] - - traitMap[dependency.identity] = Set(explicitlyEnabledTraits) - } - for (externalManifest, managedDependency, productFilter, _) in dependencies { // For edited packages, add a constraint with unversioned requirement so the // resolver doesn't try to resolve it. @@ -450,10 +420,9 @@ extension Workspace { case .sourceControlCheckout, .registryDownload, .fileSystem, .custom: break } - let enabledTraits = rootDependenciesEnabledTraitsMap[managedDependency.packageRef.identity] allConstraints += try externalManifest.dependencyConstraints( productFilter: productFilter, - enabledTraits + workspace.enabledTraitsMap[managedDependency.packageRef.identity] ) } return allConstraints @@ -590,10 +559,19 @@ extension Workspace { ) let rootManifests = try root.manifests.mapValues { manifest in + let parentEnabledTraits = self.enabledTraitsMap[manifest.packageIdentity] let deps = try manifest.dependencies.filter { dep in - guard configuration.pruneDependencies else { return true } - let enabledTraits = root.enabledTraits[manifest.packageIdentity] - let isDepUsed = try manifest.isPackageDependencyUsed(dep, enabledTraits: enabledTraits) + let explicitlyEnabledTraits = dep.traits?.filter({ + guard let condition = $0.condition else { return true } + return condition.isSatisfied(by: parentEnabledTraits) + }).map(\.name) + + let enabledTraitsSet = explicitlyEnabledTraits.flatMap({ Set($0) }) + let enabledTraits = enabledTraitsSet?.union(self.enabledTraitsMap[dep.identity]) ?? self.enabledTraitsMap[dep.identity] + + self.enabledTraitsMap[dep.identity] = enabledTraits + + let isDepUsed = try manifest.isPackageDependencyUsed(dep, enabledTraits: parentEnabledTraits) return isDepUsed } @@ -627,11 +605,21 @@ extension Workspace { // optimization: preload first level dependencies manifest (in parallel) let firstLevelDependencies = try topLevelManifests.values.map { manifest in - try manifest.dependencies.filter { dep in - guard configuration.pruneDependencies else { return true } - let enabledTraits: Set? = root.enabledTraits[manifest.packageIdentity] - let isDepUsed = try manifest.isPackageDependencyUsed(dep, enabledTraits: enabledTraits) + let parentEnabledTraits = self.enabledTraitsMap[manifest.packageIdentity] + return try manifest.dependencies.filter { dep in + let explicitlyEnabledTraits = dep.traits?.filter({ + guard let condition = $0.condition else { return true } + return condition.isSatisfied(by: parentEnabledTraits) + }).map(\.name) + + let enabledTraitsSet = explicitlyEnabledTraits.flatMap({ Set($0) }) + let enabledTraits = enabledTraitsSet?.union(self.enabledTraitsMap[dep.identity]) ?? self.enabledTraitsMap[dep.identity] + + self.enabledTraitsMap[dep.identity] = enabledTraits + + let isDepUsed = try manifest.isPackageDependencyUsed(dep, enabledTraits: parentEnabledTraits) return isDepUsed + }.map(\.packageRef) }.flatMap(\.self) @@ -648,12 +636,11 @@ extension Workspace { PackageIdentity >] = { node in // optimization: preload manifest we know about in parallel + // avoid loading dependencies that are trait-guarded here since this is redundant. let dependenciesRequired = try node.item.manifest.dependenciesRequired( for: node.item.productFilter, node.item.enabledTraits ) - let dependenciesGuarded = node.item.manifest - .dependenciesTraitGuarded(withEnabledTraits: node.item.enabledTraits) let dependenciesToLoad = dependenciesRequired.map(\.packageRef) .filter { !loadedManifests.keys.contains($0.identity) } try await prepopulateManagedDependencies(dependenciesToLoad) @@ -662,31 +649,46 @@ extension Workspace { observabilityScope: observabilityScope ) dependenciesManifests.forEach { loadedManifests[$0.key] = $0.value } - return try (dependenciesRequired + dependenciesGuarded).compactMap { dependency in - try loadedManifests[dependency.identity].flatMap { manifest in - // we also compare the location as this function may attempt to load - // dependencies that have the same identity but from a different location - // which is an error case we diagnose an report about in the GraphLoading part which - // is prepared to handle the case where not all manifest are available + return try dependenciesRequired.compactMap { dependency in + return try loadedManifests[dependency.identity].flatMap { manifest in + let explicitlyEnabledTraits = dependency.traits?.filter { - guard let conditionTraits = $0.condition?.traits else { - return true - } - return !conditionTraits.intersection(node.item.enabledTraits).isEmpty + guard let condition = $0.condition else { return true } + return condition.isSatisfied(by: node.item.enabledTraits) }.map(\.name) + var enabledTraitsSet = explicitlyEnabledTraits.flatMap { Set($0) } + let precomputedTraits = self.enabledTraitsMap[dependency.identity] + // Shouldn't union here if enabledTraitsMap returns "default" and we DO have explicitly enabled traits, since we're meant to flatten the default traits. + if precomputedTraits == ["default"], + let enabledTraitsSet { + self.enabledTraitsMap[dependency.identity] = enabledTraitsSet + } else { + // Unify traits + enabledTraitsSet?.formUnion(precomputedTraits) + if let enabledTraitsSet { + self.enabledTraitsMap[dependency.identity] = enabledTraitsSet + } + } + let calculatedTraits = try manifest.enabledTraits( - using: explicitlyEnabledTraits.flatMap { Set($0) }, - node.item.identity.description + using: self.enabledTraitsMap[dependency.identity], + .init(node.item.manifest) ) + self.enabledTraitsMap[dependency.identity] = calculatedTraits + + // we also compare the location as this function may attempt to load + // dependencies that have the same identity but from a different location + // which is an error case we diagnose an report about in the GraphLoading part which + // is prepared to handle the case where not all manifest are available return manifest.canonicalPackageLocation == dependency.packageRef.canonicalLocation ? try KeyedPair( GraphLoadingNode( identity: dependency.identity, manifest: manifest, productFilter: dependency.productFilter, - enabledTraits: calculatedTraits ?? [] + enabledTraits: calculatedTraits ), key: dependency.identity ) : @@ -699,14 +701,12 @@ extension Workspace { do { let manifestGraphRoots = try topLevelManifests.map { identity, manifest in - let isRoot = manifest.packageKind.isRoot - let enabledTraits = isRoot ? root.enabledTraits[identity] : [] return try KeyedPair( GraphLoadingNode( identity: identity, manifest: manifest, productFilter: .everything, - enabledTraits: enabledTraits ?? [] + enabledTraits: self.enabledTraitsMap[identity] ), key: identity ) @@ -717,11 +717,14 @@ extension Workspace { successors: successorNodes ) { allNodes[$0.key] = $0.item - } onDuplicate: { old, new in - allNodes[old.key]?.enabledTraits.formUnion(new.item.enabledTraits) + } onDuplicate: { _, _ in + // Nothing we need to compute here. } } + // Update enabled traits map + self.enabledTraitsMap = .init(try precomputeTraits( topLevelManifests.values.map({ $0 }), loadedManifests)) + let dependencyManifests = allNodes.filter { !$0.value.manifest.packageKind.isRoot } // TODO: this check should go away when introducing explicit overrides @@ -761,6 +764,66 @@ extension Workspace { ) } + public func precomputeTraits( + _ topLevelManifests: [Manifest], + _ manifestMap: [PackageIdentity: Manifest] + ) throws -> [PackageIdentity: Set] { + var visited: Set = [] + + func dependencies(of parent: Manifest, _ productFilter: ProductFilter = .everything) throws { + let parentTraits = self.enabledTraitsMap[parent.packageIdentity] + let requiredDependencies = try parent.dependenciesRequired(for: productFilter, parentTraits) + let guardedDependencies = parent.dependenciesTraitGuarded(withEnabledTraits: parentTraits) + + _ = try (requiredDependencies + guardedDependencies).compactMap({ dependency in + return try manifestMap[dependency.identity].flatMap({ manifest in + + let explicitlyEnabledTraits = dependency.traits?.filter { + guard let condition = $0.condition else { return true } + return condition.isSatisfied(by: parentTraits) + }.map(\.name) + + var enabledTraitsSet = explicitlyEnabledTraits.flatMap { Set($0) } + let precomputedTraits = self.enabledTraitsMap[dependency.identity] + // Shouldn't union here if enabledTraitsMap returns "default" and we DO have explicitly enabled traits, since we're meant to flatten the default traits. + if precomputedTraits == ["default"], + let enabledTraitsSet { + self.enabledTraitsMap[dependency.identity] = enabledTraitsSet + } else { + // Unify traits + enabledTraitsSet?.formUnion(precomputedTraits) + if let enabledTraitsSet { + self.enabledTraitsMap[dependency.identity] = enabledTraitsSet + } + } + + let calculatedTraits = try manifest.enabledTraits( + using: self.enabledTraitsMap[dependency.identity], + .init(parent) + ) + + self.enabledTraitsMap[dependency.identity] = calculatedTraits + let result = visited.insert(dependency.identity) + if result.inserted { + try dependencies(of: manifest, dependency.productFilter) + } + + return manifest + }) + }) + } + + for manifest in topLevelManifests { + // Track already-visited manifests to avoid cycles + let result = visited.insert(manifest.packageIdentity) + if result.inserted { + try dependencies(of: manifest) + } + } + + return self.enabledTraitsMap.dictionaryLiteral + } + /// Loads the given manifests, if it is present in the managed dependencies. /// diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index c8e6c315a33..fdfa0e8ef1d 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -103,6 +103,9 @@ public class Workspace { /// to the store. package let resolvedPackagesStore: LoadableResult + /// Computed enabled traits per package in the workspace + public var enabledTraitsMap: EnabledTraitsMap = [:] + /// The file system on which the workspace will operate. package let fileSystem: any FileSystem @@ -151,6 +154,11 @@ public class Workspace { /// The workspace configuration settings let configuration: WorkspaceConfiguration + /// The trait configuration as described in the Workspace's configuration. + public var traitConfiguration: TraitConfiguration { + configuration.traitConfiguration + } + // MARK: State /// The active package resolver. This is set during a dependency resolution operation. @@ -408,7 +416,7 @@ public class Workspace { ) } - private init( + private convenience init( // core fileSystem: FileSystem, environment: Environment, @@ -568,6 +576,7 @@ public class Workspace { // register the binary artifacts downloader with the cancellation handler cancellator?.register(name: "binary artifacts downloads", handler: binaryArtifactsManager) + var prebuiltsManager: PrebuiltsManager? if configuration.usePrebuilts, let hostPlatform = customPrebuiltsManager?.hostPlatform ?? PrebuiltsManifest.Platform.hostPlatform, let swiftCompilerVersion = hostToolchain.swiftCompilerVersion @@ -579,7 +588,7 @@ public class Workspace { rootCertPath = nil } - let prebuiltsManager = PrebuiltsManager( + let prebuiltsManagerObj = PrebuiltsManager( fileSystem: fileSystem, hostPlatform: hostPlatform, swiftCompilerVersion: customPrebuiltsManager?.swiftVersion ?? swiftCompilerVersion, @@ -592,13 +601,72 @@ public class Workspace { prebuiltsDownloadURL: configuration.prebuiltsDownloadURL, rootCertPath: customPrebuiltsManager?.rootCertPath ?? rootCertPath ) - cancellator?.register(name: "package prebuilts downloads", handler: prebuiltsManager) - self.prebuiltsManager = prebuiltsManager + cancellator?.register(name: "package prebuilts downloads", handler: prebuiltsManagerObj) + prebuiltsManager = prebuiltsManagerObj } else { - self.prebuiltsManager = nil + prebuiltsManager = nil } // initialize + let resolvedPackagesStore = LoadableResult { + try ResolvedPackagesStore( + packageResolvedFile: location.resolvedVersionsFile, + workingDirectory: location.scratchDirectory, + fileSystem: fileSystem, + mirrors: mirrors + ) + } + + let state = WorkspaceState( + fileSystem: fileSystem, + storageDirectory: location.scratchDirectory, + initializationWarningHandler: initializationWarningHandler + ) + + self.init( + fileSystem: fileSystem, + configuration: configuration, + location: location, + delegate: delegate, + mirrors: mirrors, + hostToolchain: hostToolchain, + manifestLoader: manifestLoader, + currentToolsVersion: currentToolsVersion, + customPackageContainerProvider: customPackageContainerProvider, + repositoryManager: repositoryManager, + registryClient: registryClient, + registryDownloadsManager: registryDownloadsManager, + binaryArtifactsManager: binaryArtifactsManager, + identityResolver: identityResolver, + dependencyMapper: dependencyMapper, + fingerprints: fingerprints, + resolvedPackagesStore: resolvedPackagesStore, + prebuiltsManager: prebuiltsManager, + state: state + ) + } + + private init( + fileSystem: any FileSystem, + configuration: WorkspaceConfiguration, + location: Location, + delegate: Delegate?, + mirrors: DependencyMirrors, + hostToolchain: UserToolchain, + manifestLoader: ManifestLoaderProtocol, + currentToolsVersion: ToolsVersion, + customPackageContainerProvider: PackageContainerProvider?, + repositoryManager: RepositoryManager, + registryClient: RegistryClient, + registryDownloadsManager: RegistryDownloadsManager, + binaryArtifactsManager: BinaryArtifactsManager, + identityResolver: IdentityResolver, + dependencyMapper: DependencyMapper, + fingerprints: PackageFingerprintStorage?, + resolvedPackagesStore: LoadableResult, + prebuiltsManager: PrebuiltsManager?, + state: WorkspaceState + ) { self.fileSystem = fileSystem self.configuration = configuration self.location = location @@ -619,20 +687,10 @@ public class Workspace { self.dependencyMapper = dependencyMapper self.fingerprints = fingerprints - self.resolvedPackagesStore = LoadableResult { - try ResolvedPackagesStore( - packageResolvedFile: location.resolvedVersionsFile, - workingDirectory: location.scratchDirectory, - fileSystem: fileSystem, - mirrors: mirrors - ) - } + self.resolvedPackagesStore = resolvedPackagesStore + self.prebuiltsManager = prebuiltsManager - self.state = WorkspaceState( - fileSystem: fileSystem, - storageDirectory: self.location.scratchDirectory, - initializationWarningHandler: initializationWarningHandler - ) + self.state = state } } @@ -780,19 +838,12 @@ extension Workspace { defaultRequirement } - var dependencyEnabledTraits: Set? - if let traits = root.dependencies.first(where: { $0.nameForModuleDependencyResolutionOnly == packageName })? - .traits - { - dependencyEnabledTraits = Set(traits.map(\.name)) - } - // If any products are required, the rest of the package graph will supply those constraints. let constraint = PackageContainerConstraint( package: dependency.packageRef, requirement: requirement, products: .nothing, - enabledTraits: dependencyEnabledTraits + enabledTraits: self.enabledTraitsMap[dependency.packageRef.identity] ) // Run the resolution. @@ -997,7 +1048,8 @@ extension Workspace { customXCTestMinimumDeploymentTargets: customXCTestMinimumDeploymentTargets, testEntryPointPath: testEntryPointPath, fileSystem: self.fileSystem, - observabilityScope: observabilityScope + observabilityScope: observabilityScope, + enabledTraitsMap: self.enabledTraitsMap ) try self.validateSignatures( @@ -1066,9 +1118,16 @@ extension Workspace { if let (package, manifest) = result { // Store the manifest. rootManifests[package] = manifest + + // Compute the enabled traits for roots. + let traitConfiguration = self.configuration.traitConfiguration + let enabledTraits = try manifest.enabledTraits(using: traitConfiguration) + self.enabledTraitsMap[manifest.packageIdentity] = enabledTraits } } + + // Check for duplicate root packages after all manifests are loaded. let duplicateRoots = rootManifests.values.spm_findDuplicateElements(by: \.displayName) if let firstDuplicateSet = duplicateRoots.first, let firstDuplicate = firstDuplicateSet.first { @@ -1363,8 +1422,41 @@ extension Workspace { } } + // MARK: - Utility extensions +extension Workspace { + /// Creates and returns a copy of the current workspace with an updated configuration using the passed parameters. + /// - Parameters: + /// - traitConfiguration: A configuration of traits that will override the existing trait configuration in the WorkspaceConfiguration. + public func updateConfiguration(with traitConfiguration: TraitConfiguration) -> Workspace { + var newConfig = self.configuration + newConfig.traitConfiguration = traitConfiguration + + return Workspace( + fileSystem: self.fileSystem, + configuration: newConfig, + location: self.location, + delegate: self.delegate, + mirrors: self.mirrors, + hostToolchain: self.hostToolchain, + manifestLoader: self.manifestLoader, + currentToolsVersion: self.currentToolsVersion, + customPackageContainerProvider: self.customPackageContainerProvider, + repositoryManager: self.repositoryManager, + registryClient: self.registryClient, + registryDownloadsManager: self.registryDownloadsManager, + binaryArtifactsManager: self.binaryArtifactsManager, + identityResolver: self.identityResolver, + dependencyMapper: self.dependencyMapper, + fingerprints: self.fingerprints, + resolvedPackagesStore: self.resolvedPackagesStore, + prebuiltsManager: prebuiltsManager, + state: self.state + ) + } +} + extension Workspace.ManagedArtifact { fileprivate var originURL: String? { switch self.source { diff --git a/Sources/_InternalTestSupport/ManifestExtensions.swift b/Sources/_InternalTestSupport/ManifestExtensions.swift index 225ae53d84c..3891c033a11 100644 --- a/Sources/_InternalTestSupport/ManifestExtensions.swift +++ b/Sources/_InternalTestSupport/ManifestExtensions.swift @@ -32,14 +32,14 @@ extension Manifest { dependencies: [PackageDependency] = [], products: [ProductDescription] = [], targets: [TargetDescription] = [], - traits: Set = [.init(name: "default")], + traits: Set = [], pruneDependencies: Bool = false ) -> Manifest { Self.createManifest( displayName: displayName, path: path, packageKind: .root(path), - packageIdentity: .plain(displayName), + packageIdentity: .plain(displayName.lowercased()), packageLocation: path.pathString, defaultLocalization: defaultLocalization, platforms: platforms, @@ -73,14 +73,14 @@ extension Manifest { dependencies: [PackageDependency] = [], products: [ProductDescription] = [], targets: [TargetDescription] = [], - traits: Set = [.init(name: "default")], + traits: Set = [], pruneDependencies: Bool = false ) -> Manifest { Self.createManifest( displayName: displayName, path: path, packageKind: .fileSystem(path), - packageIdentity: .plain(displayName), + packageIdentity: .plain(displayName.lowercased()), packageLocation: path.pathString, defaultLocalization: defaultLocalization, platforms: platforms, @@ -120,7 +120,7 @@ extension Manifest { displayName: displayName, path: path, packageKind: .localSourceControl(path), - packageIdentity: .plain(displayName), + packageIdentity: .plain(displayName.lowercased()), packageLocation: path.pathString, defaultLocalization: defaultLocalization, platforms: platforms, @@ -161,7 +161,7 @@ extension Manifest { displayName: displayName, path: path, packageKind: .remoteSourceControl(url), - packageIdentity: .plain(displayName), + packageIdentity: .plain(displayName.lowercased()), packageLocation: url.absoluteString, defaultLocalization: defaultLocalization, platforms: platforms, @@ -201,7 +201,7 @@ extension Manifest { displayName: displayName, path: path, packageKind: .registry(identity), - packageIdentity: .plain(displayName), + packageIdentity: .plain(displayName.lowercased()), packageLocation: identity.description, defaultLocalization: defaultLocalization, platforms: platforms, @@ -237,7 +237,7 @@ extension Manifest { dependencies: [PackageDependency] = [], products: [ProductDescription] = [], targets: [TargetDescription] = [], - traits: Set = [.init(name: "default")], + traits: Set = [], pruneDependencies: Bool = false ) -> Manifest { return Manifest( diff --git a/Sources/_InternalTestSupport/MockDependency.swift b/Sources/_InternalTestSupport/MockDependency.swift index 1553decf02b..8e9f9f0dbef 100644 --- a/Sources/_InternalTestSupport/MockDependency.swift +++ b/Sources/_InternalTestSupport/MockDependency.swift @@ -28,7 +28,7 @@ public struct MockDependency { deprecatedName: String? = nil, location: Location, products: ProductFilter = .everything, - traits: Set = [] + traits: Set = ["default"] ) { self.deprecatedName = deprecatedName self.location = location @@ -132,35 +132,35 @@ public struct MockDependency { } - public static func fileSystem(path: String, products: ProductFilter = .everything, traits: Set = []) -> MockDependency { + public static func fileSystem(path: String, products: ProductFilter = .everything, traits: Set = ["default"]) -> MockDependency { try! MockDependency(location: .fileSystem(path: RelativePath(validating: path)), products: products, traits: traits) } - public static func sourceControl(path: String, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = []) -> MockDependency { + public static func sourceControl(path: String, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = ["default"]) -> MockDependency { try! .sourceControl(path: RelativePath(validating: path), requirement: requirement, products: products, traits: traits) } - public static func sourceControl(path: RelativePath, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = []) -> MockDependency { + public static func sourceControl(path: RelativePath, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = ["default"]) -> MockDependency { MockDependency(location: .localSourceControl(path: path, requirement: requirement), products: products, traits: traits) } - public static func sourceControlWithDeprecatedName(name: String, path: String, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = []) -> MockDependency { + public static func sourceControlWithDeprecatedName(name: String, path: String, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = ["default"]) -> MockDependency { try! MockDependency(deprecatedName: name, location: .localSourceControl(path: RelativePath(validating: path), requirement: requirement), products: products, traits: traits) } - public static func sourceControl(url: String, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = []) -> MockDependency { + public static func sourceControl(url: String, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = ["default"]) -> MockDependency { .sourceControl(url: SourceControlURL(url), requirement: requirement, products: products, traits: traits) } - public static func sourceControl(url: SourceControlURL, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = []) -> MockDependency { + public static func sourceControl(url: SourceControlURL, requirement: SourceControlRequirement, products: ProductFilter = .everything, traits: Set = ["default"]) -> MockDependency { MockDependency(location: .remoteSourceControl(url: url, requirement: requirement), products: products, traits: traits) } - public static func registry(identity: String, requirement: RegistryRequirement, products: ProductFilter = .everything, traits: Set = []) -> MockDependency { + public static func registry(identity: String, requirement: RegistryRequirement, products: ProductFilter = .everything, traits: Set = ["default"]) -> MockDependency { .registry(identity: .plain(identity), requirement: requirement, traits: traits) } - public static func registry(identity: PackageIdentity, requirement: RegistryRequirement, products: ProductFilter = .everything, traits: Set = []) -> MockDependency { + public static func registry(identity: PackageIdentity, requirement: RegistryRequirement, products: ProductFilter = .everything, traits: Set = ["default"]) -> MockDependency { MockDependency(location: .registry(identity: identity, requirement: requirement), products: products, traits: traits) } diff --git a/Sources/_InternalTestSupport/MockPackageContainer.swift b/Sources/_InternalTestSupport/MockPackageContainer.swift index a6fcb46d605..9780ae4ab10 100644 --- a/Sources/_InternalTestSupport/MockPackageContainer.swift +++ b/Sources/_InternalTestSupport/MockPackageContainer.swift @@ -46,12 +46,12 @@ public class MockPackageContainer: CustomPackageContainer { return _versions } - public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set?) -> [MockPackageContainer.Constraint] { + public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) -> [MockPackageContainer.Constraint] { requestedVersions.insert(version) return getDependencies(at: version.description, productFilter: productFilter, enabledTraits) } - public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set?) -> [MockPackageContainer.Constraint] { + public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) -> [MockPackageContainer.Constraint] { let dependencies: [Dependency] if filteredMode { dependencies = filteredDependencies[productFilter]! @@ -64,7 +64,7 @@ public class MockPackageContainer: CustomPackageContainer { } } - public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set?) -> [MockPackageContainer.Constraint] { + public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) -> [MockPackageContainer.Constraint] { return unversionedDeps } @@ -72,11 +72,6 @@ public class MockPackageContainer: CustomPackageContainer { return self.package } - public func getEnabledTraits(traitConfiguration: TraitConfiguration?) async throws -> Set { - // This mock does not currently need support for traits. - return [] - } - public func isToolsVersionCompatible(at version: Version) -> Bool { return true } diff --git a/Sources/_InternalTestSupport/MockWorkspace.swift b/Sources/_InternalTestSupport/MockWorkspace.swift index 0eed3d9293f..4e8b202654f 100644 --- a/Sources/_InternalTestSupport/MockWorkspace.swift +++ b/Sources/_InternalTestSupport/MockWorkspace.swift @@ -103,6 +103,7 @@ public final class MockWorkspace { .SourceControlToRegistryDependencyTransformation var defaultRegistry: Registry? public let traitConfiguration: TraitConfiguration + public var enabledTraitsMap: EnabledTraitsMap public let pruneDependencies: Bool public init( @@ -125,7 +126,8 @@ public final class MockWorkspace { defaultRegistry: Registry? = .none, customHostTriple: Triple = hostTriple, traitConfiguration: TraitConfiguration = .default, - pruneDependencies: Bool = false + pruneDependencies: Bool = false, + enabledTraitsMap: EnabledTraitsMap = .init() ) async throws { try fileSystem.createMockToolchain() @@ -164,6 +166,7 @@ public final class MockWorkspace { self.customHostToolchain = try UserToolchain.mockHostToolchain(fileSystem, hostTriple: customHostTriple) self.traitConfiguration = traitConfiguration self.pruneDependencies = pruneDependencies + self.enabledTraitsMap = enabledTraitsMap try await self.create() } @@ -323,7 +326,7 @@ public final class MockWorkspace { displayName: package.name, path: packagePath, packageKind: packageKind, - packageIdentity: .plain(package.name), + packageIdentity: .plain(package.name.lowercased()), packageLocation: packageLocation, platforms: package.platforms, version: v, @@ -688,7 +691,8 @@ public final class MockWorkspace { let root = try PackageGraphRoot( input: rootInput, manifests: rootManifests, - observabilityScope: observability.topScope + observabilityScope: observability.topScope, + enabledTraitsMap: workspace.enabledTraitsMap ) let dependencyManifests = try await workspace.loadDependencyManifests( @@ -955,7 +959,8 @@ public final class MockWorkspace { let graphRoot = try PackageGraphRoot( input: rootInput, manifests: rootManifests, - observabilityScope: observability.topScope + observabilityScope: observability.topScope, + enabledTraitsMap: workspace.enabledTraitsMap ) let manifests = try await workspace.loadDependencyManifests( root: graphRoot, diff --git a/Sources/swift-bootstrap/main.swift b/Sources/swift-bootstrap/main.swift index dfae051c72e..a0031814e71 100644 --- a/Sources/swift-bootstrap/main.swift +++ b/Sources/swift-bootstrap/main.swift @@ -409,7 +409,7 @@ struct SwiftBootstrapBuildTool: AsyncParsableCommand { let input = loadedManifests.map { identity, manifest in KeyedPair(manifest, key: identity) } _ = try await topologicalSort(input) { pair in // When bootstrapping no special trait build configuration is used - let dependenciesRequired = try pair.item.dependenciesRequired(for: .everything, nil) + let dependenciesRequired = try pair.item.dependenciesRequired(for: .everything) let dependenciesToLoad = dependenciesRequired.map{ $0.packageRef }.filter { !loadedManifests.keys.contains($0.identity) } let dependenciesManifests = try await self.loadManifests(manifestLoader: manifestLoader, packages: dependenciesToLoad) dependenciesManifests.forEach { loadedManifests[$0.key] = $0.value } @@ -423,7 +423,8 @@ struct SwiftBootstrapBuildTool: AsyncParsableCommand { let packageGraphRoot = try PackageGraphRoot( input: .init(packages: [packagePath]), manifests: [packagePath: rootPackageManifest], - observabilityScope: observabilityScope + observabilityScope: observabilityScope, + enabledTraitsMap: .init() ) return try ModulesGraph.load( @@ -435,7 +436,8 @@ struct SwiftBootstrapBuildTool: AsyncParsableCommand { binaryArtifacts: [:], prebuilts: [:], fileSystem: fileSystem, - observabilityScope: observabilityScope + observabilityScope: observabilityScope, + enabledTraitsMap: [:] // When bootstrapping no special trait build configuration is used ) } diff --git a/Tests/FunctionalTests/TraitTests.swift b/Tests/FunctionalTests/TraitTests.swift index 78555939e33..84eafbe5c3f 100644 --- a/Tests/FunctionalTests/TraitTests.swift +++ b/Tests/FunctionalTests/TraitTests.swift @@ -43,7 +43,6 @@ struct TraitTests { let (stdout, stderr) = try await executeSwiftRun( fixturePath.appending("Example"), "Example", - extraArgs: ["--experimental-prune-unused-dependencies"], buildSystem: buildSystem, ) // We expect no warnings to be produced. Specifically no unused dependency warnings. @@ -81,7 +80,7 @@ struct TraitTests { let (stdout, stderr) = try await executeSwiftRun( fixturePath.appending("Example"), "Example", - extraArgs: ["--traits", "default,Package9,Package10", "--experimental-prune-unused-dependencies"], + extraArgs: ["--traits", "default,Package9,Package10"], buildSystem: buildSystem, ) // We expect no warnings to be produced. Specifically no unused dependency warnings. @@ -102,7 +101,7 @@ struct TraitTests { """) } } when: { - ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || (buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) } } @@ -122,7 +121,7 @@ struct TraitTests { let (stdout, stderr) = try await executeSwiftRun( fixturePath.appending("Example"), "Example", - extraArgs: ["--traits", "default,Package9", "--experimental-prune-unused-dependencies"], + extraArgs: ["--traits", "default,Package9"], buildSystem: buildSystem, ) // We expect no warnings to be produced. Specifically no unused dependency warnings. @@ -141,7 +140,7 @@ struct TraitTests { """) } } when: { - ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || (buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) } } @@ -164,7 +163,6 @@ struct TraitTests { extraArgs: [ "--traits", "default,Package5,Package7,BuildCondition3", - "--experimental-prune-unused-dependencies", ], buildSystem: buildSystem, ) @@ -185,7 +183,7 @@ struct TraitTests { """) } } when: { - ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || (buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) } } @@ -205,7 +203,7 @@ struct TraitTests { let (stdout, stderr) = try await executeSwiftRun( fixturePath.appending("Example"), "Example", - extraArgs: ["--disable-default-traits", "--experimental-prune-unused-dependencies"], + extraArgs: ["--disable-default-traits"], buildSystem: buildSystem, ) // We expect no warnings to be produced. Specifically no unused dependency warnings. @@ -218,7 +216,7 @@ struct TraitTests { """) } } when: { - ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || (buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) } } @@ -238,7 +236,7 @@ struct TraitTests { let (stdout, stderr) = try await executeSwiftRun( fixturePath.appending("Example"), "Example", - extraArgs: ["--traits", "Package5,Package7", "--experimental-prune-unused-dependencies"], + extraArgs: ["--traits", "Package5,Package7"], buildSystem: buildSystem, ) // We expect no warnings to be produced. Specifically no unused dependency warnings. @@ -254,7 +252,7 @@ struct TraitTests { """) } } when: { - ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || (buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) } } @@ -274,7 +272,7 @@ struct TraitTests { let (stdout, stderr) = try await executeSwiftRun( fixturePath.appending("Example"), "Example", - extraArgs: ["--enable-all-traits", "--experimental-prune-unused-dependencies"], + extraArgs: ["--enable-all-traits"], buildSystem: buildSystem, ) // We expect no warnings to be produced. Specifically no unused dependency warnings. @@ -298,7 +296,7 @@ struct TraitTests { """) } } when: { - ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || (buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) } } @@ -321,7 +319,6 @@ struct TraitTests { extraArgs: [ "--enable-all-traits", "--disable-default-traits", - "--experimental-prune-unused-dependencies", ], buildSystem: buildSystem, ) @@ -346,7 +343,7 @@ struct TraitTests { """) } } when: { - ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || buildSystem == .swiftbuild + ProcessInfo.hostOperatingSystem == .windows && CiEnvironment.runningInSmokeTestPipeline || (buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) } } @@ -384,7 +381,6 @@ struct TraitTests { try await fixture(name: "Traits") { fixturePath in let (stdout, _) = try await executeSwiftTest( fixturePath.appending("Example"), - extraArgs: ["--experimental-prune-unused-dependencies"], buildSystem: buildSystem, ) let expectedOut = """ @@ -421,7 +417,6 @@ struct TraitTests { extraArgs: [ "--enable-all-traits", "--disable-default-traits", - "--experimental-prune-unused-dependencies", ], buildSystem: buildSystem, ) @@ -440,12 +435,12 @@ struct TraitTests { DEFINE1 enabled DEFINE2 enabled DEFINE3 enabled - + """ #expect(stdout.contains(expectedOut)) } } when: { - buildSystem == .swiftbuild + (buildSystem == .swiftbuild && [.windows, .linux].contains(ProcessInfo.hostOperatingSystem)) } } @@ -461,7 +456,7 @@ struct TraitTests { try await fixture(name: "Traits") { fixturePath in let (stdout, _) = try await executeSwiftPackage( fixturePath.appending("Package10"), - extraArgs: ["dump-symbol-graph", "--experimental-prune-unused-dependencies"], + extraArgs: ["dump-symbol-graph"], buildSystem: buildSystem, ) let optionalPath = stdout @@ -470,6 +465,7 @@ struct TraitTests { .first { String($0).hasPrefix("Files written to ") }? .dropFirst(17) + let path = try String(#require(optionalPath)) let symbolGraph = try String(contentsOfFile: "\(path)/Package10Library1.symbols.json", encoding: .utf8) #expect(symbolGraph.contains("TypeGatedByPackage10Trait1")) @@ -489,7 +485,7 @@ struct TraitTests { try await fixture(name: "Traits") { fixturePath in let (stdout, _) = try await executeSwiftPackage( fixturePath.appending("Package10"), - extraArgs: ["plugin", "extract", "--experimental-prune-unused-dependencies"], + extraArgs: ["plugin", "extract"], buildSystem: buildSystem, ) let path = String(stdout.split(whereSeparator: \.isNewline).first!) @@ -523,7 +519,7 @@ struct TraitTests { } let expectedErr = """ - error: Disabled default traits by package 'disablingemptydefaultsexample' on package 'Package11' that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. + error: Disabled default traits by package 'disablingemptydefaultsexample' (DisablingEmptyDefaultsExample) on package 'package11' (Package11) that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. """ #expect(stderr.contains(expectedErr)) diff --git a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift index 7858462a026..66c98c79c27 100644 --- a/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift +++ b/Tests/PackageGraphPerformanceTests/PackageGraphPerfTests.swift @@ -98,7 +98,8 @@ final class PackageGraphPerfTests: XCTestCasePerf { binaryArtifacts: [:], prebuilts: [:], fileSystem: fs, - observabilityScope: observability.topScope + observabilityScope: observability.topScope, + enabledTraitsMap: [:] ) XCTAssertEqual(g.packages.count, N) XCTAssertNoDiagnostics(observability.diagnostics) diff --git a/Tests/PackageGraphTests/ModulesGraphTests.swift b/Tests/PackageGraphTests/ModulesGraphTests.swift index 384ae953c36..be0df3aa4ac 100644 --- a/Tests/PackageGraphTests/ModulesGraphTests.swift +++ b/Tests/PackageGraphTests/ModulesGraphTests.swift @@ -3052,7 +3052,7 @@ final class ModulesGraphTests: XCTestCase { ) // Make sure aliases are found properly and do not fall back to pre‐5.2 behavior, leaking across onto other // dependencies. - let required = try manifest.dependenciesRequired(for: .everything, nil) + let required = try manifest.dependenciesRequired(for: .everything) let unrelated = try XCTUnwrap( required .first(where: { $0.nameForModuleDependencyResolutionOnly == "Unrelated" }) @@ -4421,18 +4421,11 @@ final class ModulesGraphTests: XCTestCase { observabilityScope: observability.topScope ) - XCTAssertEqual(observability.diagnostics.count, 1) - testDiagnostics(observability.diagnostics) { result in - result.check( - diagnostic: "dependency 'package5' is not used by any target", - severity: .warning - ) - } - + XCTAssertEqual(observability.diagnostics.count, 0) PackageGraphTester(graph) { result in result.checkPackage("Package1") { package in XCTAssertEqual(package.enabledTraits, ["Package1Trait3"]) - XCTAssertEqual(package.dependencies.count, 3) + XCTAssertEqual(package.dependencies.count, 2) } result.checkTarget("Package1Target1") { target in target.check(dependencies: "Package2Target1", "Package4Target1") diff --git a/Tests/PackageGraphTests/PubGrubTests.swift b/Tests/PackageGraphTests/PubGrubTests.swift index 43ac9d829df..8f2bbcfa2dd 100644 --- a/Tests/PackageGraphTests/PubGrubTests.swift +++ b/Tests/PackageGraphTests/PubGrubTests.swift @@ -3165,11 +3165,11 @@ public class MockContainer: PackageContainer { return version } - public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + public func getDependencies(at version: Version, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { return try getDependencies(at: version.description, productFilter: productFilter, enabledTraits) } - public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + public func getDependencies(at revision: String, productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { guard let revisionDependencies = dependencies[revision] else { throw _MockLoadingError.unknownRevision } @@ -3183,7 +3183,7 @@ public class MockContainer: PackageContainer { }) } - public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set?) throws -> [PackageContainerConstraint] { + public func getUnversionedDependencies(productFilter: ProductFilter, _ enabledTraits: Set = ["default"]) throws -> [PackageContainerConstraint] { // FIXME: This is messy, remove unversionedDeps property. if !unversionedDeps.isEmpty { return unversionedDeps @@ -3198,11 +3198,6 @@ public class MockContainer: PackageContainer { return self.package } - public func getEnabledTraits(traitConfiguration: TraitConfiguration?) async throws -> Set { - // FIXME: This mock does not currently support traits. - return [] - } - func appendVersion(_ version: BoundVersion) { self._versions.append(version) self._versions = self._versions diff --git a/Tests/PackageModelTests/ManifestTests.swift b/Tests/PackageModelTests/ManifestTests.swift index 34a902cc470..438117b3d6f 100644 --- a/Tests/PackageModelTests/ManifestTests.swift +++ b/Tests/PackageModelTests/ManifestTests.swift @@ -93,7 +93,7 @@ class ManifestTests: XCTestCase { ) XCTAssertEqual( - try manifest.dependenciesRequired(for: .everything, nil).map(\.identity.description).sorted(), + try manifest.dependenciesRequired(for: .everything).map(\.identity.description).sorted(), [ "bar1", "bar2", @@ -113,7 +113,7 @@ class ManifestTests: XCTestCase { ) XCTAssertEqual( - try manifest.dependenciesRequired(for: .specific(["Foo"]), nil).map(\.identity.description).sorted(), + try manifest.dependenciesRequired(for: .specific(["Foo"])).map(\.identity.description).sorted(), [ "bar1", // Foo → Foo1 → Bar1 "bar2", // Foo → Foo1 → Foo2 → Bar2 @@ -133,7 +133,7 @@ class ManifestTests: XCTestCase { ) XCTAssertEqual( - try manifest.dependenciesRequired(for: .everything, nil).map(\.identity.description).sorted(), + try manifest.dependenciesRequired(for: .everything).map(\.identity.description).sorted(), [ "bar1", "bar2", @@ -208,7 +208,7 @@ class ManifestTests: XCTestCase { Trait '\( trait .name - )' is not declared by package 'Foo'. There are no available traits declared by this package. + )' is not declared by package 'foo' (Foo). There are no available traits declared by this package. """) } } @@ -253,30 +253,30 @@ class ManifestTests: XCTestCase { ) // Test `isTraitEnabled` when the trait we're querying for does not exist. - XCTAssertThrowsError(try manifest.isTraitEnabled(.init(stringLiteral: "IDontExist"), nil)) { error in + XCTAssertThrowsError(try manifest.isTraitEnabled(.init(stringLiteral: "IDontExist"), ["default"])) { error in XCTAssertEqual("\(error)", """ - Trait 'IDontExist' is not declared by package 'Foo'. The available traits declared by this package are: Trait1, Trait2. + Trait 'IDontExist' is not declared by package 'foo' (Foo). The available traits declared by this package are: Trait1, Trait2. """) } // Test `isTraitEnabled` when the set of enabled traits contains a trait that isn't defined in the package. XCTAssertThrowsError(try manifest.isTraitEnabled(.init(stringLiteral: "Trait1"), ["IDontExist"])) { error in XCTAssertEqual("\(error)", """ - Trait 'IDontExist' is not declared by package 'Foo'. The available traits declared by this package are: Trait1, Trait2. + Trait 'IDontExist' is not declared by package 'foo' (Foo). The available traits declared by this package are: Trait1, Trait2. """) } // Test `isTraitEnabled` when the set of enabled traits contains a trait that isn't defined in the package, and the queried trait is the same non-existant trait. XCTAssertThrowsError(try manifest.isTraitEnabled(.init(stringLiteral: "IDontExist"), ["IDontExist"])) { error in XCTAssertEqual("\(error)", """ - Trait 'IDontExist' is not declared by package 'Foo'. The available traits declared by this package are: Trait1, Trait2. + Trait 'IDontExist' is not declared by package 'foo' (Foo). The available traits declared by this package are: Trait1, Trait2. """) } // Test `isTraitEnabled` when the set of enabled traits contains a trait that isn't defined in the package, and the queried trait is another non-existant trait. XCTAssertThrowsError(try manifest.isTraitEnabled(.init(stringLiteral: "IDontExistPart2"), ["IDontExist"])) { error in XCTAssertEqual("\(error)", """ - Trait 'IDontExistPart2' is not declared by package 'Foo'. The available traits declared by this package are: Trait1, Trait2. + Trait 'IDontExistPart2' is not declared by package 'foo' (Foo). The available traits declared by this package are: Trait1, Trait2. """) } @@ -320,14 +320,14 @@ class ManifestTests: XCTestCase { // When passed .disableAllTraits configuration XCTAssertThrowsError(try manifest.enabledTraits(using: .disableAllTraits)) { error in XCTAssertEqual("\(error)", """ - Disabled default traits on package 'Foo' that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. + Disabled default traits on package 'foo' (Foo) that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. """) } // When passed .enableAllTraits configuration XCTAssertThrowsError(try manifest.enabledTraits(using: .enabledTraits(["Trait1"]))) { error in XCTAssertEqual("\(error)", """ - Traits [Trait1] have been enabled on package 'Foo' that declares no traits. + Traits [Trait1] have been enabled on package 'foo' (Foo) that declares no traits. """) } @@ -337,16 +337,16 @@ class ManifestTests: XCTestCase { // Enabled Traits when passed explicitly enabled traits list: // If given a parent package, and the enabled traits being passed don't exist: - XCTAssertThrowsError(try manifest.enabledTraits(using: ["Trait1"], "Qux")) { error in + XCTAssertThrowsError(try manifest.enabledTraits(using: ["Trait1"], .init(identity: "qux"))) { error in XCTAssertEqual("\(error)", """ - Package 'Qux' enables traits [Trait1] on package 'Foo' that declares no traits. + Package 'qux' enables traits [Trait1] on package 'foo' (Foo) that declares no traits. """) } // If given a parent package, and the default traits are disabled: - XCTAssertThrowsError(try manifest.enabledTraits(using: [], "Qux")) { error in + XCTAssertThrowsError(try manifest.enabledTraits(using: [], .init(identity: "qux"))) { error in XCTAssertEqual("\(error)", """ - Disabled default traits by package 'Qux' on package 'Foo' that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. + Disabled default traits by package 'qux' on package 'foo' (Foo) that declares no traits. This is prohibited to allow packages to adopt traits initially without causing an API break. """) } } @@ -388,18 +388,17 @@ class ManifestTests: XCTestCase { traits: traits ) - // Assure that the guarded dependencies aren't pruned, since we haven't enabled it for this manifest. + // Assure that the trait-guarded dependencies pruned. XCTAssertEqual( - try manifest.dependenciesRequired(for: .everything, nil).map(\.identity.description).sorted(), + try manifest.dependenciesRequired(for: .everything).map(\.identity.description).sorted(), [ - "baz", "buzz", ] ) // Assure that each trait is not enabled. for trait in traits { - XCTAssertEqual(try manifest.isTraitEnabled(trait, nil), false) + XCTAssertEqual(try manifest.isTraitEnabled(trait, ["default"]), false) } // Now, create a version of the same manifest but with the `pruneDependencies` flag set to true. @@ -416,7 +415,7 @@ class ManifestTests: XCTestCase { // Since we've enabled pruned dependencies for this manifest, we should only see "buzz" XCTAssertEqual( - try manifestPrunedDeps.dependenciesRequired(for: .everything, nil).map(\.identity.description).sorted(), + try manifestPrunedDeps.dependenciesRequired(for: .everything).map(\.identity.description).sorted(), [ "buzz", ] @@ -424,7 +423,7 @@ class ManifestTests: XCTestCase { // Assure that each trait is not enabled. for trait in traits { - XCTAssertEqual(try manifestPrunedDeps.isTraitEnabled(trait, nil), false) + XCTAssertEqual(try manifestPrunedDeps.isTraitEnabled(trait, ["default"]), false) } } } @@ -574,13 +573,13 @@ class ManifestTests: XCTestCase { // Calculate the enabled traits with an explicitly declared set of enabled traits. // This should override the default traits (since it isn't explicitly passed in here). - let allEnabledTraitsWithoutDefaults = try manifest.enabledTraits(using: .enabledTraits(["Trait3"]))?.sorted() + let allEnabledTraitsWithoutDefaults = try manifest.enabledTraits(using: .enabledTraits(["Trait3"])).sorted() XCTAssertEqual(allEnabledTraitsWithoutDefaults, ["Trait3"]) // Calculate the enabled traits with an explicitly declared set of enabled traits, // including the default traits. Since default traits are explicitly enabled in the // passed set of traits, this will be factored into the calculation. - let allEnabledTraitsWithDefaults = try manifest.enabledTraits(using: .enabledTraits(["default", "Trait3"]))?.sorted() + let allEnabledTraitsWithDefaults = try manifest.enabledTraits(using: .enabledTraits(["default", "Trait3"])).sorted() XCTAssertEqual(allEnabledTraitsWithDefaults, ["Trait1", "Trait2", "Trait3"]) } } @@ -613,7 +612,7 @@ class ManifestTests: XCTestCase { ) // Calculate the enabled traits with all traits enabled flag. - let allEnabledTraits = try manifest.enabledTraits(using: .enableAllTraits)?.sorted() + let allEnabledTraits = try manifest.enabledTraits(using: .enableAllTraits).sorted() XCTAssertEqual(allEnabledTraits, ["Trait1", "Trait2", "Trait3"]) } } @@ -744,7 +743,7 @@ class ManifestTests: XCTestCase { XCTAssertTrue(try manifest.isTargetDependencyEnabled( target: "Foo", unguardedTargetDependency, - enabledTraits: nil + enabledTraits: ["default"] )) // Test if a trait-guarded dependency is enabled when passed a set of enabled traits that @@ -755,20 +754,19 @@ class ManifestTests: XCTestCase { enabledTraits: ["Trait3"] )) - // Test if a trait-guarded dependency is enabled when passed a flag that enables all traits; + // Test if a trait-guarded dependency is enabled when passed all traits enabled; // should be true. XCTAssertTrue(try manifest.isTargetDependencyEnabled( target: "Foo", trait3GuardedTargetDependency, - enabledTraits: nil, - enableAllTraits: true + enabledTraits: ["Trait1", "Trait2", "Trait3"] )) // Test if a trait-guarded dependency is enabled when there are no enabled traits passsed. XCTAssertFalse(try manifest.isTargetDependencyEnabled( target: "Foo", trait3GuardedTargetDependency, - enabledTraits: nil + enabledTraits: ["default"] )) // Test if a target dependency guarded by default traits is enabled when passed no explicitly @@ -776,7 +774,7 @@ class ManifestTests: XCTestCase { XCTAssertTrue(try manifest.isTargetDependencyEnabled( target: "Foo", defaultTraitGuardedTargetDependency, - enabledTraits: nil + enabledTraits: ["default"] )) // Test if a target dependency guarded by default traits is enabled when passed an empty set @@ -881,11 +879,8 @@ class ManifestTests: XCTestCase { traits: traits ) - XCTAssertTrue(try manifest.isPackageDependencyUsed(bar, enabledTraits: nil)) XCTAssertTrue(try manifest.isPackageDependencyUsed(bar, enabledTraits: [])) - XCTAssertFalse(try manifest.isPackageDependencyUsed(baz, enabledTraits: nil)) XCTAssertTrue(try manifest.isPackageDependencyUsed(baz, enabledTraits: ["Trait3"])) - XCTAssertTrue(try manifest.isPackageDependencyUsed(bam, enabledTraits: nil)) XCTAssertFalse(try manifest.isPackageDependencyUsed(bam, enabledTraits: [])) XCTAssertFalse(try manifest.isPackageDependencyUsed(bam, enabledTraits: ["Trait3"])) @@ -893,7 +888,6 @@ class ManifestTests: XCTestCase { // dependency that depends on the same package as another target dependency, but // is unguarded by traits; therefore, this package dependency should be considered used // in every scenario, regardless of the passed trait configuration. - XCTAssertTrue(try manifestWithBamDependencyAlwaysUsed.isPackageDependencyUsed(bam, enabledTraits: nil)) XCTAssertTrue(try manifestWithBamDependencyAlwaysUsed.isPackageDependencyUsed(bam, enabledTraits: [])) XCTAssertTrue(try manifestWithBamDependencyAlwaysUsed.isPackageDependencyUsed(bam, enabledTraits: ["Trait3"])) } @@ -943,7 +937,7 @@ class ManifestTests: XCTestCase { // The list of required dependencies should remain the same, since all depenencies are being // used in the current manifest. - let calculatedDependencies = try manifest.dependenciesRequired(for: .everything, nil) + let calculatedDependencies = try manifest.dependenciesRequired(for: .everything) XCTAssertEqual(calculatedDependencies.map(\.identity).sorted(), dependencies.map(\.identity).sorted()) } } @@ -1004,7 +998,7 @@ class ManifestTests: XCTestCase { pruneDependencies: true ) - let calculatedDependenciesWithDefaultTraits = try manifest.dependenciesRequired(for: .everything, nil) + let calculatedDependenciesWithDefaultTraits = try manifest.dependenciesRequired(for: .everything) XCTAssertEqual( calculatedDependenciesWithDefaultTraits.map(\.identity).sorted(), [ @@ -1087,7 +1081,7 @@ class ManifestTests: XCTestCase { // When using default traits (since we omit a list of enabled traits here), // `Bar` should not be trait-guarded since `Trait1` is enabled by default. - let noTraitGuardedDependencies = manifest.dependenciesTraitGuarded(withEnabledTraits: nil) + let noTraitGuardedDependencies = manifest.dependenciesTraitGuarded(withEnabledTraits: ["default"]) XCTAssertEqual(noTraitGuardedDependencies, []) } } diff --git a/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift b/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift index 45adb1a22f6..fce433b382f 100644 --- a/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift +++ b/Tests/WorkspaceTests/SourceControlPackageContainerTests.swift @@ -265,7 +265,7 @@ final class SourceControlPackageContainerTests: XCTestCase { let container = try await provider.getContainer(for: ref) as! SourceControlPackageContainer let revision = try container.getRevision(forTag: "1.0.0") do { - _ = try await container.getDependencies(at: revision.identifier, productFilter: .nothing, nil) + _ = try await container.getDependencies(at: revision.identifier, productFilter: .nothing) } catch let error as SourceControlPackageContainer.GetDependenciesError { let error = error.underlyingError as! UnsupportedToolsVersion XCTAssertMatch(error.description, .and(.prefix("package '\(PackageIdentity(path: repoPath))' @"), .suffix("is using Swift tools version 3.1.0 which is no longer supported; consider using '// swift-tools-version:4.0' to specify the current tools version"))) @@ -438,7 +438,7 @@ final class SourceControlPackageContainerTests: XCTestCase { XCTAssertEqual( try manifest - .dependencyConstraints(productFilter: .everything, nil) + .dependencyConstraints(productFilter: .everything) .sorted(by: { $0.package.identity < $1.package.identity }), [ v5Constraints[0], @@ -460,7 +460,7 @@ final class SourceControlPackageContainerTests: XCTestCase { XCTAssertEqual( try manifest - .dependencyConstraints(productFilter: .everything, nil) + .dependencyConstraints(productFilter: .everything) .sorted(by: { $0.package.identity < $1.package.identity }), [ v5Constraints[0], @@ -482,7 +482,7 @@ final class SourceControlPackageContainerTests: XCTestCase { XCTAssertEqual( try manifest - .dependencyConstraints(productFilter: .everything, nil) + .dependencyConstraints(productFilter: .everything) .sorted(by: { $0.package.identity < $1.package.identity }), [ v5_2Constraints[0], @@ -504,7 +504,7 @@ final class SourceControlPackageContainerTests: XCTestCase { XCTAssertEqual( try manifest - .dependencyConstraints(productFilter: .specific(Set(products.map { $0.name })), nil) + .dependencyConstraints(productFilter: .specific(Set(products.map { $0.name }))) .sorted(by: { $0.package.identity < $1.package.identity }), [ v5_2Constraints[0], @@ -564,7 +564,7 @@ final class SourceControlPackageContainerTests: XCTestCase { let container = try await containerProvider.getContainer(for: packageRef) as! SourceControlPackageContainer // Simulate accessing a fictitious dependency on the `master` branch, and check that we get back the expected error. - do { _ = try await container.getDependencies(at: "master", productFilter: .everything, nil) } + do { _ = try await container.getDependencies(at: "master", productFilter: .everything) } catch let error as SourceControlPackageContainer.GetDependenciesError { // We expect to get an error message that mentions main. XCTAssertMatch(error.description, .and(.prefix("could not find a branch named ‘master’"), .suffix("(did you mean ‘main’?)"))) @@ -573,7 +573,7 @@ final class SourceControlPackageContainerTests: XCTestCase { } // Simulate accessing a fictitious dependency on some random commit that doesn't exist, and check that we get back the expected error. - do { _ = try await container.getDependencies(at: "535f4cb5b4a0872fa691473e82d7b27b9894df00", productFilter: .everything, nil) } + do { _ = try await container.getDependencies(at: "535f4cb5b4a0872fa691473e82d7b27b9894df00", productFilter: .everything) } catch let error as SourceControlPackageContainer.GetDependenciesError { // We expect to get an error message about the specific commit. XCTAssertMatch(error.description, .prefix("could not find the commit 535f4cb5b4a0872fa691473e82d7b27b9894df00")) @@ -729,8 +729,8 @@ final class SourceControlPackageContainerTests: XCTestCase { let packageReference = PackageReference.localSourceControl(identity: PackageIdentity(path: packageDirectory), path: packageDirectory) let container = try await containerProvider.getContainer(for: packageReference) - let forNothing = try await container.getDependencies(at: version, productFilter: .specific([]), nil) - let forProduct = try await container.getDependencies(at: version, productFilter: .specific(["Product"]), nil) + let forNothing = try await container.getDependencies(at: version, productFilter: .specific([]), ["default"]) + let forProduct = try await container.getDependencies(at: version, productFilter: .specific(["Product"]), ["default"]) #if ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION // If the cache overlaps (incorrectly), these will be the same. XCTAssertNotEqual(forNothing, forProduct) diff --git a/Tests/WorkspaceTests/WorkspaceTests.swift b/Tests/WorkspaceTests/WorkspaceTests.swift index 550d3f05e8d..a75787a3f12 100644 --- a/Tests/WorkspaceTests/WorkspaceTests.swift +++ b/Tests/WorkspaceTests/WorkspaceTests.swift @@ -2927,8 +2927,7 @@ final class WorkspaceTests: XCTestCase { nameForTargetDependencyResolutionOnly: settings.nameForTargetDependencyResolutionOnly, location: settings.location, requirement: .exact("1.5.0"), - productFilter: settings.productFilter, - traits: [] + productFilter: settings.productFilter ) workspace.manifestLoader.manifests[fooKey] = Manifest.createManifest( @@ -15822,7 +15821,7 @@ final class WorkspaceTests: XCTestCase { .product( name: "Boo", package: "Boo", - // Trait2 disabled; should generate unused dependency warning + // Trait2 disabled; should remove this dependency from graph condition: .init(traits: ["Trait2"]) ), ] @@ -15873,14 +15872,12 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraph(roots: ["Foo"], deps: deps) { graph, diagnostics in PackageGraphTester(graph) { result in result.check(roots: "Foo") - result.check(packages: "Baz", "Foo", "Boo") - result.check(modules: "Bar", "Baz", "Boo", "Foo") + result.check(packages: "Baz", "Foo") + result.check(modules: "Bar", "Baz", "Foo") result.checkTarget("Foo") { result in result.check(dependencies: "Baz") } result.checkTarget("Bar") { result in result.check(dependencies: "Baz") } } - testDiagnostics(diagnostics) { result in - result.check(diagnostic: .contains("dependency 'boo' is not used by any target"), severity: .warning) - } + XCTAssertNoDiagnostics(diagnostics) } await workspace.checkManagedDependencies { result in result.check(dependency: "baz", at: .checkout(.version("1.0.0"))) @@ -16187,7 +16184,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraphFailure(roots: ["Foo"], deps: deps) { diagnostics in testDiagnostics(diagnostics) { result in - result.check(diagnostic: .equal("Trait 'TraitNotFound' enabled by parent package 'foo' is not declared by package 'Baz'. The available traits declared by this package are: TraitFound."), severity: .error) + result.check(diagnostic: .equal("Trait 'TraitNotFound' enabled by parent package 'foo' (Foo) is not declared by package 'baz' (Baz). The available traits declared by this package are: TraitFound."), severity: .error) } } await workspace.checkManagedDependencies { result in @@ -16250,7 +16247,7 @@ final class WorkspaceTests: XCTestCase { try await workspace.checkPackageGraphFailure(roots: ["Foo"], deps: deps) { diagnostics in testDiagnostics(diagnostics) { result in - result.check(diagnostic: .equal("Trait 'TraitNotFound' is not declared by package 'Foo'. The available traits declared by this package are: Trait1, Trait2, default."), severity: .error) + result.check(diagnostic: .equal("Trait 'TraitNotFound' is not declared by package 'foo' (Foo). The available traits declared by this package are: Trait1, Trait2, default."), severity: .error) } } }