From 79105208d53c727e109e5cc556ca533c384ee4a2 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sun, 17 Jul 2022 20:18:46 -0700 Subject: [PATCH] SKCore,SKTestSupport: adjust test server handling The test system depended on the shebang to locate the python interpreter. However, this is not a portable system. Instead, prefer to explicitly search for the interpreter prior to the execution. This enables supporting execution of the script support on all platforms. A secondary change of the printed string is required for Windows. Python will replace `\n` with `\r\n` resulting in `\r\n` being emitted as `\r\r\n` on Windows breaking the expectations on the receiver. Adjust this by explicitly writing out a binary string to the raw underlying buffer to avoid the translation. --- Sources/SKCore/BuildServerBuildSystem.swift | 41 ++++++++++++++++--- .../server.py | 4 +- .../server.py | 4 +- .../server.py | 4 +- .../server.py | 4 +- .../server.py | 4 +- .../server.py | 4 +- .../server.py | 4 +- 8 files changed, 43 insertions(+), 26 deletions(-) diff --git a/Sources/SKCore/BuildServerBuildSystem.swift b/Sources/SKCore/BuildServerBuildSystem.swift index 328d3d64..c24c3bfc 100644 --- a/Sources/SKCore/BuildServerBuildSystem.swift +++ b/Sources/SKCore/BuildServerBuildSystem.swift @@ -18,6 +18,20 @@ import LSPLogging import SKSupport import TSCBasic +enum BuildServerTestError: Error { + case executableNotFound(String) +} + +func executable(_ name: String) -> String { +#if os(Windows) + guard !name.hasSuffix(".exe") else { return name } + return "\(name).exe" +#else + return name +#endif +} + + /// A `BuildSystem` based on communicating with a build server /// /// Provides build settings from a build server launched based on a @@ -32,6 +46,8 @@ public final class BuildServerBuildSystem { var handler: BuildServerHandler? var buildServer: JSONRPCConnection? + let searchPaths: [AbsolutePath] + public private(set) var indexDatabasePath: AbsolutePath? public private(set) var indexStorePath: AbsolutePath? @@ -47,6 +63,15 @@ public final class BuildServerBuildSystem { public init(projectRoot: AbsolutePath, buildFolder: AbsolutePath?, fileSystem: FileSystem = localFileSystem) throws { let configPath = projectRoot.appending(component: "buildServer.json") let config = try loadBuildServerConfig(path: configPath, fileSystem: fileSystem) +#if os(Windows) + self.searchPaths = + getEnvSearchPaths(pathString: ProcessInfo.processInfo.environment["Path"], + currentWorkingDirectory: fileSystem.currentWorkingDirectory) +#else + self.searchPaths = + getEnvSearchPaths(pathString: ProcessInfo.processInfo.environment["PATH"], + currentWorkingDirectory: fileSystem.currentWorkingDirectory) +#endif self.buildFolder = buildFolder self.projectRoot = projectRoot self.requestQueue = DispatchQueue(label: "build_server_request_queue") @@ -85,8 +110,14 @@ public final class BuildServerBuildSystem { } private func initializeBuildServer() throws { - let serverPath = AbsolutePath(serverConfig.argv[0], relativeTo: projectRoot) - let flags = Array(serverConfig.argv[1...]) + guard let interpreter = + lookupExecutablePath(filename: executable("python3"), + searchPaths: searchPaths) ?? + lookupExecutablePath(filename: executable("python"), + searchPaths: searchPaths) else { + throw BuildServerTestError.executableNotFound("python3") + } + let flags = [AbsolutePath(serverConfig.argv[0], relativeTo: projectRoot).pathString] + Array(serverConfig.argv[1...]) let languages = [ Language.c, Language.cpp, @@ -103,7 +134,7 @@ public final class BuildServerBuildSystem { capabilities: BuildClientCapabilities(languageIds: languages)) let handler = BuildServerHandler() - let buildServer = try makeJSONRPCBuildServer(client: handler, serverPath: serverPath, serverFlags: flags) + let buildServer = try makeJSONRPCBuildServer(client: handler, interpreter: interpreter, serverFlags: flags) let response = try buildServer.sendSync(initializeRequest) buildServer.send(InitializedBuildNotification()) log("initialized build server \(response.displayName)") @@ -269,7 +300,7 @@ struct BuildServerConfig: Codable { let argv: [String] } -private func makeJSONRPCBuildServer(client: MessageHandler, serverPath: AbsolutePath, serverFlags: [String]?) throws -> JSONRPCConnection { +private func makeJSONRPCBuildServer(client: MessageHandler, interpreter: AbsolutePath, serverFlags: [String]?) throws -> JSONRPCConnection { let clientToServer = Pipe() let serverToClient = Pipe() @@ -285,7 +316,7 @@ private func makeJSONRPCBuildServer(client: MessageHandler, serverPath: Absolute withExtendedLifetime((clientToServer, serverToClient)) {} } let process = Foundation.Process() - process.executableURL = serverPath.asURL + process.executableURL = interpreter.asURL process.arguments = serverFlags process.standardOutput = serverToClient process.standardInput = clientToServer diff --git a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/server.py b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/server.py index f5dfd349..fb961d40 100755 --- a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/server.py +++ b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetOutputs/server.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import json import sys @@ -70,7 +68,7 @@ while True: if response: responseStr = json.dumps(response) try: - sys.stdout.write("Content-Length: {}\r\n\r\n{}".format(len(responseStr), responseStr)) + sys.stdout.buffer.write(f"Content-Length: {len(responseStr)}\r\n\r\n{responseStr}".encode('utf-8')) sys.stdout.flush() except IOError: # stdout closed, time to quit diff --git a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetSources/server.py b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetSources/server.py index d0813a1d..0d410913 100755 --- a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetSources/server.py +++ b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetSources/server.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import json import sys @@ -93,7 +91,7 @@ while True: if response: responseStr = json.dumps(response) try: - sys.stdout.write("Content-Length: {}\r\n\r\n{}".format(len(responseStr), responseStr)) + sys.stdout.buffer.write(f"Content-Length: {len(responseStr)}\r\n\r\n{responseStr}".encode('utf-8')) sys.stdout.flush() except IOError: # stdout closed, time to quit diff --git a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargets/server.py b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargets/server.py index 92589275..51cd4580 100755 --- a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargets/server.py +++ b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargets/server.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import json import sys @@ -89,7 +87,7 @@ while True: if response: responseStr = json.dumps(response) try: - sys.stdout.write("Content-Length: {}\r\n\r\n{}".format(len(responseStr), responseStr)) + sys.stdout.buffer.write(f"Content-Length: {len(responseStr)}\r\n\r\n{responseStr}".encode('utf-8')) sys.stdout.flush() except IOError: # stdout closed, time to quit diff --git a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetsChanged/server.py b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetsChanged/server.py index 1497704a..c3c4d17f 100755 --- a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetsChanged/server.py +++ b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testBuildTargetsChanged/server.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import json import os import sys @@ -8,7 +6,7 @@ import sys def send(data): dataStr = json.dumps(data) try: - sys.stdout.write("Content-Length: {}\r\n\r\n{}".format(len(dataStr), dataStr)) + sys.stdout.buffer.write(f"Content-Length: {len(dataStr)}\r\n\r\n{dataStr}".encode('utf-8')) sys.stdout.flush() except IOError: # stdout closed, time to quit diff --git a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testFileRegistration/server.py b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testFileRegistration/server.py index b34f6c58..26a4fa49 100755 --- a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testFileRegistration/server.py +++ b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testFileRegistration/server.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import json import os import sys @@ -8,7 +6,7 @@ import sys def send(data): dataStr = json.dumps(data) try: - sys.stdout.write("Content-Length: {}\r\n\r\n{}".format(len(dataStr), dataStr)) + sys.stdout.buffer.write(f"Content-Length: {len(dataStr)}\r\n\r\n{dataStr}".encode('utf-8')) sys.stdout.flush() except IOError: # stdout closed, time to quit diff --git a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testServerInitialize/server.py b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testServerInitialize/server.py index f9b8ea59..2ae0f574 100755 --- a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testServerInitialize/server.py +++ b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testServerInitialize/server.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import json import sys @@ -55,7 +53,7 @@ while True: if response: responseStr = json.dumps(response) try: - sys.stdout.write("Content-Length: {}\r\n\r\n{}".format(len(responseStr), responseStr)) + sys.stdout.buffer.write(f"Content-Length: {len(responseStr)}\r\n\r\n{responseStr}".encode('utf-8')) sys.stdout.flush() except IOError: # stdout closed, time to quit diff --git a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testSettings/server.py b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testSettings/server.py index 7b947a1d..2afd6e5b 100755 --- a/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testSettings/server.py +++ b/Sources/SKTestSupport/INPUTS/BuildServerBuildSystemTests.testSettings/server.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import json import os import sys @@ -76,7 +74,7 @@ while True: if response: responseStr = json.dumps(response) try: - sys.stdout.write("Content-Length: {}\r\n\r\n{}".format(len(responseStr), responseStr)) + sys.stdout.buffer.write(f"Content-Length: {len(responseStr)}\r\n\r\n{responseStr}".encode('utf-8')) sys.stdout.flush() except IOError: # stdout closed, time to quit