ValueGraph BuildGraphInspector (#2527)
* Convert build service to use value graph. * Migrate doc service to use value graph. * Build migrate BuildGraphInspector. * Edit CHANGELOG. * Remove TargetNodeGraphMapper.
This commit is contained in:
parent
9420cf7569
commit
5c0d9efe01
|
@ -10,6 +10,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
|
|||
|
||||
### Changed
|
||||
|
||||
- Migrate `BuildGraphInspector` to `ValueGraph` [#2527](https://github.com/tuist/tuist/pull/2527) by [@fortmarek](https://github.com/fortmarek/)
|
||||
- Replace `ExpressibleByStringLiteral` with `ExpressibleByStringInterpolation` for `ProjectDescription` objects by [@DimaMishchenko](https://github.com/DimaMishchenko)
|
||||
|
||||
## 1.34.0 - Shipit
|
||||
|
|
|
@ -19,39 +19,31 @@ public protocol BuildGraphInspecting {
|
|||
/// From the list of buildable targets of the given scheme, it returns the first one.
|
||||
/// - Parameters:
|
||||
/// - scheme: Scheme in which to look up the target.
|
||||
/// - graph: Dependency graph.
|
||||
func buildableTarget(scheme: Scheme, graph: Graph) -> (Project, Target)?
|
||||
/// - graphTraverser: GraphTraversing traverser.
|
||||
func buildableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> (Project, Target)?
|
||||
|
||||
/// From the list of testable targets of the given scheme, it returns the first one.
|
||||
/// - Parameters:
|
||||
/// - scheme: Scheme in which to look up the target.
|
||||
/// - graph: Dependency graph.
|
||||
func testableTarget(scheme: Scheme, graph: Graph) -> TargetNode?
|
||||
func testableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> ValueGraphTarget?
|
||||
|
||||
/// Given a graph, it returns a list of buildable schemes.
|
||||
/// - Parameter graph: Dependency graph.
|
||||
func buildableSchemes(graph: Graph) -> [Scheme]
|
||||
/// Given a graphTraverser, it returns a list of buildable schemes.
|
||||
func buildableSchemes(graphTraverser: GraphTraversing) -> [Scheme]
|
||||
|
||||
/// Given a graph, it returns a list of buildable schemes that are part of the entry node
|
||||
/// - Parameters:
|
||||
/// - graph: Dependency graph
|
||||
func buildableEntrySchemes(graph: Graph) -> [Scheme]
|
||||
/// Given a graphTraverser, it returns a list of buildable schemes that are part of the entry node
|
||||
func buildableEntrySchemes(graphTraverser: GraphTraversing) -> [Scheme]
|
||||
|
||||
/// Given a graph, it returns a list of test schemes (those that include only one test target).
|
||||
/// - Parameter graph: Dependency graph.
|
||||
func testSchemes(graph: Graph) -> [Scheme]
|
||||
/// Given a graphTraverser, it returns a list of test schemes (those that include only one test target).
|
||||
func testSchemes(graphTraverser: GraphTraversing) -> [Scheme]
|
||||
|
||||
/// Given a graph, it returns a list of testable schemes.
|
||||
/// - Parameter graph: Dependency graph.
|
||||
func testableSchemes(graph: Graph) -> [Scheme]
|
||||
/// Given a graphTraverser, it returns a list of testable schemes.
|
||||
func testableSchemes(graphTraverser: GraphTraversing) -> [Scheme]
|
||||
|
||||
/// Schemes generated by `AutogeneratedProjectSchemeWorkspaceMapper`
|
||||
/// - Parameters:
|
||||
/// - graph: Dependency graph
|
||||
func projectSchemes(graph: Graph) -> [Scheme]
|
||||
func projectSchemes(graphTraverser: GraphTraversing) -> [Scheme]
|
||||
}
|
||||
|
||||
public class BuildGraphInspector: BuildGraphInspecting {
|
||||
public final class BuildGraphInspector: BuildGraphInspecting {
|
||||
public init() {}
|
||||
|
||||
public func buildArguments(project: Project, target: Target, configuration: String?, skipSigning: Bool) -> [XcodeBuildArgument] {
|
||||
|
@ -84,7 +76,7 @@ public class BuildGraphInspector: BuildGraphInspecting {
|
|||
return arguments
|
||||
}
|
||||
|
||||
public func buildableTarget(scheme: Scheme, graph: Graph) -> (Project, Target)? {
|
||||
public func buildableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> (Project, Target)? {
|
||||
guard
|
||||
scheme.buildAction?.targets.isEmpty == false,
|
||||
let buildTarget = scheme.buildAction?.targets.first
|
||||
|
@ -92,50 +84,52 @@ public class BuildGraphInspector: BuildGraphInspecting {
|
|||
return nil
|
||||
}
|
||||
|
||||
return graph.target(path: buildTarget.projectPath, name: buildTarget.name).map { ($0.project, $0.target) }
|
||||
return graphTraverser.target(
|
||||
path: buildTarget.projectPath,
|
||||
name: buildTarget.name
|
||||
)
|
||||
.map { ($0.project, $0.target) }
|
||||
}
|
||||
|
||||
public func testableTarget(scheme: Scheme, graph: Graph) -> TargetNode? {
|
||||
public func testableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> ValueGraphTarget? {
|
||||
guard let testTarget = scheme.testAction?.targets.first else { return nil }
|
||||
return graph.target(path: testTarget.target.projectPath, name: testTarget.target.name)
|
||||
return graphTraverser.target(path: testTarget.target.projectPath, name: testTarget.target.name)
|
||||
}
|
||||
|
||||
public func buildableSchemes(graph: Graph) -> [Scheme] {
|
||||
graph.schemes
|
||||
public func buildableSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
graphTraverser.schemes()
|
||||
.filter { $0.buildAction?.targets.isEmpty == false }
|
||||
.sorted(by: { $0.name < $1.name })
|
||||
}
|
||||
|
||||
public func buildableEntrySchemes(graph: Graph) -> [Scheme] {
|
||||
let projects = Set(graph.entryNodes.compactMap { ($0 as? TargetNode)?.project })
|
||||
public func buildableEntrySchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
let projects = Set(graphTraverser.rootTargets().map(\.project))
|
||||
return projects
|
||||
.flatMap(\.schemes)
|
||||
.filter { $0.buildAction?.targets.isEmpty == false }
|
||||
.sorted(by: { $0.name < $1.name })
|
||||
}
|
||||
|
||||
public func testableSchemes(graph: Graph) -> [Scheme] {
|
||||
graph.schemes
|
||||
public func testableSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
graphTraverser.schemes()
|
||||
.filter { $0.testAction?.targets.isEmpty == false }
|
||||
.sorted(by: { $0.name < $1.name })
|
||||
}
|
||||
|
||||
public func testSchemes(graph: Graph) -> [Scheme] {
|
||||
graph.targets.values.flatMap { target -> [Scheme] in
|
||||
target
|
||||
.filter { $0.target.product == .unitTests || $0.target.product == .uiTests }
|
||||
.flatMap { target -> [Scheme] in
|
||||
target.project.schemes
|
||||
.filter { $0.targetDependencies().map(\.name) == [target.name] }
|
||||
}
|
||||
}
|
||||
.filter { $0.testAction?.targets.isEmpty == false }
|
||||
.sorted(by: { $0.name < $1.name })
|
||||
public func testSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
graphTraverser.allTargets()
|
||||
.filter { $0.target.product == .unitTests || $0.target.product == .uiTests }
|
||||
.flatMap { target -> [Scheme] in
|
||||
target.project.schemes
|
||||
.filter { $0.targetDependencies().map(\.name) == [target.target.name] }
|
||||
}
|
||||
.filter { $0.testAction?.targets.isEmpty == false }
|
||||
.sorted(by: { $0.name < $1.name })
|
||||
}
|
||||
|
||||
public func projectSchemes(graph: Graph) -> [Scheme] {
|
||||
graph.workspace.schemes
|
||||
.filter { $0.name.contains("\(graph.workspace.name)-Project") }
|
||||
public func projectSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
graphTraverser.workspace.schemes
|
||||
.filter { $0.name.contains("\(graphTraverser.workspace.name)-Project") }
|
||||
.sorted(by: { $0.name < $1.name })
|
||||
}
|
||||
|
||||
|
|
|
@ -13,27 +13,27 @@ public final class MockBuildGraphInspector: BuildGraphInspecting {
|
|||
workspacePathStub?(directory) ?? directory
|
||||
}
|
||||
|
||||
public var buildableTargetStub: ((Scheme, Graph) -> (Project, Target)?)?
|
||||
public func buildableTarget(scheme: Scheme, graph: Graph) -> (Project, Target)? {
|
||||
public var buildableTargetStub: ((Scheme, GraphTraversing) -> (Project, Target)?)?
|
||||
public func buildableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> (Project, Target)? {
|
||||
if let buildableTargetStub = buildableTargetStub {
|
||||
return buildableTargetStub(scheme, graph)
|
||||
return buildableTargetStub(scheme, graphTraverser)
|
||||
} else {
|
||||
return (Project.test(), Target.test())
|
||||
}
|
||||
}
|
||||
|
||||
public var buildableSchemesStub: ((Graph) -> [Scheme])?
|
||||
public func buildableSchemes(graph: Graph) -> [Scheme] {
|
||||
public var buildableSchemesStub: ((GraphTraversing) -> [Scheme])?
|
||||
public func buildableSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
if let buildableSchemesStub = buildableSchemesStub {
|
||||
return buildableSchemesStub(graph)
|
||||
return buildableSchemesStub(graphTraverser)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public var buildableEntrySchemesStub: ((Graph) -> [Scheme])?
|
||||
public func buildableEntrySchemes(graph: Graph) -> [Scheme] {
|
||||
buildableEntrySchemesStub?(graph) ?? []
|
||||
public var buildableEntrySchemesStub: ((GraphTraversing) -> [Scheme])?
|
||||
public func buildableEntrySchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
buildableEntrySchemesStub?(graphTraverser) ?? []
|
||||
}
|
||||
|
||||
public var buildArgumentsStub: ((Project, Target, String?, Bool) -> [XcodeBuildArgument])?
|
||||
|
@ -45,31 +45,31 @@ public final class MockBuildGraphInspector: BuildGraphInspecting {
|
|||
}
|
||||
}
|
||||
|
||||
public var testableTargetStub: ((Scheme, Graph) -> TargetNode?)?
|
||||
public func testableTarget(scheme: Scheme, graph: Graph) -> TargetNode? {
|
||||
public var testableTargetStub: ((Scheme, GraphTraversing) -> ValueGraphTarget?)?
|
||||
public func testableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> ValueGraphTarget? {
|
||||
if let testableTargetStub = testableTargetStub {
|
||||
return testableTargetStub(scheme, graph)
|
||||
return testableTargetStub(scheme, graphTraverser)
|
||||
} else {
|
||||
return TargetNode.test()
|
||||
return ValueGraphTarget.test()
|
||||
}
|
||||
}
|
||||
|
||||
public var testableSchemesStub: ((Graph) -> [Scheme])?
|
||||
public func testableSchemes(graph: Graph) -> [Scheme] {
|
||||
public var testableSchemesStub: ((GraphTraversing) -> [Scheme])?
|
||||
public func testableSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
if let testableSchemesStub = testableSchemesStub {
|
||||
return testableSchemesStub(graph)
|
||||
return testableSchemesStub(graphTraverser)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public var testSchemesStub: ((Graph) -> [Scheme])?
|
||||
public func testSchemes(graph: Graph) -> [Scheme] {
|
||||
testSchemesStub?(graph) ?? []
|
||||
public var testSchemesStub: ((GraphTraversing) -> [Scheme])?
|
||||
public func testSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
testSchemesStub?(graphTraverser) ?? []
|
||||
}
|
||||
|
||||
public var projectSchemesStub: ((Graph) -> [Scheme])?
|
||||
public func projectSchemes(graph: Graph) -> [Scheme] {
|
||||
projectSchemesStub?(graph) ?? []
|
||||
public var projectSchemesStub: ((GraphTraversing) -> [Scheme])?
|
||||
public func projectSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
|
||||
projectSchemesStub?(graphTraverser) ?? []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
import Foundation
|
||||
import TSCBasic
|
||||
|
||||
/// Target Node Graph Mapper
|
||||
///
|
||||
/// A `GraphMapper` that allows transforming `TargetNode`s without modifying
|
||||
/// the original nodes. Additionally, any projects associated with orphaned nodes after
|
||||
/// any transformations will no longer be part of the resulting graph.
|
||||
///
|
||||
/// - Note: When mapping, the `transform` block receives a copy of the original `TargetNode`
|
||||
public class TargetNodeGraphMapper: GraphMapping {
|
||||
public let mapTargetNode: (TargetNode) -> TargetNode
|
||||
public init(transform: @escaping (TargetNode) -> TargetNode) {
|
||||
mapTargetNode = transform
|
||||
}
|
||||
|
||||
// MARK: - GraphMapping
|
||||
|
||||
public func map(graph: Graph) -> (Graph, [SideEffectDescriptor]) {
|
||||
var mappedCache = [GraphNodeMapKey: GraphNode]()
|
||||
let cache = GraphLoaderCache()
|
||||
|
||||
let updatedNodes = graph.entryNodes.map {
|
||||
map(node: $0, mappedCache: &mappedCache, cache: cache)
|
||||
}
|
||||
|
||||
return (
|
||||
Graph(
|
||||
name: graph.name,
|
||||
entryPath: graph.entryPath,
|
||||
cache: cache,
|
||||
entryNodes: updatedNodes,
|
||||
workspace: graph.workspace
|
||||
),
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func map(node: GraphNode,
|
||||
mappedCache: inout [GraphNodeMapKey: GraphNode],
|
||||
cache: GraphLoaderCache) -> GraphNode
|
||||
{
|
||||
if let cached = mappedCache[node.mapperCacheKey] {
|
||||
return cached
|
||||
}
|
||||
|
||||
switch node {
|
||||
case let packageProductNode as PackageProductNode:
|
||||
cache.add(package: packageProductNode)
|
||||
return packageProductNode
|
||||
case let precompiledNode as PrecompiledNode:
|
||||
cache.add(precompiledNode: precompiledNode)
|
||||
return precompiledNode
|
||||
case let cocoapodsNode as CocoaPodsNode:
|
||||
cache.add(cocoapods: cocoapodsNode)
|
||||
return cocoapodsNode
|
||||
case let targetNode as TargetNode:
|
||||
let updated = map(targetNode: targetNode,
|
||||
mappedCache: &mappedCache,
|
||||
cache: cache)
|
||||
cache.add(targetNode: updated)
|
||||
cache.add(project: updated.project)
|
||||
return updated
|
||||
default:
|
||||
fatalError("Unhandled graph node type")
|
||||
}
|
||||
}
|
||||
|
||||
private func map(targetNode: TargetNode,
|
||||
mappedCache: inout [GraphNodeMapKey: GraphNode],
|
||||
cache: GraphLoaderCache) -> TargetNode
|
||||
{
|
||||
var updated = TargetNode(project: targetNode.project,
|
||||
target: targetNode.target,
|
||||
dependencies: targetNode.dependencies)
|
||||
updated = mapTargetNode(updated)
|
||||
mappedCache[updated.mapperCacheKey] = updated
|
||||
|
||||
updated.dependencies = updated.dependencies.map {
|
||||
map(node: $0, mappedCache: &mappedCache, cache: cache)
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
}
|
||||
|
||||
private struct GraphNodeMapKey: Hashable {
|
||||
var name: String
|
||||
var path: AbsolutePath
|
||||
}
|
||||
|
||||
private extension GraphNode {
|
||||
var mapperCacheKey: GraphNodeMapKey {
|
||||
.init(name: name, path: path)
|
||||
}
|
||||
}
|
|
@ -30,6 +30,9 @@ public protocol GraphTraversing {
|
|||
/// Returns all the apps from the graph.
|
||||
func apps() -> Set<ValueGraphTarget>
|
||||
|
||||
/// - Returns: All the schemes of the graph
|
||||
func schemes() -> [Scheme]
|
||||
|
||||
/// Returns the targets from the project that lives in the directory from which the graph has been loaded.
|
||||
func rootTargets() -> Set<ValueGraphTarget>
|
||||
|
||||
|
@ -150,6 +153,12 @@ public protocol GraphTraversing {
|
|||
/// the groups.
|
||||
/// - Parameter path: Path to the directory where the project is defined.
|
||||
func allProjectDependencies(path: AbsolutePath) throws -> Set<GraphDependencyReference>
|
||||
|
||||
/// Returns true if the given target depends on XCTest.
|
||||
/// - Parameters:
|
||||
/// - path: Path to the project tha defines the target.
|
||||
/// - name: Target name.
|
||||
func dependsOnXCTest(path: AbsolutePath, name: String) -> Bool
|
||||
}
|
||||
|
||||
public extension GraphTraversing {
|
||||
|
|
|
@ -49,6 +49,10 @@ public class ValueGraphTraverser: GraphTraversing {
|
|||
})
|
||||
}
|
||||
|
||||
public func schemes() -> [Scheme] {
|
||||
projects.values.flatMap(\.schemes) + graph.workspace.schemes
|
||||
}
|
||||
|
||||
public func cocoapodsPaths() -> Set<AbsolutePath> {
|
||||
dependencies.reduce(into: Set<AbsolutePath>()) { acc, next in
|
||||
let fromDependency = next.key
|
||||
|
@ -396,6 +400,11 @@ public class ValueGraphTraverser: GraphTraversing {
|
|||
return references
|
||||
}
|
||||
|
||||
public func dependsOnXCTest(path: AbsolutePath, name: String) -> Bool {
|
||||
directTargetDependencies(path: path, name: name)
|
||||
.first(where: { $0.target.name == "XCTest" || $0.target.product.testsBundle }) != nil
|
||||
}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
/// The method collects the dependencies that are selected by the provided test closure.
|
||||
|
|
|
@ -403,4 +403,23 @@ final class MockGraphTraverser: GraphTraversing {
|
|||
}
|
||||
return stubbedAllProjectDependenciesResult
|
||||
}
|
||||
|
||||
var invokedDependsOnXCTest = false
|
||||
var invokedDependsOnXCTestCount = 0
|
||||
var invokedDependsOnXCTestParameters: (path: AbsolutePath, name: String)?
|
||||
var invokedDependsOnXCTestParametersList = [(path: AbsolutePath, name: String)]()
|
||||
var stubbedDependsOnXCTestResult: Bool! = false
|
||||
|
||||
func dependsOnXCTest(path: AbsolutePath, name: String) -> Bool {
|
||||
invokedDependsOnXCTest = true
|
||||
invokedDependsOnXCTestCount += 1
|
||||
invokedDependsOnXCTestParameters = (path, name)
|
||||
invokedDependsOnXCTestParametersList.append((path, name))
|
||||
return stubbedDependsOnXCTestResult
|
||||
}
|
||||
|
||||
var schemesStub: (() -> [Scheme])?
|
||||
func schemes() -> [Scheme] {
|
||||
schemesStub?() ?? []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,14 +58,15 @@ final class BuildService {
|
|||
configuration: String?,
|
||||
path: AbsolutePath
|
||||
) throws {
|
||||
let graph: Graph
|
||||
let graph: ValueGraph
|
||||
if try (generate || buildGraphInspector.workspacePath(directory: path) == nil) {
|
||||
graph = try generator.generateWithGraph(path: path, projectOnly: false).1
|
||||
graph = ValueGraph(graph: try generator.generateWithGraph(path: path, projectOnly: false).1)
|
||||
} else {
|
||||
graph = try generator.load(path: path)
|
||||
graph = ValueGraph(graph: try generator.load(path: path))
|
||||
}
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
let buildableSchemes = buildGraphInspector.buildableSchemes(graph: graph)
|
||||
let buildableSchemes = buildGraphInspector.buildableSchemes(graphTraverser: graphTraverser)
|
||||
|
||||
logger.log(level: .debug, "Found the following buildable schemes: \(buildableSchemes.map(\.name).joined(separator: ", "))")
|
||||
|
||||
|
@ -73,13 +74,13 @@ final class BuildService {
|
|||
guard let scheme = buildableSchemes.first(where: { $0.name == schemeName }) else {
|
||||
throw BuildServiceError.schemeNotFound(scheme: schemeName, existing: buildableSchemes.map(\.name))
|
||||
}
|
||||
try buildScheme(scheme: scheme, graph: graph, path: path, clean: clean, configuration: configuration)
|
||||
try buildScheme(scheme: scheme, graphTraverser: graphTraverser, path: path, clean: clean, configuration: configuration)
|
||||
} else {
|
||||
var cleaned: Bool = false
|
||||
// Run only buildable entry schemes when specific schemes has not been passed
|
||||
let buildableEntrySchemes = buildGraphInspector.buildableEntrySchemes(graph: graph)
|
||||
let buildableEntrySchemes = buildGraphInspector.buildableEntrySchemes(graphTraverser: graphTraverser)
|
||||
try buildableEntrySchemes.forEach {
|
||||
try buildScheme(scheme: $0, graph: graph, path: path, clean: !cleaned && clean, configuration: configuration)
|
||||
try buildScheme(scheme: $0, graphTraverser: graphTraverser, path: path, clean: !cleaned && clean, configuration: configuration)
|
||||
cleaned = true
|
||||
}
|
||||
}
|
||||
|
@ -89,9 +90,9 @@ final class BuildService {
|
|||
|
||||
// MARK: - private
|
||||
|
||||
private func buildScheme(scheme: Scheme, graph: Graph, path: AbsolutePath, clean: Bool, configuration: String?) throws {
|
||||
private func buildScheme(scheme: Scheme, graphTraverser: GraphTraversing, path: AbsolutePath, clean: Bool, configuration: String?) throws {
|
||||
logger.log(level: .notice, "Building scheme \(scheme.name)", metadata: .section)
|
||||
guard let (project, target) = buildGraphInspector.buildableTarget(scheme: scheme, graph: graph) else {
|
||||
guard let (project, target) = buildGraphInspector.buildableTarget(scheme: scheme, graphTraverser: graphTraverser) else {
|
||||
throw BuildServiceError.schemeWithoutBuildableTargets(scheme: scheme.name)
|
||||
}
|
||||
let workspacePath = try buildGraphInspector.workspacePath(directory: path)!
|
||||
|
|
|
@ -5,6 +5,7 @@ import TSCBasic
|
|||
import TuistCache
|
||||
import TuistCore
|
||||
import TuistDoc
|
||||
import TuistGraph
|
||||
import TuistSupport
|
||||
|
||||
// MARK: - DocServiceError
|
||||
|
@ -76,11 +77,11 @@ final class DocService {
|
|||
}
|
||||
|
||||
func run(project path: AbsolutePath, target targetName: String) throws {
|
||||
let graph = try generator.load(path: path)
|
||||
let graph = ValueGraph(graph: try generator.load(path: path))
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
let targets = graph.targets
|
||||
.flatMap(\.value)
|
||||
.filter { !$0.dependsOnXCTest }
|
||||
let targets = graphTraverser.allTargets()
|
||||
.filter { !graphTraverser.dependsOnXCTest(path: $0.path, name: $0.target.name) }
|
||||
.map(\.target)
|
||||
|
||||
guard let target = targets.first(where: { $0.name == targetName }) else {
|
||||
|
|
|
@ -83,13 +83,16 @@ final class TestService {
|
|||
testsCacheDirectory: testsCacheTemporaryDirectory.path
|
||||
)
|
||||
logger.notice("Generating project for testing", metadata: .section)
|
||||
let graph: Graph = try generator.generateWithGraph(
|
||||
path: path,
|
||||
projectOnly: false
|
||||
).1
|
||||
let graph = ValueGraph(
|
||||
graph: try generator.generateWithGraph(
|
||||
path: path,
|
||||
projectOnly: false
|
||||
).1
|
||||
)
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
let version = osVersion?.version()
|
||||
|
||||
let testableSchemes = buildGraphInspector.testableSchemes(graph: graph) + buildGraphInspector.projectSchemes(graph: graph)
|
||||
let testableSchemes = buildGraphInspector.testableSchemes(graphTraverser: graphTraverser) + buildGraphInspector.projectSchemes(graphTraverser: graphTraverser)
|
||||
logger.log(
|
||||
level: .debug,
|
||||
"Found the following testable schemes: \(Set(testableSchemes.map(\.name)).joined(separator: ", "))"
|
||||
|
@ -115,8 +118,7 @@ final class TestService {
|
|||
try testSchemes.forEach { testScheme in
|
||||
try self.testScheme(
|
||||
scheme: testScheme,
|
||||
graph: graph,
|
||||
path: path,
|
||||
graphTraverser: graphTraverser,
|
||||
clean: clean,
|
||||
configuration: configuration,
|
||||
version: version,
|
||||
|
@ -124,7 +126,7 @@ final class TestService {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
let testSchemes: [Scheme] = buildGraphInspector.projectSchemes(graph: graph)
|
||||
let testSchemes: [Scheme] = buildGraphInspector.projectSchemes(graphTraverser: graphTraverser)
|
||||
.filter {
|
||||
$0.testAction.map { !$0.targets.isEmpty } ?? false
|
||||
}
|
||||
|
@ -137,8 +139,7 @@ final class TestService {
|
|||
try testSchemes.forEach {
|
||||
try testScheme(
|
||||
scheme: $0,
|
||||
graph: graph,
|
||||
path: path,
|
||||
graphTraverser: graphTraverser,
|
||||
clean: clean,
|
||||
configuration: configuration,
|
||||
version: version,
|
||||
|
@ -172,28 +173,27 @@ final class TestService {
|
|||
|
||||
private func testScheme(
|
||||
scheme: Scheme,
|
||||
graph: Graph,
|
||||
path _: AbsolutePath,
|
||||
graphTraverser: GraphTraversing,
|
||||
clean: Bool,
|
||||
configuration: String?,
|
||||
version: Version?,
|
||||
deviceName: String?
|
||||
) throws {
|
||||
logger.log(level: .notice, "Testing scheme \(scheme.name)", metadata: .section)
|
||||
guard let buildableTarget = buildGraphInspector.testableTarget(scheme: scheme, graph: graph) else {
|
||||
guard let buildableTarget = buildGraphInspector.testableTarget(scheme: scheme, graphTraverser: graphTraverser) else {
|
||||
throw TestServiceError.schemeWithoutTestableTargets(scheme: scheme.name)
|
||||
}
|
||||
|
||||
let destination = try findDestination(
|
||||
target: buildableTarget.target,
|
||||
scheme: scheme,
|
||||
graph: graph,
|
||||
graphTraverser: graphTraverser,
|
||||
version: version,
|
||||
deviceName: deviceName
|
||||
)
|
||||
|
||||
_ = try xcodebuildController.test(
|
||||
.workspace(graph.workspace.xcWorkspacePath),
|
||||
.workspace(graphTraverser.workspace.xcWorkspacePath),
|
||||
scheme: scheme.name,
|
||||
clean: clean,
|
||||
destination: destination,
|
||||
|
@ -213,7 +213,7 @@ final class TestService {
|
|||
private func findDestination(
|
||||
target: Target,
|
||||
scheme: Scheme,
|
||||
graph: Graph,
|
||||
graphTraverser: GraphTraversing,
|
||||
version: Version?,
|
||||
deviceName: String?
|
||||
) throws -> XcodeBuildDestination {
|
||||
|
@ -224,12 +224,13 @@ final class TestService {
|
|||
minVersion = deploymentTarget.version.version()
|
||||
} else {
|
||||
minVersion = scheme.targetDependencies()
|
||||
.compactMap { graph.findTargetNode(path: $0.projectPath, name: $0.name) }
|
||||
.flatMap {
|
||||
$0.targetDependencies
|
||||
.compactMap { $0.target.deploymentTarget?.version }
|
||||
graphTraverser
|
||||
.directTargetDependencies(path: $0.projectPath, name: $0.name)
|
||||
.map(\.target)
|
||||
.map(\.deploymentTarget)
|
||||
.compactMap { $0?.version.version() }
|
||||
}
|
||||
.compactMap { $0.version() }
|
||||
.sorted()
|
||||
.first
|
||||
}
|
||||
|
|
|
@ -138,12 +138,14 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
let scheme = Scheme.test(buildAction: .test(targets: [.init(projectPath: projectPath, name: "Core")]))
|
||||
let target = Target.test(name: "Core")
|
||||
let project = Project.test(path: projectPath)
|
||||
let targetNode = TargetNode.test(project: project,
|
||||
target: target)
|
||||
let graph = Graph.test(targets: [projectPath: [targetNode]])
|
||||
let graph = ValueGraph.test(
|
||||
projects: [projectPath: project],
|
||||
targets: [projectPath: [target.name: target]]
|
||||
)
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
// When
|
||||
let got = subject.buildableTarget(scheme: scheme, graph: graph)
|
||||
let got = subject.buildableTarget(scheme: scheme, graphTraverser: graphTraverser)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got?.0, project)
|
||||
|
@ -169,16 +171,17 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
)
|
||||
)
|
||||
let workspace = Workspace.test(schemes: [workspaceScheme])
|
||||
let graph = Graph.test(
|
||||
let graph = ValueGraph.test(
|
||||
workspace: workspace,
|
||||
projects: [
|
||||
coreProject,
|
||||
kitProject,
|
||||
coreProject.path: coreProject,
|
||||
kitProject.path: kitProject,
|
||||
]
|
||||
)
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
// When
|
||||
let got = subject.buildableSchemes(graph: graph)
|
||||
let got = subject.buildableSchemes(graphTraverser: graphTraverser)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(
|
||||
|
@ -225,47 +228,57 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
path: coreProjectPath,
|
||||
schemes: [coreScheme, coreTestsScheme]
|
||||
)
|
||||
let coreTargetNode = TargetNode.test(
|
||||
project: coreProject,
|
||||
target: coreTarget
|
||||
let coreValueGraphTarget = ValueGraphTarget.test(
|
||||
target: coreTarget,
|
||||
project: coreProject
|
||||
)
|
||||
let coreTestsTarget = Target.test(
|
||||
name: "CoreTests",
|
||||
product: .unitTests,
|
||||
dependencies: [.target(name: "Core")]
|
||||
)
|
||||
let coreTestsTargetNode = TargetNode.test(
|
||||
project: coreProject,
|
||||
target: coreTestsTarget
|
||||
let coreTestsValueGraphTarget = ValueGraphTarget.test(
|
||||
target: coreTestsTarget,
|
||||
project: coreProject
|
||||
)
|
||||
let kitTarget = Target.test(name: "Kit", dependencies: [.target(name: "Core")])
|
||||
let kitProject = Project.test(
|
||||
path: projectPath,
|
||||
schemes: [kitScheme, kitTestsScheme]
|
||||
)
|
||||
let kitTargetNode = TargetNode.test(
|
||||
project: kitProject,
|
||||
target: kitTarget
|
||||
let kitValueGraphTarget = ValueGraphTarget.test(
|
||||
target: kitTarget,
|
||||
project: kitProject
|
||||
)
|
||||
let kitTestsTarget = Target.test(
|
||||
name: "KitTests",
|
||||
product: .unitTests,
|
||||
dependencies: [.target(name: "Kit")]
|
||||
)
|
||||
let kitTestsTargetNode = TargetNode.test(
|
||||
project: kitProject,
|
||||
target: kitTestsTarget
|
||||
let kitTestsValueGraphTarget = ValueGraphTarget.test(
|
||||
target: kitTestsTarget,
|
||||
project: kitProject
|
||||
)
|
||||
let graph = Graph.test(
|
||||
entryNodes: [kitTargetNode],
|
||||
let graph = ValueGraph.test(
|
||||
projects: [
|
||||
kitProject.path: kitProject,
|
||||
coreProject.path: coreProject,
|
||||
],
|
||||
targets: [
|
||||
projectPath: [kitTargetNode, kitTestsTargetNode],
|
||||
coreProjectPath: [coreTargetNode, coreTestsTargetNode],
|
||||
projectPath: [
|
||||
kitValueGraphTarget.target.name: kitValueGraphTarget.target,
|
||||
kitTestsValueGraphTarget.target.name: kitTestsValueGraphTarget.target,
|
||||
],
|
||||
coreProjectPath: [
|
||||
coreValueGraphTarget.target.name: coreValueGraphTarget.target,
|
||||
coreTestsValueGraphTarget.target.name: coreTestsValueGraphTarget.target,
|
||||
],
|
||||
]
|
||||
)
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
// When
|
||||
let got = subject.testSchemes(graph: graph)
|
||||
let got = subject.testSchemes(graphTraverser: graphTraverser)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(
|
||||
|
@ -299,19 +312,24 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
path: coreProjectPath,
|
||||
schemes: [coreScheme, coreTestsScheme]
|
||||
)
|
||||
let coreTargetNode = TargetNode.test(
|
||||
project: coreProject,
|
||||
target: coreTarget
|
||||
let coreValueGraphTarget = ValueGraphTarget.test(
|
||||
target: coreTarget,
|
||||
project: coreProject
|
||||
)
|
||||
let graph = Graph.test(
|
||||
entryNodes: [coreTargetNode],
|
||||
let graph = ValueGraph.test(
|
||||
projects: [
|
||||
coreProject,
|
||||
coreProject.path: coreProject,
|
||||
],
|
||||
targets: [
|
||||
coreProject.path: [
|
||||
coreValueGraphTarget.target.name: coreValueGraphTarget.target,
|
||||
],
|
||||
]
|
||||
)
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
// When
|
||||
let got = subject.testableSchemes(graph: graph)
|
||||
let got = subject.testableSchemes(graphTraverser: graphTraverser)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(
|
||||
|
@ -331,21 +349,24 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
let schemeA = Scheme.test(buildAction: .test(targets: [.init(projectPath: projectAPath, name: "A")]))
|
||||
let projectA = Project.test(path: projectAPath, schemes: [schemeA])
|
||||
let targetA = Target.test(name: "A")
|
||||
let targetANode = TargetNode.test(project: projectA,
|
||||
target: targetA)
|
||||
|
||||
let projectBPath = path.appending(component: "ProjectB.xcodeproj")
|
||||
let schemeB = Scheme.test(buildAction: .test(targets: [.init(projectPath: projectBPath, name: "B")]))
|
||||
let projectB = Project.test(path: projectBPath, schemes: [schemeB])
|
||||
let targetB = Target.test(name: "B")
|
||||
let targetBNode = TargetNode.test(project: projectB,
|
||||
target: targetB)
|
||||
|
||||
let graph = Graph.test(entryNodes: [targetANode],
|
||||
targets: [projectAPath: [targetANode], projectBPath: [targetBNode]])
|
||||
let graph = ValueGraph.test(
|
||||
workspace: Workspace.test(projects: [projectA.path]),
|
||||
projects: [
|
||||
projectA.path: projectA,
|
||||
projectB.path: projectB,
|
||||
],
|
||||
targets: [projectAPath: [targetA.name: targetA], projectBPath: [targetB.name: targetB]]
|
||||
)
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
// When
|
||||
let got = subject.buildableEntrySchemes(graph: graph)
|
||||
let got = subject.buildableEntrySchemes(graphTraverser: graphTraverser)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, [schemeA])
|
||||
|
@ -396,7 +417,7 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
|
||||
func test_projectSchemes_when_multiple_platforms() {
|
||||
// Given
|
||||
let graph: Graph = .test(
|
||||
let graph: ValueGraph = .test(
|
||||
workspace: .test(
|
||||
name: "WorkspaceName",
|
||||
schemes: [
|
||||
|
@ -406,9 +427,10 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
]
|
||||
)
|
||||
)
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
// When
|
||||
let got = subject.projectSchemes(graph: graph)
|
||||
let got = subject.projectSchemes(graphTraverser: graphTraverser)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(
|
||||
|
@ -422,7 +444,7 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
|
||||
func test_projectSchemes_when_single_platform() {
|
||||
// Given
|
||||
let graph: Graph = .test(
|
||||
let graph: ValueGraph = .test(
|
||||
workspace: .test(
|
||||
name: "WorkspaceName",
|
||||
schemes: [
|
||||
|
@ -431,9 +453,10 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
|
|||
]
|
||||
)
|
||||
)
|
||||
let graphTraverser = ValueGraphTraverser(graph: graph)
|
||||
|
||||
// When
|
||||
let got = subject.projectSchemes(graph: graph)
|
||||
let got = subject.projectSchemes(graphTraverser: graphTraverser)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
import TuistCore
|
||||
import TuistGraph
|
||||
import XCTest
|
||||
@testable import TuistCoreTesting
|
||||
|
||||
class TargetNodeGraphMapperTests: XCTestCase {
|
||||
func test_map() {
|
||||
// Given
|
||||
let subject = TargetNodeGraphMapper { targetNode in
|
||||
TargetNode(project: targetNode.project, target: targetNode.target, dependencies: [])
|
||||
}
|
||||
|
||||
let targetA = Target.test(name: "TargetA")
|
||||
let targetB = Target.test(name: "TargetB")
|
||||
let targetC = Target.test(name: "TargetC")
|
||||
let project = Project.test(targets: [targetA, targetB, targetC])
|
||||
let graph = Graph.create(project: project,
|
||||
dependencies: [
|
||||
(target: targetA, dependencies: [targetB]),
|
||||
(target: targetB, dependencies: [targetC]),
|
||||
(target: targetC, dependencies: []),
|
||||
])
|
||||
|
||||
// When
|
||||
let (results, _) = subject.map(graph: graph)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(results.targets(at: project.path).count, 3)
|
||||
XCTAssertEqual(results.target(path: project.path, name: "TargetA")?.dependencies.map(\.name), [])
|
||||
XCTAssertEqual(results.target(path: project.path, name: "TargetB")?.dependencies.map(\.name), [])
|
||||
XCTAssertEqual(results.target(path: project.path, name: "TargetC")?.dependencies.map(\.name), [])
|
||||
}
|
||||
|
||||
func test_map_doesNotUpdateOriginal() {
|
||||
// Given
|
||||
let subject = TargetNodeGraphMapper { targetNode in
|
||||
TargetNode(project: targetNode.project, target: targetNode.target, dependencies: [])
|
||||
}
|
||||
|
||||
let targetA = Target.test(name: "TargetA")
|
||||
let targetB = Target.test(name: "TargetB")
|
||||
let targetC = Target.test(name: "TargetC")
|
||||
let project = Project.test(targets: [targetA, targetB, targetC])
|
||||
let graph = Graph.create(project: project,
|
||||
dependencies: [
|
||||
(target: targetA, dependencies: [targetB]),
|
||||
(target: targetB, dependencies: [targetC]),
|
||||
(target: targetC, dependencies: []),
|
||||
])
|
||||
|
||||
// When
|
||||
_ = subject.map(graph: graph)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(graph.targets(at: project.path).count, 3)
|
||||
XCTAssertEqual(graph.target(path: project.path, name: "TargetA")?.dependencies.map(\.name), ["TargetB"])
|
||||
XCTAssertEqual(graph.target(path: project.path, name: "TargetB")?.dependencies.map(\.name), ["TargetC"])
|
||||
XCTAssertEqual(graph.target(path: project.path, name: "TargetC")?.dependencies.map(\.name), [])
|
||||
}
|
||||
|
||||
func test_map_removesOrphanedNodes() {
|
||||
// Given
|
||||
let subject = TargetNodeGraphMapper { targetNode in
|
||||
TargetNode(project: targetNode.project, target: targetNode.target, dependencies: [])
|
||||
}
|
||||
|
||||
let targetA = Target.test(name: "TargetA")
|
||||
let targetB = Target.test(name: "TargetB")
|
||||
let targetC = Target.test(name: "TargetC")
|
||||
let projectA = Project.test(path: "/test/ProjctA", targets: [targetA])
|
||||
let projectB = Project.test(path: "/test/ProjctB", targets: [targetB, targetC])
|
||||
let graph = Graph.create(projects: [projectA, projectB],
|
||||
entryNodes: [targetA],
|
||||
dependencies: [
|
||||
(project: projectA, target: targetA, dependencies: [targetB]),
|
||||
(project: projectB, target: targetB, dependencies: [targetC]),
|
||||
(project: projectB, target: targetC, dependencies: []),
|
||||
])
|
||||
|
||||
// When
|
||||
let (results, _) = subject.map(graph: graph)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(results.targets.flatMap(\.value).count, 1)
|
||||
XCTAssertEqual(results.projects.count, 1)
|
||||
}
|
||||
|
||||
func test_map_postMapOrphanedNodesdoNotUpdateOriginal() {
|
||||
// Given
|
||||
let subject = TargetNodeGraphMapper { targetNode in
|
||||
TargetNode(project: targetNode.project, target: targetNode.target, dependencies: [])
|
||||
}
|
||||
|
||||
let targetA = Target.test(name: "TargetA")
|
||||
let targetB = Target.test(name: "TargetB")
|
||||
let targetC = Target.test(name: "TargetC")
|
||||
let projectA = Project.test(path: "/test/ProjctA", targets: [targetA])
|
||||
let projectB = Project.test(path: "/test/ProjctB", targets: [targetB, targetC])
|
||||
let graph = Graph.create(projects: [projectA, projectB],
|
||||
entryNodes: [targetA],
|
||||
dependencies: [
|
||||
(project: projectA, target: targetA, dependencies: [targetB]),
|
||||
(project: projectB, target: targetB, dependencies: [targetC]),
|
||||
(project: projectB, target: targetC, dependencies: []),
|
||||
])
|
||||
|
||||
// When
|
||||
_ = subject.map(graph: graph)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(graph.targets.flatMap(\.value).count, 3)
|
||||
XCTAssertEqual(graph.projects.count, 2)
|
||||
}
|
||||
}
|
|
@ -114,10 +114,15 @@ final class TuistDocServiceTests: TuistUnitTestCase {
|
|||
}
|
||||
|
||||
private func mockGraph(targetName: String, atPath path: AbsolutePath) {
|
||||
let project = Project.test()
|
||||
let project = Project.test(
|
||||
path: path
|
||||
)
|
||||
let target = Target.test(name: targetName)
|
||||
let targetNode = TargetNode(project: project, target: target, dependencies: [])
|
||||
let graph = Graph.test(targets: [path: [targetNode]])
|
||||
let graph = Graph.test(
|
||||
projects: [project],
|
||||
targets: [path: [targetNode]]
|
||||
)
|
||||
|
||||
generator.loadStub = { _ in
|
||||
graph
|
||||
|
|
|
@ -85,7 +85,7 @@ final class TestServiceTests: TuistUnitTestCase {
|
|||
]
|
||||
}
|
||||
buildGraphInspector.testableTargetStub = { scheme, _ in
|
||||
TargetNode.test(
|
||||
ValueGraphTarget.test(
|
||||
target: Target.test(
|
||||
name: scheme.name
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue