Skip to content

Commit 45d36ae

Browse files
committed
Add support for posix_spawn_file_actions_addchdir_np on available Linux platforms
1 parent 11aacae commit 45d36ae

File tree

6 files changed

+81
-26
lines changed

6 files changed

+81
-26
lines changed

Sources/TSCBasic/Process.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ public final class Process: ObjectIdentifierProtocol {
121121
public enum Error: Swift.Error {
122122
/// The program requested to be executed cannot be found on the existing search paths, or is not executable.
123123
case missingExecutableProgram(program: String)
124+
125+
/// The current OS does not support the workingDirectory API.
126+
case workingDirectoryNotSupported
124127
}
125128

126129
public enum OutputRedirection {
@@ -226,7 +229,6 @@ public final class Process: ObjectIdentifierProtocol {
226229
/// Value: Path to the executable, if found.
227230
static private var validatedExecutablesMap = [String: AbsolutePath?]()
228231

229-
#if os(macOS)
230232
/// Create a new process instance.
231233
///
232234
/// - Parameters:
@@ -254,7 +256,6 @@ public final class Process: ObjectIdentifierProtocol {
254256
self.verbose = verbose
255257
self.startNewProcessGroup = startNewProcessGroup
256258
}
257-
#endif
258259

259260
/// Create a new process instance.
260261
///
@@ -404,16 +405,24 @@ public final class Process: ObjectIdentifierProtocol {
404405
posix_spawn_file_actions_init(&fileActions)
405406
defer { posix_spawn_file_actions_destroy(&fileActions) }
406407

407-
#if os(macOS)
408408
if let workingDirectory = workingDirectory?.pathString {
409+
#if os(macOS)
409410
// The only way to set a workingDirectory is using an availability-gated initializer, so we don't need
410411
// to handle the case where the posix_spawn_file_actions_addchdir_np method is unavailable. This check only
411412
// exists here to make the compiler happy.
412413
if #available(macOS 10.15, *) {
413414
posix_spawn_file_actions_addchdir_np(&fileActions, workingDirectory)
414415
}
416+
#elseif os(Linux)
417+
guard SPM_posix_spawn_file_actions_addchdir_np_supported() else {
418+
throw Process.Error.workingDirectoryNotSupported
419+
}
420+
421+
SPM_posix_spawn_file_actions_addchdir_np(&fileActions, workingDirectory)
422+
#else
423+
throw Process.Error.workingDirectoryNotSupported
424+
#endif
415425
}
416-
#endif
417426

418427
// Workaround for https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=89e435f3559c53084498e9baad22172b64429362
419428
// Change allowing for newer version of glibc
@@ -691,6 +700,8 @@ extension Process.Error: CustomStringConvertible {
691700
switch self {
692701
case .missingExecutableProgram(let program):
693702
return "could not find executable for '\(program)'"
703+
case .workingDirectoryNotSupported:
704+
return "workingDirectory is not supported in this platform"
694705
}
695706
}
696707
}

Sources/TSCclibc/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
add_library(TSCclibc STATIC
10-
libc.c)
10+
libc.c process.c)
1111
target_include_directories(TSCclibc PUBLIC
1212
include)
1313

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module TSCclibc {
22
header "TSCclibc.h"
33
header "indexstore_functions.h"
4+
header "process.h"
45
export *
56
}

Sources/TSCclibc/include/process.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#if defined(__linux__)
2+
3+
#include <spawn.h>
4+
#include <stdbool.h>
5+
6+
// Wrapper method for posix_spawn_file_actions_addchdir_np that fails on Linux versions that do not have this method available.
7+
int SPM_posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *restrict file_actions, const char *restrict path);
8+
9+
// Runtime check for the availability of posix_spawn_file_actions_addchdir_np. Returns 0 if the method is available, -1 if not.
10+
bool SPM_posix_spawn_file_actions_addchdir_np_supported();
11+
12+
#endif

Sources/TSCclibc/process.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#if defined(__linux__)
2+
3+
#include <errno.h>
4+
5+
#include "process.h"
6+
7+
int SPM_posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *restrict file_actions, const char *restrict path) {
8+
#if __GLIBC_PREREQ(2, 29)
9+
return posix_spawn_file_actions_addchdir_np(file_actions, path);
10+
#else
11+
return ENOSYS;
12+
#endif
13+
}
14+
15+
bool SPM_posix_spawn_file_actions_addchdir_np_supported() {
16+
#if __GLIBC_PREREQ(2, 29)
17+
return true;
18+
#else
19+
return false;
20+
#endif
21+
}
22+
23+
#endif

Tests/TSCBasicTests/ProcessTests.swift

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -211,32 +211,40 @@ class ProcessTests: XCTestCase {
211211
}
212212
}
213213

214-
#if os(macOS)
215214
func testWorkingDirectory() throws {
216-
if #available(macOS 10.15, *) {
217-
try! withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in
218-
let parentPath = tempDirPath.appending(component: "file")
219-
let childPath = tempDirPath.appending(component: "subdir").appending(component: "file")
215+
guard #available(macOS 10.15, *) else {
216+
// Skip this test since it's not supported in this OS.
217+
return
218+
}
220219

221-
try localFileSystem.writeFileContents(parentPath, bytes: ByteString("parent"))
222-
try localFileSystem.createDirectory(childPath.parentDirectory, recursive: true)
223-
try localFileSystem.writeFileContents(childPath, bytes: ByteString("child"))
220+
#if os(Linux)
221+
guard SPM_posix_spawn_file_actions_addchdir_np_supported() else {
222+
// Skip this test since it's not supported in this OS.
223+
return
224+
}
225+
#endif
224226

225-
do {
226-
let process = Process(arguments: ["cat", "file"], workingDirectory: tempDirPath)
227-
try process.launch()
228-
let result = try process.waitUntilExit()
229-
XCTAssertEqual(try result.utf8Output(), "parent")
230-
}
227+
try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDirPath in
228+
let parentPath = tempDirPath.appending(component: "file")
229+
let childPath = tempDirPath.appending(component: "subdir").appending(component: "file")
231230

232-
do {
233-
let process = Process(arguments: ["cat", "file"], workingDirectory: childPath.parentDirectory)
234-
try process.launch()
235-
let result = try process.waitUntilExit()
236-
XCTAssertEqual(try result.utf8Output(), "child")
237-
}
231+
try localFileSystem.writeFileContents(parentPath, bytes: ByteString("parent"))
232+
try localFileSystem.createDirectory(childPath.parentDirectory, recursive: true)
233+
try localFileSystem.writeFileContents(childPath, bytes: ByteString("child"))
234+
235+
do {
236+
let process = Process(arguments: ["cat", "file"], workingDirectory: tempDirPath)
237+
try process.launch()
238+
let result = try process.waitUntilExit()
239+
XCTAssertEqual(try result.utf8Output(), "parent")
240+
}
241+
242+
do {
243+
let process = Process(arguments: ["cat", "file"], workingDirectory: childPath.parentDirectory)
244+
try process.launch()
245+
let result = try process.waitUntilExit()
246+
XCTAssertEqual(try result.utf8Output(), "child")
238247
}
239248
}
240249
}
241-
#endif
242250
}

0 commit comments

Comments
 (0)