XcodeGen/Sources/XcodeGenKit/SchemeGenerator.swift

273 lines
13 KiB
Swift
Raw Normal View History

2018-11-04 14:30:34 +03:00
import Foundation
2018-10-18 16:29:45 +03:00
import ProjectSpec
2019-06-07 23:36:48 +03:00
import XcodeProj
2018-10-18 16:29:45 +03:00
public class SchemeGenerator {
let project: Project
let pbxProj: PBXProj
var defaultDebugConfig: Config {
return project.configs.first { $0.type == .debug }!
}
var defaultReleaseConfig: Config {
return project.configs.first { $0.type == .release }!
}
public init(project: Project, pbxProj: PBXProj) {
self.project = project
self.pbxProj = pbxProj
}
public func generateSchemes() throws -> [XCScheme] {
var xcschemes: [XCScheme] = []
for scheme in project.schemes {
let xcscheme = try generateScheme(scheme)
xcschemes.append(xcscheme)
}
for target in project.targets {
if let targetScheme = target.scheme {
if targetScheme.configVariants.isEmpty {
let schemeName = target.name
let debugConfig = project.configs.first { $0.type == .debug }!
let releaseConfig = project.configs.first { $0.type == .release }!
let scheme = Scheme(
name: schemeName,
target: target,
targetScheme: targetScheme,
debugConfig: debugConfig.name,
releaseConfig: releaseConfig.name
)
let xcscheme = try generateScheme(scheme)
xcschemes.append(xcscheme)
} else {
for configVariant in targetScheme.configVariants {
let schemeName = "\(target.name) \(configVariant)"
let debugConfig = project.configs
.first { $0.type == .debug && $0.name.contains(configVariant) }!
let releaseConfig = project.configs
.first { $0.type == .release && $0.name.contains(configVariant) }!
let scheme = Scheme(
name: schemeName,
target: target,
targetScheme: targetScheme,
debugConfig: debugConfig.name,
releaseConfig: releaseConfig.name
)
let xcscheme = try generateScheme(scheme)
xcschemes.append(xcscheme)
}
}
}
}
return xcschemes
}
public func generateScheme(_ scheme: Scheme) throws -> XCScheme {
2019-09-22 18:55:42 +03:00
func getBuildableReference(_ target: TargetReference) throws -> XCScheme.BuildableReference {
let pbxProj: PBXProj
let projectFilePath: String
2019-09-22 18:55:42 +03:00
switch target.location {
case .project(let project):
guard let externalProject = self.project.getExternalProject(project) else {
fatalError("Unable to find external project named \"\(project)\" in project.yml")
}
pbxProj = try XcodeProj(pathString: externalProject.path).pbxproj
projectFilePath = externalProject.path
case .local:
pbxProj = self.pbxProj
projectFilePath = "\(self.project.name).xcodeproj"
}
2018-10-18 16:29:45 +03:00
2019-09-22 18:55:42 +03:00
guard let pbxTarget = pbxProj.targets(named: target.name).first else {
fatalError("Unable to find target named \"\(target.name)\" in \"PBXProj.targets\"")
2018-10-18 16:29:45 +03:00
}
let buildableName = pbxTarget.productNameWithExtension() ?? pbxTarget.name
2019-09-15 18:36:52 +03:00
return XCScheme.BuildableReference(
2019-09-22 18:55:42 +03:00
referencedContainer: "container:\(projectFilePath)",
2018-10-18 16:29:45 +03:00
blueprint: pbxTarget,
buildableName: buildableName,
2019-09-22 18:55:42 +03:00
blueprintName: target.name
2019-09-15 18:36:52 +03:00
)
}
func getBuildEntry(_ buildTarget: Scheme.BuildTarget) throws -> XCScheme.BuildAction.Entry {
2019-09-22 18:55:42 +03:00
let buildableReference = try getBuildableReference(buildTarget.target)
2018-10-18 16:29:45 +03:00
return XCScheme.BuildAction.Entry(buildableReference: buildableReference, buildFor: buildTarget.buildTypes)
}
let testTargets = scheme.test?.targets ?? []
let testBuildTargets = testTargets.map {
2019-09-24 09:04:40 +03:00
Scheme.BuildTarget(target: $0.targetReference, buildTypes: BuildType.testOnly)
2018-10-18 16:29:45 +03:00
}
let testBuildTargetEntries = try testBuildTargets.map(getBuildEntry)
2018-10-18 16:29:45 +03:00
let buildActionEntries: [XCScheme.BuildAction.Entry] = try scheme.build.targets.map(getBuildEntry)
2018-10-18 16:29:45 +03:00
func getExecutionAction(_ action: Scheme.ExecutionAction) -> XCScheme.ExecutionAction {
// ExecutionActions can require the use of build settings. Xcode allows the settings to come from a build or test target.
let environmentBuildable = action.settingsTarget.flatMap { settingsTarget in
2019-09-01 15:02:53 +03:00
(buildActionEntries + testBuildTargetEntries)
2018-10-18 16:29:45 +03:00
.first { settingsTarget == $0.buildableReference.blueprintName }?
.buildableReference
}
return XCScheme.ExecutionAction(scriptText: action.script, title: action.name, environmentBuildable: environmentBuildable)
}
let target = project.getTarget(scheme.build.targets.first!.target.name)
2018-10-18 16:29:45 +03:00
let shouldExecuteOnLaunch = target?.type.isExecutable == true
let buildableReference = buildActionEntries.first!.buildableReference
let productRunable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference)
let buildAction = XCScheme.BuildAction(
buildActionEntries: buildActionEntries,
preActions: scheme.build.preActions.map(getExecutionAction),
postActions: scheme.build.postActions.map(getExecutionAction),
parallelizeBuild: scheme.build.parallelizeBuild,
buildImplicitDependencies: scheme.build.buildImplicitDependencies
)
let testables = zip(testTargets, testBuildTargetEntries).map { testTarget, testBuilEntries in
2018-11-04 14:30:34 +03:00
XCScheme.TestableReference(
skipped: false,
parallelizable: testTarget.parallelizable,
randomExecutionOrdering: testTarget.randomExecutionOrder,
2019-05-08 09:31:21 +03:00
buildableReference: testBuilEntries.buildableReference,
skippedTests: testTarget.skippedTests.map(XCScheme.SkippedTest.init)
2018-11-04 14:30:34 +03:00
)
2018-10-18 16:29:45 +03:00
}
2019-09-15 18:36:52 +03:00
let coverageBuildableTargets = try scheme.test?.coverageTargets.map {
2019-09-22 18:55:42 +03:00
try getBuildableReference($0)
2019-09-15 18:36:52 +03:00
} ?? []
2018-10-18 16:29:45 +03:00
let testCommandLineArgs = scheme.test.map { XCScheme.CommandLineArguments($0.commandLineArguments) }
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,
testables: testables,
preActions: scheme.test?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.test?.postActions.map(getExecutionAction) ?? [],
selectedDebuggerIdentifier: (scheme.test?.debugEnabled ?? Scheme.Test.debugEnabledDefault) ? XCScheme.defaultDebugger : "",
2018-10-18 16:29:45 +03:00
shouldUseLaunchSchemeArgsEnv: scheme.test?.shouldUseLaunchSchemeArgsEnv ?? true,
codeCoverageEnabled: scheme.test?.gatherCoverageData ?? Scheme.Test.gatherCoverageDataDefault,
2019-09-15 18:36:52 +03:00
codeCoverageTargets: coverageBuildableTargets,
disableMainThreadChecker: scheme.test?.disableMainThreadChecker ?? Scheme.Test.disableMainThreadCheckerDefault,
2018-10-18 16:29:45 +03:00
commandlineArguments: testCommandLineArgs,
environmentVariables: testVariables,
language: scheme.test?.language,
region: scheme.test?.region
2018-10-18 16:29:45 +03:00
)
let launchAction = XCScheme.LaunchAction(
2019-06-07 23:37:25 +03:00
runnable: shouldExecuteOnLaunch ? productRunable : nil,
2018-10-18 16:29:45 +03:00
buildConfiguration: scheme.run?.config ?? defaultDebugConfig.name,
preActions: scheme.run?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.run?.postActions.map(getExecutionAction) ?? [],
macroExpansion: shouldExecuteOnLaunch ? nil : buildableReference,
selectedDebuggerIdentifier: (scheme.run?.debugEnabled ?? Scheme.Run.debugEnabledDefault) ? XCScheme.defaultDebugger : "",
selectedLauncherIdentifier: (scheme.run?.debugEnabled ?? Scheme.Run.debugEnabledDefault) ? XCScheme.defaultLauncher : "Xcode.IDEFoundation.Launcher.PosixSpawn",
disableMainThreadChecker: scheme.run?.disableMainThreadChecker ?? Scheme.Run.disableMainThreadCheckerDefault,
2018-10-18 16:29:45 +03:00
commandlineArguments: launchCommandLineArgs,
environmentVariables: launchVariables,
language: scheme.run?.language,
region: scheme.run?.region
2018-10-18 16:29:45 +03:00
)
let profileAction = XCScheme.ProfileAction(
buildableProductRunnable: productRunable,
buildConfiguration: scheme.profile?.config ?? defaultReleaseConfig.name,
preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [],
shouldUseLaunchSchemeArgsEnv: scheme.profile?.shouldUseLaunchSchemeArgsEnv ?? true,
commandlineArguments: profileCommandLineArgs,
environmentVariables: profileVariables
)
let analyzeAction = XCScheme.AnalyzeAction(buildConfiguration: scheme.analyze?.config ?? defaultDebugConfig.name)
let archiveAction = XCScheme.ArchiveAction(
buildConfiguration: scheme.archive?.config ?? defaultReleaseConfig.name,
revealArchiveInOrganizer: scheme.archive?.revealArchiveInOrganizer ?? true,
customArchiveName: scheme.archive?.customArchiveName,
preActions: scheme.archive?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.archive?.postActions.map(getExecutionAction) ?? []
)
return XCScheme(
name: scheme.name,
lastUpgradeVersion: project.xcodeVersion,
version: project.schemeVersion,
buildAction: buildAction,
testAction: testAction,
launchAction: launchAction,
profileAction: profileAction,
analyzeAction: analyzeAction,
archiveAction: archiveAction
)
}
}
extension Scheme {
public init(name: String, target: Target, targetScheme: TargetScheme, debugConfig: String, releaseConfig: String) {
self.init(
name: name,
build: .init(targets: [Scheme.BuildTarget(target: TargetReference(name: target.name, location: .local))]),
2018-10-18 16:29:45 +03:00
run: .init(
config: debugConfig,
commandLineArguments: targetScheme.commandLineArguments,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables,
disableMainThreadChecker: targetScheme.disableMainThreadChecker
2018-10-18 16:29:45 +03:00
),
test: .init(
config: debugConfig,
gatherCoverageData: targetScheme.gatherCoverageData,
disableMainThreadChecker: targetScheme.disableMainThreadChecker,
2018-10-18 16:29:45 +03:00
commandLineArguments: targetScheme.commandLineArguments,
targets: targetScheme.testTargets,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables
),
profile: .init(
config: releaseConfig,
commandLineArguments: targetScheme.commandLineArguments,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions,
environmentVariables: targetScheme.environmentVariables
),
analyze: .init(
config: debugConfig
),
archive: .init(
config: releaseConfig,
preActions: targetScheme.preActions,
postActions: targetScheme.postActions
)
)
}
}