Skip to content

Commit 32fc9d1

Browse files
author
Luke Daley
committed
Allow logical dependencies to be forward-declared and verified at use-time
This is a skeletal implementation scoped to support a specific functional milestone.
1 parent f0db110 commit 32fc9d1

25 files changed

+579
-23
lines changed

Sources/SWBCore/Dependencies.swift

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public import SWBUtil
14+
public import SWBMacro
15+
16+
// Needed to resolve BuiltinMacros … why?
17+
import SWBCore
18+
19+
import Foundation
20+
21+
// Global/target dependency settings
22+
public struct DependencySettings : Serializable, Sendable, Encodable {
23+
public let dependencies: OrderedSet<String>
24+
public let verification: Bool
25+
26+
27+
public init(_ scope: MacroEvaluationScope) {
28+
self.init(
29+
dependencies: scope.evaluate(BuiltinMacros.DEPENDENCIES),
30+
verification: scope.evaluate(BuiltinMacros.DEPENDENCIES_VERIFICATION)
31+
)
32+
}
33+
34+
public init(
35+
dependencies: [String],
36+
verification: Bool
37+
) {
38+
self.dependencies = OrderedSet(dependencies)
39+
self.verification = verification
40+
}
41+
42+
public func serialize<T: Serializer>(to serializer: T) {
43+
serializer.serializeAggregate(2) {
44+
serializer.serialize(dependencies)
45+
serializer.serialize(verification)
46+
}
47+
}
48+
49+
public init(from deserializer: any Deserializer) throws {
50+
try deserializer.beginAggregate(2)
51+
self.dependencies = try deserializer.deserialize()
52+
self.verification = try deserializer.deserialize()
53+
}
54+
55+
}
56+
57+
// Task-specific settings
58+
public struct TaskDependencySettings : Serializable, Sendable, Encodable {
59+
60+
public let traceFile: Path
61+
public let dependencySettings: DependencySettings
62+
63+
init(traceFile: Path, dependencySettings: DependencySettings) {
64+
assert(!traceFile.isEmpty, "traceFile should never be empty")
65+
self.traceFile = traceFile
66+
self.dependencySettings = dependencySettings
67+
}
68+
69+
public func serialize<T: Serializer>(to serializer: T) {
70+
serializer.serializeAggregate(2) {
71+
serializer.serialize(traceFile)
72+
serializer.serialize(dependencySettings)
73+
}
74+
}
75+
76+
public init(from deserializer: any Deserializer) throws {
77+
try deserializer.beginAggregate(2)
78+
self.traceFile = try deserializer.deserialize()
79+
self.dependencySettings = try deserializer.deserialize()
80+
}
81+
82+
func signatureData() -> String {
83+
return "verify:\(dependencySettings.verification),deps:\(dependencySettings.dependencies.joined(separator: ":"))"
84+
}
85+
86+
}
87+
88+
// Protocol for task payloads
89+
public protocol TaskDependencySettingsPayload {
90+
var taskDependencySettings: TaskDependencySettings? { get }
91+
}

Sources/SWBCore/PlannedTaskAction.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ public protocol TaskActionCreationDelegate
330330
func createValidateProductTaskAction() -> any PlannedTaskAction
331331
func createConstructStubExecutorInputFileListTaskAction() -> any PlannedTaskAction
332332
func createClangCompileTaskAction() -> any PlannedTaskAction
333+
func createClangNonModularCompileTaskAction() -> any PlannedTaskAction
333334
func createClangScanTaskAction() -> any PlannedTaskAction
334335
func createSwiftDriverTaskAction() -> any PlannedTaskAction
335336
func createSwiftCompilationRequirementTaskAction() -> any PlannedTaskAction
@@ -339,6 +340,7 @@ public protocol TaskActionCreationDelegate
339340
func createSignatureCollectionTaskAction() -> any PlannedTaskAction
340341
func createClangModuleVerifierInputGeneratorTaskAction() -> any PlannedTaskAction
341342
func createProcessSDKImportsTaskAction() -> any PlannedTaskAction
343+
func createLdTaskAction() -> any PlannedTaskAction
342344
}
343345

344346
extension TaskActionCreationDelegate {

Sources/SWBCore/Settings/BuiltinMacros.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,8 @@ public final class BuiltinMacros {
584584
public static let DEFAULT_COMPILER = BuiltinMacros.declareStringMacro("DEFAULT_COMPILER")
585585
public static let DEFAULT_KEXT_INSTALL_PATH = BuiltinMacros.declareStringMacro("DEFAULT_KEXT_INSTALL_PATH")
586586
public static let DEFINES_MODULE = BuiltinMacros.declareBooleanMacro("DEFINES_MODULE")
587+
public static let DEPENDENCIES = BuiltinMacros.declareStringListMacro("DEPENDENCIES")
588+
public static let DEPENDENCIES_VERIFICATION = BuiltinMacros.declareBooleanMacro("DEPENDENCIES_VERIFICATION")
587589
public static let DEPENDENCY_SCOPE_INCLUDES_DIRECT_DEPENDENCIES = BuiltinMacros.declareBooleanMacro("DEPENDENCY_SCOPE_INCLUDES_DIRECT_DEPENDENCIES")
588590
public static let DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = BuiltinMacros.declareBooleanMacro("DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER")
589591
public static let __DIAGNOSE_DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER_ERROR = BuiltinMacros.declareBooleanMacro("__DIAGNOSE_DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER_ERROR")
@@ -810,6 +812,7 @@ public final class BuiltinMacros {
810812
public static let LD_RUNPATH_SEARCH_PATHS = BuiltinMacros.declareStringListMacro("LD_RUNPATH_SEARCH_PATHS")
811813
public static let LD_SDK_IMPORTS_FILE = BuiltinMacros.declarePathMacro("LD_SDK_IMPORTS_FILE")
812814
public static let LD_WARN_UNUSED_DYLIBS = BuiltinMacros.declareBooleanMacro("LD_WARN_UNUSED_DYLIBS")
815+
public static let LD_TRACE_FILE = BuiltinMacros.declarePathMacro("LD_TRACE_FILE")
813816
public static let _LD_MULTIARCH = BuiltinMacros.declareBooleanMacro("_LD_MULTIARCH")
814817
public static let _LD_MULTIARCH_PREFIX_MAP = BuiltinMacros.declareStringListMacro("_LD_MULTIARCH_PREFIX_MAP")
815818
public static let LEX = BuiltinMacros.declarePathMacro("LEX")
@@ -1568,6 +1571,8 @@ public final class BuiltinMacros {
15681571
DEFAULT_COMPILER,
15691572
DEFAULT_KEXT_INSTALL_PATH,
15701573
DEFINES_MODULE,
1574+
DEPENDENCIES,
1575+
DEPENDENCIES_VERIFICATION,
15711576
DEPENDENCY_SCOPE_INCLUDES_DIRECT_DEPENDENCIES,
15721577
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER,
15731578
__DIAGNOSE_DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER_ERROR,
@@ -1872,6 +1877,7 @@ public final class BuiltinMacros {
18721877
LD_RUNPATH_SEARCH_PATHS,
18731878
LD_SDK_IMPORTS_FILE,
18741879
LD_WARN_UNUSED_DYLIBS,
1880+
LD_TRACE_FILE,
18751881
_LD_MULTIARCH,
18761882
_LD_MULTIARCH_PREFIX_MAP,
18771883
LEGACY_DEVELOPER_DIR,

Sources/SWBCore/SpecImplementations/Tools/CCompiler.swift

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ struct ClangModuleVerifierPayload: ClangModuleVerifierPayloadType {
415415
}
416416
}
417417

418-
public struct ClangTaskPayload: ClangModuleVerifierPayloadType, DependencyInfoEditableTaskPayload, Encodable {
418+
public struct ClangTaskPayload: ClangModuleVerifierPayloadType, DependencyInfoEditableTaskPayload, TaskDependencySettingsPayload, Encodable {
419419
let dependencyInfoEditPayload: DependencyInfoEditPayload?
420420

421421
/// The path to the serialized diagnostic output. Every clang task must provide this path.
@@ -432,7 +432,9 @@ public struct ClangTaskPayload: ClangModuleVerifierPayloadType, DependencyInfoEd
432432

433433
public let fileNameMapPath: Path?
434434

435-
fileprivate init(serializedDiagnosticsPath: Path?, indexingPayload: ClangIndexingPayload?, explicitModulesPayload: ClangExplicitModulesPayload? = nil, outputObjectFilePath: Path? = nil, fileNameMapPath: Path? = nil, developerPathString: String? = nil) {
435+
public let taskDependencySettings: TaskDependencySettings?
436+
437+
fileprivate init(serializedDiagnosticsPath: Path?, indexingPayload: ClangIndexingPayload?, explicitModulesPayload: ClangExplicitModulesPayload? = nil, outputObjectFilePath: Path? = nil, fileNameMapPath: Path? = nil, developerPathString: String? = nil, taskDependencySettings: TaskDependencySettings? = nil) {
436438
if let developerPathString, explicitModulesPayload == nil {
437439
self.dependencyInfoEditPayload = .init(removablePaths: [], removableBasenames: [], developerPath: Path(developerPathString))
438440
} else {
@@ -443,16 +445,18 @@ public struct ClangTaskPayload: ClangModuleVerifierPayloadType, DependencyInfoEd
443445
self.explicitModulesPayload = explicitModulesPayload
444446
self.outputObjectFilePath = outputObjectFilePath
445447
self.fileNameMapPath = fileNameMapPath
448+
self.taskDependencySettings = taskDependencySettings
446449
}
447450

448451
public func serialize<T: Serializer>(to serializer: T) {
449-
serializer.serializeAggregate(6) {
452+
serializer.serializeAggregate(7) {
450453
serializer.serialize(serializedDiagnosticsPath)
451454
serializer.serialize(indexingPayload)
452455
serializer.serialize(explicitModulesPayload)
453456
serializer.serialize(outputObjectFilePath)
454457
serializer.serialize(fileNameMapPath)
455458
serializer.serialize(dependencyInfoEditPayload)
459+
serializer.serialize(taskDependencySettings)
456460
}
457461
}
458462

@@ -464,6 +468,7 @@ public struct ClangTaskPayload: ClangModuleVerifierPayloadType, DependencyInfoEd
464468
self.outputObjectFilePath = try deserializer.deserialize()
465469
self.fileNameMapPath = try deserializer.deserialize()
466470
self.dependencyInfoEditPayload = try deserializer.deserialize()
471+
self.taskDependencySettings = try deserializer.deserialize()
467472
}
468473
}
469474

@@ -1158,6 +1163,24 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible
11581163
dependencyData = nil
11591164
}
11601165

1166+
let taskDependencySettings = TaskDependencySettings(
1167+
traceFile: Path(outputNode.path.str + ".trace.json"),
1168+
dependencySettings: DependencySettings(cbc.scope)
1169+
)
1170+
1171+
if (taskDependencySettings.dependencySettings.verification) {
1172+
commandLine += [
1173+
"-Xclang",
1174+
"-header-include-file",
1175+
"-Xclang",
1176+
taskDependencySettings.traceFile.str,
1177+
"-Xclang",
1178+
"-header-include-filtering=only-direct-system",
1179+
"-Xclang",
1180+
"-header-include-format=json"
1181+
]
1182+
}
1183+
11611184
// Add the diagnostics serialization flag. We currently place the diagnostics file right next to the output object file.
11621185
let diagFilePath: Path?
11631186
if let serializedDiagnosticsOptions = self.serializedDiagnosticsOptions(scope: cbc.scope, outputPath: outputNode.path) {
@@ -1268,7 +1291,8 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible
12681291
explicitModulesPayload: explicitModulesPayload,
12691292
outputObjectFilePath: shouldGenerateRemarks ? outputNode.path : nil,
12701293
fileNameMapPath: verifierPayload?.fileNameMapPath,
1271-
developerPathString: recordSystemHeaderDepsOutsideSysroot ? cbc.scope.evaluate(BuiltinMacros.DEVELOPER_DIR).str : nil
1294+
developerPathString: recordSystemHeaderDepsOutsideSysroot ? cbc.scope.evaluate(BuiltinMacros.DEVELOPER_DIR).str : nil,
1295+
taskDependencySettings: taskDependencySettings
12721296
)
12731297

12741298
var inputNodes: [any PlannedNode] = inputDeps.map { delegate.createNode($0) }
@@ -1318,8 +1342,10 @@ public class ClangCompilerSpec : CompilerSpec, SpecIdentifierType, GCCCompatible
13181342
extraInputs = []
13191343
}
13201344

1345+
additionalSignatureData += taskDependencySettings.signatureData()
1346+
13211347
// Finally, create the task.
1322-
delegate.createTask(type: self, dependencyData: dependencyData, payload: payload, ruleInfo: ruleInfo, additionalSignatureData: additionalSignatureData, commandLine: commandLine, additionalOutput: additionalOutput, environment: environmentBindings, workingDirectory: compilerWorkingDirectory(cbc), inputs: inputNodes + extraInputs, outputs: [outputNode], action: action ?? delegate.taskActionCreationDelegate.createDeferredExecutionTaskActionIfRequested(userPreferences: cbc.producer.userPreferences), execDescription: resolveExecutionDescription(cbc, delegate), enableSandboxing: enableSandboxing, additionalTaskOrderingOptions: [.compilationForIndexableSourceFile], usesExecutionInputs: usesExecutionInputs, showEnvironment: true, priority: .preferred)
1348+
delegate.createTask(type: self, dependencyData: dependencyData, payload: payload, ruleInfo: ruleInfo, additionalSignatureData: additionalSignatureData, commandLine: commandLine, additionalOutput: additionalOutput, environment: environmentBindings, workingDirectory: compilerWorkingDirectory(cbc), inputs: inputNodes + extraInputs, outputs: [outputNode], action: action ?? delegate.taskActionCreationDelegate.createClangNonModularCompileTaskAction(), execDescription: resolveExecutionDescription(cbc, delegate), enableSandboxing: enableSandboxing, additionalTaskOrderingOptions: [.compilationForIndexableSourceFile], usesExecutionInputs: usesExecutionInputs, showEnvironment: true, priority: .preferred)
13231349

13241350
// If the object file verifier is enabled and we are building with explicit modules, also create a job to produce adjacent objects using implicit modules, then compare the results.
13251351
if cbc.scope.evaluate(BuiltinMacros.CLANG_ENABLE_EXPLICIT_MODULES_OBJECT_FILE_VERIFIER) && action != nil {

Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ struct LdLinkerTaskPreviewPayload: Serializable, Encodable {
5252
}
5353
}
5454

55-
fileprivate struct LdLinkerTaskPayload: DependencyInfoEditableTaskPayload {
55+
fileprivate struct LdLinkerTaskPayload: DependencyInfoEditableTaskPayload, TaskDependencySettingsPayload {
5656
/// Path that points to the output linker file.
5757
let outputPath: Path
5858

@@ -68,17 +68,21 @@ fileprivate struct LdLinkerTaskPayload: DependencyInfoEditableTaskPayload {
6868
/// Path to the object file emitted during LTO, used for optimization remarks.
6969
fileprivate let objectPathLTO: Path?
7070

71+
public let taskDependencySettings: TaskDependencySettings?
72+
7173
init(
7274
outputPath: Path,
7375
dependencyInfoEditPayload: DependencyInfoEditPayload? = nil,
7476
previewPayload: LdLinkerTaskPreviewPayload? = nil,
7577
previewStyle: PreviewStyle? = nil,
76-
objectPathLTO: Path? = nil
78+
objectPathLTO: Path? = nil,
79+
taskDependencySettings: TaskDependencySettings? = nil,
7780
) {
7881
self.outputPath = outputPath
7982
self.dependencyInfoEditPayload = dependencyInfoEditPayload
8083
self.previewPayload = previewPayload
8184
self.objectPathLTO = objectPathLTO
85+
self.taskDependencySettings = taskDependencySettings
8286
switch previewStyle {
8387
case .dynamicReplacement:
8488
self.previewStyle = .dynamicReplacement
@@ -90,22 +94,24 @@ fileprivate struct LdLinkerTaskPayload: DependencyInfoEditableTaskPayload {
9094
}
9195

9296
public func serialize<T: Serializer>(to serializer: T) {
93-
serializer.serializeAggregate(5) {
97+
serializer.serializeAggregate(6) {
9498
serializer.serialize(outputPath)
9599
serializer.serialize(dependencyInfoEditPayload)
96100
serializer.serialize(previewPayload)
97101
serializer.serialize(objectPathLTO)
98102
serializer.serialize(previewStyle)
103+
serializer.serialize(taskDependencySettings)
99104
}
100105
}
101106

102107
public init(from deserializer: any Deserializer) throws {
103-
try deserializer.beginAggregate(5)
108+
try deserializer.beginAggregate(6)
104109
self.outputPath = try deserializer.deserialize()
105110
self.dependencyInfoEditPayload = try deserializer.deserialize()
106111
self.previewPayload = try deserializer.deserialize()
107112
self.objectPathLTO = try deserializer.deserialize()
108113
self.previewStyle = try deserializer.deserialize()
114+
self.taskDependencySettings = try deserializer.deserialize()
109115
}
110116
}
111117

@@ -664,20 +670,35 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
664670
editPayload = nil
665671
}
666672

673+
let taskDependencySettings = TaskDependencySettings(
674+
traceFile: cbc.scope.evaluate(BuiltinMacros.LD_TRACE_FILE),
675+
dependencySettings: DependencySettings(cbc.scope)
676+
)
677+
678+
if (taskDependencySettings.dependencySettings.verification) {
679+
commandLine += [
680+
"-Xlinker",
681+
"-trace_file",
682+
"-Xlinker",
683+
taskDependencySettings.traceFile.str,
684+
]
685+
}
686+
667687
let payload = LdLinkerTaskPayload(
668688
outputPath: cbc.output,
669689
dependencyInfoEditPayload: editPayload,
670690
previewPayload: previewPayload,
671691
previewStyle: cbc.scope.previewStyle,
672-
objectPathLTO: shouldGenerateRemarks ? objectPathLTO : nil
692+
objectPathLTO: shouldGenerateRemarks ? objectPathLTO : nil,
693+
taskDependencySettings: taskDependencySettings,
673694
)
674695

675696
// Add dependencies on any directories in our input search paths for which the build system is creating those directories.
676697
let ldSearchPaths = Set(cbc.scope.evaluate(BuiltinMacros.FRAMEWORK_SEARCH_PATHS) + cbc.scope.evaluate(BuiltinMacros.LIBRARY_SEARCH_PATHS))
677698
let otherInputs = delegate.buildDirectories.sorted().compactMap { path in ldSearchPaths.contains(path.str) ? delegate.createBuildDirectoryNode(absolutePath: path) : nil } + cbc.commandOrderingInputs
678699

679700
// Create the task.
680-
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)
701+
delegate.createTask(type: self, dependencyData: dependencyInfo, payload: payload, ruleInfo: defaultRuleInfo(cbc, delegate), additionalSignatureData: taskDependencySettings.signatureData(), commandLine: commandLine, environment: environment, workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: inputs + otherInputs, outputs: outputs, action: delegate.taskActionCreationDelegate.createLdTaskAction(), execDescription: resolveExecutionDescription(cbc, delegate), enableSandboxing: enableSandboxing)
681702
}
682703

683704
public static func addAdditionalDependenciesFromCommandLine(_ cbc: CommandBuildContext, _ commandLine: [String], _ environment: EnvironmentBindings, _ inputs: inout [any PlannedNode], _ outputs: inout [any PlannedNode], _ delegate: any TaskGenerationDelegate) {

Sources/SWBCore/Specs/CoreBuildSystem.xcspec

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4532,6 +4532,15 @@ When this setting is enabled:
45324532
Type = Boolean;
45334533
DefaultValue = "NO";
45344534
},
4535+
{
4536+
Name = "DEPENDENCIES";
4537+
Type = StringList;
4538+
},
4539+
{
4540+
Name = "DEPENDENCIES_VERIFICATION";
4541+
Type = Boolean;
4542+
DefaultValue = YES;
4543+
},
45354544
);
45364545
},
45374546
)

Sources/SWBTaskExecution/BuildDescriptionManager.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,10 @@ extension BuildSystemTaskPlanningDelegate: TaskActionCreationDelegate {
856856
return ClangCompileTaskAction()
857857
}
858858

859+
func createClangNonModularCompileTaskAction() -> any PlannedTaskAction {
860+
return ClangNonModularCompileTaskAction()
861+
}
862+
859863
func createClangScanTaskAction() -> any PlannedTaskAction {
860864
return ClangScanTaskAction()
861865
}
@@ -891,6 +895,10 @@ extension BuildSystemTaskPlanningDelegate: TaskActionCreationDelegate {
891895
func createProcessSDKImportsTaskAction() -> any PlannedTaskAction {
892896
return ProcessSDKImportsTaskAction()
893897
}
898+
899+
func createLdTaskAction() -> any PlannedTaskAction {
900+
return LdTaskAction()
901+
}
894902
}
895903

896904
fileprivate extension BuildDescription {

Sources/SWBTaskExecution/BuiltinTaskActionsExtension.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ public struct BuiltinTaskActionsExtension: TaskActionExtension {
5151
36: ConstructStubExecutorInputFileListTaskAction.self,
5252
37: ConcatenateTaskAction.self,
5353
38: GenericCachingTaskAction.self,
54-
39: ProcessSDKImportsTaskAction.self
54+
39: ProcessSDKImportsTaskAction.self,
55+
40: LdTaskAction.self,
56+
42: ClangNonModularCompileTaskAction.self,
5557
]
5658
}
5759
}

0 commit comments

Comments
 (0)