@@ -233,16 +233,6 @@ public struct DiscoveredLdLinkerToolSpecInfo: DiscoveredCommandLineToolSpecInfo
233
233
public final class LdLinkerSpec : GenericLinkerSpec , SpecIdentifierType , @unchecked Sendable {
234
234
public static let identifier = " com.apple.pbx.linkers.ld "
235
235
236
- public override func computeExecutablePath( _ cbc: CommandBuildContext ) -> String {
237
- // TODO: We should also provide an "auto" option which chooses based on the source files in the target
238
- switch cbc. scope. evaluate ( BuiltinMacros . LINKER_DRIVER) {
239
- case . clang:
240
- return cbc. producer. hostOperatingSystem. imageFormat. executableName ( basename: " clang " )
241
- case . swiftc:
242
- return cbc. producer. hostOperatingSystem. imageFormat. executableName ( basename: " swiftc " )
243
- }
244
- }
245
-
246
236
override public var toolBasenameAliases : [ String ] {
247
237
// We use clang as our linker, so return ld and libtool in aliases in
248
238
// order to parse the errors from the actual linker.
@@ -281,7 +271,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
281
271
}
282
272
283
273
// FIXME: Is there a better way to figure out if we are linking Swift?
284
- private func isUsingSwift( _ usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] ) -> Bool {
274
+ private static func isUsingSwift( _ usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] ) -> Bool {
285
275
return usedTools. keys. map ( { type ( of: $0) } ) . contains ( where: { $0 == SwiftCompilerSpec . self } )
286
276
}
287
277
@@ -304,10 +294,35 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
304
294
return runpathSearchPaths
305
295
}
306
296
297
+ static func resolveLinkerDriver( _ cbc: CommandBuildContext , usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] ) -> LinkerDriverChoice {
298
+ switch cbc. scope. evaluate ( BuiltinMacros . LINKER_DRIVER) {
299
+ case . clang:
300
+ return . clang
301
+ case . swiftc:
302
+ return . swiftc
303
+ case . auto:
304
+ if Self . isUsingSwift ( usedTools) {
305
+ return . swiftc
306
+ } else {
307
+ return . clang
308
+ }
309
+ }
310
+ }
311
+
307
312
override public func constructLinkerTasks( _ cbc: CommandBuildContext , _ delegate: any TaskGenerationDelegate , libraries: [ LibrarySpecifier ] , usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] ) async {
313
+ let resolvedLinkerDriver = Self . resolveLinkerDriver ( cbc, usedTools: usedTools)
314
+ let linkerDriverLookup : ( ( MacroDeclaration ) -> MacroStringExpression ? ) = { macro in
315
+ switch macro {
316
+ case BuiltinMacros . LINKER_DRIVER:
317
+ return cbc. scope. namespace. parseString ( resolvedLinkerDriver. rawValue)
318
+ default :
319
+ return nil
320
+ }
321
+ }
322
+
308
323
// Validate that OTHER_LDFLAGS doesn't contain flags for constructs which we have dedicated settings for. This should be expanded over time.
309
324
let dyldEnvDiagnosticBehavior : Diagnostic . Behavior = SWBFeatureFlag . useStrictLdEnvironmentBuildSetting. value ? . error : . warning
310
- let originalLdFlags = cbc. scope. evaluate ( BuiltinMacros . OTHER_LDFLAGS)
325
+ let originalLdFlags = cbc. scope. evaluate ( BuiltinMacros . OTHER_LDFLAGS, lookup : linkerDriverLookup )
311
326
enumerateLinkerCommandLine ( arguments: originalLdFlags) { arg, value in
312
327
switch arg {
313
328
case " -dyld_env " :
@@ -354,7 +369,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
354
369
specialArgs. append ( contentsOf: sparseSDKSearchPathArguments ( cbc) )
355
370
356
371
// Define the linker file list.
357
- let fileListPath = cbc. scope. evaluate ( BuiltinMacros . __INPUT_FILE_LIST_PATH__)
372
+ let fileListPath = cbc. scope. evaluate ( BuiltinMacros . __INPUT_FILE_LIST_PATH__, lookup : linkerDriverLookup )
358
373
if !fileListPath. isEmpty {
359
374
let contents = OutputByteStream ( )
360
375
for input in cbc. inputs {
@@ -385,7 +400,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
385
400
}
386
401
387
402
// Add linker flags desired by the product type.
388
- let productTypeArgs = cbc. producer. productType? . computeAdditionalLinkerArgs ( cbc. producer, scope: cbc. scope)
403
+ let productTypeArgs = cbc. producer. productType? . computeAdditionalLinkerArgs ( cbc. producer, scope: cbc. scope, lookup : linkerDriverLookup )
389
404
specialArgs += productTypeArgs? . args ?? [ ]
390
405
inputPaths += productTypeArgs? . inputs ?? [ ]
391
406
@@ -425,7 +440,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
425
440
inputPaths. append ( contentsOf: inputs)
426
441
}
427
442
428
- let isLinkUsingSwift = isUsingSwift ( usedTools)
443
+ let isLinkUsingSwift = Self . isUsingSwift ( usedTools)
429
444
if !isLinkUsingSwift {
430
445
// Check if we need to link with Swift's standard library
431
446
// when linking a pure Objective-C/C++ target. This might be needed
@@ -483,6 +498,9 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
483
498
let frameworkSearchPathsExpr = cbc. scope. namespace. parseStringList ( frameworkSearchPaths)
484
499
485
500
func lookup( _ macro: MacroDeclaration ) -> MacroExpression ? {
501
+ if let result = linkerDriverLookup ( macro) {
502
+ return result
503
+ }
486
504
switch macro {
487
505
case BuiltinMacros . LD_RUNPATH_SEARCH_PATHS:
488
506
return runpathSearchPathsExpr
@@ -589,7 +607,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
589
607
590
608
// Select the driver to use based on the input file types, replacing the value computed by commandLineFromTemplate().
591
609
let usedCXX = usedTools. values. contains ( where: { $0. contains ( where: { $0. languageDialect? . isPlusPlus ?? false } ) } )
592
- commandLine [ 0 ] = await resolveExecutablePath ( cbc, computeLinkerPath ( cbc, usedCXX: usedCXX) , delegate: delegate) . str
610
+ commandLine [ 0 ] = await resolveExecutablePath ( cbc, computeLinkerPath ( cbc, usedCXX: usedCXX, lookup : linkerDriverLookup ) , delegate: delegate) . str
593
611
594
612
let entitlementsSection = cbc. scope. evaluate ( BuiltinMacros . LD_ENTITLEMENTS_SECTION)
595
613
if !entitlementsSection. isEmpty {
@@ -763,6 +781,15 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
763
781
}
764
782
765
783
public func constructPreviewShimLinkerTasks( _ cbc: CommandBuildContext , _ delegate: any TaskGenerationDelegate , libraries: [ LibrarySpecifier ] , usedTools: [ CommandLineToolSpec : Set < FileTypeSpec > ] , rpaths: [ String ] , ldflags: [ String ] ? ) async {
784
+ let resolvedLinkerDriver = Self . resolveLinkerDriver ( cbc, usedTools: usedTools)
785
+ let linkerDriverLookup : ( ( MacroDeclaration ) -> MacroStringExpression ? ) = { macro in
786
+ switch macro {
787
+ case BuiltinMacros . LINKER_DRIVER:
788
+ return cbc. scope. namespace. parseString ( resolvedLinkerDriver. rawValue)
789
+ default :
790
+ return nil
791
+ }
792
+ }
766
793
// Construct the "special args".
767
794
var specialArgs = [ String] ( )
768
795
var inputPaths = cbc. inputs. map ( { $0. absolutePath } )
@@ -782,6 +809,9 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
782
809
}
783
810
784
811
func lookup( _ macro: MacroDeclaration ) -> MacroExpression ? {
812
+ if let result = linkerDriverLookup ( macro) {
813
+ return result
814
+ }
785
815
switch macro {
786
816
case BuiltinMacros . LD_ENTRY_POINT where cbc. scope. previewStyle == . xojit:
787
817
return cbc. scope. namespace. parseLiteralString ( " ___debug_blank_executor_main " )
@@ -835,7 +865,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
835
865
836
866
// Select the driver to use based on the input file types, replacing the value computed by commandLineFromTemplate().
837
867
let usedCXX = usedTools. values. contains ( where: { $0. contains ( where: { $0. languageDialect? . isPlusPlus ?? false } ) } )
838
- commandLine [ 0 ] = await resolveExecutablePath ( cbc, computeLinkerPath ( cbc, usedCXX: usedCXX) , delegate: delegate) . str
868
+ commandLine [ 0 ] = await resolveExecutablePath ( cbc, computeLinkerPath ( cbc, usedCXX: usedCXX, lookup : linkerDriverLookup ) , delegate: delegate) . str
839
869
840
870
let entitlementsSection = cbc. scope. evaluate ( BuiltinMacros . LD_ENTITLEMENTS_SECTION)
841
871
if !entitlementsSection. isEmpty {
@@ -1105,31 +1135,45 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
1105
1135
]
1106
1136
}
1107
1137
1108
- private func computeLinkerPath( _ cbc: CommandBuildContext , usedCXX: Bool ) -> Path {
1138
+ public override func computeExecutablePath( _ cbc: CommandBuildContext ) -> String {
1139
+ // Placeholder fallback
1140
+ return cbc. producer. hostOperatingSystem. imageFormat. executableName ( basename: " clang " )
1141
+ }
1142
+
1143
+ public func computeLinkerPath( _ cbc: CommandBuildContext , usedCXX: Bool , lookup: @escaping ( ( MacroDeclaration ) -> MacroStringExpression ? ) ) -> Path {
1109
1144
if usedCXX {
1110
- let perArchValue = cbc. scope. evaluate ( BuiltinMacros . PER_ARCH_LDPLUSPLUS)
1145
+ let perArchValue = cbc. scope. evaluate ( BuiltinMacros . PER_ARCH_LDPLUSPLUS, lookup : lookup )
1111
1146
if !perArchValue. isEmpty {
1112
- return Path ( perArchValue)
1147
+ return Path ( cbc . producer . hostOperatingSystem . imageFormat . executableName ( basename : perArchValue) )
1113
1148
}
1114
1149
1115
- let value = cbc. scope. evaluate ( BuiltinMacros . LDPLUSPLUS)
1150
+ let value = cbc. scope. evaluate ( BuiltinMacros . LDPLUSPLUS, lookup : lookup )
1116
1151
if !value. isEmpty {
1117
- return Path ( value)
1152
+ return Path ( cbc . producer . hostOperatingSystem . imageFormat . executableName ( basename : value) )
1118
1153
}
1119
-
1120
- return Path ( " clang++ " )
1121
1154
} else {
1122
- let perArchValue = cbc. scope. evaluate ( BuiltinMacros . PER_ARCH_LD)
1155
+ let perArchValue = cbc. scope. evaluate ( BuiltinMacros . PER_ARCH_LD, lookup : lookup )
1123
1156
if !perArchValue. isEmpty {
1124
- return Path ( perArchValue)
1157
+ return Path ( cbc . producer . hostOperatingSystem . imageFormat . executableName ( basename : perArchValue) )
1125
1158
}
1126
1159
1127
- let value = cbc. scope. evaluate ( BuiltinMacros . LD)
1160
+ let value = cbc. scope. evaluate ( BuiltinMacros . LD, lookup : lookup )
1128
1161
if !value. isEmpty {
1129
- return Path ( value)
1162
+ return Path ( cbc . producer . hostOperatingSystem . imageFormat . executableName ( basename : value) )
1130
1163
}
1164
+ }
1131
1165
1132
- return Path ( computeExecutablePath ( cbc) )
1166
+ switch cbc. scope. evaluate ( BuiltinMacros . LINKER_DRIVER, lookup: lookup) {
1167
+ case . clang:
1168
+ if usedCXX {
1169
+ return Path ( cbc. producer. hostOperatingSystem. imageFormat. executableName ( basename: " clang++ " ) )
1170
+ } else {
1171
+ return Path ( cbc. producer. hostOperatingSystem. imageFormat. executableName ( basename: " clang " ) )
1172
+ }
1173
+ case . swiftc:
1174
+ return Path ( cbc. producer. hostOperatingSystem. imageFormat. executableName ( basename: " swiftc " ) )
1175
+ case . auto:
1176
+ preconditionFailure ( " LINKER_DRIVER was expected to be bound to a concrete value " )
1133
1177
}
1134
1178
}
1135
1179
0 commit comments