Skip to content

Commit eef0bfd

Browse files
committed
Make swift driver friendlier to relative paths
1 parent c835d02 commit eef0bfd

File tree

6 files changed

+123
-24
lines changed

6 files changed

+123
-24
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -252,17 +252,15 @@ public struct Driver {
252252
self.inputFiles = inputFiles
253253
self.recordedInputModificationDates = .init(uniqueKeysWithValues:
254254
Set(inputFiles).compactMap {
255-
if case .absolute(let absolutePath) = $0.file,
256-
let modTime = try? localFileSystem.getFileInfo(absolutePath).modTime {
257-
return ($0, modTime)
258-
}
259-
return nil
255+
guard let modTime = try? localFileSystem
256+
.getFileInfo($0.file).modTime else { return nil }
257+
return ($0, modTime)
260258
})
261259

262260
let outputFileMap: OutputFileMap?
263261
// Initialize an empty output file map, which will be populated when we start creating jobs.
264262
if let outputFileMapArg = parsedOptions.getLastArgument(.outputFileMap)?.asSingle {
265-
let path = try AbsolutePath(validating: outputFileMapArg)
263+
let path = try VirtualPath(path: outputFileMapArg)
266264
outputFileMap = try .load(file: path, diagnosticEngine: diagnosticEngine)
267265
} else {
268266
outputFileMap = nil
@@ -837,16 +835,8 @@ extension Driver {
837835
}
838836

839837
// Resolve the input file.
840-
let file: VirtualPath
841-
let fileExtension: String
842-
if let absolute = try? AbsolutePath(validating: input) {
843-
file = .absolute(absolute)
844-
fileExtension = absolute.extension ?? ""
845-
} else {
846-
let relative = try RelativePath(validating: input)
847-
fileExtension = relative.extension ?? ""
848-
file = .relative(relative)
849-
}
838+
let file = try VirtualPath(path: input)
839+
let fileExtension = file.extension ?? ""
850840

851841
// Determine the type of the input file based on its extension.
852842
// If we don't recognize the extension, treat it as an object file.

Sources/SwiftDriver/Driver/OutputFileMap.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public struct OutputFileMap: Equatable {
6868

6969
/// Load the output file map at the given path.
7070
public static func load(
71-
file: AbsolutePath,
71+
file: VirtualPath,
7272
diagnosticEngine: DiagnosticsEngine
7373
) throws -> OutputFileMap {
7474
// Load and decode the file.

Sources/SwiftDriver/Incremental Compilation/IncrementalCompilation.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Foundation
1616
public struct IncrementalCompilation {
1717
public let showIncrementalBuildDecisions: Bool
1818
public let enableIncrementalBuild: Bool
19-
public let buildRecordPath: AbsolutePath?
19+
public let buildRecordPath: VirtualPath?
2020
public let outputBuildRecordForModuleOnlyBuild: Bool
2121
public let argsHash: String
2222
public let lastBuildTime: Date
@@ -118,14 +118,14 @@ public struct IncrementalCompilation {
118118
outputFileMap: OutputFileMap?,
119119
compilerOutputType: FileType?,
120120
diagnosticEngine: DiagnosticsEngine?
121-
) -> AbsolutePath? {
121+
) -> VirtualPath? {
122122
// FIXME: This should work without an output file map. We should have
123123
// another way to specify a build record and where to put intermediates.
124124
guard let ofm = outputFileMap else {
125125
diagnosticEngine.map { $0.emit(.warning_incremental_requires_output_file_map) }
126126
return nil
127127
}
128-
guard let partialBuildRecordPath = ofm.existingOutputForSingleInput(outputType: .swiftDeps)?.absolutePath
128+
guard let partialBuildRecordPath = ofm.existingOutputForSingleInput(outputType: .swiftDeps)
129129
else {
130130
diagnosticEngine.map { $0.emit(.warning_incremental_requires_build_record_entry) }
131131
return nil
@@ -134,7 +134,7 @@ public struct IncrementalCompilation {
134134
// '~moduleonly'. So that module-only mode doesn't mess up build-record
135135
// file for full compilation.
136136
return compilerOutputType == .swiftModule
137-
? AbsolutePath(partialBuildRecordPath.pathString + "~moduleonly")
137+
? partialBuildRecordPath.appendingToBaseName("~moduleonly")
138138
: partialBuildRecordPath
139139
}
140140

Sources/SwiftDriver/Incremental Compilation/InputIInfoMap.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public extension InputInfoMap {
130130
argsHash: String,
131131
lastBuildTime: Date,
132132
inputFiles: [TypedVirtualPath],
133-
buildRecordPath: AbsolutePath,
133+
buildRecordPath: VirtualPath,
134134
showIncrementalBuildDecisions: Bool,
135135
diagnosticEngine: DiagnosticsEngine
136136
) -> Self? {

Sources/SwiftDriver/Utilities/VirtualPath.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,23 @@ public enum VirtualPath: Hashable {
109109
return self
110110
}
111111
}
112+
113+
/// Returns the virtual path with an additional suffix appended to base name.
114+
///
115+
/// This should not be used with `.standardInput` or `.standardOutput`.
116+
public func appendingToBaseName(_ suffix: String) -> VirtualPath {
117+
switch self {
118+
case let .absolute(path):
119+
return .absolute(AbsolutePath(path.pathString + suffix))
120+
case let .relative(path):
121+
return .relative(RelativePath(path.pathString + suffix))
122+
case let .temporary(path):
123+
return .temporary(RelativePath(path.pathString + suffix))
124+
case .standardInput, .standardOutput:
125+
assertionFailure("Can't append path component to standard in/out")
126+
return self
127+
}
128+
}
112129
}
113130

114131
extension VirtualPath: Codable {
@@ -193,3 +210,41 @@ extension VirtualPath {
193210
return try VirtualPath(path: pathString.appendingFileTypeExtension(fileType))
194211
}
195212
}
213+
214+
enum FileSystemError: Swift.Error {
215+
case noCurrentWorkingDirectory
216+
case cannotResolveTempPath(RelativePath)
217+
case cannotResolveStandardInput
218+
case cannotResolveStandardOutput
219+
}
220+
221+
extension TSCBasic.FileSystem {
222+
private func resolvingVirtualPath<T>(
223+
_ path: VirtualPath,
224+
apply f: (AbsolutePath) throws -> T
225+
) throws -> T {
226+
switch path {
227+
case let .absolute(absPath):
228+
return try f(absPath)
229+
case let .relative(relPath):
230+
guard let cwd = currentWorkingDirectory else {
231+
throw FileSystemError.noCurrentWorkingDirectory
232+
}
233+
return try f(.init(cwd, relPath))
234+
case let .temporary(relPath):
235+
throw FileSystemError.cannotResolveTempPath(relPath)
236+
case .standardInput:
237+
throw FileSystemError.cannotResolveStandardInput
238+
case .standardOutput:
239+
throw FileSystemError.cannotResolveStandardOutput
240+
}
241+
}
242+
243+
func readFileContents(_ path: VirtualPath) throws -> ByteString {
244+
try resolvingVirtualPath(path, apply: readFileContents)
245+
}
246+
247+
func getFileInfo(_ path: VirtualPath) throws -> TSCBasic.FileInfo {
248+
try resolvingVirtualPath(path, apply: getFileInfo)
249+
}
250+
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,28 @@ final class SwiftDriverTests: XCTestCase {
207207
XCTAssertEqual(driver4.inputFiles, [ TypedVirtualPath(file: .standardInput, type: .swift )])
208208
}
209209

210+
func testRecordedInputModificationDates() throws {
211+
try withTemporaryDirectory { path in
212+
guard let cwd = localFileSystem
213+
.currentWorkingDirectory else { fatalError() }
214+
let main = path.appending(component: "main.swift")
215+
let util = path.appending(component: "util.swift")
216+
let utilRelative = util.relative(to: cwd)
217+
try localFileSystem.writeFileContents(main) { $0 <<< "print(hi)" }
218+
try localFileSystem.writeFileContents(util) { $0 <<< "let hi = \"hi\"" }
219+
220+
let mainMDate = try localFileSystem.getFileInfo(main).modTime
221+
let utilMDate = try localFileSystem.getFileInfo(util).modTime
222+
let driver = try Driver(args: [
223+
"swiftc", main.pathString, utilRelative.pathString,
224+
])
225+
XCTAssertEqual(driver.recordedInputModificationDates, [
226+
.init(file: .absolute(main), type: .swift) : mainMDate,
227+
.init(file: .relative(utilRelative), type: .swift) : utilMDate,
228+
])
229+
}
230+
}
231+
210232
func testPrimaryOutputKinds() throws {
211233
let driver1 = try Driver(args: ["swiftc", "foo.swift", "-emit-module"])
212234
XCTAssertEqual(driver1.compilerOutputType, .swiftModule)
@@ -453,7 +475,7 @@ final class SwiftDriverTests: XCTestCase {
453475
try withTemporaryFile { file in
454476
try assertNoDiagnostics { diags in
455477
try localFileSystem.writeFileContents(file.path) { $0 <<< contents }
456-
let outputFileMap = try OutputFileMap.load(file: file.path, diagnosticEngine: diags)
478+
let outputFileMap = try OutputFileMap.load(file: .absolute(file.path), diagnosticEngine: diags)
457479

458480
let object = try outputFileMap.getOutput(inputFile: .init(path: "/tmp/foo/Sources/foo/foo.swift"), outputType: .object)
459481
XCTAssertEqual(object.name, "/tmp/foo/.build/x86_64-apple-macosx/debug/foo.build/foo.swift.o")
@@ -489,7 +511,7 @@ final class SwiftDriverTests: XCTestCase {
489511
try sampleOutputFileMap.store(file: file.path, diagnosticEngine: DiagnosticsEngine())
490512
let contentsForDebugging = try localFileSystem.readFileContents(file.path).cString
491513
_ = contentsForDebugging
492-
let recoveredOutputFileMap = try OutputFileMap.load(file: file.path, diagnosticEngine: DiagnosticsEngine())
514+
let recoveredOutputFileMap = try OutputFileMap.load(file: .absolute(file.path), diagnosticEngine: DiagnosticsEngine())
493515
XCTAssertEqual(sampleOutputFileMap, recoveredOutputFileMap)
494516
}
495517
}
@@ -532,6 +554,38 @@ final class SwiftDriverTests: XCTestCase {
532554
XCTAssertEqual(expectedOutputFileMap, resolvedOutputFileMap)
533555
}
534556

557+
func testOutputFileMapRelativePathArg() throws {
558+
try withTemporaryDirectory { path in
559+
guard let cwd = localFileSystem
560+
.currentWorkingDirectory else { fatalError() }
561+
let outputFileMap = path.appending(component: "outputFileMap.json")
562+
try localFileSystem.writeFileContents(outputFileMap) {
563+
$0 <<< """
564+
{
565+
"": {
566+
"swift-dependencies": "build/master.swiftdeps"
567+
},
568+
"main.swift": {
569+
"object": "build/main.o",
570+
"dependencies": "build/main.o.d"
571+
},
572+
"util.swift": {
573+
"object": "build/util.o",
574+
"dependencies": "build/util.o.d"
575+
}
576+
}
577+
"""
578+
}
579+
let outputFileMapRelative = outputFileMap.relative(to: cwd).pathString
580+
// FIXME: Needs a better way to check that outputFileMap correctly loaded
581+
XCTAssertNoThrow(try Driver(args: [
582+
"swiftc",
583+
"--output-file-map", outputFileMapRelative,
584+
"main.swift", "util.swift",
585+
]))
586+
}
587+
}
588+
535589
func testResponseFileExpansion() throws {
536590
try withTemporaryDirectory { path in
537591
let diags = DiagnosticsEngine()

0 commit comments

Comments
 (0)