Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/SWBCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ add_library(SWBCore
SpecImplementations/Tools/MergeInfoPlist.swift
SpecImplementations/Tools/MkdirTool.swift
SpecImplementations/Tools/ModulesVerifierTool.swift
SpecImplementations/Tools/ObjectLibraryAssembler.swift
SpecImplementations/Tools/PLUtilTool.swift
SpecImplementations/Tools/PrelinkedObjectLink.swift
SpecImplementations/Tools/ProcessSDKImports.swift
Expand Down
2 changes: 2 additions & 0 deletions Sources/SWBCore/PlannedTaskAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ public protocol TaskActionCreationDelegate
func createClangModuleVerifierInputGeneratorTaskAction() -> any PlannedTaskAction
func createProcessSDKImportsTaskAction() -> any PlannedTaskAction
func createValidateDependenciesTaskAction() -> any PlannedTaskAction
func createObjectLibraryAssemblerTaskAction() -> any PlannedTaskAction
func createLinkerTaskAction(expandResponseFiles: Bool) -> any PlannedTaskAction
}

extension TaskActionCreationDelegate {
Expand Down
14 changes: 8 additions & 6 deletions Sources/SWBCore/SpecImplementations/LinkerSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ open class LinkerSpec : CommandLineToolSpec, @unchecked Sendable {
case textBased
case framework
case object
case objectLibrary

public var description: String {
switch self {
case .static: return "static library"
case .dynamic: return "dynamic library"
case .textBased: return "text-based stub"
case .framework: return "framework"
case .object: return "object file"
case .static: return "static library"
case .dynamic: return "dynamic library"
case .textBased: return "text-based stub"
case .framework: return "framework"
case .object: return "object file"
case .objectLibrary: return "object library"
}
}
}
Expand Down Expand Up @@ -144,7 +146,7 @@ open class LinkerSpec : CommandLineToolSpec, @unchecked Sendable {
/// Custom entry point for constructing linker tasks.
public func constructLinkerTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, libraries: [LibrarySpecifier], usedTools: [CommandLineToolSpec: Set<FileTypeSpec>]) async {
/// Delegate to the generic machinery.
await delegate.createTask(type: self, ruleInfo: defaultRuleInfo(cbc, delegate), commandLine: commandLineFromTemplate(cbc, delegate, optionContext: discoveredCommandLineToolSpecInfo(cbc.producer, cbc.scope, delegate)).map(\.asString), environment: environmentFromSpec(cbc, delegate), workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: cbc.inputs.map({ $0.absolutePath }), outputs: [cbc.output], action: nil, execDescription: resolveExecutionDescription(cbc, delegate), enableSandboxing: enableSandboxing)
await delegate.createTask(type: self, ruleInfo: defaultRuleInfo(cbc, delegate), commandLine: commandLineFromTemplate(cbc, delegate, optionContext: discoveredCommandLineToolSpecInfo(cbc.producer, cbc.scope, delegate)).map(\.asString), environment: environmentFromSpec(cbc, delegate), workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: cbc.inputs.map({ $0.absolutePath }), outputs: [cbc.output], action: createTaskAction(cbc, delegate), execDescription: resolveExecutionDescription(cbc, delegate), enableSandboxing: enableSandboxing)
}
}

Expand Down
1 change: 1 addition & 0 deletions Sources/SWBCore/SpecImplementations/RegisterSpecs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public struct BuiltinSpecsExtension: SpecificationsExtension {
LibtoolLinkerSpec.self,
LipoToolSpec.self,
MkdirToolSpec.self,
ObjectLibraryAssemblerSpec.self,
PLUtilToolSpec.self,
ProductPackagingToolSpec.self,
ShellScriptToolSpec.self,
Expand Down
28 changes: 25 additions & 3 deletions Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
)

// Create the task.
delegate.createTask(type: self, payload: payload, ruleInfo: defaultRuleInfo(cbc, delegate), commandLine: commandLine, environment: environmentFromSpec(cbc, delegate), workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: inputs, outputs: outputs, action: nil, execDescription: resolveExecutionDescription(cbc, delegate), enableSandboxing: enableSandboxing)
delegate.createTask(type: self, payload: payload, ruleInfo: defaultRuleInfo(cbc, delegate), commandLine: commandLine, environment: environmentFromSpec(cbc, delegate), workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: inputs, outputs: outputs, action: createTaskAction(cbc, delegate), execDescription: resolveExecutionDescription(cbc, delegate), enableSandboxing: enableSandboxing)
}

public func constructPreviewsBlankInjectionDylibTask(
Expand Down Expand Up @@ -992,7 +992,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
workingDirectory: cbc.producer.defaultWorkingDirectory,
inputs: [],
outputs: outputs,
action: nil,
action: createTaskAction(cbc, delegate),
execDescription: resolveExecutionDescription(cbc, delegate),
enableSandboxing: enableSandboxing
)
Expand Down Expand Up @@ -1246,6 +1246,9 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
case .object:
// Object files are added to linker inputs in the sources task producer.
return ([], [])
case .objectLibrary:
let pathFlags = specifier.absolutePathFlagsForLd()
return (pathFlags, [specifier.path])
}
}.reduce(([], [])) { (lhs, rhs) in (lhs.args + rhs.args, lhs.inputs + rhs.inputs) }
}
Expand Down Expand Up @@ -1367,6 +1370,11 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
return LdLinkerOutputParser.self
}

override public func createTaskAction(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate) -> (any PlannedTaskAction)? {
let useResponseFile = cbc.scope.evaluate(BuiltinMacros.CLANG_USE_RESPONSE_FILE)
return delegate.taskActionCreationDelegate.createLinkerTaskAction(expandResponseFiles: !useResponseFile)
}

override public func discoveredCommandLineToolSpecInfo(_ producer: any CommandProducer, _ scope: MacroEvaluationScope, _ delegate: any CoreClientTargetDiagnosticProducingDelegate) async -> (any DiscoveredCommandLineToolSpecInfo)? {
// The ALTERNATE_LINKER is the 'name' of the linker not the executable name, clang will find the linker binary based on name passed via -fuse-ld, but we need to discover
// its properties by executing the actual binary. There is a common filename when the linker is not "ld" across all platforms using "ld.<ALTERNAME_LINKER>(.exe)"
Expand Down Expand Up @@ -1482,6 +1490,9 @@ fileprivate extension LinkerSpec.LibrarySpecifier {
case (.object, _):
// Object files are added to linker inputs in the sources task producer.
return []
case (.objectLibrary, _):
// Object libraries can't be found via search paths.
return []
}
}

Expand Down Expand Up @@ -1518,6 +1529,8 @@ fileprivate extension LinkerSpec.LibrarySpecifier {
case (.object, _):
// Object files are added to linker inputs in the sources task producer.
return []
case (.objectLibrary, _):
return ["@\(path.join("args.resp").str)"]
}
}
}
Expand Down Expand Up @@ -1574,6 +1587,11 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u
}
}

override public func createTaskAction(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate) -> (any PlannedTaskAction)? {
let useResponseFile = cbc.scope.evaluate(BuiltinMacros.LIBTOOL_USE_RESPONSE_FILE)
return delegate.taskActionCreationDelegate.createLinkerTaskAction(expandResponseFiles: !useResponseFile)
}

override public func constructLinkerTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, libraries: [LibrarySpecifier], usedTools: [CommandLineToolSpec: Set<FileTypeSpec>]) async {
var inputPaths = cbc.inputs.map({ $0.absolutePath })
var specialArgs = [String]()
Expand Down Expand Up @@ -1619,6 +1637,10 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u
// Object files are added to linker inputs in the sources task producer and so end up in the link-file-list.
return []

case .objectLibrary:
inputPaths.append(specifier.path)
return ["@\(specifier.path.join("args.resp").str)"]

case .framework:
// A static library can build against a framework, since the library in the framework could be a static library, which is valid, and we can't tell here whether it is or not. So we leave it to libtool to do the right thing here.
// Also, we wouldn't want to emit an error here even if we could determine that it contained a dylib, since the target might be only using the framework to find headers.
Expand Down Expand Up @@ -1710,7 +1732,7 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u
workingDirectory: cbc.producer.defaultWorkingDirectory,
inputs: inputs,
outputs: outputs,
action: nil,
action: createTaskAction(cbc, delegate),
execDescription: resolveExecutionDescription(cbc, delegate),
enableSandboxing: enableSandboxing
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

public final class ObjectLibraryAssemblerSpec : GenericLinkerSpec, SpecIdentifierType, @unchecked Sendable {
public static let identifier: String = "org.swift.linkers.object-library-assembler"

public override func createTaskAction(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate) -> (any PlannedTaskAction)? {
return delegate.taskActionCreationDelegate.createObjectLibraryAssemblerTaskAction()
}
}
4 changes: 4 additions & 0 deletions Sources/SWBCore/Specs/CoreBuildSystem.xcspec
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@
"mh_bundle",
staticlib,
"mh_object",
"objectlib",
);
DefaultValue = "";
},
Expand Down Expand Up @@ -1351,6 +1352,9 @@ When `GENERATE_INFOPLIST_FILE` is enabled, sets the value of the [CFBundleIdenti
{
Value = "mh_object";
},
{
Value = "objectlib";
},
);
DefaultValue = "";
ConditionFlavors = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
// When emitting remarks, for now, a dSYM is required (<rdar://problem/45458590>)
let dSYMForRemarks = scope.evaluate(BuiltinMacros.CLANG_GENERATE_OPTIMIZATION_REMARKS)
let dSYM = dSYMForDebugInfo || dSYMForRemarks
return dSYM && scope.evaluate(BuiltinMacros.MACH_O_TYPE) != "staticlib" && scope.evaluate(BuiltinMacros.MACH_O_TYPE) != "mh_object"
return dSYM && !["staticlib", "mh_object", "objectlib"].contains(scope.evaluate(BuiltinMacros.MACH_O_TYPE))
}

/// Computes and returns a list of libraries to include when linking.
Expand Down Expand Up @@ -687,6 +687,15 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
xcframeworkSourcePath: xcframeworkPath,
privacyFile: nil
)
} else if fileType.conformsTo(identifier: "compiled.object-library") {
return LinkerSpec.LibrarySpecifier(
kind: .objectLibrary,
path: absolutePath,
mode: .normal,
useSearchPaths: false,
swiftModulePaths: swiftModulePaths,
swiftModuleAdditionalLinkerArgResponseFilePaths: swiftModuleAdditionalLinkerArgResponseFilePaths,
)
} else {
// FIXME: Error handling.
return nil
Expand Down Expand Up @@ -1640,21 +1649,33 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F

/// Compute the linker to use in the given scope.
private func getLinkerToUse(_ scope: MacroEvaluationScope) -> LinkerSpec {
let isStaticLib = scope.evaluate(BuiltinMacros.MACH_O_TYPE) == "staticlib"

// Return the custom linker, if specified.
var identifier = scope.evaluate(isStaticLib ? BuiltinMacros.LIBRARIAN : BuiltinMacros.LINKER)
if !identifier.isEmpty {
let spec = context.getSpec(identifier)
if let linker = spec as? LinkerSpec {
return linker
let identifier: String
switch scope.evaluate(BuiltinMacros.MACH_O_TYPE) {
case "objectlib":
identifier = ObjectLibraryAssemblerSpec.identifier
case "staticlib":
let override = scope.evaluate(BuiltinMacros.LIBRARIAN)
if !override.isEmpty {
let spec = context.getSpec(override)
if let linker = spec as? LinkerSpec {
return linker
}

// FIXME: Emit a warning here.
}
identifier = LibtoolLinkerSpec.identifier
default:
let override = scope.evaluate(BuiltinMacros.LINKER)
if !override.isEmpty {
let spec = context.getSpec(override)
if let linker = spec as? LinkerSpec {
return linker
}

// FIXME: Emit a warning here.
// FIXME: Emit a warning here.
}
identifier = LdLinkerSpec.identifier
}

// Return the default linker.
identifier = isStaticLib ? LibtoolLinkerSpec.identifier : LdLinkerSpec.identifier
return context.getSpec(identifier) as! LinkerSpec
}

Expand Down
8 changes: 8 additions & 0 deletions Sources/SWBTaskExecution/BuildDescriptionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,14 @@ extension BuildSystemTaskPlanningDelegate: TaskActionCreationDelegate {
func createValidateDependenciesTaskAction() -> any PlannedTaskAction {
return ValidateDependenciesTaskAction()
}

func createObjectLibraryAssemblerTaskAction() -> any PlannedTaskAction {
return ObjectLibraryAssemblerTaskAction()
}

func createLinkerTaskAction(expandResponseFiles: Bool) -> any PlannedTaskAction {
return LinkerTaskAction(expandResponseFiles: expandResponseFiles)
}
}

fileprivate extension BuildDescription {
Expand Down
2 changes: 2 additions & 0 deletions Sources/SWBTaskExecution/BuiltinTaskActionsExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public struct BuiltinTaskActionsExtension: TaskActionExtension {
38: GenericCachingTaskAction.self,
39: ProcessSDKImportsTaskAction.self,
40: ValidateDependenciesTaskAction.self,
42: ObjectLibraryAssemblerTaskAction.self,
43: LinkerTaskAction.self,
]
}
}
2 changes: 2 additions & 0 deletions Sources/SWBTaskExecution/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ add_library(SWBTaskExecution
TaskActions/GenericCachingTaskAction.swift
TaskActions/InfoPlistProcessorTaskAction.swift
TaskActions/LinkAssetCatalogTaskAction.swift
TaskActions/LinkerTaskAction.swift
TaskActions/LSRegisterURLTaskAction.swift
TaskActions/MergeInfoPlistTaskAction.swift
TaskActions/ObjectLibraryAssemblerTaskAction.swift
TaskActions/ODRAssetPackManifestTaskAction.swift
TaskActions/PrecompileClangModuleTaskAction.swift
TaskActions/ProcessProductEntitlementsTaskAction.swift
Expand Down
96 changes: 96 additions & 0 deletions Sources/SWBTaskExecution/TaskActions/LinkerTaskAction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Foundation
public import SWBCore
public import SWBUtil

public final class LinkerTaskAction: TaskAction {

public override class var toolIdentifier: String {
return "linker"
}

/// Whether response files should be expanded before invoking the linker.
private let expandResponseFiles: Bool

public init(expandResponseFiles: Bool) {
self.expandResponseFiles = expandResponseFiles
super.init()
}

public override func serialize<T: Serializer>(to serializer: T) {
serializer.beginAggregate(2)
serializer.serialize(expandResponseFiles)
super.serialize(to: serializer)
serializer.endAggregate()
}

public required init(from deserializer: any Deserializer) throws {
try deserializer.beginAggregate(2)
self.expandResponseFiles = try deserializer.deserialize()
try super.init(from: deserializer)
}

public override func performTaskAction(
_ task: any ExecutableTask,
dynamicExecutionDelegate: any DynamicTaskExecutionDelegate,
executionDelegate: any TaskExecutionDelegate,
clientDelegate: any TaskExecutionClientDelegate,
outputDelegate: any TaskOutputDelegate
) async -> CommandResult {
var commandLine = Array(task.commandLineAsStrings)

if expandResponseFiles {
do {
commandLine = try ResponseFiles.expandResponseFiles(
commandLine,
fileSystem: executionDelegate.fs,
relativeTo: task.workingDirectory
)
} catch {
outputDelegate.emitError("Failed to expand response files: \(error.localizedDescription)")
return .failed
}
}

let processDelegate = TaskProcessDelegate(outputDelegate: outputDelegate)
do {
let success = try await dynamicExecutionDelegate.spawn(
commandLine: commandLine,
environment: task.environment.bindingsDictionary,
workingDirectory: task.workingDirectory,
processDelegate: processDelegate
)

if let error = processDelegate.executionError {
outputDelegate.emitError(error)
return .failed
}

if success {
if let spec = task.type as? CommandLineToolSpec, let files = task.dependencyData {
do {
try spec.adjust(dependencyFiles: files, for: task, fs: executionDelegate.fs)
} catch {
outputDelegate.emitWarning("Unable to perform dependency info modifications: \(error)")
}
}
}

return success ? .succeeded : .failed
} catch {
outputDelegate.emitError("Process execution failed: \(error.localizedDescription)")
return .failed
}
}
}
Loading
Loading