@@ -176,6 +176,60 @@ public struct DiscoveredLdLinkerToolSpecInfo: DiscoveredCommandLineToolSpecInfo
176176 /// Maximum number of undefined symbols to emit. Might be configurable in the future.
177177 let undefinedSymbolCountLimit = 100
178178
179+ override public func write( bytes: ByteString ) {
180+
181+ // Split the buffer into slices separated by newlines. The last slice represents the partial last line (there always is one, even if it's empty).
182+ var lines = bytes. split ( separator: UInt8 ( ascii: " \n " ) , maxSplits: . max, omittingEmptySubsequences: false )
183+
184+ // Any unparsed bytes belong to the first line. We don't want to run `split` over these because it can lead to accidentally quadratic behavior if write is called many times per line.
185+ lines [ 0 ] = unparsedBytes + lines[ 0 ]
186+
187+ let linesToParse = lines. dropLast ( )
188+
189+ if let target = self . task. forTarget? . target {
190+ // Linker errors and warnings take more effort to get actionable information out of build logs than those for source files. This is because the linker does not have the path to the project or target name so they are not included in the message.
191+ //
192+ // Prepend the path to the project and target name to any error or warning lines.
193+ // Example input:
194+ // ld: warning: linking with (/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio) but not using any symbols from it
195+ // Example output:
196+ // /Path/To/ProjectFolder/ProjectName.xcodeproj: TargetName: ld: warning: linking with (/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio) but not using any symbols from it
197+
198+ let workspace = self . workspaceContext. workspace
199+ let projectPath = Workspace . projectPath ( for: target, workspace: workspace)
200+ let targetName = target. name
201+
202+ let processedLines : [ ByteString ] = linesToParse. map { lineBytes in
203+ let lineString = String ( decoding: lineBytes, as: Unicode . UTF8. self)
204+ if lineString. contains ( " : error: " )
205+ || lineString. contains ( " : warning: " ) {
206+
207+ let issueString = " \( projectPath. str) : \( targetName) : \( lineString) "
208+ return ByteString ( encodingAsUTF8: issueString)
209+ }
210+ return ByteString ( lineBytes)
211+ }
212+
213+ // Forward the bytes
214+ let processedBytes = ByteString ( processedLines. joined ( separator: ByteString ( " \n " ) ) )
215+ delegate. emitOutput ( processedBytes)
216+ }
217+ else {
218+ // Forward the bytes
219+ let processedBytes = ByteString ( linesToParse. joined ( separator: ByteString ( " \n " ) ) )
220+ delegate. emitOutput ( processedBytes)
221+ }
222+
223+ // Parse any complete lines of output.
224+ for line in linesToParse {
225+ parseLine ( line)
226+ }
227+
228+ // Track the last, incomplete line to as the unparsed bytes.
229+ unparsedBytes = lines. last ?? [ ]
230+ }
231+
232+ @discardableResult
179233 override func parseLine< S: Collection > ( _ lineBytes: S ) -> Bool where S. Element == UInt8 {
180234
181235 // Create a string that we can examine. Use the non-failable constructor, so that we are robust against potentially invalid UTF-8.
@@ -190,11 +244,13 @@ public struct DiscoveredLdLinkerToolSpecInfo: DiscoveredCommandLineToolSpecInfo
190244 }
191245 else if let match = LdLinkerOutputParser . problemMessageRegEx. firstMatch ( in: lineString) , match [ 3 ] . hasPrefix ( " symbol(s) not found " ) {
192246 // It's time to emit all the symbols. We emit each as a separate message.
247+ let projectLocation = Workspace . projectLocation ( for: self . task. forTarget, workspaceContext: self . workspaceContext)
248+
193249 for symbol in undefinedSymbols. prefix ( undefinedSymbolCountLimit) {
194- delegate. diagnosticsEngine. emit ( Diagnostic ( behavior: . error, location: . unknown , data: DiagnosticData ( " Undefined symbol: \( symbol) " ) , appendToOutputStream: false ) )
250+ delegate. diagnosticsEngine. emit ( Diagnostic ( behavior: . error, location: projectLocation , data: DiagnosticData ( " Undefined symbol: \( symbol) " ) , appendToOutputStream: false ) )
195251 }
196252 if undefinedSymbols. count > undefinedSymbolCountLimit {
197- delegate. diagnosticsEngine. emit ( Diagnostic ( behavior: . note, location: . unknown , data: DiagnosticData ( " ( \( undefinedSymbols. count - undefinedSymbolCountLimit) additional undefined symbols are shown in the transcript " ) , appendToOutputStream: false ) )
253+ delegate. diagnosticsEngine. emit ( Diagnostic ( behavior: . note, location: projectLocation , data: DiagnosticData ( " ( \( undefinedSymbols. count - undefinedSymbolCountLimit) additional undefined symbols are shown in the transcript " ) , appendToOutputStream: false ) )
198254 }
199255 collectingUndefinedSymbols = false
200256 undefinedSymbols = [ ]
@@ -213,7 +269,8 @@ public struct DiscoveredLdLinkerToolSpecInfo: DiscoveredCommandLineToolSpecInfo
213269 let severity = match [ 2 ] . isEmpty ? " error " : match [ 2 ]
214270 let behavior = Diagnostic . Behavior ( name: severity) ?? . note
215271 let message = match [ 3 ] . prefix ( 1 ) . localizedCapitalized + match[ 3 ] . dropFirst ( )
216- let diagnostic = Diagnostic ( behavior: behavior, location: . unknown, data: DiagnosticData ( message) , appendToOutputStream: false )
272+ let projectLocation = Workspace . projectLocation ( for: self . task. forTarget, workspaceContext: self . workspaceContext)
273+ let diagnostic = Diagnostic ( behavior: behavior, location: projectLocation, data: DiagnosticData ( message) , appendToOutputStream: false )
217274 delegate. diagnosticsEngine. emit ( diagnostic)
218275 }
219276 return true
@@ -320,7 +377,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
320377 }
321378 }
322379
323- override public func constructLinkerTasks( _ cbc: CommandBuildContext , _ delegate: any TaskGenerationDelegate , libraries: [ LibrarySpecifier ] , usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] ) async {
380+ override public func constructLinkerTasks( _ cbc: CommandBuildContext , _ delegate: any TaskGenerationDelegate , libraries: [ LibrarySpecifier ] , usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] , workspaceContext : WorkspaceContext ) async {
324381 let resolvedLinkerDriver = Self . resolveLinkerDriver ( cbc, usedTools: usedTools)
325382 let linkerDriverLookup : ( ( MacroDeclaration ) -> MacroStringExpression ? ) = { macro in
326383 switch macro {
@@ -672,7 +729,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
672729 await inputs. append ( contentsOf: additionalInputDependencies ( cbc, delegate, optionContext: discoveredCommandLineToolSpecInfo ( cbc. producer, cbc. scope, delegate) , lookup: lookup) . map ( delegate. createNode) )
673730
674731 // Add dependencies for any arguments indicating a file path.
675- Self . addAdditionalDependenciesFromCommandLine ( cbc, commandLine, environment, & inputs, & outputs, delegate)
732+ Self . addAdditionalDependenciesFromCommandLine ( cbc, commandLine, environment, & inputs, & outputs, delegate, workspaceContext : workspaceContext )
676733
677734 let architecture = cbc. scope. evaluate ( BuiltinMacros . arch)
678735 let buildVariant = cbc. scope. evaluate ( BuiltinMacros . variant)
@@ -721,15 +778,16 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
721778 delegate. createTask ( type: self , dependencyData: dependencyInfo, payload: payload, ruleInfo: defaultRuleInfo ( cbc, delegate) , commandLine: commandLine, environment: environment, workingDirectory: cbc. producer. defaultWorkingDirectory, inputs: inputs + otherInputs, outputs: outputs, action: delegate. taskActionCreationDelegate. createDeferredExecutionTaskActionIfRequested ( userPreferences: cbc. producer. userPreferences) , execDescription: resolveExecutionDescription ( cbc, delegate) , enableSandboxing: enableSandboxing)
722779 }
723780
724- public static func addAdditionalDependenciesFromCommandLine( _ cbc: CommandBuildContext , _ commandLine: [ String ] , _ environment: EnvironmentBindings , _ inputs: inout [ any PlannedNode ] , _ outputs: inout [ any PlannedNode ] , _ delegate: any TaskGenerationDelegate ) {
781+ public static func addAdditionalDependenciesFromCommandLine( _ cbc: CommandBuildContext , _ commandLine: [ String ] , _ environment: EnvironmentBindings , _ inputs: inout [ any PlannedNode ] , _ outputs: inout [ any PlannedNode ] , _ delegate: any TaskGenerationDelegate , workspaceContext : WorkspaceContext ) {
725782 guard cbc. scope. evaluate ( BuiltinMacros . _DISCOVER_COMMAND_LINE_LINKER_INPUTS) else {
726783 return
727784 }
728785
729786 enumerateLinkerCommandLine ( arguments: commandLine, handleWl: cbc. scope. evaluate ( BuiltinMacros . _DISCOVER_COMMAND_LINE_LINKER_INPUTS_INCLUDE_WL) ) { arg, value in
730787 func emitDependencyDiagnostic( type: String , node: PlannedPathNode ) {
731788 if delegate. userPreferences. enableDebugActivityLogs {
732- delegate. note ( " Added \( type) dependency ' \( node. path. str) ' from command line argument \( arg) " , location: . unknown)
789+ let projectLocation = Workspace . projectLocation ( for: cbc. producer. configuredTarget, workspaceContext: workspaceContext)
790+ delegate. note ( " Added \( type) dependency ' \( node. path. str) ' from command line argument \( arg) " , location: projectLocation)
733791 }
734792 }
735793
@@ -1592,7 +1650,7 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u
15921650 return delegate. taskActionCreationDelegate. createLinkerTaskAction ( expandResponseFiles: !useResponseFile)
15931651 }
15941652
1595- override public func constructLinkerTasks( _ cbc: CommandBuildContext , _ delegate: any TaskGenerationDelegate , libraries: [ LibrarySpecifier ] , usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] ) async {
1653+ override public func constructLinkerTasks( _ cbc: CommandBuildContext , _ delegate: any TaskGenerationDelegate , libraries: [ LibrarySpecifier ] , usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] , workspaceContext : WorkspaceContext ) async {
15961654 var inputPaths = cbc. inputs. map ( { $0. absolutePath } )
15971655 var specialArgs = [ String] ( )
15981656
0 commit comments