From 6a9e6f887cd1f06ee311ae9efc518be57f3ede28 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Jul 2025 00:24:31 -0700 Subject: [PATCH] [BuildPlan] Fix `traverseModules` to account for tests with direct macro dependencies This change aligns `traverseModules` with `computeDestinations` in regard to test targets that have direct macro dependencies: if at least one test target directly depends on a macro, all of the tests are built for the "host". Resolves: https://github.com/swiftlang/sourcekit-lsp/issues/2205 (cherry picked from commit a3e78e2e82f6585dc820d3541785240a4c7be73c) --- Sources/Build/BuildPlan/BuildPlan.swift | 41 +++++++++++++++---- .../BuildTests/BuildPlanTraversalTests.swift | 37 +++++++++++++++++ 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index 9ef27112a17..ebf692deea1 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -1120,13 +1120,40 @@ extension BuildPlan { guard visited.insert(.package(package)).inserted else { return [] } - return package.modules.compactMap { - if case .test = $0.underlying.type, - !self.graph.rootPackages.contains(id: package.id) + + var successors: [TraversalNode] = [] + for product in package.products { + if case .test = product.underlying.type, + !graph.rootPackages.contains(id: package.id) { - return nil + continue } - return .init(module: $0, context: .target) + + successors.append(.init(product: product, context: .target)) + } + + for module in package.modules { + // Tests are discovered through an aggregate product which also + // informs their destination. + if case .test = module.type { + continue + } + successors.append(.init(module: module, context: .target)) + } + + return successors + } + + func successors( + for product: ResolvedProduct, + destination: Destination + ) -> [TraversalNode] { + guard destination == .host || product.underlying.type == .test else { + return [] + } + + return product.modules.map { module in + TraversalNode(module: module, context: destination) } } @@ -1156,8 +1183,8 @@ extension BuildPlan { successors(for: package) case .module(let module, let destination): successors(for: module, destination: destination) - case .product: - [] + case .product(let product, let destination): + successors(for: product, destination: destination) } } onNext: { current, parent in let parentModule: (ResolvedModule, BuildParameters.Destination)? = switch parent { diff --git a/Tests/BuildTests/BuildPlanTraversalTests.swift b/Tests/BuildTests/BuildPlanTraversalTests.swift index a4b2720ce83..53ba2fc4d5b 100644 --- a/Tests/BuildTests/BuildPlanTraversalTests.swift +++ b/Tests/BuildTests/BuildPlanTraversalTests.swift @@ -123,6 +123,43 @@ struct BuildPlanTraversalTests { #expect(self.getParents(in: results, for: "HAL", destination: .host) == []) } + @Test + func traversalWithTestThatDependsOnMacro() async throws { + let destinationTriple = Triple.arm64Linux + let toolsTriple = Triple.x86_64MacOS + + let (graph, fs, scope) = try macrosTestsPackageGraph() + let plan = try await BuildPlan( + destinationBuildParameters: mockBuildParameters( + destination: .target, + triple: destinationTriple + ), + toolsBuildParameters: mockBuildParameters( + destination: .host, + triple: toolsTriple + ), + graph: graph, + fileSystem: fs, + observabilityScope: scope + ) + + // Tests that if one of the test targets directly depends + // on a macro - all tests are built for the "host". + var results: [Result] = [] + plan.traverseModules { + results.append(Result(parent: $1, module: $0)) + } + + let package = try #require(graph.package(for: "swift-mmio")) + + // Tests that if one of the test targets directly depends + // on a macro - all tests are built for the "host". + for module in package.modules where module.type == .test { + let results = getResults(for: module.name, in: results) + #expect(results.allSatisfy { $0.module.1 == .host }) + } + } + @Test func recursiveDependencyTraversal() async throws { let destinationTriple = Triple.arm64Linux