Skip to content

Commit 46faa85

Browse files
committed
Support invoking the API digester in API mode in addition to ABI mode
1 parent 972a844 commit 46faa85

File tree

6 files changed

+185
-11
lines changed

6 files changed

+185
-11
lines changed

Sources/SWBCore/Settings/BuiltinMacros.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,7 @@ public final class BuiltinMacros {
974974
public static let REZ_PREFIX_FILE = BuiltinMacros.declarePathMacro("REZ_PREFIX_FILE")
975975
public static let REZ_SEARCH_PATHS = BuiltinMacros.declarePathListMacro("REZ_SEARCH_PATHS")
976976
public static let RUN_CLANG_STATIC_ANALYZER = BuiltinMacros.declareBooleanMacro("RUN_CLANG_STATIC_ANALYZER")
977+
public static let SWIFT_API_DIGESTER_MODE = BuiltinMacros.declareEnumMacro("SWIFT_API_DIGESTER_MODE") as EnumMacroDeclaration<SwiftAPIDigesterMode>
977978
public static let RUN_SWIFT_ABI_CHECKER_TOOL = BuiltinMacros.declareBooleanMacro("RUN_SWIFT_ABI_CHECKER_TOOL")
978979
public static let RUN_SWIFT_ABI_CHECKER_TOOL_DRIVER = BuiltinMacros.declareBooleanMacro("RUN_SWIFT_ABI_CHECKER_TOOL_DRIVER")
979980
public static let RUN_SWIFT_ABI_GENERATION_TOOL = BuiltinMacros.declareBooleanMacro("RUN_SWIFT_ABI_GENERATION_TOOL")
@@ -2107,6 +2108,7 @@ public final class BuiltinMacros {
21072108
SKIP_BUILDING_DOCUMENTATION,
21082109
RUN_SYMBOL_GRAPH_EXTRACT,
21092110
SYSTEM_EXTENSIONS_FOLDER_PATH,
2111+
SWIFT_API_DIGESTER_MODE,
21102112
RUN_SWIFT_ABI_CHECKER_TOOL,
21112113
RUN_SWIFT_ABI_CHECKER_TOOL_DRIVER,
21122114
RUN_SWIFT_ABI_GENERATION_TOOL,
@@ -2640,6 +2642,13 @@ public enum SwiftEnableExplicitModulesSetting: String, Equatable, Hashable, Enum
26402642
case disabled = "NO"
26412643
}
26422644

2645+
public enum SwiftAPIDigesterMode: String, Equatable, Hashable, EnumerationMacroType {
2646+
public static let defaultValue: SwiftAPIDigesterMode = .abi
2647+
2648+
case abi = "abi"
2649+
case api = "api"
2650+
}
2651+
26432652
public enum SwiftDependencyRegistrationMode: String, Equatable, Hashable, EnumerationMacroType {
26442653
public static let defaultValue: SwiftDependencyRegistrationMode = .makeStyleDependenciesSupplementedByScanner
26452654

Sources/SWBCore/SpecImplementations/ProductTypes.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,10 @@ public class StandaloneExecutableProductTypeSpec : ProductTypeSpec, SpecClassTyp
772772
public class var className: String {
773773
return "XCStandaloneExecutableProductType"
774774
}
775+
776+
public override var supportsSwiftABIChecker: Bool {
777+
true
778+
}
775779
}
776780

777781
public class LibraryProductTypeSpec: StandaloneExecutableProductTypeSpec, @unchecked Sendable {

Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ private final class EnumBuildOptionType : BuildOptionType {
113113
return try namespace.declareEnumMacro(name) as EnumMacroDeclaration<SwiftEnableExplicitModulesSetting>
114114
case "LINKER_DRIVER":
115115
return try namespace.declareEnumMacro(name) as EnumMacroDeclaration<LinkerDriverChoice>
116+
case "SWIFT_API_DIGESTER_MODE":
117+
return try namespace.declareEnumMacro(name) as EnumMacroDeclaration<SwiftAPIDigesterMode>
116118
default:
117119
return try namespace.declareStringMacro(name)
118120
}

Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/SwiftFrameworkABICheckerTaskProducer.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ fileprivate func supportSwiftABIChecking(_ context: TaskProducerContext) -> Bool
2020
// swift-api-digester is run only when the "build" component is present.
2121
guard scope.evaluate(BuiltinMacros.BUILD_COMPONENTS).contains("build") else { return false }
2222

23-
guard scope.evaluate(BuiltinMacros.SWIFT_EMIT_MODULE_INTERFACE) &&
24-
scope.evaluate(BuiltinMacros.SWIFT_ENABLE_LIBRARY_EVOLUTION) else {
23+
guard scope.evaluate(BuiltinMacros.SWIFT_API_DIGESTER_MODE) == .api ||
24+
(scope.evaluate(BuiltinMacros.SWIFT_EMIT_MODULE_INTERFACE) && scope.evaluate(BuiltinMacros.SWIFT_ENABLE_LIBRARY_EVOLUTION)) else {
2525
// BUILD_LIBRARY_FOR_DISTRIBUTION is the option clients should use (it's also what is exposed in the
2626
// Build Settings editor) and is what SWIFT_EMIT_MODULE_INTERFACE uses by default, but they are
2727
// configurable independently.
@@ -69,6 +69,7 @@ final class SwiftFrameworkABICheckerTaskProducer: PhasedTaskProducer, TaskProduc
6969
guard supportSwiftABIChecking(context) else { return [] }
7070
// All archs
7171
let archs: [String] = scope.evaluate(BuiltinMacros.ARCHS)
72+
let mode = scope.evaluate(BuiltinMacros.SWIFT_API_DIGESTER_MODE)
7273

7374
// All variants
7475
let buildVariants = scope.evaluate(BuiltinMacros.BUILD_VARIANTS)
@@ -83,7 +84,13 @@ final class SwiftFrameworkABICheckerTaskProducer: PhasedTaskProducer, TaskProduc
8384
let moduleInput = FileToBuild(absolutePath: moduleDirPath, inferringTypeUsing: context)
8485
let interfaceInput = FileToBuild(absolutePath: Path(moduleDirPath.withoutSuffix + ".swiftinterface"), inferringTypeUsing: context)
8586
let serializedDiagPath = scope.evaluate(BuiltinMacros.TARGET_TEMP_DIR).join(scope.evaluate(BuiltinMacros.PRODUCT_NAME)).join("SwiftABIChecker").join(variant).join(getBaselineFileName(scope, arch).withoutSuffix + ".dia")
86-
var allInputs = [moduleInput, interfaceInput]
87+
var allInputs: [FileToBuild]
88+
switch mode {
89+
case .abi:
90+
allInputs = [moduleInput, interfaceInput]
91+
case .api:
92+
allInputs = [moduleInput]
93+
}
8794
if scope.evaluate(BuiltinMacros.RUN_SWIFT_ABI_GENERATION_TOOL) {
8895
// If users also want to generate ABI baseline, we should generate the baseline first. This allows users to update
8996
// baseline without re-running the build.
@@ -125,6 +132,7 @@ class SwiftABIBaselineGenerationTaskProducer: PhasedTaskProducer, TaskProducer {
125132
guard supportSwiftABIChecking(context) else { return [] }
126133
// All archs
127134
let archs: [String] = scope.evaluate(BuiltinMacros.ARCHS)
135+
let mode = scope.evaluate(BuiltinMacros.SWIFT_API_DIGESTER_MODE)
128136

129137
// All variants
130138
let buildVariants = scope.evaluate(BuiltinMacros.BUILD_VARIANTS)
@@ -140,9 +148,17 @@ class SwiftABIBaselineGenerationTaskProducer: PhasedTaskProducer, TaskProducer {
140148
let moduleInput = FileToBuild(absolutePath: moduleDirPath, inferringTypeUsing: context)
141149
let interfaceInput = FileToBuild(absolutePath: Path(moduleDirPath.withoutSuffix + ".swiftinterface"), inferringTypeUsing: context)
142150

151+
let allInputs: [FileToBuild]
152+
switch mode {
153+
case .abi:
154+
allInputs = [moduleInput, interfaceInput]
155+
case .api:
156+
allInputs = [moduleInput]
157+
}
158+
143159
let baselinePath = getGeneratedBaselineFilePath(context, arch)
144160

145-
let cbc = CommandBuildContext(producer: context, scope: scope, inputs: [moduleInput, interfaceInput], output: baselinePath)
161+
let cbc = CommandBuildContext(producer: context, scope: scope, inputs: allInputs, output: baselinePath)
146162
await appendGeneratedTasks(&tasks) { delegate in
147163
// Generate baseline into the baseline directory
148164
await context.swiftABIGenerationToolSpec?.constructABIGenerationTask(cbc, delegate, baselinePath)

Sources/SWBUniversalPlatform/Specs/Swift.xcspec

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,7 +1411,7 @@
14111411
RuleName = "CheckSwiftABI $(CURRENT_VARIANT) $(CURRENT_ARCH)";
14121412
ExecDescription = "Check ABI stability for $(PRODUCT_MODULE_NAME).swiftinterface";
14131413
ProgressDescription = "Checking ABI stability for $(PRODUCT_MODULE_NAME).swiftinterface";
1414-
CommandLine = "swift-api-digester -diagnose-sdk -abort-on-module-fail -abi -compiler-style-diags [options]";
1414+
CommandLine = "swift-api-digester -diagnose-sdk -abort-on-module-fail -compiler-style-diags [options]";
14151415
CommandOutputParser = "XCGccCommandOutputParser";
14161416
Options = (
14171417
{
@@ -1440,10 +1440,26 @@
14401440
CommandLineArgs = (
14411441
"-module",
14421442
"$(value)",
1443-
"-use-interface-for-module",
1444-
"$(value)",
14451443
);
14461444
},
1445+
{
1446+
Name = "SWIFT_API_DIGESTER_MODE";
1447+
Type = Enumeration;
1448+
Values = (
1449+
abi,
1450+
api,
1451+
);
1452+
DefaultValue = abi;
1453+
CommandLineArgs = {
1454+
abi = (
1455+
"-abi",
1456+
"-use-interface-for-module",
1457+
"$(SWIFT_MODULE_NAME)",
1458+
);
1459+
api = (
1460+
);
1461+
};
1462+
},
14471463
{
14481464
Name = "OTHER_SWIFT_ABI_CHECKER_FLAGS";
14491465
Type = StringList;
@@ -1461,7 +1477,7 @@
14611477
RuleName = "GenerateSwiftABIBaseline $(CURRENT_VARIANT) $(CURRENT_ARCH)";
14621478
ExecDescription = "Generate ABI baseline for $(PRODUCT_MODULE_NAME).swiftinterface";
14631479
ProgressDescription = "Generating ABI baseline for $(PRODUCT_MODULE_NAME).swiftinterface";
1464-
CommandLine = "swift-api-digester -dump-sdk -abort-on-module-fail -abi -swift-only -avoid-tool-args [options]";
1480+
CommandLine = "swift-api-digester -dump-sdk -abort-on-module-fail -swift-only -avoid-tool-args [options]";
14651481
CommandOutputParser = "XCGccCommandOutputParser";
14661482
Options = (
14671483
{
@@ -1490,10 +1506,26 @@
14901506
CommandLineArgs = (
14911507
"-module",
14921508
"$(value)",
1493-
"-use-interface-for-module",
1494-
"$(value)",
14951509
);
14961510
},
1511+
{
1512+
Name = "SWIFT_API_DIGESTER_MODE";
1513+
Type = Enumeration;
1514+
Values = (
1515+
abi,
1516+
api,
1517+
);
1518+
DefaultValue = abi;
1519+
CommandLineArgs = {
1520+
abi = (
1521+
"-abi",
1522+
"-use-interface-for-module",
1523+
"$(SWIFT_MODULE_NAME)",
1524+
);
1525+
api = (
1526+
);
1527+
};
1528+
},
14971529
{
14981530
Name = "OTHER_SWIFT_ABI_CHECKER_FLAGS";
14991531
Type = StringList;

Tests/SWBTaskConstructionTests/SwiftABICheckerTaskConstructionTests.swift

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,59 @@ fileprivate struct SwiftABICheckerTaskConstructionTests: CoreBasedTests {
138138
}
139139
}
140140

141+
@Test(.requireSDKs(.iOS))
142+
func swiftABIBaselineGenerationModes() async throws {
143+
let testProject = try await TestProject(
144+
"aProject",
145+
sourceRoot: Path("/TEST"),
146+
groupTree: TestGroup(
147+
"SomeFiles", path: "Sources",
148+
children: [
149+
TestFile("Fwk.swift"),
150+
]),
151+
buildConfigurations: [
152+
TestBuildConfiguration("Debug", buildSettings: [
153+
"ARCHS": "arm64",
154+
"SDKROOT": "iphoneos",
155+
"PRODUCT_NAME": "$(TARGET_NAME)",
156+
"RUN_SWIFT_ABI_GENERATION_TOOL": "YES",
157+
"SWIFT_ABI_GENERATION_TOOL_OUTPUT_DIR": "/tmp/user_given_generated_baseline",
158+
"SWIFT_EXEC": swiftCompilerPath.str,
159+
"SWIFT_VERSION": swiftVersion,
160+
"CODE_SIGNING_ALLOWED": "NO",
161+
"TAPI_EXEC": tapiToolPath.str,
162+
])],
163+
targets: [
164+
TestStandardTarget(
165+
"Fwk",
166+
type: .framework,
167+
buildPhases: [
168+
TestSourcesBuildPhase(["Fwk.swift"])
169+
]),
170+
])
171+
let core = try await getCore()
172+
let tester = try TaskConstructionTester(core, testProject)
173+
await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug", overrides: ["SWIFT_API_DIGESTER_MODE": "abi"]), runDestination: .anyiOSDevice) { results in
174+
results.checkError(.contains("Swift ABI checker is only functional when BUILD_LIBRARY_FOR_DISTRIBUTION = YES"))
175+
}
176+
177+
await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug", overrides: ["SWIFT_API_DIGESTER_MODE": "abi", "BUILD_LIBRARY_FOR_DISTRIBUTION": "YES"]), runDestination: .anyiOSDevice) { results in
178+
results.checkNoDiagnostics()
179+
results.checkTask(.matchRuleType("GenerateSwiftABIBaseline")) { task in
180+
task.checkCommandLineContains(["-abi"])
181+
task.checkCommandLineContains(["-use-interface-for-module"])
182+
}
183+
}
184+
185+
await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug", overrides: ["SWIFT_API_DIGESTER_MODE": "api"]), runDestination: .anyiOSDevice) { results in
186+
results.checkNoDiagnostics()
187+
results.checkTask(.matchRuleType("GenerateSwiftABIBaseline")) { task in
188+
task.checkCommandLineDoesNotContain("-abi")
189+
task.checkCommandLineDoesNotContain("-use-interface-for-module")
190+
}
191+
}
192+
}
193+
141194
@Test(.requireSDKs(.iOS))
142195
func swiftABICheckerUsingSpecifiedBaseline() async throws {
143196
let testProject = try await TestProject(
@@ -189,7 +242,6 @@ fileprivate struct SwiftABICheckerTaskConstructionTests: CoreBasedTests {
189242
"swift-api-digester",
190243
"-diagnose-sdk",
191244
"-abort-on-module-fail",
192-
"-abi",
193245
"-compiler-style-diags",
194246
"-target",
195247
"arm64e-apple-ios\(core.loadSDK(.iOS).version)",
@@ -201,6 +253,7 @@ fileprivate struct SwiftABICheckerTaskConstructionTests: CoreBasedTests {
201253
"\(core.loadSDK(.iOS).path.str)",
202254
"-module",
203255
"Fwk",
256+
"-abi",
204257
"-use-interface-for-module",
205258
"-serialize-diagnostics-path",
206259
"/TEST/build/aProject.build/Debug-iphoneos/Fwk.build/Fwk/SwiftABIChecker/normal/arm64e-ios.dia",
@@ -216,6 +269,64 @@ fileprivate struct SwiftABICheckerTaskConstructionTests: CoreBasedTests {
216269
}
217270
}
218271

272+
@Test(.requireSDKs(.iOS))
273+
func swiftABICheckerModes() async throws {
274+
let testProject = try await TestProject(
275+
"aProject",
276+
sourceRoot: Path("/TEST"),
277+
groupTree: TestGroup(
278+
"SomeFiles", path: "Sources",
279+
children: [
280+
TestFile("Fwk.swift"),
281+
]),
282+
buildConfigurations: [
283+
TestBuildConfiguration("Debug", buildSettings: [
284+
"ARCHS": "arm64e",
285+
"SDKROOT": "iphoneos",
286+
"PRODUCT_NAME": "$(TARGET_NAME)",
287+
"RUN_SWIFT_ABI_CHECKER_TOOL": "YES",
288+
"SWIFT_EXEC": swiftCompilerPath.str,
289+
"SWIFT_VERSION": swiftVersion,
290+
"FRAMEWORK_SEARCH_PATHS": "/Target/Framework/Search/Path/A",
291+
"CODE_SIGNING_ALLOWED": "NO",
292+
"BUILD_LIBRARY_FOR_DISTRIBUTION": "YES",
293+
"SWIFT_ABI_CHECKER_BASELINE_DIR": "/tmp/mybaseline",
294+
"SWIFT_ABI_CHECKER_EXCEPTIONS_FILE": "/tmp/allow.txt",
295+
"TAPI_EXEC": tapiToolPath.str,
296+
])],
297+
targets: [
298+
TestStandardTarget(
299+
"Fwk",
300+
type: .framework,
301+
buildPhases: [
302+
TestSourcesBuildPhase(["Fwk.swift"])
303+
]),
304+
])
305+
let core = try await getCore()
306+
let tester = try TaskConstructionTester(core, testProject)
307+
308+
let fs = PseudoFS()
309+
try fs.createDirectory(.root.join("tmp"))
310+
try fs.write(.root.join("tmp").join("allow.txt"), contents: "")
311+
try await fs.writeJSON(.root.join("tmp/mybaseline/ABI/arm64e-ios.json"), .plDict([:]))
312+
313+
await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug", overrides: ["SWIFT_API_DIGESTER_MODE": "abi"]), runDestination: .iOS, fs: fs) { results in
314+
results.checkNoDiagnostics()
315+
results.checkTask(.matchRuleType("CheckSwiftABI")) { task in
316+
task.checkCommandLineContains(["-abi"])
317+
task.checkCommandLineContains(["-use-interface-for-module"])
318+
}
319+
}
320+
321+
await tester.checkBuild(BuildParameters(action: .build, configuration: "Debug", overrides: ["SWIFT_API_DIGESTER_MODE": "api"]), runDestination: .iOS, fs: fs) { results in
322+
results.checkNoDiagnostics()
323+
results.checkTask(.matchRuleType("CheckSwiftABI")) { task in
324+
task.checkCommandLineDoesNotContain("-abi")
325+
task.checkCommandLineDoesNotContain("-use-interface-for-module")
326+
}
327+
}
328+
}
329+
219330
@Test(.requireSDKs(.iOS))
220331
func swiftABICheckerTaskSequence() async throws {
221332
let testProject = try await TestProject(

0 commit comments

Comments
 (0)