Skip to content

Commit e8240e5

Browse files
committed
Sync TSC changes from master
1 parent 8d20e43 commit e8240e5

File tree

19 files changed

+329
-43
lines changed

19 files changed

+329
-43
lines changed

swift-tools-support-core/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ else()
2121
endif()
2222
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
2323

24-
option(BUILD_SHARED_LIBS "Build shared libraryes by default" YES)
24+
option(BUILD_SHARED_LIBS "Build shared libraries by default" YES)
2525

2626
find_package(dispatch QUIET)
2727
find_package(Foundation QUIET)
28+
find_package(SQLite3 REQUIRED)
2829

2930
add_subdirectory(Sources)
3031
add_subdirectory(cmake/modules)

swift-tools-support-core/Sources/TSCBasic/CMakeLists.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ add_library(TSCBasic
4949
Thread.swift
5050
Tuple.swift
5151
misc.swift)
52+
target_compile_options(TSCBasic PUBLIC
53+
# Don't use GNU strerror_r on Android.
54+
"$<$<PLATFORM_ID:Android>:SHELL:-Xcc -U_GNU_SOURCE>"
55+
# Ignore secure function warnings on Windows.
56+
"$<$<PLATFORM_ID:Windows>:SHELL:-Xcc -D_CRT_SECURE_NO_WARNINGS>")
5257
target_link_libraries(TSCBasic PUBLIC
5358
TSCLibc)
5459
if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
@@ -67,9 +72,4 @@ install(TARGETS TSCBasic
6772
RUNTIME DESTINATION bin)
6873
endif()
6974

70-
# Don't use GNU strerror_r on Android.
71-
if(CMAKE_SYSTEM_NAME STREQUAL Android)
72-
target_compile_options(TSCBasic PUBLIC "SHELL:-Xcc -U_GNU_SOURCE")
73-
endif()
74-
7575
set_property(GLOBAL APPEND PROPERTY TSC_EXPORTS TSCBasic)

swift-tools-support-core/Sources/TSCBasic/CStringArray.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ public final class CStringArray {
2020

2121
/// Creates an instance from an array of strings.
2222
public init(_ array: [String]) {
23+
#if os(Windows)
24+
cArray = array.map({ $0.withCString({ _strdup($0) }) }) + [nil]
25+
#else
2326
cArray = array.map({ $0.withCString({ strdup($0) }) }) + [nil]
27+
#endif
2428
}
2529

2630
deinit {

swift-tools-support-core/Sources/TSCBasic/FileSystem.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,7 @@ extension FileSystem {
900900
}
901901
}
902902

903+
#if !os(Windows)
903904
extension dirent {
904905
/// Get the directory name.
905906
///
@@ -911,4 +912,4 @@ extension dirent {
911912
}
912913
}
913914
}
914-
915+
#endif

swift-tools-support-core/Sources/TSCBasic/Path.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,12 @@ private struct UNIXPath: Path {
421421
return name != "" && name != "." && name != ".." && !name.contains("/")
422422
}
423423

424+
#if os(Windows)
425+
static func isAbsolutePath(_ path: String) -> Bool {
426+
return !path.withCString(encodedAs: UTF16.self, PathIsRelativeW)
427+
}
428+
#endif
429+
424430
var dirname: String {
425431
#if os(Windows)
426432
let dir = string.deletingLastPathComponent
@@ -445,7 +451,11 @@ private struct UNIXPath: Path {
445451
}
446452

447453
var isAbsolute: Bool {
448-
string.hasPrefix("/")
454+
#if os(Windows)
455+
return UNIXPath.isAbsolutePath(string)
456+
#else
457+
return string.hasPrefix("/")
458+
#endif
449459
}
450460

451461
var basename: String {
@@ -632,7 +642,7 @@ private struct UNIXPath: Path {
632642
defer { fsr.deallocate() }
633643

634644
let realpath = String(cString: fsr)
635-
if realpath.withCString(encodedAs: UTF16.self, PathIsRelativeW) {
645+
if !UNIXPath.isAbsolutePath(realpath) {
636646
throw PathValidationError.invalidAbsolutePath(path)
637647
}
638648
self.init(normalizingAbsolutePath: path)
@@ -654,7 +664,7 @@ private struct UNIXPath: Path {
654664
defer { fsr.deallocate() }
655665

656666
let realpath: String = String(cString: fsr)
657-
if !realpath.withCString(encodedAs: UTF16.self, PathIsRelativeW) {
667+
if UNIXPath.isAbsolutePath(realpath) {
658668
throw PathValidationError.invalidRelativePath(path)
659669
}
660670
self.init(normalizingRelativePath: path)

swift-tools-support-core/Sources/TSCBasic/Process.swift

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ public struct ProcessResult: CustomStringConvertible {
3131
public enum ExitStatus: Equatable {
3232
/// The process was terminated normally with a exit code.
3333
case terminated(code: Int32)
34-
34+
#if !os(Windows)
3535
/// The process was terminated due to a signal.
3636
case signalled(signal: Int32)
37+
#endif
3738
}
3839

3940
/// The arguments with which the process was launched.
@@ -177,6 +178,8 @@ public final class Process: ObjectIdentifierProtocol {
177178
/// Typealias for process id type.
178179
#if !os(Windows)
179180
public typealias ProcessID = pid_t
181+
#else
182+
public typealias ProcessID = DWORD
180183
#endif
181184

182185
/// Typealias for stdout/stderr output closure.
@@ -206,6 +209,9 @@ public final class Process: ObjectIdentifierProtocol {
206209
/// The process id of the spawned process, available after the process is launched.
207210
#if os(Windows)
208211
private var _process: Foundation.Process?
212+
public var processID: ProcessID {
213+
return DWORD(_process?.processIdentifier ?? 0)
214+
}
209215
#else
210216
public private(set) var processID = ProcessID()
211217
#endif
@@ -314,7 +320,7 @@ public final class Process: ObjectIdentifierProtocol {
314320
}
315321
// FIXME: This can be cached.
316322
let envSearchPaths = getEnvSearchPaths(
317-
pathString: ProcessEnv.vars["PATH"],
323+
pathString: ProcessEnv.path,
318324
currentWorkingDirectory: localFileSystem.currentWorkingDirectory
319325
)
320326
// Lookup and cache the executable path.
@@ -340,14 +346,15 @@ public final class Process: ObjectIdentifierProtocol {
340346
}
341347

342348
// Look for executable.
343-
guard Process.findExecutable(arguments[0]) != nil else {
344-
throw Process.Error.missingExecutableProgram(program: arguments[0])
349+
let executable = arguments[0]
350+
guard let executablePath = Process.findExecutable(executable) else {
351+
throw Process.Error.missingExecutableProgram(program: executable)
345352
}
346353

347354
#if os(Windows)
348355
_process = Foundation.Process()
349-
_process?.arguments = arguments
350-
_process?.executableURL = URL(fileURLWithPath: arguments[0])
356+
_process?.arguments = Array(arguments.dropFirst()) // Avoid including the executable URL twice.
357+
_process?.executableURL = executablePath.asURL
351358
_process?.environment = environment
352359

353360
if outputRedirection.redirectsOutput {
@@ -752,9 +759,10 @@ extension ProcessResult.Error: CustomStringConvertible {
752759
switch result.exitStatus {
753760
case .terminated(let code):
754761
stream <<< "terminated(\(code)): "
755-
762+
#if !os(Windows)
756763
case .signalled(let signal):
757764
stream <<< "signalled(\(signal)): "
765+
#endif
758766
}
759767

760768
// Strip sandbox information from arguments to keep things pretty.

swift-tools-support-core/Sources/TSCBasic/ProcessEnv.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ public enum ProcessEnv {
5757
invalidateEnv()
5858
}
5959

60+
/// `PATH` variable in the process's environment (`Path` under Windows).
61+
public static var path: String? {
62+
#if os(Windows)
63+
let pathArg = "Path"
64+
#else
65+
let pathArg = "PATH"
66+
#endif
67+
return vars[pathArg]
68+
}
69+
6070
/// The current working directory of the process.
6171
public static var cwd: AbsolutePath? {
6272
return localFileSystem.currentWorkingDirectory

swift-tools-support-core/Sources/TSCBasic/StringConversions.swift

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
4+
Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -15,6 +15,11 @@
1515
///
1616
/// - Returns: True if shell escaping is not needed.
1717
private func inShellWhitelist(_ codeUnit: UInt8) -> Bool {
18+
#if os(Windows)
19+
if codeUnit == UInt8(ascii: "\\") {
20+
return true
21+
}
22+
#endif
1823
switch codeUnit {
1924
case UInt8(ascii: "a")...UInt8(ascii: "z"),
2025
UInt8(ascii: "A")...UInt8(ascii: "Z"),
@@ -38,7 +43,7 @@ private func inShellWhitelist(_ codeUnit: UInt8) -> Bool {
3843
extension String {
3944

4045
/// Creates a shell escaped string. If the string does not need escaping, returns the original string.
41-
/// Otherwise escapes using single quotes. For example:
46+
/// Otherwise escapes using single quotes on Unix and double quotes on Windows. For example:
4247
/// hello -> hello, hello$world -> 'hello$world', input A -> 'input A'
4348
///
4449
/// - Returns: Shell escaped string.
@@ -49,23 +54,30 @@ extension String {
4954
return self
5055
}
5156

52-
// If there are no single quotes then we can just wrap the string around single quotes.
53-
guard let singleQuotePos = utf8[pos...].firstIndex(of: UInt8(ascii: "'")) else {
54-
return "'" + self + "'"
57+
#if os(Windows)
58+
let quoteCharacter: Character = "\""
59+
let escapedQuoteCharacter = "\"\""
60+
#else
61+
let quoteCharacter: Character = "'"
62+
let escapedQuoteCharacter = "'\\''"
63+
#endif
64+
// If there are no quote characters then we can just wrap the string within the quotes.
65+
guard let quotePos = utf8[pos...].firstIndex(of: quoteCharacter.asciiValue!) else {
66+
return String(quoteCharacter) + self + String(quoteCharacter)
5567
}
5668

5769
// Otherwise iterate and escape all the single quotes.
58-
var newString = "'" + String(self[..<singleQuotePos])
70+
var newString = String(quoteCharacter) + String(self[..<quotePos])
5971

60-
for char in self[singleQuotePos...] {
61-
if char == "'" {
62-
newString += "'\\''"
72+
for char in self[quotePos...] {
73+
if char == quoteCharacter {
74+
newString += escapedQuoteCharacter
6375
} else {
6476
newString += String(char)
6577
}
6678
}
6779

68-
newString += "'"
80+
newString += String(quoteCharacter)
6981

7082
return newString
7183
}

swift-tools-support-core/Sources/TSCBasic/TemporaryFile.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,13 @@ public func withTemporaryFile<Result>(
135135
let tempFile = try TemporaryFile(dir: dir, prefix: prefix, suffix: suffix)
136136
defer {
137137
if deleteOnClose {
138+
#if os(Windows)
139+
_ = tempFile.path.pathString.withCString(encodedAs: UTF16.self) {
140+
_wunlink($0)
141+
}
142+
#else
138143
unlink(tempFile.path.pathString)
144+
#endif
139145
}
140146
}
141147
return try body(tempFile)

swift-tools-support-core/Sources/TSCBasic/TerminalController.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,30 @@ public final class TerminalController {
109109

110110
/// Computes the terminal type of the stream.
111111
public static func terminalType(_ stream: LocalFileOutputByteStream) -> TerminalType {
112+
#if os(Windows)
113+
return _isatty(_fileno(stream.filePointer)) == 0 ? .file : .tty
114+
#else
112115
if ProcessEnv.vars["TERM"] == "dumb" {
113116
return .dumb
114117
}
115118
let isTTY = isatty(fileno(stream.filePointer)) != 0
116119
return isTTY ? .tty : .file
120+
#endif
117121
}
118122

119123
/// Tries to get the terminal width first using COLUMNS env variable and
120124
/// if that fails ioctl method testing on stdout stream.
121125
///
122126
/// - Returns: Current width of terminal if it was determinable.
123127
public static func terminalWidth() -> Int? {
128+
#if os(Windows)
129+
var csbi: CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO()
130+
if !GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) {
131+
// GetLastError()
132+
return nil
133+
}
134+
return Int(csbi.srWindow.Right - csbi.srWindow.Left) + 1
135+
#else
124136
// Try to get from environment.
125137
if let columns = ProcessEnv.vars["COLUMNS"], let width = Int(columns) {
126138
return width
@@ -130,13 +142,14 @@ public final class TerminalController {
130142
// Following code does not compile on ppc64le well. TIOCGWINSZ is
131143
// defined in system ioctl.h file which needs to be used. This is
132144
// a temporary arrangement and needs to be fixed.
133-
#if !(arch(powerpc64le) || os(Windows))
145+
#if !arch(powerpc64le)
134146
var ws = winsize()
135147
if ioctl(1, UInt(TIOCGWINSZ), &ws) == 0 {
136148
return Int(ws.ws_col)
137149
}
138150
#endif
139151
return nil
152+
#endif
140153
}
141154

142155
/// Flushes the stream.

swift-tools-support-core/Sources/TSCBasic/misc.swift

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
import TSCLibc
1212
import Foundation
1313

14+
#if os(Windows)
15+
public let executableFileSuffix = ".exe"
16+
#else
17+
public let executableFileSuffix = ""
18+
#endif
19+
1420
/// Replace the current process image with a new process image.
1521
///
1622
/// - Parameters:
@@ -48,16 +54,16 @@ public func getEnvSearchPaths(
4854
currentWorkingDirectory: AbsolutePath?
4955
) -> [AbsolutePath] {
5056
// Compute search paths from PATH variable.
51-
return (pathString ?? "").split(separator: ":").map(String.init).compactMap({ pathString in
52-
// If this is an absolute path, we're done.
53-
if pathString.first == "/" {
54-
return AbsolutePath(pathString)
55-
}
56-
// Otherwise convert it into absolute path relative to the working directory.
57-
guard let cwd = currentWorkingDirectory else {
58-
return nil
57+
#if os(Windows)
58+
let pathSeparator: Character = ";"
59+
#else
60+
let pathSeparator: Character = ":"
61+
#endif
62+
return (pathString ?? "").split(separator: pathSeparator).map(String.init).compactMap({ pathString in
63+
if let cwd = currentWorkingDirectory {
64+
return AbsolutePath(pathString, relativeTo: cwd)
5965
}
60-
return AbsolutePath(pathString, relativeTo: cwd)
66+
return try? AbsolutePath(validating: pathString)
6167
})
6268
}
6369

0 commit comments

Comments
 (0)