Skip to content

Commit 2310c7b

Browse files
authored
Merge pull request #1354 from artemcm/LibSwiftScanPathsEscape
[Dependency Scanning] Unify path-escaping handling using TSC's spm_shellEscaped
2 parents 661d59f + d72c6bf commit 2310c7b

File tree

3 files changed

+47
-28
lines changed

3 files changed

+47
-28
lines changed

Sources/SwiftDriver/Execution/ArgsResolver.swift

+20-22
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,16 @@ public final class ArgsResolver {
5757
}
5858
}
5959

60-
public func resolveArgumentList(for job: Job, useResponseFiles: ResponseFileHandling = .heuristic,
61-
quotePaths: Bool = false) throws -> [String] {
62-
let (arguments, _) = try resolveArgumentList(for: job, useResponseFiles: useResponseFiles,
63-
quotePaths: quotePaths)
60+
public func resolveArgumentList(for job: Job, useResponseFiles: ResponseFileHandling = .heuristic)
61+
throws -> [String] {
62+
let (arguments, _) = try resolveArgumentList(for: job, useResponseFiles: useResponseFiles)
6463
return arguments
6564
}
6665

67-
public func resolveArgumentList(for job: Job, useResponseFiles: ResponseFileHandling = .heuristic,
68-
quotePaths: Bool = false) throws -> ([String], usingResponseFile: Bool) {
69-
let tool = try resolve(.path(job.tool), quotePaths: quotePaths)
70-
var arguments = [tool] + (try job.commandLine.map { try resolve($0, quotePaths: quotePaths) })
66+
public func resolveArgumentList(for job: Job, useResponseFiles: ResponseFileHandling = .heuristic)
67+
throws -> ([String], usingResponseFile: Bool) {
68+
let tool = try resolve(.path(job.tool))
69+
var arguments = [tool] + (try job.commandLine.map { try resolve($0) })
7170
let usingResponseFile = try createResponseFileIfNeeded(for: job, resolvedArguments: &arguments,
7271
useResponseFiles: useResponseFiles)
7372
return (arguments, usingResponseFile)
@@ -77,46 +76,45 @@ public final class ArgsResolver {
7776
public func resolveArgumentList(for job: Job, forceResponseFiles: Bool,
7877
quotePaths: Bool = false) throws -> [String] {
7978
let useResponseFiles: ResponseFileHandling = forceResponseFiles ? .forced : .heuristic
80-
return try resolveArgumentList(for: job, useResponseFiles: useResponseFiles, quotePaths: quotePaths)
79+
return try resolveArgumentList(for: job, useResponseFiles: useResponseFiles)
8180
}
8281

8382
@available(*, deprecated, message: "use resolveArgumentList(for:,useResponseFiles:,quotePaths:)")
8483
public func resolveArgumentList(for job: Job, forceResponseFiles: Bool,
8584
quotePaths: Bool = false) throws -> ([String], usingResponseFile: Bool) {
8685
let useResponseFiles: ResponseFileHandling = forceResponseFiles ? .forced : .heuristic
87-
return try resolveArgumentList(for: job, useResponseFiles: useResponseFiles, quotePaths: quotePaths)
86+
return try resolveArgumentList(for: job, useResponseFiles: useResponseFiles)
8887
}
8988

9089
/// Resolve the given argument.
91-
public func resolve(_ arg: Job.ArgTemplate,
92-
quotePaths: Bool = false) throws -> String {
90+
public func resolve(_ arg: Job.ArgTemplate) throws -> String {
9391
switch arg {
9492
case .flag(let flag):
9593
return flag
9694

9795
case .path(let path):
9896
return try lock.withLock {
99-
return try unsafeResolve(path: path, quotePaths: quotePaths)
97+
return try unsafeResolve(path: path)
10098
}
10199

102100
case .responseFilePath(let path):
103-
return "@\(try resolve(.path(path), quotePaths: quotePaths))"
101+
return "@\(try resolve(.path(path)))"
104102

105103
case let .joinedOptionAndPath(option, path):
106-
return option + (try resolve(.path(path), quotePaths: quotePaths))
104+
return option + (try resolve(.path(path)))
107105

108106
case let .squashedArgumentList(option: option, args: args):
109107
return try option + args.map {
110-
try resolve($0, quotePaths: quotePaths)
108+
try resolve($0)
111109
}.joined(separator: " ")
112110
}
113111
}
114112

115113
/// Needs to be done inside of `lock`. Marked unsafe to make that more obvious.
116-
private func unsafeResolve(path: VirtualPath, quotePaths: Bool) throws -> String {
114+
private func unsafeResolve(path: VirtualPath) throws -> String {
117115
// If there was a path mapping, use it.
118116
if let actualPath = pathMapping[path] {
119-
return quotePaths ? quoteArgument(actualPath) : actualPath
117+
return actualPath
120118
}
121119

122120
// Return the path from the temporary directory if this is a temporary file.
@@ -140,21 +138,21 @@ public final class ArgsResolver {
140138

141139
let result = actualPath.name
142140
pathMapping[path] = result
143-
return quotePaths ? "'\(result)'" : result
141+
return result
144142
}
145143

146144
// Otherwise, return the path.
147145
let result = path.name
148146
pathMapping[path] = result
149-
return quotePaths ? quoteArgument(result) : result
147+
return result
150148
}
151149

152150
private func createFileList(path: VirtualPath, contents: [VirtualPath]) throws {
153151
// FIXME: Need a way to support this for distributed build systems...
154152
if let absPath = path.absolutePath {
155153
try fileSystem.writeFileContents(absPath) { out in
156154
for path in contents {
157-
try! out <<< unsafeResolve(path: path, quotePaths: false) <<< "\n"
155+
try! out <<< unsafeResolve(path: path) <<< "\n"
158156
}
159157
}
160158
}
@@ -184,7 +182,7 @@ public final class ArgsResolver {
184182
}
185183

186184
private func quoteAndEscape(path: VirtualPath) -> String {
187-
let inputNode = Node.scalar(Node.Scalar(try! unsafeResolve(path: path, quotePaths: false),
185+
let inputNode = Node.scalar(Node.Scalar(try! unsafeResolve(path: path),
188186
Tag(.str), .doubleQuoted))
189187
// Width parameter of -1 sets preferred line-width to unlimited so that no extraneous
190188
// line-breaks will be inserted during serialization.

Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift

+5-6
Original file line numberDiff line numberDiff line change
@@ -415,12 +415,11 @@ public extension Driver {
415415

416416
static func itemizedJobCommand(of job: Job, useResponseFiles: ResponseFileHandling,
417417
using resolver: ArgsResolver) throws -> [String] {
418-
// FIXME: this is to walkaround rdar://108769167
419-
let quotePaths = job.kind != .scanDependencies
420-
let (args, _) = try resolver.resolveArgumentList(for: job,
421-
useResponseFiles: useResponseFiles,
422-
quotePaths: quotePaths)
423-
return args
418+
// Because the command-line passed to libSwiftScan does not go through the shell
419+
// we must ensure that we generate a shell-escaped string for all arguments/flags that may
420+
// potentially need it.
421+
return try resolver.resolveArgumentList(for: job,
422+
useResponseFiles: useResponseFiles).0.map { $0.spm_shellEscaped() }
424423
}
425424

426425
static func getRootPath(of toolchain: Toolchain, env: [String: String])

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

+22
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,28 @@ final class ExplicitModuleBuildTests: XCTestCase {
15581558
}
15591559
}
15601560

1561+
func testDependencyScanCommandLineEscape() throws {
1562+
#if os(Windows)
1563+
let quoteCharacter: Character = "\""
1564+
#else
1565+
let quoteCharacter: Character = "'"
1566+
#endif
1567+
let swiftInputWithSpace = "/tmp/input example/test.swift"
1568+
let swiftInputWithoutSpace = "/tmp/baz.swift"
1569+
let clangInputWithSpace = "/tmp/input example/bar.o"
1570+
var driver = try Driver(args: ["swiftc", "-explicit-module-build",
1571+
"-module-name", "testDependencyScanning",
1572+
swiftInputWithSpace, swiftInputWithoutSpace,
1573+
"-Xcc", clangInputWithSpace])
1574+
let scanJob = try driver.dependencyScanningJob()
1575+
let scanJobCommand = try Driver.itemizedJobCommand(of: scanJob,
1576+
useResponseFiles: .disabled,
1577+
using: ArgsResolver(fileSystem: InMemoryFileSystem()))
1578+
XCTAssertTrue(scanJobCommand.contains(String(quoteCharacter) + swiftInputWithSpace + String(quoteCharacter)))
1579+
XCTAssertTrue(scanJobCommand.contains(String(quoteCharacter) + clangInputWithSpace + String(quoteCharacter)))
1580+
XCTAssertTrue(scanJobCommand.contains(swiftInputWithoutSpace))
1581+
}
1582+
15611583
func testExplicitSwiftModuleMap() throws {
15621584
let jsonExample : String = """
15631585
[

0 commit comments

Comments
 (0)