diff --git a/Sources/BuildServerProtocol/BuildTargets.swift b/Sources/BuildServerProtocol/BuildTargets.swift index f7875442..89f268e4 100644 --- a/Sources/BuildServerProtocol/BuildTargets.swift +++ b/Sources/BuildServerProtocol/BuildTargets.swift @@ -198,10 +198,10 @@ public struct BuildTargetOutputPaths: RequestType, Hashable { } public struct BuildTargetOutputPathsResponse: ResponseType, Hashable { - public var items: [BuildTargetOutputItems] + public var items: [OutputsItem] } -public struct BuildTargetOutputItems: Codable, Hashable { +public struct OutputsItem: Codable, Hashable { public var target: BuildTargetIdentifier /// The output paths for sources that belong to this build target. diff --git a/Sources/SKCore/BuildServerBuildSystem.swift b/Sources/SKCore/BuildServerBuildSystem.swift index 0742ba3f..0cd14772 100644 --- a/Sources/SKCore/BuildServerBuildSystem.swift +++ b/Sources/SKCore/BuildServerBuildSystem.swift @@ -194,6 +194,17 @@ extension BuildServerBuildSystem: BuildSystem { } } } + + public func buildTargetOutputPaths(targets: [BuildTargetIdentifier], reply: @escaping ([OutputsItem]?) -> Void) { + let req = BuildTargetOutputPaths(targets: targets) + _ = self.buildServer?.send(req, queue: requestQueue) { result in + if let items = result.success?.items { reply(items) } + else { + log("error fetching build target outputs: \(result)") + reply(nil) + } + } + } } private func loadBuildServerConfig(path: AbsolutePath, fileSystem: FileSystem) throws -> BuildServerConfig { diff --git a/Sources/SKCore/BuildSystem.swift b/Sources/SKCore/BuildSystem.swift index bc6f5b49..fe50fe23 100644 --- a/Sources/SKCore/BuildSystem.swift +++ b/Sources/SKCore/BuildSystem.swift @@ -56,6 +56,9 @@ public protocol BuildSystem: AnyObject { /// Returns the sources for the requested build targets func buildTargetSources(targets: [BuildTargetIdentifier], reply: @escaping ([SourcesItem]?) -> Void) + + /// Returns the output paths for the requested build targets + func buildTargetOutputPaths(targets: [BuildTargetIdentifier], reply: @escaping ([OutputsItem]?) -> Void) } public let buildTargetsNotSupported = diff --git a/Sources/SKCore/BuildSystemList.swift b/Sources/SKCore/BuildSystemList.swift index fe006038..6b1805ff 100644 --- a/Sources/SKCore/BuildSystemList.swift +++ b/Sources/SKCore/BuildSystemList.swift @@ -70,4 +70,8 @@ extension BuildSystemList: BuildSystem { public func buildTargetSources(targets: [BuildTargetIdentifier], reply: @escaping ([SourcesItem]?) -> Void) { providers.first?.buildTargetSources(targets: targets, reply: reply) } + + public func buildTargetOutputPaths(targets: [BuildTargetIdentifier], reply: @escaping ([OutputsItem]?) -> Void) { + providers.first?.buildTargetOutputPaths(targets: targets, reply: reply) + } } diff --git a/Sources/SKCore/CompilationDatabaseBuildSystem.swift b/Sources/SKCore/CompilationDatabaseBuildSystem.swift index f1635206..65197c77 100644 --- a/Sources/SKCore/CompilationDatabaseBuildSystem.swift +++ b/Sources/SKCore/CompilationDatabaseBuildSystem.swift @@ -68,6 +68,10 @@ extension CompilationDatabaseBuildSystem: BuildSystem { reply(nil) } + public func buildTargetOutputPaths(targets: [BuildTargetIdentifier], reply: @escaping ([OutputsItem]?) -> Void) { + reply(nil) + } + func database(for url: URL) -> CompilationDatabase? { if let path = try? AbsolutePath(validating: url.path) { return database(for: path) diff --git a/Sources/SKCore/FallbackBuildSystem.swift b/Sources/SKCore/FallbackBuildSystem.swift index 408827de..19519718 100644 --- a/Sources/SKCore/FallbackBuildSystem.swift +++ b/Sources/SKCore/FallbackBuildSystem.swift @@ -70,6 +70,10 @@ public final class FallbackBuildSystem: BuildSystem { reply(nil) } + public func buildTargetOutputPaths(targets: [BuildTargetIdentifier], reply: @escaping ([OutputsItem]?) -> Void) { + reply(nil) + } + func settingsSwift(_ path: AbsolutePath) -> FileBuildSettings { var args: [String] = [] if let sdkpath = sdkpath { diff --git a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift index 1aa21a5e..07d27bb8 100644 --- a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift +++ b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift @@ -243,6 +243,10 @@ extension SwiftPMWorkspace: BuildSystem { reply(nil) } + public func buildTargetOutputPaths(targets: [BuildTargetIdentifier], reply: @escaping ([OutputsItem]?) -> Void) { + reply(nil) + } + /// Returns the resolved target description for the given file, if one is known. func targetDescription(for file: AbsolutePath) -> TargetBuildDescription? { if let td = fileToTarget[file] { diff --git a/Tests/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/buildServer.json b/Tests/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/buildServer.json new file mode 100644 index 00000000..cf9e70bd --- /dev/null +++ b/Tests/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/buildServer.json @@ -0,0 +1,7 @@ +{ + "name": "client name", + "version": "10", + "bspVersion": "2.0", + "languages": ["a", "b"], + "argv": ["server.py"] +} \ No newline at end of file diff --git a/Tests/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/server.py b/Tests/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/server.py new file mode 100755 index 00000000..6f551eed --- /dev/null +++ b/Tests/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/server.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +import json +import sys + + +while True: + line = sys.stdin.readline() + if len(line) == 0: + break + + assert line.startswith('Content-Length:') + length = int(line[len('Content-Length:'):]) + sys.stdin.readline() + message = json.loads(sys.stdin.read(length)) + + response = None + if message["method"] == "build/initialize": + response = { + "jsonrpc": "2.0", + "id": message["id"], + "result": { + "displayName": "test server", + "version": "0.1", + "bspVersion": "2.0", + "rootUri": "blah", + "capabilities": {"languageIds": ["a", "b"]}, + "data": { + "indexStorePath": "some/index/store/path" + } + } + } + elif message["method"] == "build/initialized": + continue + elif message["method"] == "build/shutdown": + response = { + "jsonrpc": "2.0", + "id": message["id"], + "result": None + } + elif message["method"] == "build/exit": + break + elif message["method"] == "buildTarget/outputPaths": + response = { + "jsonrpc": "2.0", + "id": message["id"], + "result": { + "items": [ + { + "target": {"uri": "build://target/a"}, + "outputPaths": [ + "file:///path/to/a/file", + "file:///path/to/a/file2" + ] + } + ] + } + } + # ignore other notifications + elif "id" in message: + response = { + "jsonrpc": "2.0", + "id": message["id"], + "error": { + "code": 123, + "message": "unhandled method {}".format(message["method"]), + } + } + + if response: + responseStr = json.dumps(response) + sys.stdout.write("Content-Length: {}\r\n\r\n{}".format(len(responseStr), responseStr)) + sys.stdout.flush() diff --git a/Tests/SKCoreTests/BuildServerBuildSystemTests.swift b/Tests/SKCoreTests/BuildServerBuildSystemTests.swift index a7e35407..d4021a5d 100644 --- a/Tests/SKCoreTests/BuildServerBuildSystemTests.swift +++ b/Tests/SKCoreTests/BuildServerBuildSystemTests.swift @@ -131,6 +131,30 @@ final class BuildServerBuildSystemTests: XCTestCase { }) XCTAssertEqual(XCTWaiter.wait(for: [expectation], timeout: 1), .completed) } + + func testBuildTargetOutputs() { + let root = AbsolutePath( + inputsDirectory().appendingPathComponent(testDirectoryName, isDirectory: true).path) + let buildFolder = AbsolutePath(NSTemporaryDirectory()) + let buildSystem = try? BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder) + XCTAssertNotNil(buildSystem) + + let expectation = XCTestExpectation(description: "build target output expectation") + let targets = [ + BuildTargetIdentifier(uri: URL(string: "build://target/a")!), + ] + buildSystem?.buildTargetOutputPaths(targets: targets, reply: { items in + XCTAssertNotNil(items) + XCTAssertEqual(items?[0].target.uri, targets[0].uri) + XCTAssertEqual(items?[0].outputPaths, [ + URL(fileURLWithPath: "/path/to/a/file"), + URL(fileURLWithPath: "/path/to/a/file2"), + ]) + + expectation.fulfill() + }) + XCTAssertEqual(XCTWaiter.wait(for: [expectation], timeout: 1), .completed) + } } final class TestDelegate: BuildSystemDelegate { diff --git a/Tests/SKCoreTests/XCTestManifests.swift b/Tests/SKCoreTests/XCTestManifests.swift index 7f4a27ee..e321419c 100644 --- a/Tests/SKCoreTests/XCTestManifests.swift +++ b/Tests/SKCoreTests/XCTestManifests.swift @@ -6,6 +6,7 @@ extension BuildServerBuildSystemTests { // `swift test --generate-linuxmain` // to regenerate. static let __allTests__BuildServerBuildSystemTests = [ + ("testBuildTargetOutputs", testBuildTargetOutputs), ("testBuildTargets", testBuildTargets), ("testBuildTargetSources", testBuildTargetSources), ("testFileRegistration", testFileRegistration), diff --git a/Tests/SourceKitTests/BuildSystemTests.swift b/Tests/SourceKitTests/BuildSystemTests.swift index 3a5285ea..6cb79340 100644 --- a/Tests/SourceKitTests/BuildSystemTests.swift +++ b/Tests/SourceKitTests/BuildSystemTests.swift @@ -60,7 +60,11 @@ final class TestBuildSystem: BuildSystem { } func buildTargetSources(targets: [BuildTargetIdentifier], reply: @escaping ([SourcesItem]?) -> Void) { - + reply(nil) + } + + func buildTargetOutputPaths(targets: [BuildTargetIdentifier], reply: @escaping ([OutputsItem]?) -> Void) { + reply(nil) } }