Generate a scheme with all the project targets (#226)
* Generate project scheme * Test the generation of the project scheme * Sort the targets based on the dependencies between them * Add acceptance tests * Address comments * Update changelog and fix Dangerfile
This commit is contained in:
parent
d6ae3e4783
commit
92f37c4390
|
@ -13,6 +13,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
|
||||||
|
|
||||||
- Integration tests for `generate` command https://github.com/tuist/tuist/pull/208 by @marciniwanicki & @kwridan
|
- Integration tests for `generate` command https://github.com/tuist/tuist/pull/208 by @marciniwanicki & @kwridan
|
||||||
- Frequently asked questions to the documentation https://github.com/tuist/tuist/pull/223/ by @pepibumur.
|
- Frequently asked questions to the documentation https://github.com/tuist/tuist/pull/223/ by @pepibumur.
|
||||||
|
- Generate a scheme with all the project targets https://github.com/tuist/tuist/pull/226 by @pepibumur
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ unless git.modified_files.include?("CHANGELOG.md")
|
||||||
Please include a CHANGELOG entry.
|
Please include a CHANGELOG entry.
|
||||||
You can find it at [CHANGELOG.md](https://github.com/tuist/tuist/blob/master/CHANGELOG.md).
|
You can find it at [CHANGELOG.md](https://github.com/tuist/tuist/blob/master/CHANGELOG.md).
|
||||||
MESSAGE
|
MESSAGE
|
||||||
raise(message)
|
warn(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Swiftlint
|
# Swiftlint
|
||||||
|
|
|
@ -148,6 +148,9 @@ final class ProjectGenerator: ProjectGenerating {
|
||||||
targets: nativeTargets)
|
targets: nativeTargets)
|
||||||
try schemesGenerator.generateTargetSchemes(project: project,
|
try schemesGenerator.generateTargetSchemes(project: project,
|
||||||
generatedProject: generatedProject)
|
generatedProject: generatedProject)
|
||||||
|
try schemesGenerator.generateProjectScheme(project: project,
|
||||||
|
generatedProject: generatedProject,
|
||||||
|
graph: graph)
|
||||||
|
|
||||||
return generatedProject
|
return generatedProject
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,27 @@ protocol SchemesGenerating {
|
||||||
/// - Throws: A FatalError if the generation of the schemes fails.
|
/// - Throws: A FatalError if the generation of the schemes fails.
|
||||||
func generateTargetSchemes(project: Project,
|
func generateTargetSchemes(project: Project,
|
||||||
generatedProject: GeneratedProject) throws
|
generatedProject: GeneratedProject) throws
|
||||||
|
|
||||||
|
/// Generates a project scheme to build & test the all the project targets.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - project: Project manifest.
|
||||||
|
/// - generatedProject: Generated Xcode project.
|
||||||
|
/// - graph: Dependencies graph.
|
||||||
|
/// - Throws: An error if the generation of the scheme fails.
|
||||||
|
func generateProjectScheme(project: Project,
|
||||||
|
generatedProject: GeneratedProject,
|
||||||
|
graph: Graphing) throws
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SchemesGenerator: SchemesGenerating {
|
final class SchemesGenerator: SchemesGenerating {
|
||||||
|
|
||||||
|
/// Default last upgrade version for generated schemes.
|
||||||
|
private static let defaultLastUpgradeVersion = "1010"
|
||||||
|
|
||||||
|
/// Default version for generated schemes.
|
||||||
|
private static let defaultVersion = "1.3"
|
||||||
|
|
||||||
/// Instance to interact with the file system.
|
/// Instance to interact with the file system.
|
||||||
let fileHandler: FileHandling
|
let fileHandler: FileHandling
|
||||||
|
|
||||||
|
@ -42,6 +60,92 @@ final class SchemesGenerator: SchemesGenerating {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a project scheme to build & test the all the project targets.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - project: Project manifest.
|
||||||
|
/// - generatedProject: Generated Xcode project.
|
||||||
|
/// - graph: Dependencies graph.
|
||||||
|
/// - Throws: An error if the generation of the scheme fails.
|
||||||
|
func generateProjectScheme(project: Project,
|
||||||
|
generatedProject: GeneratedProject,
|
||||||
|
graph: Graphing) throws {
|
||||||
|
let name = "\(project.name)-Project"
|
||||||
|
let schemesDirectory = try createSchemesDirectory(projectPath: generatedProject.path)
|
||||||
|
let path = schemesDirectory.appending(component: "\(name).xcscheme")
|
||||||
|
|
||||||
|
let scheme = XCScheme(name: name,
|
||||||
|
lastUpgradeVersion: SchemesGenerator.defaultLastUpgradeVersion,
|
||||||
|
version: SchemesGenerator.defaultVersion,
|
||||||
|
buildAction: projectBuildAction(project: project,
|
||||||
|
generatedProject: generatedProject,
|
||||||
|
graph: graph),
|
||||||
|
testAction: projectTestAction(project: project,
|
||||||
|
generatedProject: generatedProject))
|
||||||
|
|
||||||
|
try scheme.write(path: path.path, override: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the build action for the project scheme.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - project: Project manifest.
|
||||||
|
/// - generatedProject: Generated Xcode project.
|
||||||
|
/// - graph: Dependencies graph.
|
||||||
|
/// - Returns: Scheme build action.
|
||||||
|
func projectBuildAction(project: Project,
|
||||||
|
generatedProject: GeneratedProject,
|
||||||
|
graph: Graphing) -> XCScheme.BuildAction {
|
||||||
|
|
||||||
|
let targets = project.sortedTargetsForProjectScheme(graph: graph)
|
||||||
|
let entries: [XCScheme.BuildAction.Entry] = targets.map { (target) -> XCScheme.BuildAction.Entry in
|
||||||
|
let pbxTarget = generatedProject.targets[target.name]!
|
||||||
|
let buildableReference = targetBuildableReference(target: target,
|
||||||
|
pbxTarget: pbxTarget,
|
||||||
|
projectPath: generatedProject.path)
|
||||||
|
var buildFor: [XCScheme.BuildAction.Entry.BuildFor] = []
|
||||||
|
if target.product.testsBundle {
|
||||||
|
buildFor.append(.testing)
|
||||||
|
} else {
|
||||||
|
buildFor.append(contentsOf: [.analyzing, .archiving, .profiling, .running, .testing])
|
||||||
|
}
|
||||||
|
|
||||||
|
return XCScheme.BuildAction.Entry(buildableReference: buildableReference,
|
||||||
|
buildFor: buildFor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return XCScheme.BuildAction(buildActionEntries: entries,
|
||||||
|
parallelizeBuild: true,
|
||||||
|
buildImplicitDependencies: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates the test action for the project scheme.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - project: Project manifest.
|
||||||
|
/// - generatedProject: Generated Xcode project.
|
||||||
|
/// - Returns: Scheme test action.
|
||||||
|
func projectTestAction(project: Project,
|
||||||
|
generatedProject: GeneratedProject) -> XCScheme.TestAction {
|
||||||
|
var testables: [XCScheme.TestableReference] = []
|
||||||
|
let testTargets = project.targets.filter({ $0.product.testsBundle })
|
||||||
|
|
||||||
|
testTargets.forEach { (target) in
|
||||||
|
let pbxTarget = generatedProject.targets[target.name]!
|
||||||
|
|
||||||
|
let reference = targetBuildableReference(target: target,
|
||||||
|
pbxTarget: pbxTarget,
|
||||||
|
projectPath: generatedProject.path)
|
||||||
|
let testable = XCScheme.TestableReference(skipped: false,
|
||||||
|
buildableReference: reference)
|
||||||
|
testables.append(testable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return XCScheme.TestAction(buildConfiguration: "Debug",
|
||||||
|
macroExpansion: nil,
|
||||||
|
testables: testables)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates the scheme for a given target.
|
/// Generates the scheme for a given target.
|
||||||
///
|
///
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
|
@ -56,22 +160,22 @@ final class SchemesGenerator: SchemesGenerating {
|
||||||
let schemePath = schemesDirectory.appending(component: "\(target.name).xcscheme")
|
let schemePath = schemesDirectory.appending(component: "\(target.name).xcscheme")
|
||||||
|
|
||||||
let scheme = XCScheme(name: target.name,
|
let scheme = XCScheme(name: target.name,
|
||||||
lastUpgradeVersion: "1010",
|
lastUpgradeVersion: SchemesGenerator.defaultLastUpgradeVersion,
|
||||||
version: "1.3",
|
version: SchemesGenerator.defaultVersion,
|
||||||
buildAction: buildAction(target: target,
|
buildAction: targetBuildAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath),
|
projectPath: projectPath),
|
||||||
testAction: testAction(target: target,
|
testAction: targetTestAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath),
|
projectPath: projectPath),
|
||||||
launchAction: launchAction(target: target,
|
launchAction: targetLaunchAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath),
|
projectPath: projectPath),
|
||||||
profileAction: profileAction(target: target,
|
profileAction: targetProfileAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath),
|
projectPath: projectPath),
|
||||||
analyzeAction: analyzeAction(),
|
analyzeAction: targetAnalyzeAction(),
|
||||||
archiveAction: archiveAction())
|
archiveAction: targetArchiveAction())
|
||||||
try scheme.write(path: schemePath.path, override: true)
|
try scheme.write(path: schemePath.path, override: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,12 +186,12 @@ final class SchemesGenerator: SchemesGenerating {
|
||||||
/// - pbxTarget: Xcode native target.
|
/// - pbxTarget: Xcode native target.
|
||||||
/// - projectPath: Path to the Xcode project.
|
/// - projectPath: Path to the Xcode project.
|
||||||
/// - Returns: Scheme test action.
|
/// - Returns: Scheme test action.
|
||||||
func testAction(target: Target,
|
func targetTestAction(target: Target,
|
||||||
pbxTarget: PBXNativeTarget,
|
pbxTarget: PBXNativeTarget,
|
||||||
projectPath: AbsolutePath) -> XCScheme.TestAction? {
|
projectPath: AbsolutePath) -> XCScheme.TestAction? {
|
||||||
var testables: [XCScheme.TestableReference] = []
|
var testables: [XCScheme.TestableReference] = []
|
||||||
if target.product.testsBundle {
|
if target.product.testsBundle {
|
||||||
let reference = buildableReference(target: target,
|
let reference = targetBuildableReference(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
let testable = XCScheme.TestableReference(skipped: false,
|
let testable = XCScheme.TestableReference(skipped: false,
|
||||||
|
@ -106,14 +210,14 @@ final class SchemesGenerator: SchemesGenerating {
|
||||||
/// - pbxTarget: Xcode native target.
|
/// - pbxTarget: Xcode native target.
|
||||||
/// - projectPath: Path to the Xcode project.
|
/// - projectPath: Path to the Xcode project.
|
||||||
/// - Returns: Scheme build action.
|
/// - Returns: Scheme build action.
|
||||||
func buildAction(target: Target,
|
func targetBuildAction(target: Target,
|
||||||
pbxTarget: PBXNativeTarget,
|
pbxTarget: PBXNativeTarget,
|
||||||
projectPath: AbsolutePath) -> XCScheme.BuildAction? {
|
projectPath: AbsolutePath) -> XCScheme.BuildAction? {
|
||||||
let buildFor: [XCScheme.BuildAction.Entry.BuildFor] = [
|
let buildFor: [XCScheme.BuildAction.Entry.BuildFor] = [
|
||||||
.analyzing, .archiving, .profiling, .running, .testing
|
.analyzing, .archiving, .profiling, .running, .testing
|
||||||
]
|
]
|
||||||
|
|
||||||
let buildableReference = self.buildableReference(target: target,
|
let buildableReference = targetBuildableReference(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
var entries: [XCScheme.BuildAction.Entry] = []
|
var entries: [XCScheme.BuildAction.Entry] = []
|
||||||
|
@ -131,12 +235,12 @@ final class SchemesGenerator: SchemesGenerating {
|
||||||
/// - pbxTarget: Xcode native target.
|
/// - pbxTarget: Xcode native target.
|
||||||
/// - projectPath: Path to the Xcode project.
|
/// - projectPath: Path to the Xcode project.
|
||||||
/// - Returns: Scheme launch action.
|
/// - Returns: Scheme launch action.
|
||||||
func launchAction(target: Target,
|
func targetLaunchAction(target: Target,
|
||||||
pbxTarget: PBXNativeTarget,
|
pbxTarget: PBXNativeTarget,
|
||||||
projectPath: AbsolutePath) -> XCScheme.LaunchAction? {
|
projectPath: AbsolutePath) -> XCScheme.LaunchAction? {
|
||||||
var buildableProductRunnable: XCScheme.BuildableProductRunnable?
|
var buildableProductRunnable: XCScheme.BuildableProductRunnable?
|
||||||
var macroExpansion: XCScheme.BuildableReference?
|
var macroExpansion: XCScheme.BuildableReference?
|
||||||
let buildableReference = self.buildableReference(target: target, pbxTarget: pbxTarget, projectPath: projectPath)
|
let buildableReference = targetBuildableReference(target: target, pbxTarget: pbxTarget, projectPath: projectPath)
|
||||||
if target.product.runnable {
|
if target.product.runnable {
|
||||||
buildableProductRunnable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference, runnableDebuggingMode: "0")
|
buildableProductRunnable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference, runnableDebuggingMode: "0")
|
||||||
} else {
|
} else {
|
||||||
|
@ -158,12 +262,12 @@ final class SchemesGenerator: SchemesGenerating {
|
||||||
/// - pbxTarget: Xcode native target.
|
/// - pbxTarget: Xcode native target.
|
||||||
/// - projectPath: Path to the Xcode project.
|
/// - projectPath: Path to the Xcode project.
|
||||||
/// - Returns: Scheme profile action.
|
/// - Returns: Scheme profile action.
|
||||||
func profileAction(target: Target,
|
func targetProfileAction(target: Target,
|
||||||
pbxTarget: PBXNativeTarget,
|
pbxTarget: PBXNativeTarget,
|
||||||
projectPath: AbsolutePath) -> XCScheme.ProfileAction? {
|
projectPath: AbsolutePath) -> XCScheme.ProfileAction? {
|
||||||
var buildableProductRunnable: XCScheme.BuildableProductRunnable?
|
var buildableProductRunnable: XCScheme.BuildableProductRunnable?
|
||||||
var macroExpansion: XCScheme.BuildableReference?
|
var macroExpansion: XCScheme.BuildableReference?
|
||||||
let buildableReference = self.buildableReference(target: target, pbxTarget: pbxTarget, projectPath: projectPath)
|
let buildableReference = targetBuildableReference(target: target, pbxTarget: pbxTarget, projectPath: projectPath)
|
||||||
|
|
||||||
if target.product.runnable {
|
if target.product.runnable {
|
||||||
buildableProductRunnable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference, runnableDebuggingMode: "0")
|
buildableProductRunnable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference, runnableDebuggingMode: "0")
|
||||||
|
@ -182,7 +286,7 @@ final class SchemesGenerator: SchemesGenerating {
|
||||||
/// - pbxTarget: Xcode native target.
|
/// - pbxTarget: Xcode native target.
|
||||||
/// - projectPath: Path to the Xcode project.
|
/// - projectPath: Path to the Xcode project.
|
||||||
/// - Returns: Buildable reference.
|
/// - Returns: Buildable reference.
|
||||||
func buildableReference(target: Target, pbxTarget: PBXNativeTarget, projectPath: AbsolutePath) -> XCScheme.BuildableReference {
|
func targetBuildableReference(target: Target, pbxTarget: PBXNativeTarget, projectPath: AbsolutePath) -> XCScheme.BuildableReference {
|
||||||
let projectName = projectPath.components.last!
|
let projectName = projectPath.components.last!
|
||||||
return XCScheme.BuildableReference(referencedContainer: "container:\(projectName)",
|
return XCScheme.BuildableReference(referencedContainer: "container:\(projectName)",
|
||||||
blueprint: pbxTarget,
|
blueprint: pbxTarget,
|
||||||
|
@ -194,14 +298,14 @@ final class SchemesGenerator: SchemesGenerating {
|
||||||
/// Returns the scheme analyze action for a given target.
|
/// Returns the scheme analyze action for a given target.
|
||||||
///
|
///
|
||||||
/// - Returns: Scheme analyze action.
|
/// - Returns: Scheme analyze action.
|
||||||
func analyzeAction() -> XCScheme.AnalyzeAction {
|
func targetAnalyzeAction() -> XCScheme.AnalyzeAction {
|
||||||
return XCScheme.AnalyzeAction(buildConfiguration: "Debug")
|
return XCScheme.AnalyzeAction(buildConfiguration: "Debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the scheme archive action for a given target.
|
/// Returns the scheme archive action for a given target.
|
||||||
///
|
///
|
||||||
/// - Returns: Scheme archive action.
|
/// - Returns: Scheme archive action.
|
||||||
func archiveAction() -> XCScheme.ArchiveAction {
|
func targetArchiveAction() -> XCScheme.ArchiveAction {
|
||||||
return XCScheme.ArchiveAction(buildConfiguration: "Release",
|
return XCScheme.ArchiveAction(buildConfiguration: "Release",
|
||||||
revealArchiveInOrganizer: true)
|
revealArchiveInOrganizer: true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,9 +164,9 @@ final class TargetGenerator: TargetGenerating {
|
||||||
graph: Graphing) throws {
|
graph: Graphing) throws {
|
||||||
try targets.forEach { targetSpec in
|
try targets.forEach { targetSpec in
|
||||||
let dependencies = graph.targetDependencies(path: path, name: targetSpec.name)
|
let dependencies = graph.targetDependencies(path: path, name: targetSpec.name)
|
||||||
try dependencies.forEach { dependencyName in
|
try dependencies.forEach { dependency in
|
||||||
let target = nativeTargets[targetSpec.name]!
|
let target = nativeTargets[targetSpec.name]!
|
||||||
let dependency = nativeTargets[dependencyName]!
|
let dependency = nativeTargets[dependency.target.name]!
|
||||||
_ = try target.addDependency(target: dependency)
|
_ = try target.addDependency(target: dependency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ protocol Graphing: AnyObject {
|
||||||
func linkableDependencies(path: AbsolutePath, name: String) throws -> [DependencyReference]
|
func linkableDependencies(path: AbsolutePath, name: String) throws -> [DependencyReference]
|
||||||
func librariesPublicHeadersFolders(path: AbsolutePath, name: String) -> [AbsolutePath]
|
func librariesPublicHeadersFolders(path: AbsolutePath, name: String) -> [AbsolutePath]
|
||||||
func embeddableFrameworks(path: AbsolutePath, name: String, system: Systeming) throws -> [DependencyReference]
|
func embeddableFrameworks(path: AbsolutePath, name: String, system: Systeming) throws -> [DependencyReference]
|
||||||
func targetDependencies(path: AbsolutePath, name: String) -> [String]
|
func targetDependencies(path: AbsolutePath, name: String) -> [TargetNode]
|
||||||
func staticLibraryDependencies(path: AbsolutePath, name: String) -> [DependencyReference]
|
func staticLibraryDependencies(path: AbsolutePath, name: String) -> [DependencyReference]
|
||||||
|
|
||||||
// MARK: - Depth First Search
|
// MARK: - Depth First Search
|
||||||
|
@ -91,14 +91,13 @@ class Graph: Graphing {
|
||||||
return cache.precompiledNodes.values.compactMap { $0 as? FrameworkNode }
|
return cache.precompiledNodes.values.compactMap { $0 as? FrameworkNode }
|
||||||
}
|
}
|
||||||
|
|
||||||
func targetDependencies(path: AbsolutePath, name: String) -> [String] {
|
func targetDependencies(path: AbsolutePath, name: String) -> [TargetNode] {
|
||||||
guard let targetNode = findTargetNode(path: path, name: name) else {
|
guard let targetNode = findTargetNode(path: path, name: name) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetNode.targetDependencies
|
return targetNode.targetDependencies
|
||||||
.filter { $0.path == path }
|
.filter { $0.path == path }
|
||||||
.map(\.target.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func staticLibraryDependencies(path: AbsolutePath, name: String) -> [DependencyReference] {
|
func staticLibraryDependencies(path: AbsolutePath, name: String) -> [DependencyReference] {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Basic
|
||||||
import Foundation
|
import Foundation
|
||||||
import TuistCore
|
import TuistCore
|
||||||
|
|
||||||
class Project: Equatable {
|
class Project: Equatable, CustomStringConvertible {
|
||||||
// MARK: - Attributes
|
// MARK: - Attributes
|
||||||
|
|
||||||
/// Path to the folder that contains the project manifest.
|
/// Path to the folder that contains the project manifest.
|
||||||
|
@ -74,6 +74,47 @@ class Project: Equatable {
|
||||||
settings = try settingsJSON.map({ try Settings(dictionary: $0, projectPath: path, fileHandler: fileHandler) })
|
settings = try settingsJSON.map({ try Settings(dictionary: $0, projectPath: path, fileHandler: fileHandler) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// It returns the project targets sorted based on the target type and the dependencies between them.
|
||||||
|
/// The most dependent and non-tests targets are sorted first in the list.
|
||||||
|
///
|
||||||
|
/// - Parameter graph: Dependencies graph.
|
||||||
|
/// - Returns: Sorted targets.
|
||||||
|
func sortedTargetsForProjectScheme(graph: Graphing) -> [Target] {
|
||||||
|
return targets.sorted { (first, second) -> Bool in
|
||||||
|
// First criteria: Test bundles at the end
|
||||||
|
if first.product.testsBundle && !second.product.testsBundle {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !first.product.testsBundle && second.product.testsBundle {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second criteria: Most dependent targets first.
|
||||||
|
let secondDependencies = graph.targetDependencies(path: self.path, name: second.name)
|
||||||
|
.filter({ $0.path == self.path })
|
||||||
|
.map({ $0.target.name })
|
||||||
|
let firstDependencies = graph.targetDependencies(path: self.path, name: first.name)
|
||||||
|
.filter({ $0.path == self.path })
|
||||||
|
.map({ $0.target.name })
|
||||||
|
|
||||||
|
if secondDependencies.contains(first.name) {
|
||||||
|
return true
|
||||||
|
} else if firstDependencies.contains(second.name) {
|
||||||
|
return false
|
||||||
|
|
||||||
|
// Third criteria: Name
|
||||||
|
} else {
|
||||||
|
return first.name < second.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - CustomStringConvertible
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
return self.name
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Equatable
|
// MARK: - Equatable
|
||||||
|
|
||||||
static func == (lhs: Project, rhs: Project) -> Bool {
|
static func == (lhs: Project, rhs: Project) -> Bool {
|
||||||
|
|
|
@ -130,10 +130,14 @@ class Target: GraphInitiatable, Equatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if the target can be linked.
|
||||||
|
///
|
||||||
|
/// - Returns: True if the target can be linked from another target.
|
||||||
func isLinkable() -> Bool {
|
func isLinkable() -> Bool {
|
||||||
return product == .dynamicLibrary || product == .staticLibrary || product == .framework
|
return product == .dynamicLibrary || product == .staticLibrary || product == .framework
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the product name including the extension.
|
||||||
var productName: String {
|
var productName: String {
|
||||||
switch product {
|
switch product {
|
||||||
case .staticLibrary, .dynamicLibrary:
|
case .staticLibrary, .dynamicLibrary:
|
||||||
|
@ -185,3 +189,17 @@ class Target: GraphInitiatable, Equatable {
|
||||||
lhs.environment == rhs.environment
|
lhs.environment == rhs.environment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Sequence where Element == Target {
|
||||||
|
|
||||||
|
/// Filters and returns only the targets that are test bundles.
|
||||||
|
var testBundles: [Target] {
|
||||||
|
return filter({ $0.product.testsBundle })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filters and returns only the targets that are apps.
|
||||||
|
var apps: [Target] {
|
||||||
|
return filter({ $0.product == .app})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,13 @@ import Foundation
|
||||||
|
|
||||||
final class MockSchemesGenerator: SchemesGenerating {
|
final class MockSchemesGenerator: SchemesGenerating {
|
||||||
var generateTargetSchemesArgs: [(project: Project, generatedProject: GeneratedProject)] = []
|
var generateTargetSchemesArgs: [(project: Project, generatedProject: GeneratedProject)] = []
|
||||||
|
var generateProjectSchemeArgs: [(project: Project, generatedProject: GeneratedProject, graph: Graphing)] = []
|
||||||
|
|
||||||
func generateTargetSchemes(project: Project, generatedProject: GeneratedProject) throws {
|
func generateTargetSchemes(project: Project, generatedProject: GeneratedProject) throws {
|
||||||
generateTargetSchemesArgs.append((project: project, generatedProject: generatedProject))
|
generateTargetSchemesArgs.append((project: project, generatedProject: generatedProject))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateProjectScheme(project: Project, generatedProject: GeneratedProject, graph: Graphing) throws {
|
||||||
|
generateProjectSchemeArgs.append((project: project, generatedProject: generatedProject, graph: graph))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,77 @@ final class SchemeGeneratorTests: XCTestCase {
|
||||||
subject = SchemesGenerator()
|
subject = SchemesGenerator()
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_testAction_when_notTestsTarget() {
|
func test_projectBuildAction() {
|
||||||
|
let app = Target.test(name: "App", product: .app)
|
||||||
|
let appTests = Target.test(name: "AppTests", product: .unitTests)
|
||||||
|
let appUITests = Target.test(name: "AppUITests", product: .uiTests)
|
||||||
|
let targets = [app, appTests, appUITests]
|
||||||
|
|
||||||
|
let project = Project.test(targets: targets)
|
||||||
|
let graphCache = GraphLoaderCache()
|
||||||
|
let graph = Graph.test(cache: graphCache)
|
||||||
|
|
||||||
|
let got = subject.projectBuildAction(project: project,
|
||||||
|
generatedProject: generatedProject(targets: targets),
|
||||||
|
graph: graph)
|
||||||
|
|
||||||
|
XCTAssertTrue(got.parallelizeBuild)
|
||||||
|
XCTAssertTrue(got.buildImplicitDependencies)
|
||||||
|
XCTAssertEqual(got.buildActionEntries.count, 3)
|
||||||
|
|
||||||
|
let appEntry = got.buildActionEntries[0]
|
||||||
|
let testsEntry = got.buildActionEntries[1]
|
||||||
|
let uiTestsEntry = got.buildActionEntries[2]
|
||||||
|
|
||||||
|
XCTAssertEqual(appEntry.buildFor, [.analyzing, .archiving, .profiling, .running, .testing])
|
||||||
|
XCTAssertEqual(appEntry.buildableReference.referencedContainer, "container:project.xcodeproj")
|
||||||
|
XCTAssertEqual(appEntry.buildableReference.buildableName, app.productName)
|
||||||
|
XCTAssertEqual(appEntry.buildableReference.blueprintName, app.name)
|
||||||
|
XCTAssertEqual(appEntry.buildableReference.buildableIdentifier, "primary")
|
||||||
|
|
||||||
|
XCTAssertEqual(testsEntry.buildFor, [.testing])
|
||||||
|
XCTAssertEqual(testsEntry.buildableReference.referencedContainer, "container:project.xcodeproj")
|
||||||
|
XCTAssertEqual(testsEntry.buildableReference.buildableName, appTests.productName)
|
||||||
|
XCTAssertEqual(testsEntry.buildableReference.blueprintName, appTests.name)
|
||||||
|
XCTAssertEqual(testsEntry.buildableReference.buildableIdentifier, "primary")
|
||||||
|
|
||||||
|
XCTAssertEqual(uiTestsEntry.buildFor, [.testing])
|
||||||
|
XCTAssertEqual(uiTestsEntry.buildableReference.referencedContainer, "container:project.xcodeproj")
|
||||||
|
XCTAssertEqual(uiTestsEntry.buildableReference.buildableName, appUITests.productName)
|
||||||
|
XCTAssertEqual(uiTestsEntry.buildableReference.blueprintName, appUITests.name)
|
||||||
|
XCTAssertEqual(uiTestsEntry.buildableReference.buildableIdentifier, "primary")
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_projectTestAction() {
|
||||||
|
let app = Target.test(name: "App", product: .app)
|
||||||
|
let appTests = Target.test(name: "AppTests", product: .unitTests)
|
||||||
|
let targets = [app, appTests]
|
||||||
|
let project = Project.test(targets: targets)
|
||||||
|
|
||||||
|
let got = subject.projectTestAction(project: project,
|
||||||
|
generatedProject: generatedProject(targets: targets))
|
||||||
|
|
||||||
|
XCTAssertEqual(got.buildConfiguration, "Debug")
|
||||||
|
XCTAssertNil(got.macroExpansion)
|
||||||
|
XCTAssertEqual(got.testables.count, 1)
|
||||||
|
|
||||||
|
let testable = got.testables.first
|
||||||
|
XCTAssertEqual(testable?.skipped, false)
|
||||||
|
|
||||||
|
XCTAssertEqual(testable?.buildableReference.referencedContainer, "container:project.xcodeproj")
|
||||||
|
XCTAssertEqual(testable?.buildableReference.buildableName, appTests.productName)
|
||||||
|
XCTAssertEqual(testable?.buildableReference.blueprintName, appTests.name)
|
||||||
|
XCTAssertEqual(testable?.buildableReference.buildableIdentifier, "primary")
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_targetTestAction_when_notTestsTarget() {
|
||||||
let target = Target.test(name: "AppTests", product: .app)
|
let target = Target.test(name: "AppTests", product: .app)
|
||||||
let pbxTarget = PBXNativeTarget(name: "App")
|
let pbxTarget = PBXNativeTarget(name: "App")
|
||||||
let projectPath = AbsolutePath("/project.xcodeproj")
|
let projectPath = AbsolutePath("/project.xcodeproj")
|
||||||
|
|
||||||
let got = subject.testAction(target: target,
|
let got = subject.targetTestAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
|
|
||||||
XCTAssertEqual(got?.buildConfiguration, "Debug")
|
XCTAssertEqual(got?.buildConfiguration, "Debug")
|
||||||
XCTAssertEqual(got?.shouldUseLaunchSchemeArgsEnv, true)
|
XCTAssertEqual(got?.shouldUseLaunchSchemeArgsEnv, true)
|
||||||
|
@ -30,12 +93,12 @@ final class SchemeGeneratorTests: XCTestCase {
|
||||||
XCTAssertEqual(got?.testables.count, 0)
|
XCTAssertEqual(got?.testables.count, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_testAction_when_testsTarget() {
|
func test_targetTestAction_when_testsTarget() {
|
||||||
let target = Target.test(name: "AppTests", product: .unitTests)
|
let target = Target.test(name: "AppTests", product: .unitTests)
|
||||||
let pbxTarget = PBXNativeTarget(name: "App")
|
let pbxTarget = PBXNativeTarget(name: "App")
|
||||||
let projectPath = AbsolutePath("/project.xcodeproj")
|
let projectPath = AbsolutePath("/project.xcodeproj")
|
||||||
|
|
||||||
let got = subject.testAction(target: target,
|
let got = subject.targetTestAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
|
|
||||||
|
@ -52,12 +115,12 @@ final class SchemeGeneratorTests: XCTestCase {
|
||||||
XCTAssertEqual(buildableReference?.buildableIdentifier, "primary")
|
XCTAssertEqual(buildableReference?.buildableIdentifier, "primary")
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_buildAction() {
|
func test_targetBuildAction() {
|
||||||
let target = Target.test(name: "App", product: .app)
|
let target = Target.test(name: "App", product: .app)
|
||||||
let pbxTarget = PBXNativeTarget(name: "App")
|
let pbxTarget = PBXNativeTarget(name: "App")
|
||||||
let projectPath = AbsolutePath("/project.xcodeproj")
|
let projectPath = AbsolutePath("/project.xcodeproj")
|
||||||
|
|
||||||
let got = subject.buildAction(target: target,
|
let got = subject.targetBuildAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
|
|
||||||
|
@ -75,11 +138,11 @@ final class SchemeGeneratorTests: XCTestCase {
|
||||||
XCTAssertEqual(got?.buildImplicitDependencies, true)
|
XCTAssertEqual(got?.buildImplicitDependencies, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_launchAction_when_runnableTarget() {
|
func test_targetLaunchAction_when_runnableTarget() {
|
||||||
let target = Target.test(name: "App", product: .app, environment: ["a": "b"])
|
let target = Target.test(name: "App", product: .app, environment: ["a": "b"])
|
||||||
let pbxTarget = PBXNativeTarget(name: "App")
|
let pbxTarget = PBXNativeTarget(name: "App")
|
||||||
let projectPath = AbsolutePath("/project.xcodeproj")
|
let projectPath = AbsolutePath("/project.xcodeproj")
|
||||||
let got = subject.launchAction(target: target,
|
let got = subject.targetLaunchAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
|
|
||||||
|
@ -94,13 +157,13 @@ final class SchemeGeneratorTests: XCTestCase {
|
||||||
XCTAssertEqual(buildableReference?.buildableIdentifier, "primary")
|
XCTAssertEqual(buildableReference?.buildableIdentifier, "primary")
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_launchAction_when_notRunnableTarget() {
|
func test_targetLaunchAction_when_notRunnableTarget() {
|
||||||
let target = Target.test(name: "Library",
|
let target = Target.test(name: "Library",
|
||||||
platform: .iOS,
|
platform: .iOS,
|
||||||
product: .dynamicLibrary)
|
product: .dynamicLibrary)
|
||||||
let pbxTarget = PBXNativeTarget(name: "App")
|
let pbxTarget = PBXNativeTarget(name: "App")
|
||||||
let projectPath = AbsolutePath("/project.xcodeproj")
|
let projectPath = AbsolutePath("/project.xcodeproj")
|
||||||
let got = subject.launchAction(target: target,
|
let got = subject.targetLaunchAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
|
|
||||||
|
@ -113,13 +176,13 @@ final class SchemeGeneratorTests: XCTestCase {
|
||||||
XCTAssertEqual(got?.macroExpansion?.buildableIdentifier, "primary")
|
XCTAssertEqual(got?.macroExpansion?.buildableIdentifier, "primary")
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_profileAction_when_runnableTarget() {
|
func test_targetProfileAction_when_runnableTarget() {
|
||||||
let target = Target.test(name: "App",
|
let target = Target.test(name: "App",
|
||||||
platform: .iOS,
|
platform: .iOS,
|
||||||
product: .app)
|
product: .app)
|
||||||
let pbxTarget = PBXNativeTarget(name: "App")
|
let pbxTarget = PBXNativeTarget(name: "App")
|
||||||
let projectPath = AbsolutePath("/project.xcodeproj")
|
let projectPath = AbsolutePath("/project.xcodeproj")
|
||||||
let got = subject.profileAction(target: target,
|
let got = subject.targetProfileAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
|
|
||||||
|
@ -145,13 +208,13 @@ final class SchemeGeneratorTests: XCTestCase {
|
||||||
XCTAssertEqual(got?.enableTestabilityWhenProfilingTests, true)
|
XCTAssertEqual(got?.enableTestabilityWhenProfilingTests, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_profileAction_when_notRunnableTarget() {
|
func test_targetProfileAction_when_notRunnableTarget() {
|
||||||
let target = Target.test(name: "Library",
|
let target = Target.test(name: "Library",
|
||||||
platform: .iOS,
|
platform: .iOS,
|
||||||
product: .dynamicLibrary)
|
product: .dynamicLibrary)
|
||||||
let pbxTarget = PBXNativeTarget(name: "App")
|
let pbxTarget = PBXNativeTarget(name: "App")
|
||||||
let projectPath = AbsolutePath("/project.xcodeproj")
|
let projectPath = AbsolutePath("/project.xcodeproj")
|
||||||
let got = subject.profileAction(target: target,
|
let got = subject.targetProfileAction(target: target,
|
||||||
pbxTarget: pbxTarget,
|
pbxTarget: pbxTarget,
|
||||||
projectPath: projectPath)
|
projectPath: projectPath)
|
||||||
|
|
||||||
|
@ -177,14 +240,23 @@ final class SchemeGeneratorTests: XCTestCase {
|
||||||
XCTAssertEqual(got?.macroExpansion?.buildableIdentifier, "primary")
|
XCTAssertEqual(got?.macroExpansion?.buildableIdentifier, "primary")
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_analyzeAction() {
|
func test_targetAnalyzeAction() {
|
||||||
let got = subject.analyzeAction()
|
let got = subject.targetAnalyzeAction()
|
||||||
XCTAssertEqual(got.buildConfiguration, "Debug")
|
XCTAssertEqual(got.buildConfiguration, "Debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_archiveAction() {
|
func test_targetArchiveAction() {
|
||||||
let got = subject.archiveAction()
|
let got = subject.targetArchiveAction()
|
||||||
XCTAssertEqual(got.buildConfiguration, "Release")
|
XCTAssertEqual(got.buildConfiguration, "Release")
|
||||||
XCTAssertEqual(got.revealArchiveInOrganizer, true)
|
XCTAssertEqual(got.revealArchiveInOrganizer, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func generatedProject(targets: [Target]) -> GeneratedProject {
|
||||||
|
var pbxTargets: [String: PBXNativeTarget] = [:]
|
||||||
|
targets.forEach { pbxTargets[$0.name] = PBXNativeTarget(name: $0.name) }
|
||||||
|
let projectPath = AbsolutePath("/project.xcodeproj")
|
||||||
|
return GeneratedProject(path: projectPath, targets: pbxTargets)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ final class GraphTests: XCTestCase {
|
||||||
let graph = Graph.test(cache: cache)
|
let graph = Graph.test(cache: cache)
|
||||||
let dependencies = graph.targetDependencies(path: project.path,
|
let dependencies = graph.targetDependencies(path: project.path,
|
||||||
name: target.name)
|
name: target.name)
|
||||||
XCTAssertEqual(dependencies.first, "Dependency")
|
XCTAssertEqual(dependencies.first?.target.name, "Dependency")
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_linkableDependencies_whenPrecompiled() throws {
|
func test_linkableDependencies_whenPrecompiled() throws {
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import Foundation
|
||||||
|
import Basic
|
||||||
|
@testable import TuistKit
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class ProjectTests: XCTestCase {
|
||||||
|
|
||||||
|
func test_sortedTargetsForProjectScheme() {
|
||||||
|
let framework = Target.test(name: "Framework", product: .framework)
|
||||||
|
let app = Target.test(name: "App", product: .app)
|
||||||
|
let appTests = Target.test(name: "AppTets", product: .unitTests)
|
||||||
|
let frameworkTests = Target.test(name: "FrameworkTests", product: .unitTests)
|
||||||
|
let project = Project.test(targets: [
|
||||||
|
framework, app, appTests, frameworkTests
|
||||||
|
])
|
||||||
|
|
||||||
|
let graph = createGraph(project: project, dependencies: [
|
||||||
|
(target: framework, dependencies: []),
|
||||||
|
(target: frameworkTests, dependencies: [framework]),
|
||||||
|
(target: app, dependencies: [framework]),
|
||||||
|
(target: appTests, dependencies: [app]),
|
||||||
|
])
|
||||||
|
|
||||||
|
let got = project.sortedTargetsForProjectScheme(graph: graph)
|
||||||
|
XCTAssertEqual(got.count, 4)
|
||||||
|
XCTAssertEqual(got[0], framework)
|
||||||
|
XCTAssertEqual(got[1], app)
|
||||||
|
XCTAssertEqual(got[2], appTests)
|
||||||
|
XCTAssertEqual(got[3], frameworkTests)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func createTargetNodes(project: Project,
|
||||||
|
dependencies: [(target: Target, dependencies: [Target])]) -> [TargetNode] {
|
||||||
|
let nodesCache = Dictionary(uniqueKeysWithValues: dependencies.map {
|
||||||
|
($0.target.name, TargetNode(project: project,
|
||||||
|
target: $0.target,
|
||||||
|
dependencies: []))
|
||||||
|
})
|
||||||
|
|
||||||
|
return dependencies.map {
|
||||||
|
let node = nodesCache[$0.target.name]!
|
||||||
|
node.dependencies = $0.dependencies.map { nodesCache[$0.name]! }
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createGraph(project: Project,
|
||||||
|
dependencies: [(target: Target, dependencies: [Target])]) -> Graph {
|
||||||
|
let targetNodes = createTargetNodes(project: project, dependencies: dependencies)
|
||||||
|
|
||||||
|
let cache = GraphLoaderCache()
|
||||||
|
let graph = Graph.test(cache: cache)
|
||||||
|
|
||||||
|
targetNodes.forEach { cache.add(targetNode: $0) }
|
||||||
|
|
||||||
|
return graph
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,4 +21,20 @@ final class TargetTests: XCTestCase {
|
||||||
let target = Target.test(name: "Test", product: .app)
|
let target = Target.test(name: "Test", product: .app)
|
||||||
XCTAssertEqual(target.productName, "Test.app")
|
XCTAssertEqual(target.productName, "Test.app")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_sequence_testBundles() {
|
||||||
|
let app = Target.test(product: .app)
|
||||||
|
let tests = Target.test(product: .unitTests)
|
||||||
|
let targets = [app, tests]
|
||||||
|
|
||||||
|
XCTAssertEqual(targets.testBundles, [tests])
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_sequence_apps() {
|
||||||
|
let app = Target.test(product: .app)
|
||||||
|
let tests = Target.test(product: .unitTests)
|
||||||
|
let targets = [app, tests]
|
||||||
|
|
||||||
|
XCTAssertEqual(targets.apps, [app])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
### macOS ###
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### Xcode ###
|
||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
|
## User settings
|
||||||
|
xcuserdata/
|
||||||
|
|
||||||
|
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||||
|
*.xcscmblueprint
|
||||||
|
*.xccheckout
|
||||||
|
|
||||||
|
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||||
|
build/
|
||||||
|
DerivedData/
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
|
||||||
|
### Xcode Patch ###
|
||||||
|
*.xcodeproj/*
|
||||||
|
!*.xcodeproj/project.pbxproj
|
||||||
|
!*.xcodeproj/xcshareddata/
|
||||||
|
!*.xcworkspace/contents.xcworkspacedata
|
||||||
|
/*.gcno
|
||||||
|
|
||||||
|
### Projects ###
|
||||||
|
*.xcodeproj
|
||||||
|
*.xcworkspace
|
|
@ -0,0 +1,20 @@
|
||||||
|
import UIKit
|
||||||
|
import Framework
|
||||||
|
|
||||||
|
@UIApplicationMain
|
||||||
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
|
var window: UIWindow?
|
||||||
|
var framework: FrameworkClass = FrameworkClass()
|
||||||
|
|
||||||
|
func application(_ application: UIApplication,
|
||||||
|
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||||
|
window = UIWindow(frame: UIScreen.main.bounds)
|
||||||
|
let viewController = UIViewController()
|
||||||
|
viewController.view.backgroundColor = .white
|
||||||
|
window?.rootViewController = viewController
|
||||||
|
window?.makeKeyAndVisible()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import App
|
||||||
|
|
||||||
|
final class AppTests: XCTestCase {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class FrameworkClass {
|
||||||
|
public init() {}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
@testable import Framework
|
||||||
|
|
||||||
|
final class FrameworkTests: XCTestCase {
|
||||||
|
var subject: FrameworkClass = FrameworkClass()
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright ©. All rights reserved.</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>armv7</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,40 @@
|
||||||
|
import ProjectDescription
|
||||||
|
|
||||||
|
let project = Project(name: "App",
|
||||||
|
targets: [
|
||||||
|
Target(name: "App",
|
||||||
|
platform: .iOS,
|
||||||
|
product: .app,
|
||||||
|
bundleId: "io.tuist.app",
|
||||||
|
infoPlist: "Info.plist",
|
||||||
|
sources: "App/**",
|
||||||
|
dependencies: [
|
||||||
|
.target(name: "Framework")
|
||||||
|
]),
|
||||||
|
Target(name: "AppTests",
|
||||||
|
platform: .iOS,
|
||||||
|
product: .unitTests,
|
||||||
|
bundleId: "io.tuist.appTests",
|
||||||
|
infoPlist: "Info.plist",
|
||||||
|
sources: "AppTests/**",
|
||||||
|
dependencies: [
|
||||||
|
.target(name: "App")
|
||||||
|
]),
|
||||||
|
Target(name: "Framework",
|
||||||
|
platform: .iOS,
|
||||||
|
product: .framework,
|
||||||
|
bundleId: "io.tuist.framework",
|
||||||
|
infoPlist: "Info.plist",
|
||||||
|
sources: "Framework/**",
|
||||||
|
dependencies: [
|
||||||
|
]),
|
||||||
|
Target(name: "FrameworkTests",
|
||||||
|
platform: .iOS,
|
||||||
|
product: .unitTests,
|
||||||
|
bundleId: "io.tuist.frameworkTests",
|
||||||
|
infoPlist: "Info.plist",
|
||||||
|
sources: "FrameworkTests/**",
|
||||||
|
dependencies: [
|
||||||
|
.target(name: "Framework")
|
||||||
|
])
|
||||||
|
])
|
Loading…
Reference in New Issue