Skip to content

Commit c12f1d2

Browse files
authored
Merge pull request #697 from CodaFi/statistically-speaking
Use `stat` Instead of `lstat`
2 parents f2a4eec + 5ee692c commit c12f1d2

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

Sources/SwiftDriver/Utilities/VirtualPath.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -721,18 +721,33 @@ extension TSCBasic.FileSystem {
721721
try resolvingVirtualPath(path, apply: exists)
722722
}
723723

724+
/// Retrieves the last modification time of the file referenced at the given path.
725+
///
726+
/// If the given file path references a symbolic link, the modification time for the *linked file*
727+
/// - not the symlink itself - is returned.
728+
///
729+
/// - Parameter file: The path to a file.
730+
/// - Throws: `SystemError` if the underlying `stat` operation fails.
731+
/// - Returns: A `Date` value containing the last modification time.
724732
public func lastModificationTime(for file: VirtualPath) throws -> Date {
725733
try resolvingVirtualPath(file) { path in
726734
#if os(macOS)
727735
var s = Darwin.stat()
728-
let err = lstat(path.pathString, &s)
736+
let err = stat(path.pathString, &s)
729737
guard err == 0 else {
730738
throw SystemError.stat(errno, path.pathString)
731739
}
732740
let ti = (TimeInterval(s.st_mtimespec.tv_sec) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtimespec.tv_nsec))
733741
return Date(timeIntervalSinceReferenceDate: ti)
734742
#else
735-
return try localFileSystem.getFileInfo(file).modTime
743+
// `getFileInfo` is going to ask Foundation to stat this path, and
744+
// Foundation is always going to use `lstat` to do so. This is going to
745+
// do the wrong thing for symbolic links, for which we always want to
746+
// retrieve the mod time of the underlying file. This makes build systems
747+
// that regenerate lots of symlinks but do not otherwise alter the
748+
// contents of files - like Bazel - quite happy.
749+
let path = resolveSymlinks(path)
750+
return try localFileSystem.getFileInfo(path).modTime
736751
#endif
737752
}
738753
}

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,22 @@ extension IncrementalCompilationTests {
285285
try checkNullBuild(checkDiagnostics: true)
286286
}
287287
}
288+
289+
func testSymlinkModification() throws {
290+
// Remap
291+
// main.swift -> links/main.swift
292+
// other.swift -> links/other.swift
293+
for (file, _) in self.inputPathsAndContents {
294+
try localFileSystem.createDirectory(tempDir.appending(component: "links"))
295+
let linkTarget = tempDir.appending(component: "links").appending(component: file.basename)
296+
try localFileSystem.move(from: file, to: linkTarget)
297+
try localFileSystem.removeFileTree(file)
298+
try localFileSystem.createSymbolicLink(file, pointingAt: linkTarget, relative: false)
299+
}
300+
try buildInitialState()
301+
try checkReactionToTouchingSymlinks(checkDiagnostics: true)
302+
try checkReactionToTouchingSymlinkTargets(checkDiagnostics: true)
303+
}
288304
}
289305

290306
// MARK: - Test adding an input
@@ -826,6 +842,68 @@ extension IncrementalCompilationTests {
826842

827843
return graph
828844
}
845+
846+
private func checkReactionToTouchingSymlinks(
847+
checkDiagnostics: Bool = false,
848+
extraArguments: [String] = []
849+
) throws {
850+
for (file, _) in self.inputPathsAndContents {
851+
try localFileSystem.removeFileTree(file)
852+
let linkTarget = tempDir.appending(component: "links").appending(component: file.basename)
853+
try localFileSystem.createSymbolicLink(file, pointingAt: linkTarget, relative: false)
854+
}
855+
try doABuild(
856+
"touch both symlinks; non-propagating",
857+
checkDiagnostics: checkDiagnostics,
858+
extraArguments: extraArguments,
859+
expectingRemarks: [
860+
"Enabling incremental cross-module building",
861+
"Incremental compilation: Read dependency graph",
862+
"Incremental compilation: May skip current input: {compile: main.o <= main.swift}",
863+
"Incremental compilation: May skip current input: {compile: other.o <= other.swift}",
864+
"Incremental compilation: Skipping input: {compile: main.o <= main.swift}",
865+
"Incremental compilation: Skipping input: {compile: other.o <= other.swift}",
866+
"Incremental compilation: Skipping job: Linking theModule",
867+
"Skipped Compiling main.swift",
868+
"Skipped Compiling other.swift",
869+
],
870+
whenAutolinking: autolinkLifecycleExpectations)
871+
}
872+
873+
private func checkReactionToTouchingSymlinkTargets(
874+
checkDiagnostics: Bool = false,
875+
extraArguments: [String] = []
876+
) throws {
877+
for (file, contents) in self.inputPathsAndContents {
878+
let linkTarget = tempDir.appending(component: "links").appending(component: file.basename)
879+
try! localFileSystem.writeFileContents(linkTarget) { $0 <<< contents }
880+
}
881+
try doABuild(
882+
"touch both symlink targets; non-propagating",
883+
checkDiagnostics: checkDiagnostics,
884+
extraArguments: extraArguments,
885+
expectingRemarks: [
886+
"Enabling incremental cross-module building",
887+
"Incremental compilation: Read dependency graph",
888+
"Incremental compilation: Scheduing changed input {compile: main.o <= main.swift}",
889+
"Incremental compilation: Scheduing changed input {compile: other.o <= other.swift}",
890+
"Incremental compilation: Queuing (initial): {compile: main.o <= main.swift}",
891+
"Incremental compilation: Queuing (initial): {compile: other.o <= other.swift}",
892+
"Incremental compilation: not scheduling dependents of main.swift; unknown changes",
893+
"Incremental compilation: not scheduling dependents of other.swift; unknown changes",
894+
"Found 2 batchable jobs",
895+
"Forming into 1 batch",
896+
"Adding {compile: main.swift} to batch 0",
897+
"Adding {compile: other.swift} to batch 0",
898+
"Forming batch job from 2 constituents: main.swift, other.swift",
899+
"Starting Compiling main.swift, other.swift",
900+
"Finished Compiling main.swift, other.swift",
901+
"Incremental compilation: Scheduling all post-compile jobs because something was compiled",
902+
"Starting Linking theModule",
903+
"Finished Linking theModule",
904+
],
905+
whenAutolinking: autolinkLifecycleExpectations)
906+
}
829907
}
830908

831909
// MARK: - Incremental test perturbation helpers

0 commit comments

Comments
 (0)