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