diff --git a/Sources/ProjectSpec/AdditionalOptions.swift b/Sources/ProjectSpec/AdditionalOptions.swift
new file mode 100644
index 000000000..ce2a7b7b4
--- /dev/null
+++ b/Sources/ProjectSpec/AdditionalOptions.swift
@@ -0,0 +1,39 @@
+import Foundation
+import JSONUtilities
+
+public struct AdditionalOptions: Equatable, Hashable {
+ public var mallocScribble: Bool
+ public var mallocGuardEdges: Bool
+ public var guardMalloc: Bool
+ public var zombieObjects: Bool
+
+ public init(mallocScribble: Bool,
+ mallocGuardEdges: Bool,
+ guardMalloc: Bool,
+ zombieObjects: Bool) {
+ self.mallocScribble = mallocScribble
+ self.mallocGuardEdges = mallocGuardEdges
+ self.guardMalloc = guardMalloc
+ self.zombieObjects = zombieObjects
+ }
+}
+
+extension AdditionalOptions: JSONObjectConvertible {
+ public init(jsonDictionary: JSONDictionary) throws {
+ mallocScribble = jsonDictionary.json(atKeyPath: "mallocScribble") ?? false
+ mallocGuardEdges = jsonDictionary.json(atKeyPath: "mallocGuardEdges") ?? false
+ guardMalloc = jsonDictionary.json(atKeyPath: "guardMalloc") ?? false
+ zombieObjects = jsonDictionary.json(atKeyPath: "zombieObjects") ?? false
+ }
+}
+
+extension AdditionalOptions: JSONEncodable {
+ public func toJSONValue() -> Any {
+ [
+ "mallocScribble": mallocScribble,
+ "mallocGuardEdges": mallocGuardEdges,
+ "guardMalloc": guardMalloc,
+ "zombieObjects": zombieObjects,
+ ]
+ }
+}
diff --git a/Sources/ProjectSpec/Scheme.swift b/Sources/ProjectSpec/Scheme.swift
index 00ffafd19..361285c14 100644
--- a/Sources/ProjectSpec/Scheme.swift
+++ b/Sources/ProjectSpec/Scheme.swift
@@ -111,6 +111,7 @@ public struct Scheme: Equatable {
public var environmentVariables: [XCScheme.EnvironmentVariable]
public var disableMainThreadChecker: Bool
public var stopOnEveryMainThreadCheckerIssue: Bool
+ public var additionalOptions: AdditionalOptions?
public var language: String?
public var region: String?
public var askForAppToLaunch: Bool?
@@ -131,6 +132,7 @@ public struct Scheme: Equatable {
environmentVariables: [XCScheme.EnvironmentVariable] = [],
disableMainThreadChecker: Bool = disableMainThreadCheckerDefault,
stopOnEveryMainThreadCheckerIssue: Bool = stopOnEveryMainThreadCheckerIssueDefault,
+ additionalOptions: AdditionalOptions? = nil,
language: String? = nil,
region: String? = nil,
askForAppToLaunch: Bool? = nil,
@@ -148,6 +150,7 @@ public struct Scheme: Equatable {
self.environmentVariables = environmentVariables
self.disableMainThreadChecker = disableMainThreadChecker
self.stopOnEveryMainThreadCheckerIssue = stopOnEveryMainThreadCheckerIssue
+ self.additionalOptions = additionalOptions
self.language = language
self.region = region
self.askForAppToLaunch = askForAppToLaunch
@@ -171,6 +174,8 @@ public struct Scheme: Equatable {
public var gatherCoverageData: Bool
public var coverageTargets: [TestableTargetReference]
public var disableMainThreadChecker: Bool
+ public var additionalOptions: AdditionalOptions?
+
public var commandLineArguments: [String: Bool]
public var targets: [TestTarget]
public var preActions: [ExecutionAction]
@@ -236,6 +241,7 @@ public struct Scheme: Equatable {
gatherCoverageData: Bool = gatherCoverageDataDefault,
coverageTargets: [TestableTargetReference] = [],
disableMainThreadChecker: Bool = disableMainThreadCheckerDefault,
+ additionalOptions: AdditionalOptions? = nil,
randomExecutionOrder: Bool = false,
parallelizable: Bool = false,
commandLineArguments: [String: Bool] = [:],
@@ -255,6 +261,7 @@ public struct Scheme: Equatable {
self.gatherCoverageData = gatherCoverageData
self.coverageTargets = coverageTargets
self.disableMainThreadChecker = disableMainThreadChecker
+ self.additionalOptions = additionalOptions
self.commandLineArguments = commandLineArguments
self.targets = targets
self.preActions = preActions
@@ -410,6 +417,7 @@ extension Scheme.Run: JSONObjectConvertible {
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
disableMainThreadChecker = jsonDictionary.json(atKeyPath: "disableMainThreadChecker") ?? Scheme.Run.disableMainThreadCheckerDefault
stopOnEveryMainThreadCheckerIssue = jsonDictionary.json(atKeyPath: "stopOnEveryMainThreadCheckerIssue") ?? Scheme.Run.stopOnEveryMainThreadCheckerIssueDefault
+ additionalOptions = jsonDictionary.json(atKeyPath: "additionalOptions")
language = jsonDictionary.json(atKeyPath: "language")
region = jsonDictionary.json(atKeyPath: "region")
debugEnabled = jsonDictionary.json(atKeyPath: "debugEnabled") ?? Scheme.Run.debugEnabledDefault
@@ -505,6 +513,7 @@ extension Scheme.Test: JSONObjectConvertible {
}
disableMainThreadChecker = jsonDictionary.json(atKeyPath: "disableMainThreadChecker") ?? Scheme.Test.disableMainThreadCheckerDefault
+ additionalOptions = jsonDictionary.json(atKeyPath: "additionalOptions")
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
if let targets = jsonDictionary["targets"] as? [Any] {
self.targets = try targets.compactMap { target in
diff --git a/Sources/ProjectSpec/TargetScheme.swift b/Sources/ProjectSpec/TargetScheme.swift
index 59818a070..4396f03f3 100644
--- a/Sources/ProjectSpec/TargetScheme.swift
+++ b/Sources/ProjectSpec/TargetScheme.swift
@@ -17,6 +17,7 @@ public struct TargetScheme: Equatable {
public var region: String?
public var disableMainThreadChecker: Bool
public var stopOnEveryMainThreadCheckerIssue: Bool
+ public var additionalOptions: AdditionalOptions?
public var buildImplicitDependencies: Bool
public var commandLineArguments: [String: Bool]
public var environmentVariables: [XCScheme.EnvironmentVariable]
@@ -35,6 +36,7 @@ public struct TargetScheme: Equatable {
region: String? = nil,
disableMainThreadChecker: Bool = disableMainThreadCheckerDefault,
stopOnEveryMainThreadCheckerIssue: Bool = stopOnEveryMainThreadCheckerIssueDefault,
+ additionalOptions: AdditionalOptions? = nil,
buildImplicitDependencies: Bool = buildImplicitDependenciesDefault,
commandLineArguments: [String: Bool] = [:],
environmentVariables: [XCScheme.EnvironmentVariable] = [],
@@ -51,6 +53,7 @@ public struct TargetScheme: Equatable {
self.region = region
self.disableMainThreadChecker = disableMainThreadChecker
self.stopOnEveryMainThreadCheckerIssue = stopOnEveryMainThreadCheckerIssue
+ self.additionalOptions = additionalOptions
self.buildImplicitDependencies = buildImplicitDependencies
self.commandLineArguments = commandLineArguments
self.environmentVariables = environmentVariables
@@ -100,6 +103,7 @@ extension TargetScheme: JSONObjectConvertible {
region = jsonDictionary.json(atKeyPath: "region")
disableMainThreadChecker = jsonDictionary.json(atKeyPath: "disableMainThreadChecker") ?? TargetScheme.disableMainThreadCheckerDefault
stopOnEveryMainThreadCheckerIssue = jsonDictionary.json(atKeyPath: "stopOnEveryMainThreadCheckerIssue") ?? TargetScheme.stopOnEveryMainThreadCheckerIssueDefault
+ additionalOptions = jsonDictionary.json(atKeyPath: "additionalOptions")
buildImplicitDependencies = jsonDictionary.json(atKeyPath: "buildImplicitDependencies") ?? TargetScheme.buildImplicitDependenciesDefault
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
@@ -136,6 +140,10 @@ extension TargetScheme: JSONEncodable {
if stopOnEveryMainThreadCheckerIssue != TargetScheme.stopOnEveryMainThreadCheckerIssueDefault {
dict["stopOnEveryMainThreadCheckerIssue"] = stopOnEveryMainThreadCheckerIssue
}
+
+ if let additionalOptions = additionalOptions {
+ dict["additionalOptions"] = additionalOptions.toJSONValue()
+ }
if buildImplicitDependencies != TargetScheme.buildImplicitDependenciesDefault {
dict["buildImplicitDependencies"] = buildImplicitDependencies
diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift
index 60922f29e..bd595f698 100644
--- a/Sources/XcodeGenKit/SchemeGenerator.swift
+++ b/Sources/XcodeGenKit/SchemeGenerator.swift
@@ -249,7 +249,24 @@ public class SchemeGenerator {
let testPlans = scheme.test?.testPlans.enumerated().map { index, testPlan in
XCScheme.TestPlanReference(reference: "container:\(testPlan.path)", default: defaultTestPlanIndex == index)
} ?? []
-
+
+ func getAdditionalOptions(_ options: AdditionalOptions) -> [XCScheme.AdditionalOption] {
+ var additionalOptions: [XCScheme.AdditionalOption] = []
+ if options.mallocScribble {
+ additionalOptions.append(XCScheme.AdditionalOption(key: "MallocScribble", value: "", isEnabled: true))
+ }
+ if options.mallocGuardEdges {
+ additionalOptions.append(XCScheme.AdditionalOption(key: "MallocGuardEdges", value: "", isEnabled: true))
+ }
+ if options.guardMalloc {
+ additionalOptions.append(XCScheme.AdditionalOption(key: "DYLD_INSERT_LIBRARIES", value: "/usr/lib/libgmalloc.dylib", isEnabled: true))
+ }
+ if options.zombieObjects {
+ additionalOptions.append(XCScheme.AdditionalOption(key: "NSZombieEnabled", value: "YES", isEnabled: true))
+ }
+ return additionalOptions
+ }
+
let testAction = XCScheme.TestAction(
buildConfiguration: scheme.test?.config ?? defaultDebugConfig.name,
macroExpansion: buildableReference,
@@ -264,6 +281,7 @@ public class SchemeGenerator {
codeCoverageTargets: coverageBuildableTargets,
onlyGenerateCoverageForSpecifiedTargets: !coverageBuildableTargets.isEmpty,
disableMainThreadChecker: scheme.test?.disableMainThreadChecker ?? Scheme.Test.disableMainThreadCheckerDefault,
+ additionalOptions: scheme.test?.additionalOptions.map(getAdditionalOptions) ?? [],
commandlineArguments: testCommandLineArgs,
environmentVariables: testVariables,
language: scheme.test?.language,
@@ -310,6 +328,7 @@ public class SchemeGenerator {
locationScenarioReference: locationScenarioReference,
disableMainThreadChecker: scheme.run?.disableMainThreadChecker ?? Scheme.Run.disableMainThreadCheckerDefault,
stopOnEveryMainThreadCheckerIssue: scheme.run?.stopOnEveryMainThreadCheckerIssue ?? Scheme.Run.stopOnEveryMainThreadCheckerIssueDefault,
+ additionalOptions: scheme.run?.additionalOptions.map(getAdditionalOptions) ?? [],
commandlineArguments: launchCommandLineArgs,
environmentVariables: launchVariables,
language: scheme.run?.language,
@@ -433,6 +452,7 @@ extension Scheme {
environmentVariables: targetScheme.environmentVariables,
disableMainThreadChecker: targetScheme.disableMainThreadChecker,
stopOnEveryMainThreadCheckerIssue: targetScheme.stopOnEveryMainThreadCheckerIssue,
+ additionalOptions: targetScheme.additionalOptions,
language: targetScheme.language,
region: targetScheme.region,
storeKitConfiguration: targetScheme.storeKitConfiguration
@@ -442,6 +462,7 @@ extension Scheme {
gatherCoverageData: targetScheme.gatherCoverageData,
coverageTargets: targetScheme.coverageTargets,
disableMainThreadChecker: targetScheme.disableMainThreadChecker,
+ additionalOptions: targetScheme.additionalOptions,
commandLineArguments: targetScheme.commandLineArguments,
targets: targetScheme.testTargets,
environmentVariables: targetScheme.environmentVariables,
diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Production.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Production.xcscheme
index 36f91c749..8ccc8a246 100644
--- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Production.xcscheme
+++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_iOS Production.xcscheme
@@ -81,6 +81,28 @@
ReferencedContainer = "container:Project.xcodeproj">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+