Address Sanitizer options in run/test schemes (#1550)

* Expose address sanitizer flags in run and test BuildActions in Schemes

* Update testJSONEncodable to test the new fields

* Also test the asan setting values for run scheme

* Update changelog

---------

Co-authored-by: Yonas Kolb <yonaskolb@users.noreply.github.com>
This commit is contained in:
Himanshu Kumar
2025-07-16 22:48:00 -07:00
committed by GitHub
parent 38d76e7fa1
commit ae42dd5b19
4 changed files with 92 additions and 1 deletions
+3
View File
@@ -2,6 +2,9 @@
## Next Version
### Added
- Added sanitizer options to run and test actions in Scheme #1550 @hi-kumar
### Fixed
- Added validation to ensure that all values in `settings.configs` are mappings. Previously, passing non-mapping values did not raise an error, making it difficult to detect misconfigurations. Now, `SpecParsingError.invalidConfigsMappingFormat` is thrown if misused. #1547 @Ryu0118
+73 -1
View File
@@ -127,6 +127,10 @@ public struct Scheme: Equatable {
}
public struct Run: BuildAction {
public static let enableAddressSanitizerDefault = false
public static let enableASanStackUseAfterReturnDefault = false
public static let enableThreadSanitizerDefault = false
public static let enableUBSanitizerDefault = false
public static let disableMainThreadCheckerDefault = false
public static let stopOnEveryMainThreadCheckerIssueDefault = false
public static let disableThreadPerformanceCheckerDefault = false
@@ -140,6 +144,10 @@ public struct Scheme: Equatable {
public var environmentVariables: [XCScheme.EnvironmentVariable]
public var enableGPUFrameCaptureMode: XCScheme.LaunchAction.GPUFrameCaptureMode
public var enableGPUValidationMode: Bool
public var enableAddressSanitizer: Bool
public var enableASanStackUseAfterReturn: Bool
public var enableThreadSanitizer: Bool
public var enableUBSanitizer: Bool
public var disableMainThreadChecker: Bool
public var stopOnEveryMainThreadCheckerIssue: Bool
public var disableThreadPerformanceChecker: Bool
@@ -163,6 +171,10 @@ public struct Scheme: Equatable {
environmentVariables: [XCScheme.EnvironmentVariable] = [],
enableGPUFrameCaptureMode: XCScheme.LaunchAction.GPUFrameCaptureMode = XCScheme.LaunchAction.defaultGPUFrameCaptureMode,
enableGPUValidationMode: Bool = enableGPUValidationModeDefault,
enableAddressSanitizer: Bool = enableAddressSanitizerDefault,
enableASanStackUseAfterReturn: Bool = enableASanStackUseAfterReturnDefault,
enableThreadSanitizer: Bool = enableThreadSanitizerDefault,
enableUBSanitizer: Bool = enableUBSanitizerDefault,
disableMainThreadChecker: Bool = disableMainThreadCheckerDefault,
stopOnEveryMainThreadCheckerIssue: Bool = stopOnEveryMainThreadCheckerIssueDefault,
disableThreadPerformanceChecker: Bool = disableThreadPerformanceCheckerDefault,
@@ -181,6 +193,10 @@ public struct Scheme: Equatable {
self.preActions = preActions
self.postActions = postActions
self.environmentVariables = environmentVariables
self.enableAddressSanitizer = enableAddressSanitizer
self.enableASanStackUseAfterReturn = enableASanStackUseAfterReturn
self.enableThreadSanitizer = enableThreadSanitizer
self.enableUBSanitizer = enableUBSanitizer
self.disableMainThreadChecker = disableMainThreadChecker
self.enableGPUFrameCaptureMode = enableGPUFrameCaptureMode
self.enableGPUValidationMode = enableGPUValidationMode
@@ -200,6 +216,10 @@ public struct Scheme: Equatable {
public struct Test: BuildAction {
public static let gatherCoverageDataDefault = false
public static let enableAddressSanitizerDefault = false
public static let enableASanStackUseAfterReturnDefault = false
public static let enableThreadSanitizerDefault = false
public static let enableUBSanitizerDefault = false
public static let disableMainThreadCheckerDefault = false
public static let debugEnabledDefault = true
public static let captureScreenshotsAutomaticallyDefault = true
@@ -209,6 +229,10 @@ public struct Scheme: Equatable {
public var config: String?
public var gatherCoverageData: Bool
public var coverageTargets: [TestableTargetReference]
public var enableAddressSanitizer: Bool
public var enableASanStackUseAfterReturn: Bool
public var enableThreadSanitizer: Bool
public var enableUBSanitizer: Bool
public var disableMainThreadChecker: Bool
public var commandLineArguments: [String: Bool]
public var targets: [TestTarget]
@@ -276,6 +300,10 @@ public struct Scheme: Equatable {
config: String? = nil,
gatherCoverageData: Bool = gatherCoverageDataDefault,
coverageTargets: [TestableTargetReference] = [],
enableAddressSanitizer: Bool = enableAddressSanitizerDefault,
enableASanStackUseAfterReturn: Bool = enableASanStackUseAfterReturnDefault,
enableThreadSanitizer: Bool = enableThreadSanitizerDefault,
enableUBSanitizer: Bool = enableUBSanitizerDefault,
disableMainThreadChecker: Bool = disableMainThreadCheckerDefault,
randomExecutionOrder: Bool = false,
parallelizable: Bool = false,
@@ -297,6 +325,10 @@ public struct Scheme: Equatable {
self.config = config
self.gatherCoverageData = gatherCoverageData
self.coverageTargets = coverageTargets
self.enableAddressSanitizer = enableAddressSanitizer
self.enableASanStackUseAfterReturn = enableASanStackUseAfterReturn
self.enableThreadSanitizer = enableThreadSanitizer
self.enableUBSanitizer = enableUBSanitizer
self.disableMainThreadChecker = disableMainThreadChecker
self.commandLineArguments = commandLineArguments
self.targets = targets
@@ -500,6 +532,10 @@ extension Scheme.Run: JSONObjectConvertible {
} else {
enableGPUValidationMode = jsonDictionary.json(atKeyPath: "enableGPUValidationMode") ?? Scheme.Run.enableGPUValidationModeDefault
}
enableAddressSanitizer = jsonDictionary.json(atKeyPath: "enableAddressSanitizer") ?? Scheme.Run.enableAddressSanitizerDefault
enableASanStackUseAfterReturn = jsonDictionary.json(atKeyPath: "enableASanStackUseAfterReturn") ?? Scheme.Run.enableASanStackUseAfterReturnDefault
enableThreadSanitizer = jsonDictionary.json(atKeyPath: "enableThreadSanitizer") ?? Scheme.Run.enableThreadSanitizerDefault
enableUBSanitizer = jsonDictionary.json(atKeyPath: "enableUBSanitizer") ?? Scheme.Run.enableUBSanitizerDefault
disableMainThreadChecker = jsonDictionary.json(atKeyPath: "disableMainThreadChecker") ?? Scheme.Run.disableMainThreadCheckerDefault
stopOnEveryMainThreadCheckerIssue = jsonDictionary.json(atKeyPath: "stopOnEveryMainThreadCheckerIssue") ?? Scheme.Run.stopOnEveryMainThreadCheckerIssueDefault
disableThreadPerformanceChecker = jsonDictionary.json(atKeyPath: "disableThreadPerformanceChecker") ?? Scheme.Run.disableThreadPerformanceCheckerDefault
@@ -550,6 +586,22 @@ extension Scheme.Run: JSONEncodable {
dict["enableGPUValidationMode"] = enableGPUValidationMode
}
if enableAddressSanitizer != Scheme.Run.enableAddressSanitizerDefault {
dict["enableAddressSanitizer"] = enableAddressSanitizer
}
if enableASanStackUseAfterReturn != Scheme.Run.enableASanStackUseAfterReturnDefault {
dict["enableASanStackUseAfterReturn"] = enableASanStackUseAfterReturn
}
if enableThreadSanitizer != Scheme.Run.enableThreadSanitizerDefault {
dict["enableThreadSanitizer"] = enableThreadSanitizer
}
if enableUBSanitizer != Scheme.Run.enableUBSanitizerDefault {
dict["enableUBSanitizer"] = enableUBSanitizer
}
if disableMainThreadChecker != Scheme.Run.disableMainThreadCheckerDefault {
dict["disableMainThreadChecker"] = disableMainThreadChecker
}
@@ -608,7 +660,11 @@ extension Scheme.Test: JSONObjectConvertible {
} else {
coverageTargets = []
}
enableAddressSanitizer = jsonDictionary.json(atKeyPath: "enableAddressSanitizer") ?? Scheme.Test.enableAddressSanitizerDefault
enableASanStackUseAfterReturn = jsonDictionary.json(atKeyPath: "enableASanStackUseAfterReturn") ?? Scheme.Test.enableASanStackUseAfterReturnDefault
enableThreadSanitizer = jsonDictionary.json(atKeyPath: "enableThreadSanitizer") ?? Scheme.Test.enableThreadSanitizerDefault
enableUBSanitizer = jsonDictionary.json(atKeyPath: "enableUBSanitizer") ?? Scheme.Test.enableUBSanitizerDefault
disableMainThreadChecker = jsonDictionary.json(atKeyPath: "disableMainThreadChecker") ?? Scheme.Test.disableMainThreadCheckerDefault
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
if let targets = jsonDictionary["targets"] as? [Any] {
@@ -659,6 +715,22 @@ extension Scheme.Test: JSONEncodable {
dict["gatherCoverageData"] = gatherCoverageData
}
if enableAddressSanitizer != Scheme.Test.enableAddressSanitizerDefault {
dict["enableAddressSanitizer"] = enableAddressSanitizer
}
if enableASanStackUseAfterReturn != Scheme.Test.enableASanStackUseAfterReturnDefault {
dict["enableASanStackUseAfterReturn"] = enableASanStackUseAfterReturn
}
if enableThreadSanitizer != Scheme.Test.enableThreadSanitizerDefault {
dict["enableThreadSanitizer"] = enableThreadSanitizer
}
if enableUBSanitizer != Scheme.Test.enableUBSanitizerDefault {
dict["enableUBSanitizer"] = enableUBSanitizer
}
if disableMainThreadChecker != Scheme.Test.disableMainThreadCheckerDefault {
dict["disableMainThreadChecker"] = disableMainThreadChecker
}
@@ -311,6 +311,10 @@ public class SchemeGenerator {
codeCoverageEnabled: scheme.test?.gatherCoverageData ?? Scheme.Test.gatherCoverageDataDefault,
codeCoverageTargets: coverageBuildableTargets,
onlyGenerateCoverageForSpecifiedTargets: !coverageBuildableTargets.isEmpty,
enableAddressSanitizer: scheme.test?.enableAddressSanitizer ?? Scheme.Test.enableAddressSanitizerDefault,
enableASanStackUseAfterReturn: scheme.test?.enableASanStackUseAfterReturn ?? Scheme.Test.enableASanStackUseAfterReturnDefault,
enableThreadSanitizer: scheme.test?.enableThreadSanitizer ?? Scheme.Test.enableThreadSanitizerDefault,
enableUBSanitizer: scheme.test?.enableUBSanitizer ?? Scheme.Test.enableUBSanitizerDefault,
disableMainThreadChecker: scheme.test?.disableMainThreadChecker ?? Scheme.Test.disableMainThreadCheckerDefault,
commandlineArguments: testCommandLineArgs,
environmentVariables: testVariables,
@@ -359,6 +363,10 @@ public class SchemeGenerator {
locationScenarioReference: locationScenarioReference,
enableGPUFrameCaptureMode: scheme.run?.enableGPUFrameCaptureMode ?? XCScheme.LaunchAction.defaultGPUFrameCaptureMode,
disableGPUValidationMode: !(scheme.run?.enableGPUValidationMode ?? Scheme.Run.enableGPUValidationModeDefault),
enableAddressSanitizer: scheme.run?.enableAddressSanitizer ?? Scheme.Run.enableAddressSanitizerDefault,
enableASanStackUseAfterReturn: scheme.run?.enableASanStackUseAfterReturn ?? Scheme.Run.enableASanStackUseAfterReturnDefault,
enableThreadSanitizer: scheme.run?.enableThreadSanitizer ?? Scheme.Run.enableThreadSanitizerDefault,
enableUBSanitizer: scheme.run?.enableUBSanitizer ?? Scheme.Run.enableUBSanitizerDefault,
disableMainThreadChecker: scheme.run?.disableMainThreadChecker ?? Scheme.Run.disableMainThreadCheckerDefault,
disablePerformanceAntipatternChecker: scheme.run?.disableThreadPerformanceChecker ?? Scheme.Run.disableThreadPerformanceCheckerDefault,
stopOnEveryMainThreadCheckerIssue: scheme.run?.stopOnEveryMainThreadCheckerIssue ?? Scheme.Run.stopOnEveryMainThreadCheckerIssueDefault,
@@ -768,10 +768,18 @@ class ProjectSpecTests: XCTestCase {
value: "bar",
enabled: false)],
enableGPUFrameCaptureMode: .openGL,
enableAddressSanitizer: true,
enableASanStackUseAfterReturn: true,
enableThreadSanitizer: true,
enableUBSanitizer: true,
launchAutomaticallySubstyle: "2",
storeKitConfiguration: "Configuration.storekit"),
test: Scheme.Test(config: "Config",
gatherCoverageData: true,
enableAddressSanitizer: true,
enableASanStackUseAfterReturn: true,
enableThreadSanitizer: true,
enableUBSanitizer: true,
disableMainThreadChecker: true,
randomExecutionOrder: false,
parallelizable: false,