Skip to content

Commit d880e2e

Browse files
author
David Ungar
authored
Merge pull request #687 from davidungar/add-addition-test
[Incremental] Add input addition test and fix nondeterminism bug
2 parents 30882fe + bc3ed35 commit d880e2e

File tree

3 files changed

+176
-4
lines changed

3 files changed

+176
-4
lines changed

Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
193193
}
194194

195195
private func sortByCommandLineOrder(_ inputs: Set<TypedVirtualPath>) -> [TypedVirtualPath] {
196-
let indices = Dictionary(uniqueKeysWithValues: inputs.enumerated()
197-
.map {offset, element in (element, offset)})
198-
return inputs.sorted {indices[$0]! < indices[$1]!}
196+
inputFiles.filter (inputs.contains)
199197
}
200198

201199
/// Encapsulates information about an input the driver has determined has

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraphParts/Node.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ extension ModuleDependencyGraph {
3636
/// Must be able to change the fingerprint
3737
private(set) var keyAndFingerprint: KeyAndFingerprintHolder
3838

39-
var key: DependencyKey { keyAndFingerprint.key }
39+
/*@_spi(Testing)*/ public var key: DependencyKey { keyAndFingerprint.key }
4040
/*@_spi(Testing)*/ public var fingerprint: String? { keyAndFingerprint.fingerprint }
4141

4242
/// The dependencySource file that holds this entity iff the entities .swiftdeps (or in future, .swiftmodule) is known.

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,47 @@ extension IncrementalCompilationTests {
280280
try tryNoChange(checkDiagnostics: true)
281281
}
282282
}
283+
284+
func testAddingInput() throws {
285+
#if !os(Linux)
286+
try testAddingInput(basenameWithoutExt: "another", defining: "nameInAnother")
287+
#endif
288+
}
289+
290+
fileprivate struct AddingInputGraphs {
291+
let initial, afterAddition, afterAfterAddition: ModuleDependencyGraph
292+
var all: [ModuleDependencyGraph] {
293+
[initial, afterAddition, afterAfterAddition]
294+
}
295+
func verify(newInput: String, topLevelName: String) {
296+
initial.ensureOmits(sourceBasenameWithoutExt: newInput)
297+
initial.ensureOmits(name: topLevelName)
298+
XCTAssert(afterAddition.contains(sourceBasenameWithoutExt: newInput))
299+
XCTAssert(afterAfterAddition.contains(sourceBasenameWithoutExt: newInput))
300+
XCTAssert(afterAddition.contains(name: topLevelName))
301+
XCTAssert(afterAfterAddition.contains(name: topLevelName))
302+
}
303+
}
304+
305+
private func testAddingInput(basenameWithoutExt: String, defining topLevelName: String
306+
) throws {
307+
let initial = try tryInitial(checkDiagnostics: true).moduleDependencyGraph()
308+
309+
write("func \(topLevelName)() {}", to: basenameWithoutExt)
310+
let newInputsPath = inputPath(basename: basenameWithoutExt)
311+
OutputFileMapCreator.write(module: module,
312+
inputPaths: inputPathsAndContents.map {$0.0} + [newInputsPath],
313+
derivedData: derivedDataPath,
314+
to: OFM)
315+
let afterAddition = try tryAfterAddition( newInputBasenameWithoutExt: basenameWithoutExt, definingTopLevel: topLevelName)
316+
let afterAfterAddition = try tryAfterAfterAddition( newInputBasenameWithoutExt: basenameWithoutExt)
317+
318+
let resultantGraphs = AddingInputGraphs(initial: initial,
319+
afterAddition: afterAddition,
320+
afterAfterAddition: afterAfterAddition)
321+
322+
resultantGraphs.verify(newInput: basenameWithoutExt, topLevelName: topLevelName)
323+
}
283324
}
284325

285326
// MARK: - Incremental test stages
@@ -467,6 +508,139 @@ extension IncrementalCompilationTests {
467508
],
468509
whenAutolinking: autolinkLifecycleExpectations)
469510
}
511+
512+
private func tryAfterAddition(
513+
newInputBasenameWithoutExt: String,
514+
definingTopLevel topLevelName: String
515+
) throws -> ModuleDependencyGraph {
516+
let newInputsPath = inputPath(basename: newInputBasenameWithoutExt)
517+
return try doABuild(
518+
"after addition of \( newInputBasenameWithoutExt)",
519+
checkDiagnostics: true,
520+
extraArguments: [newInputsPath.pathString],
521+
expectingRemarks: [
522+
"Incremental compilation: Read dependency graph",
523+
"Incremental compilation: Enabling incremental cross-module building",
524+
"Incremental compilation: May skip current input: {compile: main.o <= main.swift}",
525+
"Incremental compilation: May skip current input: {compile: other.o <= other.swift}",
526+
"Incremental compilation: Scheduling new {compile: \( newInputBasenameWithoutExt).o <= \( newInputBasenameWithoutExt).swift}",
527+
"Incremental compilation: Has malformed dependency source; will queue {compile: \( newInputBasenameWithoutExt).o <= \( newInputBasenameWithoutExt).swift}",
528+
"Incremental compilation: Missing an output; will queue {compile: \( newInputBasenameWithoutExt).o <= \( newInputBasenameWithoutExt).swift}",
529+
"Incremental compilation: Queuing (initial): {compile: \( newInputBasenameWithoutExt).o <= \( newInputBasenameWithoutExt).swift}",
530+
"Incremental compilation: not scheduling dependents of \( newInputBasenameWithoutExt).swift: no entry in build record or dependency graph",
531+
"Incremental compilation: Skipping input: {compile: main.o <= main.swift}",
532+
"Incremental compilation: Skipping input: {compile: other.o <= other.swift}",
533+
"Found 1 batchable job",
534+
"Forming into 1 batch",
535+
"Adding {compile: \( newInputBasenameWithoutExt).swift} to batch 0",
536+
"Forming batch job from 1 constituents: \( newInputBasenameWithoutExt).swift",
537+
"Starting Compiling \( newInputBasenameWithoutExt).swift",
538+
"Finished Compiling \( newInputBasenameWithoutExt).swift",
539+
"Incremental compilation: New definition: interface of source file \( newInputBasenameWithoutExt).swiftdeps in \( newInputBasenameWithoutExt).swiftdeps",
540+
"Incremental compilation: New definition: implementation of source file \( newInputBasenameWithoutExt).swiftdeps in \( newInputBasenameWithoutExt).swiftdeps",
541+
"Incremental compilation: New definition: interface of top-level name '\(topLevelName)' in \( newInputBasenameWithoutExt).swiftdeps",
542+
"Incremental compilation: New definition: implementation of top-level name '\(topLevelName)' in \( newInputBasenameWithoutExt).swiftdeps",
543+
"Incremental compilation: Scheduling all post-compile jobs because something was compiled",
544+
"Starting Linking theModule",
545+
"Finished Linking theModule",
546+
"Skipped Compiling main.swift",
547+
"Skipped Compiling other.swift",
548+
],
549+
whenAutolinking: autolinkLifecycleExpectations)
550+
.moduleDependencyGraph()
551+
}
552+
553+
private func tryAfterAfterAddition(newInputBasenameWithoutExt: String
554+
) throws -> ModuleDependencyGraph {
555+
let newInputPath = inputPath(basename: newInputBasenameWithoutExt)
556+
return try doABuild(
557+
"after after addition of \(newInputBasenameWithoutExt)",
558+
checkDiagnostics: true,
559+
extraArguments: [newInputPath.pathString],
560+
expectingRemarks: [
561+
"Incremental compilation: Read dependency graph",
562+
"Incremental compilation: Enabling incremental cross-module building",
563+
"Incremental compilation: May skip current input: {compile: main.o <= main.swift}",
564+
"Incremental compilation: May skip current input: {compile: other.o <= other.swift}",
565+
"Incremental compilation: May skip current input: {compile: \(newInputBasenameWithoutExt).o <= \(newInputBasenameWithoutExt).swift}",
566+
"Incremental compilation: Skipping input: {compile: main.o <= main.swift}",
567+
"Incremental compilation: Skipping input: {compile: other.o <= other.swift}",
568+
"Incremental compilation: Skipping input: {compile: \(newInputBasenameWithoutExt).o <= \(newInputBasenameWithoutExt).swift}",
569+
"Incremental compilation: Skipping job: Linking theModule; oldest output is current",
570+
"Skipped Compiling \(newInputBasenameWithoutExt).swift",
571+
"Skipped Compiling main.swift",
572+
"Skipped Compiling other.swift",
573+
],
574+
whenAutolinking: autolinkLifecycleExpectations)
575+
.moduleDependencyGraph()
576+
}
577+
}
578+
fileprivate extension Driver {
579+
func moduleDependencyGraph() throws -> ModuleDependencyGraph {
580+
do {return try XCTUnwrap(incrementalCompilationState?.moduleDependencyGraph) }
581+
catch {
582+
XCTFail("no graph")
583+
throw error
584+
}
585+
}
586+
}
587+
588+
fileprivate extension ModuleDependencyGraph {
589+
/// A convenience for testing
590+
var allNodes: [Node] {
591+
var nodes = [Node]()
592+
nodeFinder.forEachNode {nodes.append($0)}
593+
return nodes
594+
}
595+
func contains(sourceBasenameWithoutExt target: String) -> Bool {
596+
allNodes.contains {$0.contains(sourceBasenameWithoutExt: target)}
597+
}
598+
func contains(name target: String) -> Bool {
599+
allNodes.contains {$0.contains(name: target)}
600+
}
601+
func ensureOmits(sourceBasenameWithoutExt target: String) {
602+
nodeFinder.forEachNode { node in
603+
XCTAssertFalse(node.contains(sourceBasenameWithoutExt: target))
604+
}
605+
}
606+
func ensureOmits(name: String) {
607+
nodeFinder.forEachNode { node in
608+
XCTAssertFalse(node.contains(name: name))
609+
}
610+
}
611+
}
612+
613+
fileprivate extension ModuleDependencyGraph.Node {
614+
func contains(sourceBasenameWithoutExt target: String) -> Bool {
615+
switch key.designator {
616+
case .sourceFileProvide(name: let name):
617+
return (try? VirtualPath(path: name))
618+
.map {$0.basenameWithoutExt == target}
619+
?? false
620+
case .externalDepend(let externalDependency):
621+
return externalDependency.path.map {
622+
$0.basenameWithoutExt == target
623+
}
624+
?? false
625+
case .topLevel, .dynamicLookup, .nominal, .member, .potentialMember:
626+
return false
627+
}
628+
}
629+
630+
func contains(name target: String) -> Bool {
631+
switch key.designator {
632+
case .topLevel(name: let name),
633+
.dynamicLookup(name: let name):
634+
return name == target
635+
case .externalDepend, .sourceFileProvide:
636+
return false
637+
case .nominal(context: let context),
638+
.potentialMember(context: let context):
639+
return context.range(of: target) != nil
640+
case .member(context: let context, name: let name):
641+
return context.range(of: target) != nil || name == target
642+
}
643+
}
470644
}
471645

472646
// MARK: - Incremental test perturbation helpers

0 commit comments

Comments
 (0)