Skip to content

Commit df08551

Browse files
committed
When setting a working directory on process launch is not supported, set it using sh
In particular, this affects Amazon Linux 2, which has a glibc that doesn’t support `posix_spawn_file_actions_addchdir_np`. rdar://128016626
1 parent bce4cf6 commit df08551

File tree

5 files changed

+126
-7
lines changed

5 files changed

+126
-7
lines changed

Sources/SKSupport/Process+Run.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,33 @@ extension Process {
6969
do {
7070
try process.launch()
7171
} catch Process.Error.workingDirectoryNotSupported where workingDirectory != nil {
72-
// TODO (indexing): We need to figure out how to set the working directory on all platforms.
72+
return try Process.launchWithWorkingDirectoryUsingSh(
73+
arguments: arguments,
74+
environmentBlock: environmentBlock,
75+
workingDirectory: workingDirectory!,
76+
outputRedirection: outputRedirection,
77+
startNewProcessGroup: startNewProcessGroup,
78+
loggingHandler: loggingHandler
79+
)
80+
}
81+
return process
82+
}
83+
84+
private static func launchWithWorkingDirectoryUsingSh(
85+
arguments: [String],
86+
environmentBlock: ProcessEnvironmentBlock = ProcessEnv.block,
87+
workingDirectory: AbsolutePath,
88+
outputRedirection: OutputRedirection = .collect,
89+
startNewProcessGroup: Bool = true,
90+
loggingHandler: LoggingHandler? = .none
91+
) throws -> Process {
92+
let shPath = "/usr/bin/sh"
93+
guard FileManager.default.fileExists(atPath: shPath) else {
7394
logger.error(
74-
"Working directory not supported on the platform. Launching process without working directory \(workingDirectory!.pathString)"
95+
"""
96+
Working directory not supported on the platform and 'sh' could not be found. \
97+
Launching process without working directory \(workingDirectory.pathString)
98+
"""
7599
)
76100
return try Process.launch(
77101
arguments: arguments,
@@ -82,7 +106,14 @@ extension Process {
82106
loggingHandler: loggingHandler
83107
)
84108
}
85-
return process
109+
return try Process.launch(
110+
arguments: [shPath, "-c", #"cd "$0"; exec "$@""#, workingDirectory.pathString] + arguments,
111+
environmentBlock: environmentBlock,
112+
workingDirectory: nil,
113+
outputRedirection: outputRedirection,
114+
startNewProcessGroup: startNewProcessGroup,
115+
loggingHandler: loggingHandler
116+
)
86117
}
87118

88119
/// Runs a new process with the given parameters and waits for it to exit, sending SIGINT if this task is cancelled.

Sources/SKTestSupport/FindTool.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Foundation
14+
15+
import class TSCBasic.Process
16+
17+
#if os(Windows)
18+
import WinSDK
19+
#endif
20+
21+
// Returns the path to the given tool, as found by `xcrun --find` on macOS, or `which` on Linux.
22+
public func findTool(name: String) async -> URL? {
23+
#if os(macOS)
24+
let cmd = ["/usr/bin/xcrun", "--find", name]
25+
#elseif os(Windows)
26+
var buf = [WCHAR](repeating: 0, count: Int(MAX_PATH))
27+
GetWindowsDirectoryW(&buf, DWORD(MAX_PATH))
28+
var wherePath = String(decodingCString: &buf, as: UTF16.self)
29+
.appendingPathComponent("system32")
30+
.appendingPathComponent("where.exe")
31+
let cmd = [wherePath, name]
32+
#elseif os(Android)
33+
let cmd = ["/system/bin/which", name]
34+
#else
35+
let cmd = ["/usr/bin/which", name]
36+
#endif
37+
38+
guard let result = try? await Process.run(arguments: cmd, workingDirectory: nil) else {
39+
return nil
40+
}
41+
guard var path = try? String(bytes: result.output.get(), encoding: .utf8) else {
42+
return nil
43+
}
44+
#if os(Windows)
45+
path = String((path.split { $0.isNewline })[0])
46+
#endif
47+
path = path.trimmingCharacters(in: .whitespacesAndNewlines)
48+
return URL(fileURLWithPath: path, isDirectory: false)
49+
}

Sources/SKTestSupport/SwiftPMDependencyProject.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Foundation
14-
import ISDBTibs
14+
import SKSupport
1515
import XCTest
1616

1717
import struct TSCBasic.AbsolutePath
@@ -35,7 +35,7 @@ public class SwiftPMDependencyProject {
3535
case cannotFindGit
3636
case processedTerminatedWithNonZeroExitCode(ProcessResult)
3737
}
38-
guard let toolUrl = findTool(name: "git") else {
38+
guard let git = await findTool(name: "git") else {
3939
if ProcessEnv.block["SWIFTCI_USE_LOCAL_DEPS"] == nil {
4040
// Never skip the test in CI, similar to what SkipUnless does.
4141
throw XCTSkip("git cannot be found")
@@ -45,7 +45,7 @@ public class SwiftPMDependencyProject {
4545
// We can't use `workingDirectory` because Amazon Linux doesn't support working directories (or at least
4646
// TSCBasic.Process doesn't support working directories on Amazon Linux)
4747
let process = TSCBasic.Process(
48-
arguments: [toolUrl.path, "-C", workingDirectory.path] + arguments
48+
arguments: [git.path, "-C", workingDirectory.path] + arguments
4949
)
5050
try process.launch()
5151
let processResult = try await process.waitUntilExit()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import LSPTestSupport
14+
import SKSupport
15+
import SKTestSupport
16+
import XCTest
17+
18+
import struct TSCBasic.AbsolutePath
19+
import class TSCBasic.Process
20+
21+
final class ProcessLaunchTests: XCTestCase {
22+
func testWorkingDirectory() async throws {
23+
let project = try await MultiFileTestProject(files: [
24+
"parent dir/subdir A/a.txt": "",
25+
"parent dir/subdir B/b.txt": "",
26+
"parent dir/subdir C/c.txt": "",
27+
])
28+
29+
let ls = try await unwrap(findTool(name: "ls"))
30+
31+
let result = try await Process.run(
32+
arguments: [ls.path, "subdir A", "subdir B"],
33+
workingDirectory: AbsolutePath(validating: project.scratchDirectory.path).appending(component: "parent dir")
34+
)
35+
let stdout = try unwrap(String(bytes: result.output.get(), encoding: .utf8))
36+
XCTAssert(stdout.contains("a.txt"), "Directory did not contain a.txt:\n\(stdout)")
37+
XCTAssert(stdout.contains("b.txt"), "Directory did not contain b.txt:\n\(stdout)")
38+
XCTAssert(!stdout.contains("c.txt"), "Directory contained c.txt:\n\(stdout)")
39+
}
40+
}

Tests/SourceKitDTests/SourceKitDTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
import Foundation
1414
import ISDBTestSupport
15-
import ISDBTibs
1615
import LSPTestSupport
1716
import LanguageServerProtocol
1817
@_spi(Testing) import SKCore

0 commit comments

Comments
 (0)