Implement setting schema variables in xcodeproj

This commit is contained in:
Jakub Turek
2018-02-26 22:22:07 +01:00
parent 232a20375d
commit dcb098a39f
9 changed files with 193 additions and 17 deletions
+1
View File
@@ -6,6 +6,7 @@
- Added `carthageExecutablePath` option [244](https://github.com/yonaskolb/XcodeGen/pull/244) @akkyie
- Added `parallelizeBuild` and `buildImplicitDependencies` to Schemes [241](https://github.com/yonaskolb/XcodeGen/pull/241) @rahul-malik
- Added scheme environment variables [239](https://github.com/yonaskolb/XcodeGen/pull/239) @turekj
#### Fixed
- Fixed Mint installation from reading setting presets [248](https://github.com/yonaskolb/XcodeGen/pull/248) @yonaskolb
+15
View File
@@ -357,6 +357,7 @@ This is a convenience used to automatically generate schemes for a target based
- [ ] **testTargets**: **[String]** - a list of test targets that should be included in the scheme. These will be added to the build targets and the test entries
- [ ] **gatherCoverageData**: **Bool** - a boolean that indicates if this scheme should gather coverage data. This defaults to false
- [ ] **commandLineArguments**: **[String:Bool]** - a dictionary from the argument name (`String`) to if it is enabled (`Bool`). These arguments will be added to the Test, Profile and Run scheme actions
- [ ] **environmentVariables**: **[[Environment Variable](#environment-variable)]** or **[String:String]** - environment variables for Run, Test and Profile scheme actions. When passing a dictionary, every key-value entry maps to a corresponding variable that is enabled.
For example, the spec below would create 3 schemes called:
@@ -388,6 +389,8 @@ targets
commandLineArguments:
"-MyEnabledArg": true
"-MyDisabledArg": false
environmentVariables:
MY_ENV_VAR: VALUE
MyUnitTests:
sources: Tests
```
@@ -444,6 +447,7 @@ The different actions share some properties:
- [ ] **commandLineArguments**: **[String:Bool]** - `run`, `test` and `profile` actions have a map of command line arguments to whether they are enabled
- [ ] **preActions**: **[[Execution Action](#execution-action)]** - Scheme run scripts that run *before* the action is run
- [ ] **postActions**: **[[Execution Action](#execution-action)]** - Scheme run scripts that run *after* the action is run
- [ ] **environmentVariables**: **[[Environment Variable](#environment-variable)]** or **[String:String]** - `run`, `test` and `profile` actions can define the environment variables. When passing a dictionary, every key-value entry maps to a corresponding variable that is enabled.
### Execution Action
Scheme run scripts added via **preActions** or **postActions**. They run before or after a build action, respectively, and in the order defined. Each execution action can contain:
@@ -458,6 +462,11 @@ A multiline script can be written using the various YAML multiline methods, for
- [ ] **gatherCoverageData**: **Bool** - a boolean that indicates if this scheme should gather coverage data. This defaults to false
- [ ] **targets**: **[String]** - a list of targets to test
### Environment Variable
- [x] **key**: **String** - variable's key.
- [x] **value**: **String** - variable's value.
- [ ] **isEnabled**: **Bool** - indicates whether the environment variable is enabled. This defaults to true.
```yaml
scheme:
Production:
@@ -468,11 +477,17 @@ scheme:
run:
config: prod-debug
commandLineArguments: "--option value"
environmentVariables:
RUN_ENV_VAR: VALUE
test:
config: prod-debug
commandLineArguments: "--option testValue"
gatherCoverageData: true
targets: [Tester1, Tester2]
environmentVariables:
- key: TEST_ENV_VAR
value: VALUE
isEnabled: false
profile:
config: prod-release
analyze:
+4 -4
View File
@@ -75,11 +75,11 @@
},
{
"package": "xcproj",
"repositoryURL": "https://github.com/xcodeswift/xcproj.git",
"repositoryURL": "https://github.com/elpassion/xcproj",
"state": {
"branch": null,
"revision": "5f68ad74627a537cdd3d022644edd0fa7a80d4a7",
"version": "4.0.0"
"branch": "environment_variables",
"revision": "44ed558ef9f52e4e45b6820f8b60254fb63bc212",
"version": null
}
}
]
+1 -1
View File
@@ -16,7 +16,7 @@ let package = Package(
.package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "3.3.0"),
.package(url: "https://github.com/kylef/Spectre.git", from: "0.8.0"),
.package(url: "https://github.com/onevcat/Rainbow.git", from: "3.0.0"),
.package(url: "https://github.com/xcodeswift/xcproj.git", from: "4.0.0")
.package(url: "https://github.com/elpassion/xcproj", .branch("environment_variables"))
],
targets: [
.target(name: "XcodeGen", dependencies: [
+47 -6
View File
@@ -81,23 +81,27 @@ public struct Scheme: Equatable {
public var commandLineArguments: [String: Bool]
public var preActions: [ExecutionAction]
public var postActions: [ExecutionAction]
public var environmentVariables: [XCScheme.EnvironmentVariable]
public init(
config: String,
commandLineArguments: [String: Bool] = [:],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []
postActions: [ExecutionAction] = [],
environmentVariables: [XCScheme.EnvironmentVariable] = []
) {
self.config = config
self.commandLineArguments = commandLineArguments
self.preActions = preActions
self.postActions = postActions
self.environmentVariables = environmentVariables
}
public static func == (lhs: Run, rhs: Run) -> Bool {
return lhs.config == rhs.config &&
lhs.commandLineArguments == rhs.commandLineArguments &&
lhs.preActions == rhs.postActions &&
lhs.postActions == rhs.postActions
lhs.postActions == rhs.postActions &&
lhs.environmentVariables == rhs.environmentVariables
}
}
@@ -108,13 +112,15 @@ public struct Scheme: Equatable {
public var targets: [String]
public var preActions: [ExecutionAction]
public var postActions: [ExecutionAction]
public var environmentVariables: [XCScheme.EnvironmentVariable]
public init(
config: String,
gatherCoverageData: Bool = false,
commandLineArguments: [String: Bool] = [:],
targets: [String] = [],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []
postActions: [ExecutionAction] = [],
environmentVariables: [XCScheme.EnvironmentVariable] = []
) {
self.config = config
self.gatherCoverageData = gatherCoverageData
@@ -122,6 +128,7 @@ public struct Scheme: Equatable {
self.targets = targets
self.preActions = preActions
self.postActions = postActions
self.environmentVariables = environmentVariables
}
public static func == (lhs: Test, rhs: Test) -> Bool {
@@ -130,7 +137,8 @@ public struct Scheme: Equatable {
lhs.gatherCoverageData == rhs.gatherCoverageData &&
lhs.targets == rhs.targets &&
lhs.preActions == rhs.postActions &&
lhs.postActions == rhs.postActions
lhs.postActions == rhs.postActions &&
lhs.environmentVariables == rhs.environmentVariables
}
}
@@ -150,23 +158,27 @@ public struct Scheme: Equatable {
public var commandLineArguments: [String: Bool]
public var preActions: [ExecutionAction]
public var postActions: [ExecutionAction]
public var environmentVariables: [XCScheme.EnvironmentVariable]
public init(
config: String,
commandLineArguments: [String: Bool] = [:],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []
postActions: [ExecutionAction] = [],
environmentVariables: [XCScheme.EnvironmentVariable] = []
) {
self.config = config
self.commandLineArguments = commandLineArguments
self.preActions = preActions
self.postActions = postActions
self.environmentVariables = environmentVariables
}
public static func == (lhs: Profile, rhs: Profile) -> Bool {
return lhs.config == rhs.config &&
lhs.commandLineArguments == rhs.commandLineArguments &&
lhs.preActions == rhs.postActions &&
lhs.postActions == rhs.postActions
lhs.postActions == rhs.postActions &&
lhs.environmentVariables == rhs.environmentVariables
}
}
@@ -235,6 +247,7 @@ extension Scheme.Run: JSONObjectConvertible {
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
preActions = try jsonDictionary.json(atKeyPath: "preActions")?.map(Scheme.ExecutionAction.init) ?? []
postActions = try jsonDictionary.json(atKeyPath: "postActions")?.map(Scheme.ExecutionAction.init) ?? []
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
}
}
@@ -247,6 +260,7 @@ extension Scheme.Test: JSONObjectConvertible {
targets = jsonDictionary.json(atKeyPath: "targets") ?? []
preActions = try jsonDictionary.json(atKeyPath: "preActions")?.map(Scheme.ExecutionAction.init) ?? []
postActions = try jsonDictionary.json(atKeyPath: "postActions")?.map(Scheme.ExecutionAction.init) ?? []
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
}
}
@@ -257,6 +271,7 @@ extension Scheme.Profile: JSONObjectConvertible {
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
preActions = try jsonDictionary.json(atKeyPath: "preActions")?.map(Scheme.ExecutionAction.init) ?? []
postActions = try jsonDictionary.json(atKeyPath: "postActions")?.map(Scheme.ExecutionAction.init) ?? []
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
}
}
@@ -340,3 +355,29 @@ extension BuildType: JSONPrimitiveConvertible {
return [.running, .testing, .profiling, .analyzing, .archiving]
}
}
extension XCScheme.EnvironmentVariable: JSONObjectConvertible, Equatable {
public init(jsonDictionary: JSONDictionary) throws {
variable = try jsonDictionary.json(atKeyPath: "variable")
value = try jsonDictionary.json(atKeyPath: "value")
enabled = (try? jsonDictionary.json(atKeyPath: "isEnabled")) ?? true
}
static func parseAll(jsonDictionary: JSONDictionary) throws -> [XCScheme.EnvironmentVariable] {
if let variablesDictionary: [String: String] = jsonDictionary.json(atKeyPath: "environmentVariables") {
return variablesDictionary.map { XCScheme.EnvironmentVariable(variable: $0.key, value: $0.value, enabled: true) }
} else if let variablesArray: [JSONDictionary] = jsonDictionary.json(atKeyPath: "environmentVariables") {
return try variablesArray.map(XCScheme.EnvironmentVariable.init)
} else {
return []
}
}
public static func == (lhs: XCScheme.EnvironmentVariable, rhs: XCScheme.EnvironmentVariable) -> Bool {
return lhs.variable == rhs.variable &&
lhs.value == rhs.value &&
lhs.enabled == rhs.enabled
}
}
+5 -1
View File
@@ -172,17 +172,20 @@ public struct TargetScheme {
public var configVariants: [String]
public var gatherCoverageData: Bool
public var commandLineArguments: [String: Bool]
public var environmentVariables: [XCScheme.EnvironmentVariable]
public init(
testTargets: [String] = [],
configVariants: [String] = [],
gatherCoverageData: Bool = false,
commandLineArguments: [String: Bool] = [:]
commandLineArguments: [String: Bool] = [:],
environmentVariables: [XCScheme.EnvironmentVariable] = []
) {
self.testTargets = testTargets
self.configVariants = configVariants
self.gatherCoverageData = gatherCoverageData
self.commandLineArguments = commandLineArguments
self.environmentVariables = environmentVariables
}
}
@@ -201,6 +204,7 @@ extension TargetScheme: JSONObjectConvertible {
configVariants = jsonDictionary.json(atKeyPath: "configVariants") ?? []
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
}
}
+15 -5
View File
@@ -90,6 +90,10 @@ public class ProjectGenerator {
let launchCommandLineArgs = scheme.run.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
let profileCommandLineArgs = scheme.profile.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
let testVariables = scheme.test.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let launchVariables = scheme.run.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let profileVariables = scheme.profile.flatMap { $0.environmentVariables.isEmpty ? nil : $0.environmentVariables }
let testAction = XCScheme.TestAction(
buildConfiguration: scheme.test?.config ?? defaultDebugConfig.name,
macroExpansion: buildableReference,
@@ -99,6 +103,7 @@ public class ProjectGenerator {
shouldUseLaunchSchemeArgsEnv: scheme.test?.commandLineArguments.isEmpty ?? true,
codeCoverageEnabled: scheme.test?.gatherCoverageData ?? false,
commandlineArguments: testCommandLineArgs,
environmentVariables: testVariables,
language: ""
)
@@ -107,7 +112,8 @@ public class ProjectGenerator {
buildConfiguration: scheme.run?.config ?? defaultDebugConfig.name,
preActions: scheme.run?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.run?.postActions.map(getExecutionAction) ?? [],
commandlineArguments: launchCommandLineArgs
commandlineArguments: launchCommandLineArgs,
environmentVariables: launchVariables
)
let profileAction = XCScheme.ProfileAction(
@@ -116,7 +122,8 @@ public class ProjectGenerator {
preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [],
shouldUseLaunchSchemeArgsEnv: scheme.profile?.commandLineArguments.isEmpty ?? true,
commandlineArguments: profileCommandLineArgs
commandlineArguments: profileCommandLineArgs,
environmentVariables: profileVariables
)
let analyzeAction = XCScheme.AnalyzeAction(buildConfiguration: scheme.analyze?.config ?? defaultDebugConfig.name)
@@ -202,17 +209,20 @@ extension Scheme {
build: .init(targets: [Scheme.BuildTarget(target: target.name)]),
run: .init(
config: debugConfig,
commandLineArguments: targetScheme.commandLineArguments
commandLineArguments: targetScheme.commandLineArguments,
environmentVariables: targetScheme.environmentVariables
),
test: .init(
config: debugConfig,
gatherCoverageData: targetScheme.gatherCoverageData,
commandLineArguments: targetScheme.commandLineArguments,
targets: targetScheme.testTargets
targets: targetScheme.testTargets,
environmentVariables: targetScheme.environmentVariables
),
profile: .init(
config: releaseConfig,
commandLineArguments: targetScheme.commandLineArguments
commandLineArguments: targetScheme.commandLineArguments,
environmentVariables: targetScheme.environmentVariables
),
analyze: .init(config: debugConfig),
archive: .init(config: releaseConfig)
@@ -352,6 +352,41 @@ func projectGeneratorTests() {
try expect(xcscheme.archiveAction?.buildConfiguration) == "Release"
}
$0.it("sets environment variables for a scheme") {
let runVariables: [XCScheme.EnvironmentVariable] = [
XCScheme.EnvironmentVariable(variable: "RUN_ENV", value: "ENABLED", enabled: true),
XCScheme.EnvironmentVariable(variable: "OTHER_RUN_ENV", value: "DISABLED", enabled: false)
]
let scheme = Scheme(
name: "EnvironmentVariablesScheme",
build: Scheme.Build(targets: [buildTarget]),
run: Scheme.Run(config: "Debug", environmentVariables: runVariables),
test: Scheme.Test(config: "Debug"),
profile: Scheme.Profile(config: "Debug")
)
let spec = ProjectSpec(
basePath: "",
name: "test",
targets: [application, framework],
schemes: [scheme]
)
let project = try getProject(spec)
guard let target = project.pbxproj.objects.nativeTargets.objectReferences
.first(where: { $0.object.name == application.name }) else {
throw failure("Target not found")
}
guard let xcscheme = project.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(xcscheme.launchAction?.environmentVariables) == runVariables
try expect(xcscheme.testAction?.environmentVariables).to.beNil()
try expect(xcscheme.profileAction?.environmentVariables).to.beNil()
}
$0.it("generates target schemes from config variant") {
let configVariants = ["Test", "Production"]
var target = application
@@ -387,6 +422,25 @@ func projectGeneratorTests() {
try expect(xcscheme.analyzeAction?.buildConfiguration) == "Test Debug"
try expect(xcscheme.archiveAction?.buildConfiguration) == "Test Release"
}
$0.it("generates environment variables for target schemes") {
let variables: [XCScheme.EnvironmentVariable] = [XCScheme.EnvironmentVariable(variable: "env", value: "var", enabled: false)]
var target = application
target.scheme = TargetScheme(environmentVariables: variables)
let spec = ProjectSpec(basePath: "", name: "test", targets: [target, framework])
let project = try getProject(spec)
try expect(project.sharedData?.schemes.count) == 1
guard let xcscheme = project.sharedData?.schemes.first else {
throw failure("Scheme not found")
}
try expect(xcscheme.launchAction?.environmentVariables) == variables
try expect(xcscheme.testAction?.environmentVariables) == variables
try expect(xcscheme.profileAction?.environmentVariables) == variables
}
}
$0.describe("Sources") {
@@ -154,6 +154,21 @@ func specLoadingTests() {
try expect(spec.targets) == [target_iOS, target_tvOS]
}
$0.it("parses target schemes") {
var targetDictionary = validTarget
targetDictionary["scheme"] = [
"environmentVariables": [
"TEST_VAR": "TEST_VAL"
]
]
let target = try Target(name: "test", jsonDictionary: targetDictionary)
let expectedVariables = [XCScheme.EnvironmentVariable(variable: "TEST_VAR", value: "TEST_VAL", enabled: true)]
try expect(target.scheme?.environmentVariables) == expectedVariables
}
$0.it("parses schemes") {
let schemeDictionary: [String: Any] = [
"build": [
@@ -195,6 +210,42 @@ func specLoadingTests() {
try expect(scheme.build.buildImplicitDependencies) == false
}
$0.it("parses schemes variables") {
let schemeDictionary: [String: Any] = [
"build": [
"targets": ["Target1": "all"],
],
"run": [
"environmentVariables": [
["key": "ENVIRONMENT", "value": "VARIABLE"],
["key": "OTHER_ENV_VAR", "value": "VAL", "isEnabled": false],
],
],
"test": [
"environmentVariables": [
"TEST": "VARIABLE"
]
],
"profile": [
"config": "Release"
]
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
let expectedRunVariables = [
XCScheme.EnvironmentVariable(variable: "ENVIRONMENT", value: "VARIABLE", enabled: true),
XCScheme.EnvironmentVariable(variable: "OTHER_ENV_VAR", value: "VAL", enabled: false)
]
let expectedTestVariables = [XCScheme.EnvironmentVariable(variable: "TEST", value: "VARIABLE", enabled: true)]
try expect(scheme.run?.environmentVariables) == expectedRunVariables
try expect(scheme.test?.environmentVariables) == expectedTestVariables
try expect(scheme.profile?.config) == "Release"
try expect(scheme.profile?.environmentVariables.isEmpty) == true
}
$0.it("parses settings") {
let spec = try ProjectSpec(path: fixturePath + "settings_test.yml")
let buildSettings: BuildSettings = ["SETTING": "value"]