add aggregate targets

This commit is contained in:
Yonas Kolb
2018-07-24 22:05:29 +10:00
parent 31c3f1de0f
commit 3b17164bbf
13 changed files with 491 additions and 155 deletions
+11
View File
@@ -23,6 +23,7 @@ Required properties are marked with checkbox. Some of the YAML examples don't sh
- [Dependency](#dependency)
- [Target Scheme](#target-scheme)
- [Legacy Target](#legacy-target)
- [Aggregate Target](#aggregate-target)
- [Scheme](#scheme)
## Project
@@ -449,6 +450,16 @@ By providing a legacy target, you are opting in to the "Legacy Target" mode. Thi
- [ ] ***passSettings***: Bool - Whether or not to pass build settings down to the build tool in the legacy target.
- [ ] ***workingDirectory***: String - The working directory under which the build tool will be invoked in the legacy target.
## Aggregate Target
This is used to override settings or run build scripts in specific targets
- [x] **targets**: **[String]** - The list of target names to include as target dependencies
- [ ] **configFiles**: **[Config Files](#config-files)** - `.xcconfig` files per config
- [ ] **settings**: **[Settings](#settings)** - Target specific build settings.
- [ ] **buildScripts**: **[[Build Script](#build-script)]** - Build scripts to run
- [ ] **scheme**: **[Target Scheme](#target-scheme)** - Generated scheme
## Scheme
Schemes allows for more control than the convenience [Target Scheme](#target-scheme) on [Target](#target)
+46
View File
@@ -0,0 +1,46 @@
import Foundation
import JSONUtilities
public struct AggregateTarget: ProjectTarget, Equatable {
public var name: String
public var targets: [String]
public var settings: Settings
public var buildScripts: [BuildScript]
public var configFiles: [String: String]
public var scheme: TargetScheme?
public init(
name: String,
targets: [String],
settings: Settings = .empty,
configFiles: [String: String] = [:],
buildScripts: [BuildScript] = [],
scheme: TargetScheme? = nil
) {
self.name = name
self.targets = targets
self.settings = settings
self.configFiles = configFiles
self.buildScripts = buildScripts
self.scheme = scheme
}
}
extension AggregateTarget: CustomStringConvertible {
public var description: String {
return "\(name): \(targets.joined(separator: ", "))"
}
}
extension AggregateTarget: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
self.name = jsonDictionary.json(atKeyPath: "name") ?? name
targets = jsonDictionary.json(atKeyPath: "targets") ?? []
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
configFiles = jsonDictionary.json(atKeyPath: "configFiles") ?? [:]
buildScripts = jsonDictionary.json(atKeyPath: "buildScripts") ?? []
scheme = jsonDictionary.json(atKeyPath: "scheme")
}
}
+8
View File
@@ -13,6 +13,7 @@ public struct Project {
targetsMap = Dictionary(uniqueKeysWithValues: targets.map { ($0.name, $0) })
}
}
public var aggregateTargets: [AggregateTarget]
public var settings: Settings
public var settingGroups: [String: Settings]
@@ -30,6 +31,7 @@ public struct Project {
name: String,
configs: [Config] = Config.defaultConfigs,
targets: [Target] = [],
aggregateTargets: [AggregateTarget] = [],
settings: Settings = .empty,
settingGroups: [String: Settings] = [:],
schemes: [Scheme] = [],
@@ -42,6 +44,7 @@ public struct Project {
self.name = name
self.targets = targets
targetsMap = Dictionary(uniqueKeysWithValues: self.targets.map { ($0.name, $0) })
self.aggregateTargets = aggregateTargets
self.configs = configs
self.settings = settings
self.settingGroups = settingGroups
@@ -80,6 +83,9 @@ extension Project: CustomDebugStringConvertible {
if !targets.isEmpty {
string += "\nTargets:\n\(indent)" + targets.map { $0.description }.joined(separator: "\n\(indent)")
}
if !aggregateTargets.isEmpty {
string += "\nAggregate Targets:\n\(indent)" + aggregateTargets.map { $0.description }.joined(separator: "\n\(indent)")
}
return string
}
@@ -90,6 +96,7 @@ extension Project: Equatable {
public static func == (lhs: Project, rhs: Project) -> Bool {
return lhs.name == rhs.name &&
lhs.targets == rhs.targets &&
lhs.aggregateTargets == rhs.aggregateTargets &&
lhs.settings == rhs.settings &&
lhs.settingGroups == rhs.settingGroups &&
lhs.configs == rhs.configs &&
@@ -114,6 +121,7 @@ extension Project {
self.configs = configs.isEmpty ? Config.defaultConfigs :
configs.map { Config(name: $0, type: ConfigType(rawValue: $1)) }.sorted { $0.name < $1.name }
targets = try jsonDictionary.json(atKeyPath: "targets").sorted { $0.name < $1.name }
aggregateTargets = try jsonDictionary.json(atKeyPath: "aggregateTargets").sorted { $0.name < $1.name }
schemes = try jsonDictionary.json(atKeyPath: "schemes")
fileGroups = jsonDictionary.json(atKeyPath: "fileGroups") ?? []
configFiles = jsonDictionary.json(atKeyPath: "configFiles") ?? [:]
+24
View File
@@ -0,0 +1,24 @@
//
// ProjectTarget.swift
// ProjectSpec
//
// Created by Yonas Kolb on 22/7/18.
//
import Foundation
protocol ProjectTarget {
var name: String { get }
var settings: Settings { get }
var buildScripts: [BuildScript] { get }
var configFiles: [String: String] { get }
var scheme: TargetScheme? { get }
}
extension Target {
var buildScripts: [BuildScript] {
return prebuildScripts + postbuildScripts
}
}
+29 -17
View File
@@ -53,12 +53,9 @@ extension Project {
errors += validateSettings(settings)
}
for target in targets {
for dependency in target.dependencies {
if dependency.type == .target, getTarget(dependency.reference) == nil {
errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference))
}
}
let projectTargets: [ProjectTarget] = targets.map { $0 as ProjectTarget } + aggregateTargets.map { $0 as ProjectTarget }
for target in projectTargets {
for (config, configFile) in target.configFiles {
if !(basePath + configFile).exists {
@@ -69,13 +66,6 @@ extension Project {
}
}
for source in target.sources {
let sourcePath = basePath + source.path
if !source.optional && !sourcePath.exists {
errors.append(.invalidTargetSource(target: target.name, source: sourcePath.string))
}
}
if let scheme = target.scheme {
for configVariant in scheme.configVariants {
@@ -84,14 +74,14 @@ extension Project {
target: target.name,
configVariant: configVariant,
configType: .debug
))
))
}
if !configs.contains(where: { $0.name.contains(configVariant) && $0.type == .release }) {
errors.append(.invalidTargetSchemeConfigVariant(
target: target.name,
configVariant: configVariant,
configType: .release
))
))
}
}
@@ -111,8 +101,7 @@ extension Project {
}
}
let scripts = target.prebuildScripts + target.postbuildScripts
for script in scripts {
for script in target.buildScripts {
if case let .path(pathString) = script.script {
let scriptPath = basePath + pathString
if !scriptPath.exists {
@@ -124,6 +113,29 @@ extension Project {
errors += validateSettings(target.settings)
}
for target in aggregateTargets {
for dependency in target.targets {
if getTarget(dependency) == nil {
errors.append(.invalidTargetDependency(target: target.name, dependency: dependency))
}
}
}
for target in targets {
for dependency in target.dependencies {
if dependency.type == .target, getTarget(dependency.reference) == nil {
errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference))
}
}
for source in target.sources {
let sourcePath = basePath + source.path
if !source.optional && !sourcePath.exists {
errors.append(.invalidTargetSource(target: target.name, source: sourcePath.string))
}
}
}
for scheme in schemes {
for buildTarget in scheme.build.targets {
if getTarget(buildTarget.target) == nil {
+1 -42
View File
@@ -21,7 +21,7 @@ public struct LegacyTarget: Equatable {
}
}
public struct Target {
public struct Target: ProjectTarget {
public var name: String
public var type: PBXProductType
public var platform: Platform
@@ -209,47 +209,6 @@ extension Target: Equatable {
}
}
public struct TargetScheme: Equatable {
public var testTargets: [String]
public var configVariants: [String]
public var gatherCoverageData: Bool
public var commandLineArguments: [String: Bool]
public var environmentVariables: [XCScheme.EnvironmentVariable]
public var preActions: [Scheme.ExecutionAction]
public var postActions: [Scheme.ExecutionAction]
public init(
testTargets: [String] = [],
configVariants: [String] = [],
gatherCoverageData: Bool = false,
commandLineArguments: [String: Bool] = [:],
environmentVariables: [XCScheme.EnvironmentVariable] = [],
preActions: [Scheme.ExecutionAction] = [],
postActions: [Scheme.ExecutionAction] = []
) {
self.testTargets = testTargets
self.configVariants = configVariants
self.gatherCoverageData = gatherCoverageData
self.commandLineArguments = commandLineArguments
self.environmentVariables = environmentVariables
self.preActions = preActions
self.postActions = postActions
}
}
extension TargetScheme: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
testTargets = jsonDictionary.json(atKeyPath: "testTargets") ?? []
configVariants = jsonDictionary.json(atKeyPath: "configVariants") ?? []
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
preActions = jsonDictionary.json(atKeyPath: "preActions") ?? []
postActions = jsonDictionary.json(atKeyPath: "postActions") ?? []
}
}
extension LegacyTarget: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
+44
View File
@@ -0,0 +1,44 @@
import Foundation
import JSONUtilities
import xcproj
public struct TargetScheme: Equatable {
public var testTargets: [String]
public var configVariants: [String]
public var gatherCoverageData: Bool
public var commandLineArguments: [String: Bool]
public var environmentVariables: [XCScheme.EnvironmentVariable]
public var preActions: [Scheme.ExecutionAction]
public var postActions: [Scheme.ExecutionAction]
public init(
testTargets: [String] = [],
configVariants: [String] = [],
gatherCoverageData: Bool = false,
commandLineArguments: [String: Bool] = [:],
environmentVariables: [XCScheme.EnvironmentVariable] = [],
preActions: [Scheme.ExecutionAction] = [],
postActions: [Scheme.ExecutionAction] = []
) {
self.testTargets = testTargets
self.configVariants = configVariants
self.gatherCoverageData = gatherCoverageData
self.commandLineArguments = commandLineArguments
self.environmentVariables = environmentVariables
self.preActions = preActions
self.postActions = postActions
}
}
extension TargetScheme: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
testTargets = jsonDictionary.json(atKeyPath: "testTargets") ?? []
configVariants = jsonDictionary.json(atKeyPath: "configVariants") ?? []
gatherCoverageData = jsonDictionary.json(atKeyPath: "gatherCoverageData") ?? false
commandLineArguments = jsonDictionary.json(atKeyPath: "commandLineArguments") ?? [:]
environmentVariables = try XCScheme.EnvironmentVariable.parseAll(jsonDictionary: jsonDictionary)
preActions = jsonDictionary.json(atKeyPath: "preActions") ?? []
postActions = jsonDictionary.json(atKeyPath: "postActions") ?? []
}
}
+90 -43
View File
@@ -150,6 +150,7 @@ public class PBXProjGenerator {
}
try project.targets.forEach(generateTarget)
try project.aggregateTargets.forEach(generateAggregateTarget)
let productGroup = createObject(
id: "Products",
@@ -218,6 +219,92 @@ public class PBXProjGenerator {
return pbxProj
}
func generateAggregateTarget(_ target: AggregateTarget) throws {
let configs: [ObjectReference<XCBuildConfiguration>] = project.configs.map { config in
let buildSettings = project.getBuildSettings(settings: target.settings, config: config)
var baseConfigurationReference: String?
if let configPath = target.configFiles[config.name] {
baseConfigurationReference = sourceGenerator.getContainedFileReference(path: project.basePath + configPath)
}
let buildConfig = XCBuildConfiguration(
name: config.name,
baseConfigurationReference: baseConfigurationReference,
buildSettings: buildSettings
)
return createObject(id: config.name + target.name, buildConfig)
}
let dependencies: [String] = target.targets.map { generateTargetDependency(from: target.name, to: $0).reference }
let buildConfigList = createObject(id: target.name, XCConfigurationList(
buildConfigurations: configs.map { $0.reference },
defaultConfigurationName: ""
))
var buildPhases: [String] = []
buildPhases += try target.buildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }
let aggregateTarget = PBXAggregateTarget(name: target.name,
buildConfigurationList:
buildConfigList.reference,
buildPhases: buildPhases,
buildRules: [],
dependencies: dependencies,
productName: target.name,
productReference: nil,
productType: nil
)
_ = addObject(id: target.name, aggregateTarget)
}
func generateTargetDependency(from: String, to target: String) -> ObjectReference<PBXTargetDependency> {
let targetProxy = createObject(
id: "\(from)-\(target)",
PBXContainerItemProxy(
containerPortal: pbxProj.rootObject,
remoteGlobalIDString: targetObjects[target]!.reference,
proxyType: .nativeTarget,
remoteInfo: target
)
)
let targetDependency = createObject(
id: "\(from)-\(target)",
PBXTargetDependency(
target: targetObjects[target]!.reference,
targetProxy: targetProxy.reference
)
)
return targetDependency
}
func generateBuildScript(targetName: String, buildScript: BuildScript) throws -> String {
let shellScript: String
switch buildScript.script {
case let .path(path):
shellScript = try (project.basePath + path).read()
case let .script(script):
shellScript = script
}
let shellScriptPhase = PBXShellScriptBuildPhase(
files: [],
name: buildScript.name ?? "Run Script",
inputPaths: buildScript.inputFiles,
outputPaths: buildScript.outputFiles,
shellPath: buildScript.shell ?? "/bin/sh",
shellScript: shellScript
)
shellScriptPhase.runOnlyForDeploymentPostprocessing = buildScript.runOnlyWhenInstalling
shellScriptPhase.showEnvVarsInLog = buildScript.showEnvVars
return createObject(id: String(describing: buildScript.name) + shellScript + targetName, shellScriptPhase).reference
}
func generateTargetAttributes() -> [String: Any]? {
var targetAttributes: [String: [String: Any]] = [:]
@@ -415,23 +502,7 @@ public class PBXProjGenerator {
guard let dependencyTarget = project.getTarget(dependencyTargetName) else { continue }
let dependencyFileReference = targetFileReferences[dependencyTargetName]!
let targetProxy = createObject(
id: "\(target.name)-\(dependency.reference)",
PBXContainerItemProxy(
containerPortal: pbxProj.rootObject,
remoteGlobalIDString: targetObjects[dependencyTargetName]!.reference,
proxyType: .nativeTarget,
remoteInfo: dependencyTargetName
)
)
let targetDependency = createObject(
id: dependencyTargetName + target.name,
PBXTargetDependency(
target: targetObjects[dependencyTargetName]!.reference,
targetProxy: targetProxy.reference
)
)
let targetDependency = generateTargetDependency(from: target.name, to: dependencyTargetName)
dependencies.append(targetDependency.reference)
@@ -549,31 +620,7 @@ public class PBXProjGenerator {
.map { $0.reference }
}
func generateBuildScript(buildScript: BuildScript) throws {
let shellScript: String
switch buildScript.script {
case let .path(path):
shellScript = try (project.basePath + path).read()
case let .script(script):
shellScript = script
}
let shellScriptPhase = PBXShellScriptBuildPhase(
files: [],
name: buildScript.name ?? "Run Script",
inputPaths: buildScript.inputFiles,
outputPaths: buildScript.outputFiles,
shellPath: buildScript.shell ?? "/bin/sh",
shellScript: shellScript
)
shellScriptPhase.runOnlyForDeploymentPostprocessing = buildScript.runOnlyWhenInstalling
shellScriptPhase.showEnvVarsInLog = buildScript.showEnvVars
let shellScriptPhaseReference = createObject(id: String(describing: buildScript.name) + shellScript + target.name, shellScriptPhase)
buildPhases.append(shellScriptPhaseReference.reference)
}
try target.prebuildScripts.forEach(generateBuildScript)
buildPhases += try target.prebuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }
let sourcesBuildPhaseFiles = getBuildFilesForPhase(.sources)
let sourcesBuildPhase = createObject(id: target.name, PBXSourcesBuildPhase(files: sourcesBuildPhaseFiles))
@@ -687,7 +734,7 @@ public class PBXProjGenerator {
).reference
}
try target.postbuildScripts.forEach(generateBuildScript)
buildPhases += try target.postbuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }
let targetObject = targetObjects[target.name]!.object
@@ -6,6 +6,22 @@
objectVersion = 46;
objects = {
/* Begin PBXAggregateTarget section */
AT_445731917037 /* SuperTarget */ = {
isa = PBXAggregateTarget;
buildConfigurationList = CL_445731917037 /* Build configuration list for PBXAggregateTarget "SuperTarget" */;
buildPhases = (
SSBP_8280041834 /* MyScript */,
);
dependencies = (
TD_208010900457 /* PBXTargetDependency */,
TD_747418473860 /* PBXTargetDependency */,
);
name = SuperTarget;
productName = SuperTarget;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
BF_130062884703 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FR_257516580010 /* Alamofire.framework */; };
BF_138356261076 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR_363921640403 /* InterfaceController.swift */; };
@@ -90,6 +106,13 @@
remoteGlobalIDString = NT_507023492251;
remoteInfo = "App_watchOS Extension";
};
CIP_20801090045 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = P_8448771205358 /* Project object */;
proxyType = 1;
remoteGlobalIDString = NT_825232110500;
remoteInfo = App_iOS;
};
CIP_28856087625 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = P_8448771205358 /* Project object */;
@@ -132,6 +155,13 @@
remoteGlobalIDString = NT_399755008402;
remoteInfo = StaticLibrary_ObjC;
};
CIP_74741847386 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = P_8448771205358 /* Project object */;
proxyType = 1;
remoteGlobalIDString = NT_472296042419;
remoteInfo = Framework_iOS;
};
CIP_78441234790 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = P_8448771205358 /* Project object */;
@@ -642,7 +672,7 @@
buildRules = (
);
dependencies = (
TD_432517223942 /* PBXTargetDependency */,
TD_676191581124 /* PBXTargetDependency */,
);
name = App_iOS_UITests;
productName = App_iOS_UITests;
@@ -662,7 +692,7 @@
buildRules = (
);
dependencies = (
TD_249869083204 /* PBXTargetDependency */,
TD_193291182921 /* PBXTargetDependency */,
);
name = App_watchOS;
productName = App_watchOS;
@@ -696,7 +726,7 @@
buildRules = (
);
dependencies = (
TD_562366646359 /* PBXTargetDependency */,
TD_784412347908 /* PBXTargetDependency */,
);
name = Framework_watchOS;
productName = Framework_watchOS;
@@ -715,7 +745,7 @@
buildRules = (
);
dependencies = (
TD_202479103763 /* PBXTargetDependency */,
TD_667455552356 /* PBXTargetDependency */,
);
name = Framework_iOS;
productName = Framework_iOS;
@@ -751,7 +781,7 @@
buildRules = (
);
dependencies = (
TD_698828390883 /* PBXTargetDependency */,
TD_337103940101 /* PBXTargetDependency */,
);
name = Framework_macOS;
productName = Framework_macOS;
@@ -786,7 +816,7 @@
buildRules = (
);
dependencies = (
TD_883611297273 /* PBXTargetDependency */,
TD_957476409477 /* PBXTargetDependency */,
);
name = Framework_tvOS;
productName = Framework_tvOS;
@@ -803,7 +833,7 @@
buildRules = (
);
dependencies = (
TD_436638162860 /* PBXTargetDependency */,
TD_887143865471 /* PBXTargetDependency */,
);
name = App_iOS_Tests;
productName = App_iOS_Tests;
@@ -826,10 +856,10 @@
buildRules = (
);
dependencies = (
TD_257389546865 /* PBXTargetDependency */,
TD_354342487294 /* PBXTargetDependency */,
TD_490183489588 /* PBXTargetDependency */,
TD_669996692733 /* PBXTargetDependency */,
TD_824101787753 /* PBXTargetDependency */,
TD_109724965966 /* PBXTargetDependency */,
TD_721522128177 /* PBXTargetDependency */,
TD_288560876254 /* PBXTargetDependency */,
);
name = App_iOS;
productName = App_iOS;
@@ -847,7 +877,7 @@
buildRules = (
);
dependencies = (
TD_762620471828 /* PBXTargetDependency */,
TD_317921131342 /* PBXTargetDependency */,
);
name = iMessageApp;
productName = iMessageApp;
@@ -1088,6 +1118,20 @@
shellPath = /bin/sh;
shellScript = "echo \"You ran a script\"\n";
};
SSBP_8280041834 /* MyScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = MyScript;
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "echo \"do the thing\"";
};
SSBP_8706434794 /* MyScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1215,62 +1259,72 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
TD_202479103763 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_66745555235 /* PBXContainerItemProxy */;
};
TD_249869083204 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_507023492251 /* App_watchOS Extension */;
targetProxy = CIP_19329118292 /* PBXContainerItemProxy */;
};
TD_257389546865 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_324671077936 /* App_watchOS */;
targetProxy = CIP_82410178775 /* PBXContainerItemProxy */;
};
TD_354342487294 /* PBXTargetDependency */ = {
TD_109724965966 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_472296042419 /* Framework_iOS */;
targetProxy = CIP_10972496596 /* PBXContainerItemProxy */;
};
TD_432517223942 /* PBXTargetDependency */ = {
TD_193291182921 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_507023492251 /* App_watchOS Extension */;
targetProxy = CIP_19329118292 /* PBXContainerItemProxy */;
};
TD_208010900457 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_825232110500 /* App_iOS */;
targetProxy = CIP_67619158112 /* PBXContainerItemProxy */;
targetProxy = CIP_20801090045 /* PBXContainerItemProxy */;
};
TD_436638162860 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_825232110500 /* App_iOS */;
targetProxy = CIP_88714386547 /* PBXContainerItemProxy */;
};
TD_490183489588 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_72152212817 /* PBXContainerItemProxy */;
};
TD_562366646359 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_78441234790 /* PBXContainerItemProxy */;
};
TD_669996692733 /* PBXTargetDependency */ = {
TD_288560876254 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_935153865209 /* iMessageApp */;
targetProxy = CIP_28856087625 /* PBXContainerItemProxy */;
};
TD_698828390883 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_33710394010 /* PBXContainerItemProxy */;
};
TD_762620471828 /* PBXTargetDependency */ = {
TD_317921131342 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_618687462494 /* iMessageExtension */;
targetProxy = CIP_31792113134 /* PBXContainerItemProxy */;
};
TD_883611297273 /* PBXTargetDependency */ = {
TD_337103940101 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_33710394010 /* PBXContainerItemProxy */;
};
TD_667455552356 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_66745555235 /* PBXContainerItemProxy */;
};
TD_676191581124 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_825232110500 /* App_iOS */;
targetProxy = CIP_67619158112 /* PBXContainerItemProxy */;
};
TD_721522128177 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_72152212817 /* PBXContainerItemProxy */;
};
TD_747418473860 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_472296042419 /* Framework_iOS */;
targetProxy = CIP_74741847386 /* PBXContainerItemProxy */;
};
TD_784412347908 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_78441234790 /* PBXContainerItemProxy */;
};
TD_824101787753 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_324671077936 /* App_watchOS */;
targetProxy = CIP_82410178775 /* PBXContainerItemProxy */;
};
TD_887143865471 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_825232110500 /* App_iOS */;
targetProxy = CIP_88714386547 /* PBXContainerItemProxy */;
};
TD_957476409477 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = NT_399755008402 /* StaticLibrary_ObjC */;
targetProxy = CIP_95747640947 /* PBXContainerItemProxy */;
@@ -1365,6 +1419,13 @@
};
name = "Test Debug";
};
BC_122527152613 /* Test Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
MY_SETTING = hello;
};
name = "Test Release";
};
BC_128103534773 /* Production Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1719,6 +1780,13 @@
};
name = "Staging Debug";
};
BC_334821934458 /* Production Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
MY_SETTING = hello;
};
name = "Production Release";
};
BC_351179072988 /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1777,6 +1845,13 @@
};
name = "Production Release";
};
BC_370290618113 /* Production Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
MY_SETTING = hello;
};
name = "Production Debug";
};
BC_380703106572 /* Test Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1903,6 +1978,13 @@
};
name = "Production Release";
};
BC_439666751882 /* Staging Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
MY_SETTING = hello;
};
name = "Staging Debug";
};
BC_444705348320 /* Staging Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1936,6 +2018,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.project.StaticLibrary-ObjC";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "Test Release";
@@ -2291,6 +2374,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.project.StaticLibrary-ObjC";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "Production Release";
@@ -2343,6 +2427,13 @@
};
name = "Production Release";
};
BC_612244244384 /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
MY_SETTING = hello;
};
name = "Test Debug";
};
BC_622471558373 /* Production Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -2374,6 +2465,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.project.StaticLibrary-ObjC";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "Production Debug";
@@ -2605,6 +2697,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.project.StaticLibrary-ObjC";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "Staging Debug";
@@ -2789,6 +2882,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.project.StaticLibrary-ObjC";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "Staging Release";
@@ -2985,6 +3079,13 @@
};
name = "Test Debug";
};
BC_851086512031 /* Staging Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
MY_SETTING = hello;
};
name = "Staging Release";
};
BC_864485046941 /* Staging Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -3008,6 +3109,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.project.StaticLibrary-ObjC";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "Test Debug";
@@ -3092,6 +3194,19 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "";
};
CL_445731917037 /* Build configuration list for PBXAggregateTarget "SuperTarget" */ = {
isa = XCConfigurationList;
buildConfigurations = (
BC_370290618113 /* Production Debug */,
BC_334821934458 /* Production Release */,
BC_439666751882 /* Staging Debug */,
BC_851086512031 /* Staging Release */,
BC_612244244384 /* Test Debug */,
BC_122527152613 /* Test Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "";
};
CL_472296042419 /* Build configuration list for PBXNativeTarget "Framework_iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
+10
View File
@@ -158,3 +158,13 @@ schemes:
targetTemplates:
MyTemplate:
scheme: {}
aggregateTargets:
SuperTarget:
targets:
- App_iOS
- Framework_iOS
settings:
MY_SETTING: hello
buildScripts:
- name: MyScript
script: echo "do the thing"
@@ -187,6 +187,29 @@ class ProjectGeneratorTests: XCTestCase {
}
}
func testAggregateTargets() {
describe {
let aggregateTarget = AggregateTarget(name: "AggregateTarget", targets: ["MyApp", "MyFramework"] )
let project = Project(basePath: "", name: "test", targets: targets, aggregateTargets: [aggregateTarget])
$0.it("generates aggregate targets") {
let pbxProject = try project.generatePbxProj()
let aggregateTargets = pbxProject.objects.aggregateTargets.referenceValues
try expect(aggregateTargets.count) == 1
guard let pbxAggregateTarget = aggregateTargets.first else {
throw failure("Couldn't find AggregateTarget")
}
try expect(pbxAggregateTarget.name) == "AggregateTarget"
try expect(pbxAggregateTarget.dependencies.count) == 2
let targetDependencies = pbxProject.objects.targetDependencies.referenceValues
try expect(targetDependencies.count) == 4
}
}
}
func testTargets() {
describe {
@@ -132,6 +132,31 @@ class ProjectSpecTests: XCTestCase {
try expectValidationError(project, .invalidTargetSchemeConfigVariant(target: "target1", configVariant: "invalidVariant", configType: .debug))
}
$0.it("fails with invalid aggregate target") {
var project = baseProject
project.aggregateTargets = [AggregateTarget(
name: "target1",
targets: ["invalidDependency"],
settings: invalidSettings,
configFiles: ["invalidConfig": "invalidConfigFile"],
buildScripts: [BuildScript(script: .path("invalidPrebuildScript"), name: "buildScript1")],
scheme: TargetScheme(testTargets: ["invalidTarget"])
)]
try expectValidationError(project, .invalidTargetDependency(target: "target1", dependency: "invalidDependency"))
try expectValidationError(project, .invalidTargetConfigFile(target: "target1", configFile: "invalidConfigFile", config: "invalidConfig"))
try expectValidationError(project, .invalidTargetSchemeTest(target: "target1", testTarget: "invalidTarget"))
try expectValidationError(project, .invalidBuildSettingConfig("invalidConfig"))
try expectValidationError(project, .invalidSettingsGroup("invalidSettingGroup"))
try expectValidationError(project, .invalidBuildScriptPath(target: "target1", name: "buildScript1", path: "invalidPrebuildScript"))
try expectValidationError(project, .missingConfigForTargetScheme(target: "target1", configType: .debug))
try expectValidationError(project, .missingConfigForTargetScheme(target: "target1", configType: .release))
project.aggregateTargets[0].scheme?.configVariants = ["invalidVariant"]
try expectValidationError(project, .invalidTargetSchemeConfigVariant(target: "target1", configVariant: "invalidVariant", configType: .debug))
}
$0.it("fails with invalid scheme") {
var project = baseProject
project.schemes = [Scheme(
@@ -206,6 +206,18 @@ class SpecLoadingTests: XCTestCase {
try expect(target.sources) == ["templateSource", "targetSource"] // merges array in order
}
$0.it("parses aggregate targets") {
let dictionary: [String: Any] = [
"targets": ["target_1", "target_2"],
"settings": ["SETTING": "VALUE"],
"configFiles": ["debug": "file.xcconfig"],
]
let project = try getProjectSpec(["aggregateTargets": ["AggregateTarget": dictionary]])
let expectedTarget = AggregateTarget(name: "AggregateTarget", targets: ["target_1", "target_2"], settings: ["SETTING": "VALUE"], configFiles: ["debug": "file.xcconfig"])
try expect(project.aggregateTargets) == [expectedTarget]
}
$0.it("parses target schemes") {
var targetDictionary = validTarget
targetDictionary["scheme"] = [