@@ -242,6 +242,10 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
242242
243243 public var hasIntegratedAPIDigesterSupport : Bool { true }
244244
245+ public var enableTaskBacktraces : Bool {
246+ self . buildParameters. outputParameters. enableTaskBacktraces
247+ }
248+
245249 public init (
246250 buildParameters: BuildParameters ,
247251 packageGraphLoader: @escaping ( ) async throws -> ModulesGraph ,
@@ -546,12 +550,14 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
546550 return try await withService ( connectionMode: . inProcessStatic( swiftbuildServiceEntryPoint) ) { service in
547551 let derivedDataPath = self . buildParameters. dataPath
548552
549- let progressAnimation = ProgressAnimation . ninja (
550- stream: self . outputStream,
551- verbose: self . logLevel. isVerbose
553+ let buildMessageHandler = SwiftBuildSystemMessageHandler (
554+ observabilityScope: self . observabilityScope,
555+ outputStream: self . outputStream,
556+ logLevel: self . logLevel,
557+ enableBacktraces: self . enableTaskBacktraces,
558+ buildDelegate: self . delegate
552559 )
553560
554- var serializedDiagnosticPathsByTargetName : [ String : [ Basics . AbsolutePath ] ] = [ : ]
555561 do {
556562 try await withSession ( service: service, name: self . buildParameters. pifManifest. pathString, toolchain: self . buildParameters. toolchain, packageManagerResourcesDirectory: self . packageManagerResourcesDirectory) { session, _ in
557563 self . outputStream. send ( " Building for \( self . buildParameters. configuration == . debug ? " debugging " : " production " ) ... \n " )
@@ -591,173 +597,32 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
591597
592598 let request = try await self . makeBuildRequest ( session: session, configuredTargets: configuredTargets, derivedDataPath: derivedDataPath, symbolGraphOptions: symbolGraphOptions)
593599
594- struct BuildState {
595- private var targetsByID : [ Int : SwiftBuild . SwiftBuildMessage . TargetStartedInfo ] = [ : ]
596- private var activeTasks : [ Int : SwiftBuild . SwiftBuildMessage . TaskStartedInfo ] = [ : ]
597- var collectedBacktraceFrames = SWBBuildOperationCollectedBacktraceFrames ( )
598-
599- mutating func started( task: SwiftBuild . SwiftBuildMessage . TaskStartedInfo ) throws {
600- if activeTasks [ task. taskID] != nil {
601- throw Diagnostics . fatalError
602- }
603- activeTasks [ task. taskID] = task
604- }
605-
606- mutating func completed( task: SwiftBuild . SwiftBuildMessage . TaskCompleteInfo ) throws -> SwiftBuild . SwiftBuildMessage . TaskStartedInfo {
607- guard let task = activeTasks [ task. taskID] else {
608- throw Diagnostics . fatalError
609- }
610- return task
611- }
612-
613- mutating func started( target: SwiftBuild . SwiftBuildMessage . TargetStartedInfo ) throws {
614- if targetsByID [ target. targetID] != nil {
615- throw Diagnostics . fatalError
616- }
617- targetsByID [ target. targetID] = target
618- }
619-
620- mutating func target( for task: SwiftBuild . SwiftBuildMessage . TaskStartedInfo ) throws -> SwiftBuild . SwiftBuildMessage . TargetStartedInfo ? {
621- guard let id = task. targetID else {
622- return nil
623- }
624- guard let target = targetsByID [ id] else {
625- throw Diagnostics . fatalError
626- }
627- return target
628- }
629- }
630-
631- func emitEvent( _ message: SwiftBuild . SwiftBuildMessage , buildState: inout BuildState ) throws {
632- guard !self . logLevel. isQuiet else { return }
633- switch message {
634- case . buildCompleted( let info) :
635- progressAnimation. complete ( success: info. result == . ok)
636- if info. result == . cancelled {
637- self . delegate? . buildSystemDidCancel ( self )
638- } else {
639- self . delegate? . buildSystem ( self , didFinishWithResult: info. result == . ok)
640- }
641- case . didUpdateProgress( let progressInfo) :
642- var step = Int ( progressInfo. percentComplete)
643- if step < 0 { step = 0 }
644- let message = if let targetName = progressInfo. targetName {
645- " \( targetName) \( progressInfo. message) "
646- } else {
647- " \( progressInfo. message) "
648- }
649- progressAnimation. update ( step: step, total: 100 , text: message)
650- self . delegate? . buildSystem ( self , didUpdateTaskProgress: message)
651- case . diagnostic( let info) :
652- func emitInfoAsDiagnostic( info: SwiftBuildMessage . DiagnosticInfo ) {
653- let fixItsDescription = if info. fixIts. hasContent {
654- " : " + info. fixIts. map { String ( describing: $0) } . joined ( separator: " , " )
655- } else {
656- " "
657- }
658- let message = if let locationDescription = info. location. userDescription {
659- " \( locationDescription) \( info. message) \( fixItsDescription) "
660- } else {
661- " \( info. message) \( fixItsDescription) "
662- }
663- let severity : Diagnostic . Severity = switch info. kind {
664- case . error: . error
665- case . warning: . warning
666- case . note: . info
667- case . remark: . debug
668- }
669- self . observabilityScope. emit ( severity: severity, message: " \( message) \n " )
670-
671- for childDiagnostic in info. childDiagnostics {
672- emitInfoAsDiagnostic ( info: childDiagnostic)
673- }
674- }
675-
676- emitInfoAsDiagnostic ( info: info)
677- case . output( let info) :
678- self . observabilityScope. emit ( info: " \( String ( decoding: info. data, as: UTF8 . self) ) " )
679- case . taskStarted( let info) :
680- try buildState. started ( task: info)
681-
682- if let commandLineDisplay = info. commandLineDisplayString {
683- self . observabilityScope. emit ( info: " \( info. executionDescription) \n \( commandLineDisplay) " )
684- } else {
685- self . observabilityScope. emit ( info: " \( info. executionDescription) " )
686- }
687-
688- if self . logLevel. isVerbose {
689- if let commandLineDisplay = info. commandLineDisplayString {
690- self . outputStream. send ( " \( info. executionDescription) \n \( commandLineDisplay) " )
691- } else {
692- self . outputStream. send ( " \( info. executionDescription) " )
693- }
694- }
695- let targetInfo = try buildState. target ( for: info)
696- self . delegate? . buildSystem ( self , willStartCommand: BuildSystemCommand ( info, targetInfo: targetInfo) )
697- self . delegate? . buildSystem ( self , didStartCommand: BuildSystemCommand ( info, targetInfo: targetInfo) )
698- case . taskComplete( let info) :
699- let startedInfo = try buildState. completed ( task: info)
700- if info. result != . success {
701- self . observabilityScope. emit ( severity: . error, message: " \( startedInfo. ruleInfo) failed with a nonzero exit code. Command line: \( startedInfo. commandLineDisplayString ?? " <no command line> " ) " )
702- }
703- let targetInfo = try buildState. target ( for: startedInfo)
704- self . delegate? . buildSystem ( self , didFinishCommand: BuildSystemCommand ( startedInfo, targetInfo: targetInfo) )
705- if let targetName = targetInfo? . targetName {
706- serializedDiagnosticPathsByTargetName [ targetName, default: [ ] ] . append ( contentsOf: startedInfo. serializedDiagnosticsPaths. compactMap {
707- try ? Basics . AbsolutePath ( validating: $0. pathString)
708- } )
709- }
710- if self . buildParameters. outputParameters. enableTaskBacktraces {
711- if let id = SWBBuildOperationBacktraceFrame . Identifier ( taskSignatureData: Data ( startedInfo. taskSignature. utf8) ) ,
712- let backtrace = SWBTaskBacktrace ( from: id, collectedFrames: buildState. collectedBacktraceFrames) {
713- let formattedBacktrace = backtrace. renderTextualRepresentation ( )
714- if !formattedBacktrace. isEmpty {
715- self . observabilityScope. emit ( info: " Task backtrace: \n \( formattedBacktrace) " )
716- }
717- }
718- }
719- case . targetStarted( let info) :
720- try buildState. started ( target: info)
721- case . backtraceFrame( let info) :
722- if self . buildParameters. outputParameters. enableTaskBacktraces {
723- buildState. collectedBacktraceFrames. add ( frame: info)
724- }
725- case . planningOperationStarted, . planningOperationCompleted, . reportBuildDescription, . reportPathMap, . preparedForIndex, . buildStarted, . preparationComplete, . targetUpToDate, . targetComplete, . taskUpToDate:
726- break
727- case . buildDiagnostic, . targetDiagnostic, . taskDiagnostic:
728- break // deprecated
729- case . buildOutput, . targetOutput, . taskOutput:
730- break // deprecated
731- @unknown default :
732- break
733- }
734- }
735-
736600 let operation = try await session. createBuildOperation (
737601 request: request,
738602 delegate: SwiftBuildSystemPlanningOperationDelegate ( ) ,
739603 retainBuildDescription: true
740604 )
741605
742606 var buildDescriptionID : SWBBuildDescriptionID ? = nil
743- var buildState = BuildState ( )
744607 for try await event in try await operation. start ( ) {
745608 if case . reportBuildDescription( let info) = event {
746609 if buildDescriptionID != nil {
747610 self . observabilityScope. emit ( debug: " build unexpectedly reported multiple build description IDs " )
748611 }
749612 buildDescriptionID = SWBBuildDescriptionID ( info. buildDescriptionID)
750613 }
751- try emitEvent ( event, buildState: & buildState)
614+ if let delegateCallback = try buildMessageHandler. emitEvent ( event) {
615+ delegateCallback ( self )
616+ }
752617 }
753618
754619 await operation. waitForCompletion ( )
755620
756621 switch operation. state {
757622 case . succeeded:
758623 guard !self . logLevel. isQuiet else { return }
759- progressAnimation. update ( step: 100 , total: 100 , text: " " )
760- progressAnimation. complete ( success: true )
624+ buildMessageHandler . progressAnimation. update ( step: 100 , total: 100 , text: " " )
625+ buildMessageHandler . progressAnimation. complete ( success: true )
761626 let duration = ContinuousClock . Instant. now - buildStartTime
762627 let formattedDuration = duration. formatted ( . units( allowed: [ . seconds] , fractionalPart: . show( length: 2 , rounded: . up) ) )
763628 self . outputStream. send ( " Build complete! ( \( formattedDuration) ) \n " )
@@ -824,7 +689,7 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
824689 }
825690
826691 return BuildResult (
827- serializedDiagnosticPathsByTargetName: . success( serializedDiagnosticPathsByTargetName) ,
692+ serializedDiagnosticPathsByTargetName: . success( buildMessageHandler . serializedDiagnosticPathsByTargetName) ,
828693 symbolGraph: SymbolGraphResult (
829694 outputLocationForTarget: { target, buildParameters in
830695 return [ " \( buildParameters. triple. archName) " , " \( target) .symbolgraphs " ]
@@ -1299,46 +1164,6 @@ extension String {
12991164 }
13001165}
13011166
1302- fileprivate extension SwiftBuild . SwiftBuildMessage . DiagnosticInfo . Location {
1303- var userDescription : String ? {
1304- switch self {
1305- case . path( let path, let fileLocation) :
1306- switch fileLocation {
1307- case . textual( let line, let column) :
1308- var description = " \( path) : \( line) "
1309- if let column { description += " : \( column) " }
1310- return description
1311- case . object( let identifier) :
1312- return " \( path) : \( identifier) "
1313- case . none:
1314- return path
1315- }
1316-
1317- case . buildSettings( let names) :
1318- return names. joined ( separator: " , " )
1319-
1320- case . buildFiles( let buildFiles, let targetGUID) :
1321- return " \( targetGUID) : " + buildFiles. map { String ( describing: $0) } . joined ( separator: " , " )
1322-
1323- case . unknown:
1324- return nil
1325- }
1326- }
1327- }
1328-
1329- fileprivate extension BuildSystemCommand {
1330- init ( _ taskStartedInfo: SwiftBuildMessage . TaskStartedInfo , targetInfo: SwiftBuildMessage . TargetStartedInfo ? ) {
1331- self = . init(
1332- name: taskStartedInfo. executionDescription,
1333- targetName: targetInfo? . targetName,
1334- description: taskStartedInfo. commandLineDisplayString ?? " " ,
1335- serializedDiagnosticPaths: taskStartedInfo. serializedDiagnosticsPaths. compactMap {
1336- try ? Basics . AbsolutePath ( validating: $0. pathString)
1337- }
1338- )
1339- }
1340- }
1341-
13421167fileprivate extension Triple {
13431168 var deploymentTargetSettingName : String ? {
13441169 switch ( self . os, self . environment) {
0 commit comments