@@ -280,6 +280,47 @@ extension IncrementalCompilationTests {
280
280
try tryNoChange ( checkDiagnostics: true )
281
281
}
282
282
}
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
+ }
283
324
}
284
325
285
326
// MARK: - Incremental test stages
@@ -467,6 +508,139 @@ extension IncrementalCompilationTests {
467
508
] ,
468
509
whenAutolinking: autolinkLifecycleExpectations)
469
510
}
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
+ }
470
644
}
471
645
472
646
// MARK: - Incremental test perturbation helpers
0 commit comments