@@ -176,6 +176,60 @@ public struct DiscoveredLdLinkerToolSpecInfo: DiscoveredCommandLineToolSpecInfo
176
176
/// Maximum number of undefined symbols to emit. Might be configurable in the future.
177
177
let undefinedSymbolCountLimit = 100
178
178
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
179
233
override func parseLine< S: Collection > ( _ lineBytes: S ) -> Bool where S. Element == UInt8 {
180
234
181
235
// 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
190
244
}
191
245
else if let match = LdLinkerOutputParser . problemMessageRegEx. firstMatch ( in: lineString) , match [ 3 ] . hasPrefix ( " symbol(s) not found " ) {
192
246
// 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
+
193
249
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 ) )
195
251
}
196
252
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 ) )
198
254
}
199
255
collectingUndefinedSymbols = false
200
256
undefinedSymbols = [ ]
@@ -213,7 +269,8 @@ public struct DiscoveredLdLinkerToolSpecInfo: DiscoveredCommandLineToolSpecInfo
213
269
let severity = match [ 2 ] . isEmpty ? " error " : match [ 2 ]
214
270
let behavior = Diagnostic . Behavior ( name: severity) ?? . note
215
271
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 )
217
274
delegate. diagnosticsEngine. emit ( diagnostic)
218
275
}
219
276
return true
@@ -320,7 +377,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
320
377
}
321
378
}
322
379
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 {
324
381
let resolvedLinkerDriver = Self . resolveLinkerDriver ( cbc, usedTools: usedTools)
325
382
let linkerDriverLookup : ( ( MacroDeclaration ) -> MacroStringExpression ? ) = { macro in
326
383
switch macro {
@@ -672,7 +729,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
672
729
await inputs. append ( contentsOf: additionalInputDependencies ( cbc, delegate, optionContext: discoveredCommandLineToolSpecInfo ( cbc. producer, cbc. scope, delegate) , lookup: lookup) . map ( delegate. createNode) )
673
730
674
731
// 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 )
676
733
677
734
let architecture = cbc. scope. evaluate ( BuiltinMacros . arch)
678
735
let buildVariant = cbc. scope. evaluate ( BuiltinMacros . variant)
@@ -721,15 +778,16 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
721
778
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)
722
779
}
723
780
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 ) {
725
782
guard cbc. scope. evaluate ( BuiltinMacros . _DISCOVER_COMMAND_LINE_LINKER_INPUTS) else {
726
783
return
727
784
}
728
785
729
786
enumerateLinkerCommandLine ( arguments: commandLine, handleWl: cbc. scope. evaluate ( BuiltinMacros . _DISCOVER_COMMAND_LINE_LINKER_INPUTS_INCLUDE_WL) ) { arg, value in
730
787
func emitDependencyDiagnostic( type: String , node: PlannedPathNode ) {
731
788
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)
733
791
}
734
792
}
735
793
@@ -1592,7 +1650,7 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u
1592
1650
return delegate. taskActionCreationDelegate. createLinkerTaskAction ( expandResponseFiles: !useResponseFile)
1593
1651
}
1594
1652
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 {
1596
1654
var inputPaths = cbc. inputs. map ( { $0. absolutePath } )
1597
1655
var specialArgs = [ String] ( )
1598
1656
0 commit comments