forked from tuist/tuist
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Project.swift
142 lines (119 loc) · 4.88 KB
/
Project.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import Basic
import Foundation
import TuistCore
public class Project: Equatable, CustomStringConvertible {
// MARK: - Attributes
/// Path to the folder that contains the project manifest.
public let path: AbsolutePath
/// Project name.
public let name: String
/// Project file name.
public let fileName: String
/// Project targets.
public let targets: [Target]
/// Project schemes
public let schemes: [Scheme]
/// Project settings.
public let settings: Settings
/// The group to place project files within
public let filesGroup: ProjectGroup
/// Additional files to include in the project
public let additionalFiles: [FileElement]
// MARK: - Init
/// Initializes the project with its attributes.
///
/// - Parameters:
/// - path: Path to the folder that contains the project manifest.
/// - name: Project name.
/// - settings: The settings to apply at the project level
/// - filesGroup: The root group to place project files within
/// - targets: The project targets
/// - additionalFiles: The additional files to include in the project
/// *(Those won't be included in any build phases)*
public init(path: AbsolutePath,
name: String,
fileName: String? = nil,
settings: Settings,
filesGroup: ProjectGroup,
targets: [Target],
schemes: [Scheme],
additionalFiles: [FileElement] = []) {
self.path = path
self.name = name
self.fileName = fileName ?? name
self.targets = targets
self.schemes = schemes
self.settings = settings
self.filesGroup = filesGroup
self.additionalFiles = additionalFiles
}
// MARK: - Init
/// Returns a project model from the cache if present, otherwise loads a new instance.
///
/// - Parameters:
/// - path: Path of the project
/// - cache: Cache instance to cache projects and dependencies.
/// - circularDetector: Utility to find circular dependencies between targets.
/// - modelLoader: Entity responsible for providing new instances of project models
/// - Returns: Project instance.
/// - Throws: An error if the project can't be loaded, or if circular dependencies are detected.
static func at(_ path: AbsolutePath,
cache: GraphLoaderCaching,
circularDetector: GraphCircularDetecting,
modelLoader: GeneratorModelLoading) throws -> Project {
if let project = cache.project(path) {
return project
} else {
let project = try modelLoader.loadProject(at: path)
cache.add(project: project)
for target in project.targets {
if cache.targetNode(path, name: target.name) != nil { continue }
_ = try TargetNode.read(name: target.name, path: path, cache: cache, circularDetector: circularDetector, modelLoader: modelLoader)
}
return project
}
}
/// 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
public var description: String {
return name
}
// MARK: - Equatable
public static func == (lhs: Project, rhs: Project) -> Bool {
return lhs.path == rhs.path &&
lhs.name == rhs.name &&
lhs.targets == rhs.targets &&
lhs.schemes == rhs.schemes &&
lhs.settings == rhs.settings
}
}