From 47e61e8563cd988a0648a84072e6e3eb3435a478 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Wed, 26 Feb 2025 18:35:21 -0800 Subject: [PATCH] Begin adding support for using swiftc as a linker driver This is controlled via the LINKER_DRIVER setting --- Sources/SWBCore/Settings/BuiltinMacros.swift | 9 + .../SpecImplementations/ProductTypes.swift | 14 +- .../PropertyDomainSpec.swift | 2 + .../Tools/LinkerTools.swift | 8 +- .../Specs/UnixLd.xcspec | 39 +++- .../Specs/Clang LLVM 1.0.xcspec | 2 +- Sources/SWBUniversalPlatform/Specs/Ld.xcspec | 170 +++++++++++++++++- .../SWBUniversalPlatform/Specs/Swift.xcspec | 2 +- .../SWBWindowsPlatform/Specs/WindowsLd.xcspec | 45 ++++- .../BuildOperationTests.swift | 5 +- 10 files changed, 275 insertions(+), 21 deletions(-) diff --git a/Sources/SWBCore/Settings/BuiltinMacros.swift b/Sources/SWBCore/Settings/BuiltinMacros.swift index f8cc26934..2d3ee4651 100644 --- a/Sources/SWBCore/Settings/BuiltinMacros.swift +++ b/Sources/SWBCore/Settings/BuiltinMacros.swift @@ -822,6 +822,7 @@ public final class BuiltinMacros { public static let LIBTOOL_DEPENDENCY_INFO_FILE = BuiltinMacros.declarePathMacro("LIBTOOL_DEPENDENCY_INFO_FILE") public static let LIBTOOL_USE_RESPONSE_FILE = BuiltinMacros.declareBooleanMacro("LIBTOOL_USE_RESPONSE_FILE") public static let LINKER = BuiltinMacros.declareStringMacro("LINKER") + public static let LINKER_DRIVER = BuiltinMacros.declareEnumMacro("LINKER_DRIVER") as EnumMacroDeclaration public static let ALTERNATE_LINKER = BuiltinMacros.declareStringMacro("ALTERNATE_LINKER") public static let LINK_OBJC_RUNTIME = BuiltinMacros.declareBooleanMacro("LINK_OBJC_RUNTIME") public static let LINK_WITH_STANDARD_LIBRARIES = BuiltinMacros.declareBooleanMacro("LINK_WITH_STANDARD_LIBRARIES") @@ -1371,6 +1372,7 @@ public final class BuiltinMacros { ALL_OTHER_LDFLAGS, ALL_SETTINGS, ALTERNATE_GROUP, + LINKER_DRIVER, ALTERNATE_LINKER, ALTERNATE_MODE, ALTERNATE_OWNER, @@ -2662,6 +2664,13 @@ public enum ModuleVerifierKind: String, Equatable, Hashable, EnumerationMacroTyp case both } +public enum LinkerDriverChoice: String, Equatable, Hashable, EnumerationMacroType { + public static let defaultValue: LinkerDriverChoice = .clang + + case clang + case swiftc +} + /// Enumeration macro type for the value of the `INFOPLIST_KEY_LSApplicationCategoryType` build setting. public enum ApplicationCategory: String, Equatable, Hashable, EnumerationMacroType { public static let defaultValue = ApplicationCategory.none diff --git a/Sources/SWBCore/SpecImplementations/ProductTypes.swift b/Sources/SWBCore/SpecImplementations/ProductTypes.swift index 397f727de..7cb6a1d5d 100644 --- a/Sources/SWBCore/SpecImplementations/ProductTypes.swift +++ b/Sources/SWBCore/SpecImplementations/ProductTypes.swift @@ -270,12 +270,22 @@ public class ProductTypeSpec : Spec, SpecType, @unchecked Sendable { if producer.isApplePlatform { let compatibilityVersion = scope.evaluate(BuiltinMacros.DYLIB_COMPATIBILITY_VERSION) if !compatibilityVersion.isEmpty { - args += ["-compatibility_version", compatibilityVersion] + switch scope.evaluate(BuiltinMacros.LINKER_DRIVER) { + case .clang: + args += ["-compatibility_version", compatibilityVersion] + case .swiftc: + args += ["-Xlinker", "-compatibility_version", "-Xlinker", compatibilityVersion] + } } let currentVersion = scope.evaluate(BuiltinMacros.DYLIB_CURRENT_VERSION) if !currentVersion.isEmpty { - args += ["-current_version", currentVersion] + switch scope.evaluate(BuiltinMacros.LINKER_DRIVER) { + case .clang: + args += ["-current_version", currentVersion] + case .swiftc: + args += ["-Xlinker", "-current_version", "-Xlinker", currentVersion] + } } } diff --git a/Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift b/Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift index cb87fd269..7dec160d2 100644 --- a/Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift +++ b/Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift @@ -110,6 +110,8 @@ private final class EnumBuildOptionType : BuildOptionType { return try namespace.declareEnumMacro(name) as EnumMacroDeclaration case "SWIFT_ENABLE_EXPLICIT_MODULES": return try namespace.declareEnumMacro(name) as EnumMacroDeclaration + case "LINKER_DRIVER": + return try namespace.declareEnumMacro(name) as EnumMacroDeclaration default: return try namespace.declareStringMacro(name) } diff --git a/Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift b/Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift index aaea70257..2e55651ee 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift @@ -234,7 +234,13 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec public static let identifier = "com.apple.pbx.linkers.ld" public override func computeExecutablePath(_ cbc: CommandBuildContext) -> String { - return cbc.producer.hostOperatingSystem.imageFormat.executableName(basename: "clang") + // TODO: We should also provide an "auto" option which chooses based on the source files in the target + switch cbc.scope.evaluate(BuiltinMacros.LINKER_DRIVER) { + case .clang: + return cbc.producer.hostOperatingSystem.imageFormat.executableName(basename: "clang") + case .swiftc: + return cbc.producer.hostOperatingSystem.imageFormat.executableName(basename: "swiftc") + } } override public var toolBasenameAliases: [String] { diff --git a/Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec b/Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec index 0c7fdd60b..d5c723cdb 100644 --- a/Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec +++ b/Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec @@ -26,7 +26,7 @@ }; Options = ( { - Name = "MACH_O_TYPE"; + Name = "CLANG_MACH_O_TYPE"; Type = Enumeration; Values = ( { @@ -46,13 +46,26 @@ CommandLineFlag = "-r"; }, ); + Condition = "$(LINKER_DRIVER) == clang"; + DefaultValue = "$(MACH_O_TYPE)"; }, + // We can inherit SWIFTC_MACHO_TYPE from the universal platform Ld.xcspec { - Name = SDKROOT; + Name = CLANG_SDKROOT_LINKER_INPUT; Type = Path; + DefaultValue = "$(SDKROOT)"; + Condition = "$(LINKER_DRIVER) == clang"; CommandLineFlag = "--sysroot"; IsInputDependency = Yes; }, + { + Name = SWIFTC_SDKROOT_LINKER_INPUT; + Type = Path; + DefaultValue = "$(SDKROOT)"; + Condition = "$(LINKER_DRIVER) == swiftc"; + CommandLineFlag = "-sysroot"; + IsInputDependency = Yes; + }, { Name = "LD_DYLIB_INSTALL_NAME"; Type = String; @@ -65,6 +78,17 @@ ); Condition = "$(MACH_O_TYPE) == mh_dylib"; }, + // Override the differentiated settings to no-ops, both linker drivers use the same flags. + { + Name = "CLANG_LD_DYLIB_INSTALL_NAME"; + Type = String; + Condition = "NO"; + }, + { + Name = "SWIFTC_LD_DYLIB_INSTALL_NAME"; + Type = String; + Condition = "NO"; + }, { Name = GOLD_BUILDID; Type = Boolean; @@ -130,6 +154,17 @@ ); IsInputDependency = Yes; }, + // Override the differentiated settings to no-ops, both linker drivers use the same flags. + { + Name = "CLANG__INPUT_FILE_LIST_PATH__"; + Type = Path; + Condition = "NO"; + }, + { + Name = "SWIFTC__INPUT_FILE_LIST_PATH__"; + Type = Path; + Condition = "NO"; + } ); }, ) diff --git a/Sources/SWBUniversalPlatform/Specs/Clang LLVM 1.0.xcspec b/Sources/SWBUniversalPlatform/Specs/Clang LLVM 1.0.xcspec index 788ed2a02..28ee0ac25 100644 --- a/Sources/SWBUniversalPlatform/Specs/Clang LLVM 1.0.xcspec +++ b/Sources/SWBUniversalPlatform/Specs/Clang LLVM 1.0.xcspec @@ -320,7 +320,7 @@ // that the ObjC runtime must be linked in (with possible // backwards compatibility libraries linked in). AdditionalLinkerArgs = { - YES = ( "-fobjc-link-runtime" ); + YES = ( "$(LD_OBJC_RUNTIME_ARGS)" ); NO = (); }; FileTypes = ( diff --git a/Sources/SWBUniversalPlatform/Specs/Ld.xcspec b/Sources/SWBUniversalPlatform/Specs/Ld.xcspec index a5075ba86..b3acd33c3 100644 --- a/Sources/SWBUniversalPlatform/Specs/Ld.xcspec +++ b/Sources/SWBUniversalPlatform/Specs/Ld.xcspec @@ -38,6 +38,15 @@ CommandOutputParser = "XCGccCommandOutputParser"; "SupportsInputFileList" = Yes; Options = ( + { + Name = "LINKER_DRIVER"; + Type = Enumeration; + Values = ( + clang, + swiftc, + ); + DefaultValue = "clang"; + }, { Name = LD_DETERMINISTIC_MODE; Type = Boolean; DefaultValue = YES; @@ -72,6 +81,24 @@ { Name = "MACH_O_TYPE"; Type = Enumeration; + Values = ( + { + Value = "mh_execute"; + }, + { + Value = "mh_dylib"; + }, + { + Value = "mh_bundle"; + }, + { + Value = "mh_object"; + }, + ); + }, + { + Name = "CLANG_MACH_O_TYPE"; + Type = Enumeration; Values = ( { Value = "mh_execute"; @@ -90,15 +117,65 @@ CommandLineFlag = "-r"; }, ); + Condition = "$(LINKER_DRIVER) == clang"; + DefaultValue = "$(MACH_O_TYPE)"; + }, + { + Name = "SWIFTC_MACH_O_TYPE"; + Type = Enumeration; + Values = ( + { + Value = "mh_execute"; + CommandLineFlag = "-emit-executable"; + }, + { + Value = "mh_dylib"; + CommandLineFlag = "-emit-library"; + }, + { + Value = "mh_bundle"; + CommandLineArgs = ("-Xlinker", "-bundle"); + }, + { + Value = "mh_object"; + CommandLineArgs = ("-Xlinker", "-r"); + }, + ); + Condition = "$(LINKER_DRIVER) == swiftc"; + DefaultValue = "$(MACH_O_TYPE)"; + }, + { + Name = LD_OBJC_RUNTIME_ARGS; + DefaultValue = "$(LD_OBJC_RUNTIME_ARGS_$(LINKER_DRIVER))"; + }, + { + Name = LD_OBJC_RUNTIME_ARGS_clang; + DefaultValue = "-fobjc-link-runtime"; + }, + { + Name = LD_OBJC_RUNTIME_ARGS_swiftc; + DefaultValue = "-link-objc-runtime"; }, { - Name = SDKROOT; + Name = CLANG_SDKROOT_LINKER_INPUT; Type = Path; + DefaultValue = "$(SDKROOT)"; + Condition = "$(LINKER_DRIVER) == clang"; CommandLineFlag = "-isysroot"; IsInputDependency = Yes; }, - { Name = "LD_OPTIMIZATION_LEVEL"; + { + Name = SWIFTC_SDKROOT_LINKER_INPUT; + Type = Path; + DefaultValue = "$(SDKROOT)"; + Condition = "$(LINKER_DRIVER) == swiftc"; + CommandLineFlag = "-sdk"; + IsInputDependency = Yes; + }, + { + Name = "LD_OPTIMIZATION_LEVEL"; Type = String; + Condition = "$(LINKER_DRIVER) == clang"; DefaultValue = "$(GCC_OPTIMIZATION_LEVEL)"; "CommandLinePrefixFlag" = "-O"; }, @@ -106,8 +183,21 @@ Name = "LD_SUPPRESS_WARNINGS"; Type = Boolean; DefaultValue = "$(SUPPRESS_WARNINGS)"; + }, + { + Name = "CLANG_LD_SUPPRESS_WARNINGS"; + Type = Boolean; + Condition = "$(LINKER_DRIVER) == clang"; + DefaultValue = "$(LD_SUPPRESS_WARNINGS)"; CommandLineFlag = "-w"; }, + { + Name = "SWIFTC_LD_SUPPRESS_WARNINGS"; + Type = Boolean; + Condition = "$(LINKER_DRIVER) == swiftc"; + DefaultValue = "$(LD_SUPPRESS_WARNINGS)"; + CommandLineFlag = "-suppress-warnings"; + }, { Name = "LD_WARN_UNUSED_DYLIBS"; Type = Boolean; @@ -147,9 +237,23 @@ { Name = "SYSTEM_FRAMEWORK_SEARCH_PATHS"; Type = PathList; + }, + { + Name = "CLANG_SYSTEM_FRAMEWORK_SEARCH_PATHS"; + Type = PathList; FlattenRecursiveSearchPathsInValue = Yes; + Condition = "$(LINKER_DRIVER) == clang"; + DefaultValue = "$(SYSTEM_FRAMEWORK_SEARCH_PATHS)"; CommandLineFlag = "-iframework"; }, + { + Name = "SWIFTC_SYSTEM_FRAMEWORK_SEARCH_PATHS"; + Type = PathList; + FlattenRecursiveSearchPathsInValue = Yes; + Condition = "$(LINKER_DRIVER) == swiftc"; + DefaultValue = "$(SYSTEM_FRAMEWORK_SEARCH_PATHS)"; + CommandLineFlag = "-Fsystem"; + }, { Name = "PRODUCT_TYPE_LIBRARY_SEARCH_PATHS"; Type = PathList; @@ -166,7 +270,24 @@ Name = "__INPUT_FILE_LIST_PATH__"; Type = Path; DefaultValue = "$(LINK_FILE_LIST_$(variant)_$(arch))"; + }, + { + Name = "CLANG__INPUT_FILE_LIST_PATH__"; + Type = Path; + DefaultValue = "$(__INPUT_FILE_LIST_PATH__)"; CommandLineFlag = "-filelist"; + Condition = "$(LINKER_DRIVER) == clang"; + IsInputDependency = Yes; + }, + { + Name = "SWIFTC__INPUT_FILE_LIST_PATH__"; + Type = Path; + DefaultValue = "$(__INPUT_FILE_LIST_PATH__)"; + CommandLineArgs = { + "" = (); + "<>" = "@$(value)"; + }; + Condition = "$(LINKER_DRIVER) == swiftc"; IsInputDependency = Yes; }, { @@ -184,6 +305,7 @@ { Name = "INIT_ROUTINE"; Type = String; + Condition = "$(LINKER_DRIVER) == clang"; CommandLineFlag = "-init"; }, { @@ -243,7 +365,7 @@ { Name = "GENERATE_PROFILING_CODE"; Type = Boolean; - Condition = "$(variant) == profile"; + Condition = "$(variant) == profile && $(LINKER_DRIVER) == clang"; CommandLineFlag = "-pg"; }, { @@ -262,9 +384,30 @@ Name = "LD_DYLIB_INSTALL_NAME"; Type = String; DefaultValue = ""; - CommandLineFlag = "-install_name"; Condition = "$(MACH_O_TYPE) == mh_dylib"; }, + { + Name = "CLANG_LD_DYLIB_INSTALL_NAME"; + Type = String; + DefaultValue = "$(LD_DYLIB_INSTALL_NAME)"; + CommandLineFlag = "-install_name"; + Condition = "$(MACH_O_TYPE) == mh_dylib && $(LINKER_DRIVER) == clang"; + }, + { + Name = "SWIFTC_LD_DYLIB_INSTALL_NAME"; + Type = String; + DefaultValue = "$(LD_DYLIB_INSTALL_NAME)"; + CommandLineArgs = { + "" = (); + "<>" = ( + "-Xlinker", + "-dylib_install_name", + "-Xlinker", + "$(value)", + ); + }; + Condition = "$(MACH_O_TYPE) == mh_dylib && $(LINKER_DRIVER) == swiftc"; + }, { Name = "LD_RUNPATH_SEARCH_PATHS"; //Note: Cannot be of type 'PathList' as value is used with relative '../' paths @@ -308,6 +451,7 @@ Name = "KEEP_PRIVATE_EXTERNS"; Type = Boolean; DefaultValue = NO; + Condition = "$(LINKER_DRIVER) == clang"; CommandLineFlag = "-keep_private_externs"; }, { @@ -697,6 +841,12 @@ { Name = "ALTERNATE_LINKER"; Type = String; + }, + { + Name = "CLANG_ALTERNATE_LINKER"; + Type = String; + DefaultValue = "$(ALTERNATE_LINKER)"; + Condition = "$(LINKER_DRIVER) == clang"; CommandLineArgs = { "" = (); "<>" = ( @@ -704,6 +854,18 @@ ); }; }, + { + Name = "SWIFTC_ALTERNATE_LINKER"; + Type = String; + DefaultValue = "$(ALTERNATE_LINKER)"; + Condition = "$(LINKER_DRIVER) == swiftc"; + CommandLineArgs = { + "" = (); + "<>" = ( + "-use-ld=$(value)" + ); + }; + }, ); } ) diff --git a/Sources/SWBUniversalPlatform/Specs/Swift.xcspec b/Sources/SWBUniversalPlatform/Specs/Swift.xcspec index 3c349af1b..846b1e6c1 100644 --- a/Sources/SWBUniversalPlatform/Specs/Swift.xcspec +++ b/Sources/SWBUniversalPlatform/Specs/Swift.xcspec @@ -975,7 +975,7 @@ // that the ObjC runtime must be linked in (with possible // backwards compatibility libraries linked in). AdditionalLinkerArgs = { - YES = ( "-fobjc-link-runtime" ); + YES = ( "$(LD_OBJC_RUNTIME_ARGS)" ); NO = (); }; }, diff --git a/Sources/SWBWindowsPlatform/Specs/WindowsLd.xcspec b/Sources/SWBWindowsPlatform/Specs/WindowsLd.xcspec index 13c8f67b2..8e2d588fb 100644 --- a/Sources/SWBWindowsPlatform/Specs/WindowsLd.xcspec +++ b/Sources/SWBWindowsPlatform/Specs/WindowsLd.xcspec @@ -22,10 +22,11 @@ SupportsInputFileList = No; EnvironmentVariables = { "TEMP" = "$(TEMP)"; + "PATH" = "$(TOOLCHAIN_DIR)/usr/bin;$(DEVELOPER_DIR)/Runtimes/$(TOOLCHAIN_VERSION)/usr/bin;$(PATH)"; }; Options = ( { - Name = "MACH_O_TYPE"; + Name = "CLANG_MACH_O_TYPE"; Type = Enumeration; Values = ( { @@ -45,13 +46,26 @@ CommandLineFlag = ""; }, ); + Condition = "$(LINKER_DRIVER) == clang"; + DefaultValue = "$(MACH_O_TYPE)"; }, + // We can inherit SWIFTC_MACHO_TYPE from the universal platform Ld.xcspec { - Name = SDKROOT; + Name = CLANG_SDKROOT_LINKER_INPUT; Type = Path; + DefaultValue = "$(SDKROOT)"; + Condition = "$(LINKER_DRIVER) == clang"; CommandLineFlag = "--sysroot"; IsInputDependency = Yes; }, + { + Name = SWIFTC_SDKROOT_LINKER_INPUT; + Type = Path; + DefaultValue = "$(SDKROOT)"; + Condition = "$(LINKER_DRIVER) == swiftc"; + CommandLineFlag = "-sdk"; + IsInputDependency = Yes; + }, { Name = _LD_MULTIARCH; Type = Boolean; @@ -78,6 +92,16 @@ Condition = "NO"; CommandLineArgs = (); }, + { + Name = "CLANG_LD_DYLIB_INSTALL_NAME"; + Type = String; + Condition = "NO"; + }, + { + Name = "SWIFTC_LD_DYLIB_INSTALL_NAME"; + Type = String; + Condition = "NO"; + }, { // No such concept Name = "LD_RUNPATH_SEARCH_PATHS"; @@ -145,16 +169,21 @@ ); IsInputDependency = Yes; }, + // Override the differentiated settings to no-ops, both linker drivers use the same flags. + { + Name = "CLANG__INPUT_FILE_LIST_PATH__"; + Type = Path; + Condition = "NO"; + }, + { + Name = "SWIFTC__INPUT_FILE_LIST_PATH__"; + Type = Path; + Condition = "NO"; + }, { Name = "ALTERNATE_LINKER"; Type = String; DefaultValue = "lld-link"; - CommandLineArgs = { - "" = (); - "<>" = ( - "-fuse-ld=$(value)" - ); - }; }, ); }, diff --git a/Tests/SWBBuildSystemTests/BuildOperationTests.swift b/Tests/SWBBuildSystemTests/BuildOperationTests.swift index 88ada81ae..252806349 100644 --- a/Tests/SWBBuildSystemTests/BuildOperationTests.swift +++ b/Tests/SWBBuildSystemTests/BuildOperationTests.swift @@ -30,8 +30,8 @@ import SWBTestSupport @Suite(.requireXcode16()) fileprivate struct BuildOperationTests: CoreBasedTests { - @Test(.requireSDKs(.host), .requireThreadSafeWorkingDirectory) - func commandLineTool() async throws { + @Test(.requireSDKs(.host), .requireThreadSafeWorkingDirectory, arguments: ["clang", "swiftc"]) + func commandLineTool(linkerDriver: String) async throws { try await withTemporaryDirectory { (tmpDir: Path) in let testProject = try await TestProject( "TestProject", @@ -54,6 +54,7 @@ fileprivate struct BuildOperationTests: CoreBasedTests { "SDKROOT": "$(HOST_PLATFORM)", "SUPPORTED_PLATFORMS": "$(HOST_PLATFORM)", "SWIFT_VERSION": swiftVersion, + "LINKER_DRIVER": linkerDriver, ]) ], targets: [