@@ -233,16 +233,6 @@ public struct DiscoveredLdLinkerToolSpecInfo: DiscoveredCommandLineToolSpecInfo
233233public final class LdLinkerSpec : GenericLinkerSpec , SpecIdentifierType , @unchecked Sendable {
234234 public static let identifier = " com.apple.pbx.linkers.ld "
235235
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-
246236 override public var toolBasenameAliases : [ String ] {
247237 // We use clang as our linker, so return ld and libtool in aliases in
248238 // order to parse the errors from the actual linker.
@@ -281,7 +271,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
281271 }
282272
283273 // 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 {
285275 return usedTools. keys. map ( { type ( of: $0) } ) . contains ( where: { $0 == SwiftCompilerSpec . self } )
286276 }
287277
@@ -304,10 +294,35 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
304294 return runpathSearchPaths
305295 }
306296
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+
307312 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+
308323 // Validate that OTHER_LDFLAGS doesn't contain flags for constructs which we have dedicated settings for. This should be expanded over time.
309324 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 )
311326 enumerateLinkerCommandLine ( arguments: originalLdFlags) { arg, value in
312327 switch arg {
313328 case " -dyld_env " :
@@ -354,7 +369,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
354369 specialArgs. append ( contentsOf: sparseSDKSearchPathArguments ( cbc) )
355370
356371 // 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 )
358373 if !fileListPath. isEmpty {
359374 let contents = OutputByteStream ( )
360375 for input in cbc. inputs {
@@ -385,7 +400,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
385400 }
386401
387402 // 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 )
389404 specialArgs += productTypeArgs? . args ?? [ ]
390405 inputPaths += productTypeArgs? . inputs ?? [ ]
391406
@@ -425,7 +440,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
425440 inputPaths. append ( contentsOf: inputs)
426441 }
427442
428- let isLinkUsingSwift = isUsingSwift ( usedTools)
443+ let isLinkUsingSwift = Self . isUsingSwift ( usedTools)
429444 if !isLinkUsingSwift {
430445 // Check if we need to link with Swift's standard library
431446 // when linking a pure Objective-C/C++ target. This might be needed
@@ -483,6 +498,9 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
483498 let frameworkSearchPathsExpr = cbc. scope. namespace. parseStringList ( frameworkSearchPaths)
484499
485500 func lookup( _ macro: MacroDeclaration ) -> MacroExpression ? {
501+ if let result = linkerDriverLookup ( macro) {
502+ return result
503+ }
486504 switch macro {
487505 case BuiltinMacros . LD_RUNPATH_SEARCH_PATHS:
488506 return runpathSearchPathsExpr
@@ -589,7 +607,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
589607
590608 // Select the driver to use based on the input file types, replacing the value computed by commandLineFromTemplate().
591609 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
593611
594612 let entitlementsSection = cbc. scope. evaluate ( BuiltinMacros . LD_ENTITLEMENTS_SECTION)
595613 if !entitlementsSection. isEmpty {
@@ -763,6 +781,15 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
763781 }
764782
765783 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+ }
766793 // Construct the "special args".
767794 var specialArgs = [ String] ( )
768795 var inputPaths = cbc. inputs. map ( { $0. absolutePath } )
@@ -782,6 +809,9 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
782809 }
783810
784811 func lookup( _ macro: MacroDeclaration ) -> MacroExpression ? {
812+ if let result = linkerDriverLookup ( macro) {
813+ return result
814+ }
785815 switch macro {
786816 case BuiltinMacros . LD_ENTRY_POINT where cbc. scope. previewStyle == . xojit:
787817 return cbc. scope. namespace. parseLiteralString ( " ___debug_blank_executor_main " )
@@ -835,7 +865,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
835865
836866 // Select the driver to use based on the input file types, replacing the value computed by commandLineFromTemplate().
837867 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
839869
840870 let entitlementsSection = cbc. scope. evaluate ( BuiltinMacros . LD_ENTITLEMENTS_SECTION)
841871 if !entitlementsSection. isEmpty {
@@ -1105,31 +1135,45 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
11051135 ]
11061136 }
11071137
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 {
11091144 if usedCXX {
1110- let perArchValue = cbc. scope. evaluate ( BuiltinMacros . PER_ARCH_LDPLUSPLUS)
1145+ let perArchValue = cbc. scope. evaluate ( BuiltinMacros . PER_ARCH_LDPLUSPLUS, lookup : lookup )
11111146 if !perArchValue. isEmpty {
1112- return Path ( perArchValue)
1147+ return Path ( cbc . producer . hostOperatingSystem . imageFormat . executableName ( basename : perArchValue) )
11131148 }
11141149
1115- let value = cbc. scope. evaluate ( BuiltinMacros . LDPLUSPLUS)
1150+ let value = cbc. scope. evaluate ( BuiltinMacros . LDPLUSPLUS, lookup : lookup )
11161151 if !value. isEmpty {
1117- return Path ( value)
1152+ return Path ( cbc . producer . hostOperatingSystem . imageFormat . executableName ( basename : value) )
11181153 }
1119-
1120- return Path ( " clang++ " )
11211154 } else {
1122- let perArchValue = cbc. scope. evaluate ( BuiltinMacros . PER_ARCH_LD)
1155+ let perArchValue = cbc. scope. evaluate ( BuiltinMacros . PER_ARCH_LD, lookup : lookup )
11231156 if !perArchValue. isEmpty {
1124- return Path ( perArchValue)
1157+ return Path ( cbc . producer . hostOperatingSystem . imageFormat . executableName ( basename : perArchValue) )
11251158 }
11261159
1127- let value = cbc. scope. evaluate ( BuiltinMacros . LD)
1160+ let value = cbc. scope. evaluate ( BuiltinMacros . LD, lookup : lookup )
11281161 if !value. isEmpty {
1129- return Path ( value)
1162+ return Path ( cbc . producer . hostOperatingSystem . imageFormat . executableName ( basename : value) )
11301163 }
1164+ }
11311165
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 " )
11331177 }
11341178 }
11351179
0 commit comments