diff --git a/Sources/PackageGraph/ModulesGraph+Loading.swift b/Sources/PackageGraph/ModulesGraph+Loading.swift index 2ea14951129..a269cd0e478 100644 --- a/Sources/PackageGraph/ModulesGraph+Loading.swift +++ b/Sources/PackageGraph/ModulesGraph+Loading.swift @@ -30,7 +30,7 @@ extension ModulesGraph { requiredDependencies: [PackageReference] = [], unsafeAllowedPackages: Set = [], binaryArtifacts: [PackageIdentity: [String: BinaryArtifact]], - prebuilts: [PackageIdentity: [String: PrebuiltLibrary]], // Product name to library mapping + prebuilts: [PackageIdentity: [String: PrebuiltLibrary]], // Package -> Product name -> library shouldCreateMultipleTestProducts: Bool = false, createREPLProduct: Bool = false, customPlatformsRegistry: PlatformRegistry? = .none, @@ -167,7 +167,6 @@ extension ModulesGraph { path: packagePath, additionalFileRules: additionalFileRules, binaryArtifacts: binaryArtifacts[node.identity] ?? [:], - prebuilts: prebuilts, shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts, testEntryPointPath: testEntryPointPath, createREPLProduct: manifest.packageKind.isRoot ? createREPLProduct : false, @@ -214,7 +213,7 @@ extension ModulesGraph { let rootPackages = resolvedPackages.filter { root.manifests.values.contains($0.manifest) } checkAllDependenciesAreUsed( packages: resolvedPackages, - rootPackages, + rootPackages: rootPackages, prebuilts: prebuilts, observabilityScope: observabilityScope ) @@ -231,7 +230,7 @@ extension ModulesGraph { private func checkAllDependenciesAreUsed( packages: IdentifiableSet, - _ rootPackages: [ResolvedPackage], + rootPackages: [ResolvedPackage], prebuilts: [PackageIdentity: [String: PrebuiltLibrary]], observabilityScope: ObservabilityScope ) { @@ -311,7 +310,7 @@ private func checkAllDependenciesAreUsed( // We check if any of the products of this dependency is guarded by a trait. let traitGuarded = traitGuardedProductDependencies.contains(product.name) // Consider prebuilts as used - let prebuilt = prebuilts[dependency.identity]?.keys.contains(product.name) ?? false + let prebuilt = prebuilts[dependency.identity]?[product.name] != nil return usedByPackage || traitGuarded || prebuilt } @@ -729,24 +728,6 @@ private func createResolvedPackages( // Establish product dependencies. for case .product(let productRef, let conditions) in moduleBuilder.module.dependencies { - if let package = productRef.package, prebuilts[.plain(package)]?[productRef.name] != nil { - // See if we're using a prebuilt instead - if moduleBuilder.module.type == .macro { - continue - } else if moduleBuilder.module.type == .test { - // use prebuilt if this is a test that depends a macro target - // these are guaranteed built for host - if moduleBuilder.module.dependencies.contains(where: { dep in - guard let module = dep.module else { - return false - } - return module.type == .macro - }) { - continue - } - } - } - // Find the product in this package's dependency products. // Look it up by ID if module aliasing is used, otherwise by name. let product = lookupByProductIDs ? productDependencyMap[productRef.identity] : @@ -800,6 +781,91 @@ private func createResolvedPackages( } } + // Prebuilts + for packageBuilder in packageBuilders { + for moduleBuilder in packageBuilder.modules { + // Currently we only support prebuilts for macros and their tests + switch moduleBuilder.module.type { + case .macro: + // Skip if the module has a dependency that depends on a prebuilt + // This is causing a mix of release and debug modes which is causing + // macros to crash. + // TODO: Find a way to build those dependencies with the prebuilts on host + if moduleBuilder.dependencies.contains(where: { + switch $0 { + case .module(let moduleBuilder, conditions: _): + return moduleBuilder.recursiveDependenciesContains(where: { + switch $0 { + case .product(let productBuilder, conditions: _): + return prebuilts[productBuilder.packageBuilder.package.identity]?[productBuilder.product.name] != nil + case .module: + return false + } + }) + case .product(let productBuilder, conditions: _): + return productBuilder.moduleBuilders.contains(where: { + $0.recursiveDependenciesContains(where: { + switch $0 { + case .product(let productBuilder, conditions: _): + return prebuilts[productBuilder.packageBuilder.package.identity]?[productBuilder.product.name] != nil + case .module: + return false + } + }) + }) + } + }) { + // Skip + continue + } + case .test: + // Check for macro tests + if !moduleBuilder.dependencies.contains(where: { + switch $0 { + case .module(let depModuleBuilder, conditions: _): + return depModuleBuilder.module.type == .macro + case .product: + return false + } + }) { + // Skip + continue + } + default: + // Skip + continue + } + + var prebuiltLibraries: [PrebuiltLibrary] = [] + for dep in moduleBuilder.dependencies { + switch dep { + case .product(let productBuilder, conditions: _): + if let prebuilt = prebuilts[productBuilder.packageBuilder.package.identity]?[productBuilder.product.name] { + // Use the prebuilt + if !prebuiltLibraries.contains(where: { $0.libraryName == prebuilt.libraryName }) { + prebuiltLibraries.append(prebuilt) + } + } + case .module: + break + } + } + + for prebuiltLibrary in prebuiltLibraries { + moduleBuilder.module.use(prebuiltLibrary: prebuiltLibrary) + + moduleBuilder.dependencies = moduleBuilder.dependencies.filter({ + switch $0 { + case .product(let productBuilder, conditions: _): + return prebuilts[productBuilder.packageBuilder.package.identity]?[productBuilder.product.name] == nil + case .module: + return true + } + }) + } + } + } + // If a module with similar name was encountered before, we emit a diagnostic. if foundDuplicateModule { var duplicateModules = [String: [Package]]() @@ -1337,6 +1403,23 @@ private final class ResolvedModuleBuilder: ResolvedBuilder { /// The module dependencies of this module. var dependencies: [Dependency] = [] + func recursiveDependenciesContains(where check: (Dependency) -> Bool) -> Bool { + dependencies.contains(where: { + if check($0) { + return true + } else { + switch $0 { + case .module(let moduleBuilder, conditions: _): + return moduleBuilder.recursiveDependenciesContains(where: check) + case .product(let productBuilder, conditions: _): + return productBuilder.moduleBuilders.contains(where: { + $0.recursiveDependenciesContains(where: check) + }) + } + } + }) + } + /// The defaultLocalization for this package var defaultLocalization: String? = nil diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 08535c82807..a1e055508b0 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -279,48 +279,6 @@ public struct BinaryArtifact { } } -/// A structure representing a prebuilt library to be used instead of a source dependency -public struct PrebuiltLibrary { - /// The package identity. - public let identity: PackageIdentity - - /// The name of the binary target the artifact corresponds to. - public let libraryName: String - - /// The path to the extracted prebuilt artifacts - public let path: AbsolutePath - - /// The path to the checked out source - public let checkoutPath: AbsolutePath? - - /// The products in the library - public let products: [String] - - /// The include path relative to the checkouts dir - public let includePath: [RelativePath]? - - /// The C modules that need their includes directory added to the include path - public let cModules: [String] - - public init( - identity: PackageIdentity, - libraryName: String, - path: AbsolutePath, - checkoutPath: AbsolutePath?, - products: [String], - includePath: [RelativePath]? = nil, - cModules: [String] = [] - ) { - self.identity = identity - self.libraryName = libraryName - self.path = path - self.checkoutPath = checkoutPath - self.products = products - self.includePath = includePath - self.cModules = cModules - } -} - /// Helper for constructing a package following the convention system. /// /// The 'builder' here refers to the builder pattern and not any build system @@ -348,9 +306,6 @@ public final class PackageBuilder { /// Information concerning the different downloaded or local (archived) binary target artifacts. private let binaryArtifacts: [String: BinaryArtifact] - /// Prebuilts that may referenced from this package's targets - private let prebuilts: [PackageIdentity: [Product.ID: PrebuiltLibrary]] - /// Create multiple test products. /// /// If set to true, one test product will be created for each test target. @@ -407,7 +362,6 @@ public final class PackageBuilder { path: AbsolutePath, additionalFileRules: [FileRuleDescription], binaryArtifacts: [String: BinaryArtifact], - prebuilts: [PackageIdentity: [String: PrebuiltLibrary]], shouldCreateMultipleTestProducts: Bool = false, testEntryPointPath: AbsolutePath? = nil, warnAboutImplicitExecutableTargets: Bool = true, @@ -422,7 +376,6 @@ public final class PackageBuilder { self.packagePath = path self.additionalFileRules = additionalFileRules self.binaryArtifacts = binaryArtifacts - self.prebuilts = prebuilts self.shouldCreateMultipleTestProducts = shouldCreateMultipleTestProducts self.testEntryPointPath = testEntryPointPath self.createREPLProduct = createREPLProduct @@ -1407,42 +1360,6 @@ public final class PackageBuilder { table.add(assignment, for: .SWIFT_ACTIVE_COMPILATION_CONDITIONS) } - // Add in flags for prebuilts if the target is a macro or a macro test. - // Currently we only support prebuilts for macros. - if target.type == .macro || target.isMacroTest(in: manifest) { - let prebuiltLibraries: [String: PrebuiltLibrary] = target.dependencies.reduce(into: .init()) { - guard case let .product(name: name, package: package, moduleAliases: _, condition: _) = $1, - let package = package, - let prebuilt = prebuilts[.plain(package)]?[name] - else { - return - } - - $0[prebuilt.libraryName] = prebuilt - } - - for prebuilt in prebuiltLibraries.values { - let lib = prebuilt.path.appending(components: ["lib", "lib\(prebuilt.libraryName).a"]).pathString - var ldFlagsAssignment = BuildSettings.Assignment() - ldFlagsAssignment.values = [lib] - table.add(ldFlagsAssignment, for: .OTHER_LDFLAGS) - - var includeDirs: [AbsolutePath] = [prebuilt.path.appending(component: "Modules")] - if let checkoutPath = prebuilt.checkoutPath, let includePath = prebuilt.includePath { - for includeDir in includePath { - includeDirs.append(checkoutPath.appending(includeDir)) - } - } else { - for cModule in prebuilt.cModules { - includeDirs.append(prebuilt.path.appending(components: "include", cModule)) - } - } - var includeAssignment = BuildSettings.Assignment() - includeAssignment.values = includeDirs.map({ "-I\($0.pathString)" }) - table.add(includeAssignment, for: .OTHER_SWIFT_FLAGS) - } - } - return table } diff --git a/Sources/PackageModel/CMakeLists.txt b/Sources/PackageModel/CMakeLists.txt index aa8848b4155..5d2ffa9d7f2 100644 --- a/Sources/PackageModel/CMakeLists.txt +++ b/Sources/PackageModel/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(PackageModel Module/BinaryModule.swift Module/ClangModule.swift Module/PluginModule.swift + Module/PrebuiltLibrary.swift Module/SwiftModule.swift Module/SystemLibraryModule.swift Module/Module.swift diff --git a/Sources/PackageModel/Module/Module.swift b/Sources/PackageModel/Module/Module.swift index 7001c244212..39f0dce69e7 100644 --- a/Sources/PackageModel/Module/Module.swift +++ b/Sources/PackageModel/Module/Module.swift @@ -194,8 +194,44 @@ public class Module { return false } + public func use(prebuiltLibrary: PrebuiltLibrary) { + dependencies = dependencies.filter({ + switch $0 { + case .product(let product, conditions: _): + if let packageName = product.package, + prebuiltLibrary.identity == .plain(packageName) && prebuiltLibrary.products.contains(product.name) { + return false + } else { + return true + } + case .module: + return true + } + }) + + // Add build settings to use the prebuilts + let lib = prebuiltLibrary.path.appending(components: ["lib", "lib\(prebuiltLibrary.libraryName).a"]).pathString + var ldFlagsAssignment = BuildSettings.Assignment() + ldFlagsAssignment.values = [lib] + buildSettings.add(ldFlagsAssignment, for: .OTHER_LDFLAGS) + + var includeDirs: [AbsolutePath] = [prebuiltLibrary.path.appending(component: "Modules")] + if let checkoutPath = prebuiltLibrary.checkoutPath, let includePath = prebuiltLibrary.includePath { + for includeDir in includePath { + includeDirs.append(checkoutPath.appending(includeDir)) + } + } else { + for cModule in prebuiltLibrary.cModules { + includeDirs.append(prebuiltLibrary.path.appending(components: "include", cModule)) + } + } + var includeAssignment = BuildSettings.Assignment() + includeAssignment.values = includeDirs.map({ "-I\($0.pathString)" }) + buildSettings.add(includeAssignment, for: .OTHER_SWIFT_FLAGS) + } + /// The dependencies of this module. - public let dependencies: [Dependency] + public private(set) var dependencies: [Dependency] /// The language-level module name. public private(set) var c99name: String diff --git a/Sources/PackageModel/Module/PrebuiltLibrary.swift b/Sources/PackageModel/Module/PrebuiltLibrary.swift new file mode 100644 index 00000000000..efc2d3a95a0 --- /dev/null +++ b/Sources/PackageModel/Module/PrebuiltLibrary.swift @@ -0,0 +1,51 @@ +// +// PrebuiltLibrary.swift +// SwiftPM +// +// Created by Doug Schaefer on 2025-10-16. +// + +import Basics + +/// A structure representing a prebuilt library to be used instead of a source dependency +public struct PrebuiltLibrary { + /// The package identity. + public let identity: PackageIdentity + + /// The name of the binary target the artifact corresponds to. + public let libraryName: String + + /// The path to the extracted prebuilt artifacts + public let path: AbsolutePath + + /// The path to the checked out source + public let checkoutPath: AbsolutePath? + + /// The products in the library + public let products: [String] + + /// The include path relative to the checkouts dir + public let includePath: [RelativePath]? + + /// The C modules that need their includes directory added to the include path + public let cModules: [String] + + public init( + identity: PackageIdentity, + libraryName: String, + path: AbsolutePath, + checkoutPath: AbsolutePath?, + products: [String], + includePath: [RelativePath]? = nil, + cModules: [String] = [] + ) { + self.identity = identity + self.libraryName = libraryName + self.path = path + self.checkoutPath = checkoutPath + self.products = products + self.includePath = includePath + self.cModules = cModules + } +} + diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index b5243e1a170..0a1595a3fde 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -1248,7 +1248,6 @@ extension Workspace { path: path, additionalFileRules: [], binaryArtifacts: binaryArtifacts, - prebuilts: [:], fileSystem: self.fileSystem, observabilityScope: observabilityScope, enabledTraits: try manifest.enabledTraits(using: .default) @@ -1313,7 +1312,6 @@ extension Workspace { path: previousPackage.path, additionalFileRules: self.configuration.additionalFileRules, binaryArtifacts: packageGraph.binaryArtifacts[identity] ?? [:], - prebuilts: [:], shouldCreateMultipleTestProducts: self.configuration.shouldCreateMultipleTestProducts, createREPLProduct: self.configuration.createREPLProduct, fileSystem: self.fileSystem, diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index c0f61204e73..a1c735bc423 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -4617,16 +4617,48 @@ class BuildPlanTestCase: BuildSystemProviderTestCase { emptyFiles: [ "/MyPackage/Sources/MyMacroMacros/MyMacroMacros.swift", "/MyPackage/Sources/MyMacros/MyMacros.swift", - "/MyPackage/Sources/MyMacroTests/MyMacroTests.swift" + "/MyPackage/Sources/MyMacroTests/MyMacroTests.swift", + "/swift-syntax/Sources/SwiftSyntaxMacrosTestSupport/SwiftSyntaxMacrosTestSupport.swift", + "/swift-syntax/Sources/SwiftSyntaxMacros/SwiftSyntaxMacros.swift", + "/swift-syntax/Sources/SwiftCompilerPlugin/SwiftCompilerPlugin.swift", ] ) let graph = try loadModulesGraph( fileSystem: fs, manifests: [ + Manifest.createRemoteSourceControlManifest( + displayName: "swift-syntax", + url: "https://github.com/swiftlang/swift-syntax", + path: "/swift-syntax", + products: [ + .init( + name: "SwiftSyntaxMacrosTestSupport", + type: .library(.automatic), + targets: ["SwiftSyntaxMacrosTestSupport"] + ), + .init( + name: "SwiftSyntaxMacros", + type: .library(.automatic), + targets: ["SwiftSyntaxMacros"] + ), + .init( + name: "SwiftCompilerPlugin", + type: .library(.automatic), + targets: ["SwiftCompilerPlugin"]) + ], + targets: [ + .init(name: "SwiftSyntaxMacrosTestSupport"), + .init(name: "SwiftSyntaxMacros"), + .init(name: "SwiftCompilerPlugin"), + ] + ), Manifest.createRootManifest( displayName: "MyPackage", path: "/MyPackage", + dependencies: [ + .remoteSourceControl(url: "https://github.com/swiftlang/swift-syntax", requirement: .exact("600.0.1")), + ], targets: [ TargetDescription( name: "MyMacroMacros", @@ -4723,27 +4755,60 @@ class BuildPlanTestCase: BuildSystemProviderTestCase { "/MyPackage/Sources/MyMacroLibrary/MyMacroLibrary.swift", "/MyPackage/Sources/MyMacroMacros/MyMacroMacros.swift", "/MyPackage/Sources/MyMacros/MyMacros.swift", - "/MyPackage/Sources/MyMacroTests/MyMacroTests.swift" + "/MyPackage/Sources/MyMacroTests/MyMacroTests.swift", + "/swift-syntax/Sources/SwiftSyntaxMacrosTestSupport/SwiftSyntaxMacrosTestSupport.swift", + "/swift-syntax/Sources/SwiftSyntaxMacros/SwiftSyntaxMacros.swift", + "/swift-syntax/Sources/SwiftCompilerPlugin/SwiftCompilerPlugin.swift", + "/swift-syntax/Sources/SwiftSyntax/SwiftSyntax.swift" ] ) let graph = try loadModulesGraph( fileSystem: fs, manifests: [ + Manifest.createRemoteSourceControlManifest( + displayName: "swift-syntax", + url: "https://github.com/swiftlang/swift-syntax", + path: "/swift-syntax", + products: [ + .init( + name: "SwiftSyntaxMacrosTestSupport", + type: .library(.automatic), + targets: ["SwiftSyntaxMacrosTestSupport"] + ), + .init( + name: "SwiftSyntaxMacros", + type: .library(.automatic), + targets: ["SwiftSyntaxMacros"] + ), + .init( + name: "SwiftCompilerPlugin", + type: .library(.automatic), + targets: ["SwiftCompilerPlugin"] + ), + .init( + name: "SwiftSyntax", + type: .library(.automatic), + targets: ["SwiftSyntax"] + ), + ], + targets: [ + .init(name: "SwiftSyntaxMacrosTestSupport"), + .init(name: "SwiftSyntaxMacros"), + .init(name: "SwiftCompilerPlugin"), + .init(name: "SwiftSyntax"), + ] + ), Manifest.createRootManifest( displayName: "MyPackage", path: "/MyPackage", + dependencies: [ + .remoteSourceControl(url: "https://github.com/swiftlang/swift-syntax", requirement: .exact("600.0.1")), + ], targets: [ - TargetDescription( - name: "MyMacroLibrary", - dependencies: [ - .product(name: "SwiftSyntax", package: "swift-syntax"), - ] - ), TargetDescription( name: "MyMacroMacros", dependencies: [ - "MyMacroLibrary", .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), ], diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index c7c685ecab9..8e866e063f3 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -3587,7 +3587,6 @@ final class PackageBuilderTester { _ manifest: Manifest, path: AbsolutePath = .root, binaryArtifacts: [String: BinaryArtifact] = [:], - prebuilts: [PackageIdentity: [String: PrebuiltLibrary]] = [:], shouldCreateMultipleTestProducts: Bool = false, createREPLProduct: Bool = false, supportXCBuildTypes: Bool = false, @@ -3606,7 +3605,6 @@ final class PackageBuilderTester { path: path, additionalFileRules: supportXCBuildTypes ? FileRuleDescription.xcbuildFileTypes : FileRuleDescription.swiftpmFileTypes, binaryArtifacts: binaryArtifacts, - prebuilts: prebuilts, shouldCreateMultipleTestProducts: shouldCreateMultipleTestProducts, warnAboutImplicitExecutableTargets: true, createREPLProduct: createREPLProduct, diff --git a/Tests/WorkspaceTests/PrebuiltsTests.swift b/Tests/WorkspaceTests/PrebuiltsTests.swift index 759e94c3721..19c1c6f6a6f 100644 --- a/Tests/WorkspaceTests/PrebuiltsTests.swift +++ b/Tests/WorkspaceTests/PrebuiltsTests.swift @@ -32,6 +32,7 @@ final class PrebuiltsTests: XCTestCase { artifact: Data, swiftSyntaxVersion: String, swiftSyntaxURL: String? = nil, + additionalTargets: [MockTarget] = [], run: (Workspace.SignedPrebuiltsManifest, AbsolutePath, MockPackage, MockPackage) async throws -> () ) async throws { try await fixtureXCTest(name: "Signing") { fixturePath in @@ -121,7 +122,7 @@ final class PrebuiltsTests: XCTestCase { ], type: .test ), - ], + ] + additionalTargets, dependencies: [ .sourceControl( url: swiftSyntaxURL, @@ -963,6 +964,92 @@ final class PrebuiltsTests: XCTestCase { } } } + + func testPrebuiltsInDeps() async throws { + // This is the case where macros that depend on libraries that also depend on swift-syntax. + // Currently we disable the use of prebuilts in the macros since the libraries are + // not able to take advantage of the prebuilts at this time and macros crash when they mix. + let sandbox = AbsolutePath("/tmp/ws") + let fs = InMemoryFileSystem() + let artifact = Data() + + let extraTargets: [MockTarget] = [ + try MockTarget( + name: "ExtraLibrary", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + ], + type: .regular + ), + try MockTarget( + name: "ExtraMacros", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + .target(name: "ExtraLibrary") + ], + type: .macro + ), + ] + + try await with(fileSystem: fs, artifact: artifact, swiftSyntaxVersion: "600.0.1", additionalTargets: extraTargets) + { manifest, rootCertPath, rootPackage, swiftSyntax in + let manifestData = try JSONEncoder().encode(manifest) + + let httpClient = HTTPClient { request, progressHandler in + guard case .download(let fileSystem, let destination) = request.kind else { + throw StringError("invalid request \(request.kind)") + } + + if request.url == "https://download.swift.org/prebuilts/swift-syntax/600.0.1/\(self.swiftVersion)-manifest.json" { + try fileSystem.writeFileContents(destination, data: manifestData) + return .okay() + } else if request.url == "https://download.swift.org/prebuilts/swift-syntax/600.0.1/\(self.swiftVersion)-MacroSupport-macos_aarch64.zip" { + try fileSystem.writeFileContents(destination, data: artifact) + return .okay() + } else { + XCTFail("Unexpected URL \(request.url)") + return .notFound() + } + } + + let archiver = MockArchiver(handler: { _, archivePath, destination, completion in + XCTAssertEqual(archivePath, sandbox.appending(components: ".build", "prebuilts", "swift-syntax", "600.0.1", "\(self.swiftVersion)-MacroSupport-macos_aarch64.zip")) + XCTAssertEqual(destination, sandbox.appending(components: ".build", "prebuilts", "swift-syntax", "600.0.1", "\(self.swiftVersion)-MacroSupport-macos_aarch64")) + completion(.success(())) + }) + + let workspace = try await MockWorkspace( + sandbox: sandbox, + fileSystem: fs, + roots: [ + rootPackage + ], + packages: [ + swiftSyntax + ], + prebuiltsManager: .init( + swiftVersion: swiftVersion, + httpClient: httpClient, + archiver: archiver, + hostPlatform: .macos_aarch64, + rootCertPath: rootCertPath + ), + ) + + try await workspace.checkPackageGraph(roots: ["Foo"]) { modulesGraph, diagnostics in + XCTAssertTrue(diagnostics.filter({ $0.severity == .error || $0.severity == .warning }).isEmpty) + let rootPackage = try XCTUnwrap(modulesGraph.rootPackages.first) + try checkSettings(rootPackage, "FooMacros", usePrebuilt: true) + try checkSettings(rootPackage, "FooTests", usePrebuilt: true) + try checkSettings(rootPackage, "Foo", usePrebuilt: false) + try checkSettings(rootPackage, "FooClient", usePrebuilt: false) + try checkSettings(rootPackage, "ExtraLibrary", usePrebuilt: false) + try checkSettings(rootPackage, "ExtraMacros", usePrebuilt: false) + } + } + + } } extension String {