@@ -353,8 +353,6 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
353
353
}
354
354
}
355
355
356
-
357
-
358
356
/// Intended to be called during task dependency setup.
359
357
/// If remote caching is enabled along with integrated cache queries, it will request
360
358
/// a `ClangCachingMaterializeKeyTaskAction` as task dependency.
@@ -485,35 +483,49 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
485
483
outputDelegate: any TaskOutputDelegate
486
484
) throws {
487
485
let payload : DependencyValidationInfo . Payload
488
- if let traceFilePath {
489
- let traceFileContent = try fileSystem. read ( traceFilePath)
490
-
491
- let traceData : Array < TraceData >
492
- // clang will emit an empty file instead of an empty array when there's nothing to trace
493
- if traceFileContent. isEmpty {
494
- traceData = [ ]
495
- } else {
496
- do {
497
- traceData = try JSONDecoder ( ) . decode ( Array< TraceData> . self , from: Data ( traceFileContent) )
498
- } catch {
499
- throw StubError . error ( " Failed to decode json trace at \( traceFilePath. str) : \( error) " )
500
- }
501
- }
502
-
503
- var allFiles = Set < Path > ( )
504
- traceData. forEach { allFiles. formUnion ( Set ( $0. includes) ) }
486
+ if let traceFilePath,
487
+ let traceData = try parseTraceData ( Data ( fileSystem. read ( traceFilePath) ) ) {
505
488
506
489
outputDelegate. incrementTaskCounter ( . headerDependenciesValidatedTasks)
507
-
508
490
if isModular {
509
- let ( imports, includes) = separateImportsFromIncludes ( allFiles)
510
491
outputDelegate. incrementTaskCounter ( . moduleDependenciesValidatedTasks)
511
- outputDelegate. incrementTaskCounter ( . moduleDependenciesScanned, by: imports. count)
492
+ }
493
+
494
+ switch traceData {
495
+ case let . V1( traceDataV1) :
496
+ // mapping each header path to the set of locations that include it
497
+ var allFiles = [ Path : Set < SWBUtil . Diagnostic . Location > ] ( ) ;
498
+
499
+ for entry in traceDataV1 {
500
+ entry. includes. forEach { allFiles [ $0, default: [ ] ] . insert ( . path( entry. source, fileLocation: nil ) ) }
501
+ }
502
+
503
+ if isModular {
504
+ let ( imports, includes) = separateImportsFromIncludes ( allFiles)
505
+ outputDelegate. incrementTaskCounter ( . moduleDependenciesScanned, by: imports. count)
506
+ outputDelegate. incrementTaskCounter ( . headerDependenciesScanned, by: includes. count)
507
+ payload = . clangDependencies( imports: imports, includes: includes)
508
+ } else {
509
+ let includes = allFiles. map { file, locations in DependencyValidationInfo . Include ( path: file, includeLocations: Array ( locations) ) }
510
+ outputDelegate. incrementTaskCounter ( . headerDependenciesScanned, by: includes. count)
511
+ payload = . clangDependencies( imports: [ ] , includes: includes)
512
+ }
513
+ case let . V2( traceDataV2) :
514
+ var allIncludes = [ Path : Set < SWBUtil . Diagnostic . Location > ] ( ) ;
515
+ var allImports = [ String : Set < SWBUtil . Diagnostic . Location > ] ( ) ;
516
+
517
+ for entry in traceDataV2. dependencies {
518
+ entry. includes. forEach { allIncludes [ $0. file, default: [ ] ] . insert ( parseTraceSourceLocation ( $0. location) ) }
519
+ if isModular {
520
+ entry. imports. forEach { allImports [ $0. module, default: [ ] ] . insert ( parseTraceSourceLocation ( $0. location) ) }
521
+ }
522
+ }
523
+
524
+ let imports = allImports. map { name, locations in DependencyValidationInfo . Import ( dependency: ModuleDependency ( name: name, accessLevel: . Private, optional: false ) , importLocations: Array ( locations) ) }
525
+ let includes = allIncludes. map { file, locations in DependencyValidationInfo . Include ( path: file, includeLocations: Array ( locations) ) }
512
526
outputDelegate. incrementTaskCounter ( . headerDependenciesScanned, by: includes. count)
527
+ outputDelegate. incrementTaskCounter ( . moduleDependenciesScanned, by: imports. count)
513
528
payload = . clangDependencies( imports: imports, includes: includes)
514
- } else {
515
- outputDelegate. incrementTaskCounter ( . headerDependenciesScanned, by: allFiles. count)
516
- payload = . clangDependencies( imports: [ ] , includes: Array ( allFiles) )
517
529
}
518
530
} else {
519
531
outputDelegate. incrementTaskCounter ( . headerDependenciesNotValidatedTasks)
@@ -534,29 +546,27 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
534
546
}
535
547
}
536
548
537
- // Clang's dependency tracing does not currently clearly distinguish modular imports from non-modular includes.
538
- // Until that gets fixed , just guess that if the file is contained in a framework, it comes from a module with
549
+ // Clang's dependency tracing V1 did not clearly distinguish modular imports from non-modular includes.
550
+ // To keep supporting those trace files , just guess that if the file is contained in a framework, it comes from a module with
539
551
// the same name. That is obviously not going to be reliable but it unblocks us from continuing experiments with
540
552
// dependency specifications.
541
- private static func separateImportsFromIncludes( _ files: Set < Path > ) -> ( [ DependencyValidationInfo . Import ] , [ Path ] ) {
553
+ private static func separateImportsFromIncludes( _ files: [ Path : Set < SWBUtil . Diagnostic . Location > ] ) -> ( [ DependencyValidationInfo . Import ] , [ DependencyValidationInfo . Include ] ) {
542
554
func findFrameworkName( _ file: Path ) -> String ? {
543
555
if file. fileExtension == " framework " {
544
556
return file. basenameWithoutSuffix
545
557
}
546
558
return file. dirname. isEmpty || file. dirname. isRoot ? nil : findFrameworkName ( file. dirname)
547
559
}
548
- var moduleNames : [ String ] = [ ]
549
- var includeFiles : [ Path ] = [ ]
550
- for file in files {
560
+ var moduleImports : [ DependencyValidationInfo . Import ] = [ ]
561
+ var headerIncludes : [ DependencyValidationInfo . Include ] = [ ]
562
+ for ( file, includeLocations ) in files {
551
563
if let frameworkName = findFrameworkName ( file) {
552
- moduleNames . append ( frameworkName)
564
+ moduleImports . append ( DependencyValidationInfo . Import ( dependency : ModuleDependency ( name : frameworkName, accessLevel : . Private , optional : false ) , importLocations : Array ( includeLocations ) ) )
553
565
} else {
554
- includeFiles . append ( file)
566
+ headerIncludes . append ( DependencyValidationInfo . Include ( path : file, includeLocations : Array ( includeLocations ) ) )
555
567
}
556
568
}
557
- let moduleDependencies = moduleNames. map { ModuleDependency ( name: $0, accessLevel: . Private, optional: false ) }
558
- let moduleImports = moduleDependencies. map { DependencyValidationInfo . Import ( dependency: $0, importLocations: [ ] ) }
559
- return ( moduleImports, includeFiles)
569
+ return ( moduleImports, headerIncludes)
560
570
}
561
571
}
562
572
@@ -623,9 +633,65 @@ public final class ClangNonModularCompileTaskAction: TaskAction {
623
633
}
624
634
}
625
635
636
+ fileprivate func parseTraceData( _ data: Data ) throws -> TraceData ? {
637
+ if let jsonObject = try JSONSerialization . jsonObject ( with: data) as? [ String : Any ] ,
638
+ let version = jsonObject [ " version " ] as? String {
639
+ if version == " 2.0.0 " {
640
+ return . V2( try JSONDecoder ( ) . decode ( TraceData . TraceFileV2. self, from: data) )
641
+ }
642
+ return nil
643
+ } else {
644
+ // The initial unversioned format (v1) of the trace file generated from clang
645
+ // is a JSON array so deserializing it as a dictionary will fail.
646
+ return . V1( try JSONDecoder ( ) . decode ( TraceData . TraceFileV1. self, from: data) )
647
+ }
648
+ }
649
+
650
+ fileprivate func parseTraceSourceLocation( _ locationStr: String ) -> SWBUtil . Diagnostic . Location {
651
+ guard let match = locationStr. wholeMatch ( of: #/(.+):(\d+):(\d+)/# ) else {
652
+ return . unknown
653
+ }
654
+ let filename = Path ( match. 1 )
655
+ let line = Int ( match. 2 )
656
+ let column = Int ( match. 3 )
657
+ if let line {
658
+ return . path( filename, fileLocation: . textual( line: line, column: column) )
659
+ }
660
+ return . unknown
661
+ }
662
+
626
663
// Results from tracing header includes with "direct-per-file" filtering.
627
664
// This is used to validate dependencies.
628
- fileprivate struct TraceData : Decodable {
629
- let source : Path
630
- let includes : [ Path ]
665
+ fileprivate enum TraceData : Decodable {
666
+ fileprivate struct Include : Decodable {
667
+ let location : String
668
+ let file : Path
669
+ }
670
+
671
+ fileprivate struct Import : Decodable {
672
+ let location : String
673
+ let module : String
674
+ let file : Path
675
+ }
676
+
677
+ fileprivate struct TraceDataObjectV1 : Decodable {
678
+ let source : Path
679
+ let includes : [ Path ]
680
+ }
681
+
682
+ fileprivate struct TraceDataObjectV2 : Decodable {
683
+ let source : Path
684
+ let includes : [ Include ]
685
+ let imports : [ Import ]
686
+ }
687
+
688
+ fileprivate typealias TraceFileV1 = [ TraceDataObjectV1 ]
689
+
690
+ fileprivate struct TraceFileV2 : Decodable {
691
+ let version : String
692
+ let dependencies : [ TraceDataObjectV2 ]
693
+ }
694
+
695
+ case V1( TraceFileV1 )
696
+ case V2( TraceFileV2 )
631
697
}
0 commit comments