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"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +