Skip to content

Commit 08d26bc

Browse files
authored
Add a SourceKit-LSP API to get the output paths of a clang target (#8317)
To facilitate swiftlang/sourcekit-lsp#2017.
1 parent 2d03ce7 commit 08d26bc

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

Sources/SourceKitLSPAPI/BuildDescription.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public enum BuildDestination {
2727
case target
2828
}
2929

30+
public enum BuildTargetCompiler {
31+
case swift
32+
case clang
33+
}
34+
3035
public protocol BuildTarget {
3136
/// Source files in the target
3237
var sources: [URL] { get }
@@ -46,13 +51,18 @@ public protocol BuildTarget {
4651
/// The name of the target. It should be possible to build a target by passing this name to `swift build --target`
4752
var name: String { get }
4853

54+
/// The compiler that is responsible for building this target.
55+
var compiler: BuildTargetCompiler { get }
56+
4957
var destination: BuildDestination { get }
5058

5159
/// Whether the target is part of the root package that the user opened or if it's part of a package dependency.
5260
var isPartOfRootPackage: Bool { get }
5361

5462
var isTestTarget: Bool { get }
5563

64+
var outputPaths: [URL] { get throws }
65+
5666
func compileArguments(for fileURL: URL) throws -> [String]
5767
}
5868

@@ -100,10 +110,19 @@ private struct WrappedClangTargetBuildDescription: BuildTarget {
100110
return description.clangTarget.name
101111
}
102112

113+
var compiler: BuildTargetCompiler { .clang }
114+
115+
103116
public var destination: BuildDestination {
104117
return description.destination == .host ? .host : .target
105118
}
106119

120+
var outputPaths: [URL] {
121+
get throws {
122+
return try description.compilePaths().map(\.object.asURL)
123+
}
124+
}
125+
107126
public func compileArguments(for fileURL: URL) throws -> [String] {
108127
let filePath = try resolveSymlinks(try Basics.AbsolutePath(validating: fileURL.path))
109128
let commandLine = try description.emitCommandLine(for: filePath)
@@ -127,6 +146,8 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget {
127146
return description.target.name
128147
}
129148

149+
var compiler: BuildTargetCompiler { .swift }
150+
130151
public var destination: BuildDestination {
131152
return description.destination == .host ? .host : .target
132153
}
@@ -155,6 +176,15 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget {
155176
return others.map(\.asURL)
156177
}
157178

179+
var outputPaths: [URL] {
180+
get throws {
181+
struct NotSupportedError: Error, CustomStringConvertible {
182+
var description: String { "Getting output paths for a Swift target is not supported" }
183+
}
184+
throw NotSupportedError()
185+
}
186+
}
187+
158188
func compileArguments(for fileURL: URL) throws -> [String] {
159189
// Note: we ignore the `fileURL` here as the expectation is that we get a command line for the entire target
160190
// in case of Swift.

Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,22 @@ struct PluginTargetBuildDescription: BuildTarget {
5454
return target.name
5555
}
5656

57+
var compiler: BuildTargetCompiler { .swift }
58+
5759
var destination: BuildDestination {
5860
// Plugins are always built for the host.
5961
.host
6062
}
6163

64+
var outputPaths: [URL] {
65+
get throws {
66+
struct NotSupportedError: Error, CustomStringConvertible {
67+
var description: String { "Getting output paths for a plugin target is not supported" }
68+
}
69+
throw NotSupportedError()
70+
}
71+
}
72+
6273
func compileArguments(for fileURL: URL) throws -> [String] {
6374
// FIXME: This is very odd and we should clean this up by merging `ManifestLoader` and `DefaultPluginScriptRunner` again.
6475
var args = ManifestLoader.interpreterFlags(for: self.toolsVersion, toolchain: toolchain)

Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,51 @@ final class SourceKitLSPAPITests: XCTestCase {
319319
)
320320
}
321321
}
322+
323+
func testClangOutputPaths() async throws {
324+
let fs = InMemoryFileSystem(emptyFiles:
325+
"/Pkg/Sources/lib/include/lib.h",
326+
"/Pkg/Sources/lib/lib.cpp",
327+
)
328+
329+
let observability = ObservabilitySystem.makeForTesting()
330+
let graph = try loadModulesGraph(
331+
fileSystem: fs,
332+
manifests: [
333+
Manifest.createRootManifest(
334+
displayName: "Pkg",
335+
path: "/Pkg",
336+
toolsVersion: .v5_10,
337+
targets: [
338+
TargetDescription(
339+
name: "lib",
340+
dependencies: []
341+
)
342+
]),
343+
],
344+
observabilityScope: observability.topScope
345+
)
346+
XCTAssertNoDiagnostics(observability.diagnostics)
347+
let plan = try await BuildPlan(
348+
destinationBuildParameters: mockBuildParameters(
349+
destination: .target,
350+
shouldLinkStaticSwiftStdlib: true
351+
),
352+
toolsBuildParameters: mockBuildParameters(
353+
destination: .host,
354+
shouldLinkStaticSwiftStdlib: true
355+
),
356+
graph: graph,
357+
fileSystem: fs,
358+
observabilityScope: observability.topScope
359+
)
360+
let description = BuildDescription(buildPlan: plan)
361+
362+
let target = try XCTUnwrap(description.getBuildTarget(for: XCTUnwrap(graph.module(for: "lib")), destination: .target))
363+
XCTAssertEqual(target.compiler, .clang)
364+
XCTAssertEqual(try target.outputPaths.count, 1)
365+
XCTAssertEqual(try target.outputPaths.last?.lastPathComponent, "lib.cpp.o")
366+
}
322367
}
323368

324369
extension SourceKitLSPAPI.BuildDescription {

0 commit comments

Comments
 (0)