@@ -337,28 +337,12 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
337337
338338 if lastResult == . succeeded {
339339 // Verify the dependencies from the trace data.
340- let payload : DependencyValidationInfo . Payload
341- if let traceFilePath {
342- let fs = executionDelegate. fs
343- let traceData = try JSONDecoder ( ) . decode ( Array< TraceData> . self , from: Data ( fs. read ( traceFilePath) ) )
344-
345- var allFiles = Set < Path > ( )
346- traceData. forEach { allFiles. formUnion ( Set ( $0. includes) ) }
347- let ( imports, includes) = separateImportsFromIncludes ( allFiles)
348- payload = . clangDependencies( imports: imports, includes: includes)
349- } else {
350- payload = . unsupported
351- }
352-
353- if let dependencyValidationOutputPath {
354- let validationInfo = DependencyValidationInfo ( payload: payload)
355- _ = try executionDelegate. fs. writeIfChanged (
356- dependencyValidationOutputPath,
357- contents: ByteString (
358- JSONEncoder ( outputFormatting: . sortedKeys) . encode ( validationInfo)
359- )
360- )
361- }
340+ try Self . handleDependencyValidation (
341+ traceFilePath: traceFilePath,
342+ dependencyValidationOutputPath: dependencyValidationOutputPath,
343+ fileSystem: executionDelegate. fs,
344+ isModular: true
345+ )
362346 }
363347
364348 return lastResult ?? . failed
@@ -368,30 +352,7 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
368352 }
369353 }
370354
371- // Clang's dependency tracing does not currently clearly distinguish modular imports from non-modular includes.
372- // Until that gets fixed, just guess that if the file is contained in a framework, it comes from a module with
373- // the same name. That is obviously not going to be reliable but it unblocks us from continuing experiments with
374- // dependency specifications.
375- private func separateImportsFromIncludes( _ files: Set < Path > ) -> ( [ DependencyValidationInfo . Import ] , [ Path ] ) {
376- func findFrameworkName( _ file: Path ) -> String ? {
377- if file. fileExtension == " framework " {
378- return file. basenameWithoutSuffix
379- }
380- return file. dirname. isEmpty || file. dirname. isRoot ? nil : findFrameworkName ( file. dirname)
381- }
382- var moduleNames : [ String ] = [ ]
383- var includeFiles : [ Path ] = [ ]
384- for file in files {
385- if let frameworkName = findFrameworkName ( file) {
386- moduleNames. append ( frameworkName)
387- } else {
388- includeFiles. append ( file)
389- }
390- }
391- let moduleDependencies = moduleNames. map { ModuleDependency ( name: $0, accessLevel: . Private, optional: false ) }
392- let moduleImports = moduleDependencies. map { DependencyValidationInfo . Import ( dependency: $0, importLocations: [ ] ) }
393- return ( moduleImports, includeFiles)
394- }
355+
395356
396357 /// Intended to be called during task dependency setup.
397358 /// If remote caching is enabled along with integrated cache queries, it will request
@@ -512,6 +473,66 @@ public final class ClangCompileTaskAction: TaskAction, BuildValueValidatingTaskA
512473 activityReporter: dynamicExecutionDelegate
513474 )
514475 }
476+
477+ /// Handles dependency validation by reading trace data and writing out DependencyValidationInfo.
478+ /// This is shared between modular and non-modular compilation tasks.
479+ fileprivate static func handleDependencyValidation(
480+ traceFilePath: Path ? ,
481+ dependencyValidationOutputPath: Path ? ,
482+ fileSystem: any FSProxy ,
483+ isModular: Bool
484+ ) throws {
485+ let payload : DependencyValidationInfo . Payload
486+ if let traceFilePath {
487+ let traceData = try JSONDecoder ( ) . decode ( Array< TraceData> . self , from: Data ( fileSystem. read ( traceFilePath) ) )
488+ var allFiles = Set < Path > ( )
489+ traceData. forEach { allFiles. formUnion ( Set ( $0. includes) ) }
490+
491+ if isModular {
492+ let ( imports, includes) = separateImportsFromIncludes ( allFiles)
493+ payload = . clangDependencies( imports: imports, includes: includes)
494+ } else {
495+ payload = . clangDependencies( imports: [ ] , includes: Array ( allFiles) )
496+ }
497+ } else {
498+ payload = . unsupported
499+ }
500+
501+ if let dependencyValidationOutputPath {
502+ let validationInfo = DependencyValidationInfo ( payload: payload)
503+ _ = try fileSystem. writeIfChanged (
504+ dependencyValidationOutputPath,
505+ contents: ByteString (
506+ JSONEncoder ( outputFormatting: . sortedKeys) . encode ( validationInfo)
507+ )
508+ )
509+ }
510+ }
511+
512+ // Clang's dependency tracing does not currently clearly distinguish modular imports from non-modular includes.
513+ // Until that gets fixed, just guess that if the file is contained in a framework, it comes from a module with
514+ // the same name. That is obviously not going to be reliable but it unblocks us from continuing experiments with
515+ // dependency specifications.
516+ private static func separateImportsFromIncludes( _ files: Set < Path > ) -> ( [ DependencyValidationInfo . Import ] , [ Path ] ) {
517+ func findFrameworkName( _ file: Path ) -> String ? {
518+ if file. fileExtension == " framework " {
519+ return file. basenameWithoutSuffix
520+ }
521+ return file. dirname. isEmpty || file. dirname. isRoot ? nil : findFrameworkName ( file. dirname)
522+ }
523+ var moduleNames : [ String ] = [ ]
524+ var includeFiles : [ Path ] = [ ]
525+ for file in files {
526+ if let frameworkName = findFrameworkName ( file) {
527+ moduleNames. append ( frameworkName)
528+ } else {
529+ includeFiles. append ( file)
530+ }
531+ }
532+ let moduleDependencies = moduleNames. map { ModuleDependency ( name: $0, accessLevel: . Private, optional: false ) }
533+ let moduleImports = moduleDependencies. map { DependencyValidationInfo . Import ( dependency: $0, importLocations: [ ] ) }
534+ return ( moduleImports, includeFiles)
535+ }
515536}
516537
517538public final class ClangNonModularCompileTaskAction : TaskAction {
@@ -560,27 +581,12 @@ public final class ClangNonModularCompileTaskAction: TaskAction {
560581 let execResult = processDelegate. commandResult ?? . failed
561582
562583 if execResult == . succeeded {
563- let payload : DependencyValidationInfo . Payload
564- if let traceFilePath {
565- let fs = executionDelegate. fs
566- let traceData = try JSONDecoder ( ) . decode ( Array< TraceData> . self , from: fs. readMemoryMapped ( traceFilePath) )
567-
568- var allFiles = Set < Path > ( )
569- traceData. forEach { allFiles. formUnion ( Set ( $0. includes) ) }
570- payload = . clangDependencies( imports: [ ] , includes: Array ( allFiles) )
571- } else {
572- payload = . unsupported
573- }
574-
575- if let dependencyValidationOutputPath {
576- let validationInfo = DependencyValidationInfo ( payload: payload)
577- _ = try executionDelegate. fs. writeIfChanged (
578- dependencyValidationOutputPath,
579- contents: ByteString (
580- JSONEncoder ( outputFormatting: . sortedKeys) . encode ( validationInfo)
581- )
582- )
583- }
584+ try ClangCompileTaskAction . handleDependencyValidation (
585+ traceFilePath: traceFilePath,
586+ dependencyValidationOutputPath: dependencyValidationOutputPath,
587+ fileSystem: executionDelegate. fs,
588+ isModular: false
589+ )
584590 }
585591
586592 return execResult
0 commit comments