Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a SourceKit-LSP API to get the output paths of a clang target #8317

Merged
merged 1 commit into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Sources/SourceKitLSPAPI/BuildDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public enum BuildDestination {
case target
}

public enum BuildTargetCompiler {
case swift
case clang
}

public protocol BuildTarget {
/// Source files in the target
var sources: [URL] { get }
Expand All @@ -46,13 +51,18 @@ public protocol BuildTarget {
/// The name of the target. It should be possible to build a target by passing this name to `swift build --target`
var name: String { get }

/// The compiler that is responsible for building this target.
var compiler: BuildTargetCompiler { get }

var destination: BuildDestination { get }

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

var isTestTarget: Bool { get }

var outputPaths: [URL] { get throws }

func compileArguments(for fileURL: URL) throws -> [String]
}

Expand Down Expand Up @@ -100,10 +110,19 @@ private struct WrappedClangTargetBuildDescription: BuildTarget {
return description.clangTarget.name
}

var compiler: BuildTargetCompiler { .clang }


public var destination: BuildDestination {
return description.destination == .host ? .host : .target
}

var outputPaths: [URL] {
get throws {
return try description.compilePaths().map(\.object.asURL)
}
}

public func compileArguments(for fileURL: URL) throws -> [String] {
let filePath = try resolveSymlinks(try Basics.AbsolutePath(validating: fileURL.path))
let commandLine = try description.emitCommandLine(for: filePath)
Expand All @@ -127,6 +146,8 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget {
return description.target.name
}

var compiler: BuildTargetCompiler { .swift }

public var destination: BuildDestination {
return description.destination == .host ? .host : .target
}
Expand Down Expand Up @@ -155,6 +176,15 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget {
return others.map(\.asURL)
}

var outputPaths: [URL] {
get throws {
struct NotSupportedError: Error, CustomStringConvertible {
var description: String { "Getting output paths for a Swift target is not supported" }
}
throw NotSupportedError()
}
}

func compileArguments(for fileURL: URL) throws -> [String] {
// Note: we ignore the `fileURL` here as the expectation is that we get a command line for the entire target
// in case of Swift.
Expand Down
11 changes: 11 additions & 0 deletions Sources/SourceKitLSPAPI/PluginTargetBuildDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,22 @@ struct PluginTargetBuildDescription: BuildTarget {
return target.name
}

var compiler: BuildTargetCompiler { .swift }

var destination: BuildDestination {
// Plugins are always built for the host.
.host
}

var outputPaths: [URL] {
get throws {
struct NotSupportedError: Error, CustomStringConvertible {
var description: String { "Getting output paths for a plugin target is not supported" }
}
throw NotSupportedError()
}
}

func compileArguments(for fileURL: URL) throws -> [String] {
// FIXME: This is very odd and we should clean this up by merging `ManifestLoader` and `DefaultPluginScriptRunner` again.
var args = ManifestLoader.interpreterFlags(for: self.toolsVersion, toolchain: toolchain)
Expand Down
45 changes: 45 additions & 0 deletions Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,51 @@ final class SourceKitLSPAPITests: XCTestCase {
)
}
}

func testClangOutputPaths() async throws {
let fs = InMemoryFileSystem(emptyFiles:
"/Pkg/Sources/lib/include/lib.h",
"/Pkg/Sources/lib/lib.cpp",
)

let observability = ObservabilitySystem.makeForTesting()
let graph = try loadModulesGraph(
fileSystem: fs,
manifests: [
Manifest.createRootManifest(
displayName: "Pkg",
path: "/Pkg",
toolsVersion: .v5_10,
targets: [
TargetDescription(
name: "lib",
dependencies: []
)
]),
],
observabilityScope: observability.topScope
)
XCTAssertNoDiagnostics(observability.diagnostics)
let plan = try await BuildPlan(
destinationBuildParameters: mockBuildParameters(
destination: .target,
shouldLinkStaticSwiftStdlib: true
),
toolsBuildParameters: mockBuildParameters(
destination: .host,
shouldLinkStaticSwiftStdlib: true
),
graph: graph,
fileSystem: fs,
observabilityScope: observability.topScope
)
let description = BuildDescription(buildPlan: plan)

let target = try XCTUnwrap(description.getBuildTarget(for: XCTUnwrap(graph.module(for: "lib")), destination: .target))
XCTAssertEqual(target.compiler, .clang)
XCTAssertEqual(try target.outputPaths.count, 1)
XCTAssertEqual(try target.outputPaths.last?.lastPathComponent, "lib.cpp.o")
}
}

extension SourceKitLSPAPI.BuildDescription {
Expand Down