Define all graph methods in the traverser protocol and provide an implementation for the value graph traverser

This commit is contained in:
Pedro Piñera 2020-12-04 10:28:51 +01:00
parent bb5c419bae
commit 47bbc00f52
24 changed files with 3003 additions and 1440 deletions

View File

@ -94,6 +94,10 @@ class CacheGraphMutator: CacheGraphMutating {
return
}
// Transitive bundles
// get all the transitive bundles
// declare them as direct dependencies.
// If the target cannot be replaced with its associated .(xc)framework we return
guard !sources.contains(targetDependency.target.name), let precompiledFrameworkPath = precompiledFrameworkPath(target: targetDependency,
precompiledFrameworks: precompiledFrameworks,

View File

@ -353,7 +353,7 @@ public class Graph: Encodable, Equatable {
/// - Parameters:
/// - path: Path to the directory where the project that defines the target is located.
/// - name: Name of the target.
public func embeddableFrameworks(path: AbsolutePath, name: String) throws -> [GraphDependencyReference] {
public func embeddableFrameworks(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
guard let targetNode = findTargetNode(path: path, name: name),
canEmbedProducts(targetNode: targetNode)
else {
@ -382,7 +382,7 @@ public class Graph: Encodable, Equatable {
// Exclude any products embed in unit test host apps
if targetNode.target.product == .unitTests {
if let hostApp = hostApplication(for: targetNode) {
references.subtract(try embeddableFrameworks(path: hostApp.path, name: hostApp.name))
references.subtract(embeddableFrameworks(path: hostApp.path, name: hostApp.name))
} else {
references = []
}
@ -417,8 +417,8 @@ public class Graph: Encodable, Equatable {
try self.linkableDependencies(path: project.path, name: $0.name)
}
let embeddableDependencies = try project.targets.flatMap {
try self.embeddableFrameworks(path: project.path, name: $0.name)
let embeddableDependencies = project.targets.flatMap {
self.embeddableFrameworks(path: project.path, name: $0.name)
}
let copyProductDependencies = project.targets.flatMap {
@ -446,7 +446,7 @@ public class Graph: Encodable, Equatable {
.filter { validProducts.contains($0.target.product) }
}
public func appClipsDependency(path: AbsolutePath, name: String) -> TargetNode? {
public func appClipDependencies(path: AbsolutePath, name: String) -> TargetNode? {
guard let targetNode = findTargetNode(path: path, name: name) else {
return nil
}

View File

@ -10,7 +10,6 @@ public enum GraphDependencyReference: Equatable, Comparable, Hashable {
)
case library(
path: AbsolutePath,
binaryPath: AbsolutePath,
linking: BinaryLinking,
architectures: [BinaryArchitecture],
product: Product
@ -40,7 +39,6 @@ public enum GraphDependencyReference: Equatable, Comparable, Hashable {
product: frameworkNode.product)
} else if let libraryNode = precompiledNode as? LibraryNode {
self = .library(path: libraryNode.path,
binaryPath: libraryNode.binaryPath,
linking: libraryNode.linking,
architectures: libraryNode.architectures,
product: libraryNode.product)
@ -56,7 +54,7 @@ public enum GraphDependencyReference: Equatable, Comparable, Hashable {
public func hash(into hasher: inout Hasher) {
switch self {
case let .library(path, _, _, _, _):
case let .library(path, _, _, _):
hasher.combine(path)
case let .framework(path, _, _, _, _, _, _, _):
hasher.combine(path)
@ -78,7 +76,7 @@ public enum GraphDependencyReference: Equatable, Comparable, Hashable {
switch self {
case let .framework(path, _, _, _, _, _, _, _):
return path
case let .library(path, _, _, _, _):
case let .library(path, _, _, _):
return path
case let .xcframework(path, _, _, _):
return path
@ -93,7 +91,7 @@ public enum GraphDependencyReference: Equatable, Comparable, Hashable {
return lhsPath < rhsPath
case let (.xcframework(lhsPath, _, _, _), .xcframework(rhsPath, _, _, _)):
return lhsPath < rhsPath
case let (.library(lhsPath, _, _, _, _), .library(rhsPath, _, _, _, _)):
case let (.library(lhsPath, _, _, _), .library(rhsPath, _, _, _)):
return lhsPath < rhsPath
case let (.product(lhsTarget, lhsProductName), .product(rhsTarget, rhsProductName)):
if lhsTarget == rhsTarget {

View File

@ -3,39 +3,92 @@ import TSCBasic
public final class GraphTraverser: GraphTraversing {
private let graph: Graph
public var name: String { graph.name }
public var hasPackages: Bool { !graph.packages.isEmpty }
public var path: AbsolutePath { graph.entryPath }
public var workspace: Workspace { graph.workspace }
public var projects: [AbsolutePath: Project]
public init(graph: Graph) {
self.graph = graph
projects = Dictionary(uniqueKeysWithValues: graph.projects.map { ($0.path, $0) })
}
public func target(path: AbsolutePath, name: String) -> Target? {
graph.target(path: path, name: name).map(\.target)
public func target(path: AbsolutePath, name: String) -> ValueGraphTarget? {
graph.target(path: path, name: name).map {
ValueGraphTarget(path: $0.path, target: $0.target, project: $0.project)
}
}
public func targets(at path: AbsolutePath) -> [Target] {
graph.targets(at: path).map(\.target)
public func targets(at path: AbsolutePath) -> Set<ValueGraphTarget> {
Set(graph.targets(at: path).map {
ValueGraphTarget(path: $0.path, target: $0.target, project: $0.project)
})
}
public func directTargetDependencies(path: AbsolutePath, name: String) -> [Target] {
graph.targetDependencies(path: path, name: name).map(\.target)
public func directTargetDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
Set(graph.targetDependencies(path: path, name: name).map {
ValueGraphTarget(path: $0.path, target: $0.target, project: $0.project)
})
}
public func appExtensionDependencies(path: AbsolutePath, name: String) -> [Target] {
graph.appExtensionDependencies(path: path, name: name).map(\.target)
public func appExtensionDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
Set(graph.appExtensionDependencies(path: path, name: name).map {
ValueGraphTarget(path: $0.path, target: $0.target, project: $0.project)
})
}
public func resourceBundleDependencies(path: AbsolutePath, name: String) -> [Target] {
graph.resourceBundleDependencies(path: path, name: name).map(\.target)
public func resourceBundleDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
Set(graph.resourceBundleDependencies(path: path, name: name).map {
ValueGraphTarget(path: $0.path, target: $0.target, project: $0.project)
})
}
public func testTargetsDependingOn(path: AbsolutePath, name: String) -> [Target] {
graph.testTargetsDependingOn(path: path, name: name).map(\.target)
public func testTargetsDependingOn(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
Set(graph.testTargetsDependingOn(path: path, name: name).map {
ValueGraphTarget(path: $0.path, target: $0.target, project: $0.project)
})
}
public func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
graph.staticDependencies(path: path, name: name)
public func directStaticDependencies(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
Set(graph.staticDependencies(path: path, name: name))
}
public func appClipsDependency(path: AbsolutePath, name: String) -> Target? {
graph.appClipsDependency(path: path, name: name).map(\.target)
public func appClipDependencies(path: AbsolutePath, name: String) -> ValueGraphTarget? {
graph.appClipDependencies(path: path, name: name).map { ValueGraphTarget(path: $0.path, target: $0.target, project: $0.project) }
}
public func embeddableFrameworks(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
Set(graph.embeddableFrameworks(path: path, name: name))
}
public func linkableDependencies(path: AbsolutePath, name: String) throws -> Set<GraphDependencyReference> {
try Set(graph.linkableDependencies(path: path, name: name))
}
public func copyProductDependencies(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
guard let target = graph.target(path: path, name: name) else { return Set() }
return Set(graph.copyProductDependencies(path: path, target: target.target))
}
public func librariesPublicHeadersFolders(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
Set(graph.librariesPublicHeadersFolders(path: path, name: name))
}
public func librariesSearchPaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
Set(graph.librariesSearchPaths(path: path, name: name))
}
public func librariesSwiftIncludePaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
Set(graph.librariesSwiftIncludePaths(path: path, name: name))
}
public func runPathSearchPaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
Set(graph.runPathSearchPaths(path: path, name: name))
}
public func hostTargetFor(path: AbsolutePath, name: String) -> ValueGraphTarget? {
graph.hostTargetNodeFor(path: path, name: name)
.map { ValueGraphTarget(path: $0.path, target: $0.target, project: $0.project) }
}
}

View File

@ -2,49 +2,114 @@ import Foundation
import TSCBasic
public protocol GraphTraversing {
/// Graph name
var name: String { get }
/// Returns true if the project has package dependencies.
var hasPackages: Bool { get }
/// The path to the directory from where the graph has been loaded.
var path: AbsolutePath { get }
/// Returns the graph's workspace.
var workspace: Workspace { get }
/// Returns the graph projects.
var projects: [AbsolutePath: Project] { get }
/// It returns the target with the given name in the project that is defined in the given directory path.
/// - Parameters:
/// - path: Path to the directory that contains the definition of the project with the target is defined.
/// - name: Name of the target.
func target(path: AbsolutePath, name: String) -> Target?
func target(path: AbsolutePath, name: String) -> ValueGraphTarget?
/// It returns the targets of the project defined in the directory at the given path.
/// - Parameter path: Path to the directory that contains the definition of the project.
func targets(at path: AbsolutePath) -> [Target]
func targets(at path: AbsolutePath) -> Set<ValueGraphTarget>
/// Given a project directory and target name, it returns all its direct target dependencies.
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name.
func directTargetDependencies(path: AbsolutePath, name: String) -> [Target]
func directTargetDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget>
/// Given a project directory and a target name, it returns all the dependencies that are extensions.
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name.
func appExtensionDependencies(path: AbsolutePath, name: String) -> [Target]
func appExtensionDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget>
/// Returns the transitive resource bundle dependencies for the given target.
/// - Parameters:
/// - path: Path to the directory where the project that defines the target is located.
/// - name: Name of the target.
func resourceBundleDependencies(path: AbsolutePath, name: String) -> [Target]
func resourceBundleDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget>
/// Returns the list of test targets that depend on the one with the given name at the given path.
/// - Parameters:
/// - path: Path to the directory that contains the project definition.
/// - name: Name of the target whose dependant test targets will be returned.
func testTargetsDependingOn(path: AbsolutePath, name: String) -> [Target]
func testTargetsDependingOn(path: AbsolutePath, name: String) -> Set<ValueGraphTarget>
/// Returns all non-transitive target static dependencies for the given target.
/// - Parameters:
/// - path: Path to the directory where the project that defines the target is located.
/// - name: Name of the target.
func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference]
func directStaticDependencies(path: AbsolutePath, name: String) -> Set<GraphDependencyReference>
/// Given a project directory and a target name, it returns an appClips dependency.
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name.
func appClipsDependency(path: AbsolutePath, name: String) -> Target?
func appClipDependencies(path: AbsolutePath, name: String) -> ValueGraphTarget?
/// Given a project directory and a target name, it returns the list of dependencies that need to be embedded into the target product.
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name.
func embeddableFrameworks(path: AbsolutePath, name: String) -> Set<GraphDependencyReference>
/// Given a project directory and a target name, it returns the list of dependencies that need to be linked from the target.
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name.
func linkableDependencies(path: AbsolutePath, name: String) throws -> Set<GraphDependencyReference>
/// Given a project directory and a target name, it returns a list of dependencies that need to be included in a copy files build phase
///
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name
func copyProductDependencies(path: AbsolutePath, name: String) -> Set<GraphDependencyReference>
/// Given a project directory and a target name, it returns the list of header folders that should be exposed to the target.
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name
func librariesPublicHeadersFolders(path: AbsolutePath, name: String) -> Set<AbsolutePath>
/// Given a project directory and a target name, it returns the list of library folders that should be exposed to the target.
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name.
func librariesSearchPaths(path: AbsolutePath, name: String) -> Set<AbsolutePath>
/// Given a project directory and a target name, it returns the list of foldres with Swift modules that should be expoed to the target.
/// - Parameters:
/// - path: Path to the directory that contains the project.
/// - name: Target name.
func librariesSwiftIncludePaths(path: AbsolutePath, name: String) -> Set<AbsolutePath>
/// Returns all runpath search paths of the given target
/// Currently applied only to test targets with no host application
/// - Parameters:
/// - path; Path to the directory where the project that defines the target
/// - name: Name of the target
func runPathSearchPaths(path: AbsolutePath, name: String) -> Set<AbsolutePath>
/// It returns the host target for the given target.
/// - Parameters:
/// - path; Path to the directory where the project that defines the target
/// - name: Name of the target
func hostTargetFor(path: AbsolutePath, name: String) -> ValueGraphTarget?
}

View File

@ -174,6 +174,10 @@ public enum Product: String, CustomStringConvertible, CaseIterable, Encodable {
[.framework, .staticFramework].contains(self)
}
public var isDynamic: Bool {
[.framework, .dynamicLibrary].contains(self)
}
public var xcodeValue: PBXProductType {
switch self {
case .app:

View File

@ -9,6 +9,9 @@ public struct ValueGraph: Equatable {
/// The path where the graph has been loaded from.
public let path: AbsolutePath
/// Graph's workspace.
public let workspace: Workspace
/// A dictionary where the keys are the paths to the directories where the projects are defined,
/// and the values are the projects defined in the directories.
public let projects: [AbsolutePath: Project]
@ -26,6 +29,7 @@ public struct ValueGraph: Equatable {
public init(name: String,
path: AbsolutePath,
workspace: Workspace,
projects: [AbsolutePath: Project],
packages: [AbsolutePath: [String: Package]],
targets: [AbsolutePath: [String: Target]],
@ -33,6 +37,7 @@ public struct ValueGraph: Equatable {
{
self.name = name
self.path = path
self.workspace = workspace
self.projects = projects
self.packages = packages
self.targets = targets
@ -42,6 +47,7 @@ public struct ValueGraph: Equatable {
public init(graph: Graph) {
name = graph.name
path = graph.entryPath
workspace = graph.workspace
projects = graph.projects.reduce(into: [AbsolutePath: Project]()) { $0[$1.path] = $1 }
packages = graph.packages.reduce(into: [AbsolutePath: [String: Package]]()) { acc, package in
var packages = acc[package.path, default: [:]]
@ -88,10 +94,12 @@ public struct ValueGraph: Equatable {
return .target(name: node.name, path: node.path)
case let node as FrameworkNode:
return .framework(path: node.path,
binaryPath: node.binaryPath,
dsymPath: node.dsymPath,
bcsymbolmapPaths: node.bcsymbolmapPaths,
linking: node.linking,
architectures: node.architectures)
architectures: node.architectures,
isCarthage: node.isCarthage)
case let node as XCFrameworkNode:
return .xcframework(path: node.path,
infoPlist: node.infoPlist,

View File

@ -13,10 +13,12 @@ public enum ValueGraphDependency: Hashable {
/// A dependency that represents a pre-compiled framework.
case framework(
path: AbsolutePath,
binaryPath: AbsolutePath,
dsymPath: AbsolutePath?,
bcsymbolmapPaths: [AbsolutePath],
linking: BinaryLinking,
architectures: [BinaryArchitecture]
architectures: [BinaryArchitecture],
isCarthage: Bool
)
/// A dependency that represents a pre-compiled library.
@ -45,7 +47,7 @@ public enum ValueGraphDependency: Hashable {
case let .xcframework(path, _, _, _):
hasher.combine("xcframework")
hasher.combine(path)
case let .framework(path, _, _, _, _):
case let .framework(path, _, _, _, _, _, _):
hasher.combine("framework")
hasher.combine(path)
case let .library(path, _, _, _, _):
@ -70,4 +72,28 @@ public enum ValueGraphDependency: Hashable {
hasher.combine(path)
}
}
public var isTarget: Bool {
switch self {
case .xcframework: return false
case .framework: return false
case .library: return false
case .packageProduct: return false
case .target: return true
case .sdk: return false
case .cocoapods: return false
}
}
public var isPrecompiled: Bool {
switch self {
case .xcframework: return true
case .framework: return true
case .library: return true
case .packageProduct: return false
case .target: return false
case .sdk: return false
case .cocoapods: return false
}
}
}

View File

@ -0,0 +1,27 @@
import Foundation
import TSCBasic
public struct ValueGraphTarget: Equatable, Hashable, Comparable, CustomDebugStringConvertible, CustomStringConvertible {
/// Path to the directory that contains the project where the target is defined.
public let path: AbsolutePath
/// Target representation.
public let target: Target
/// Project that contains the target.
public let project: Project
public static func < (lhs: ValueGraphTarget, rhs: ValueGraphTarget) -> Bool {
(lhs.path, lhs.target) < (rhs.path, rhs.target)
}
// MARK: - CustomDebugStringConvertible/CustomStringConvertible
public var debugDescription: String {
description
}
public var description: String {
"Target '\(target.name)' at path '\(project.path)'"
}
}

View File

@ -3,41 +3,51 @@ import TSCBasic
import TuistSupport
public class ValueGraphTraverser: GraphTraversing {
public var name: String { graph.name }
public var hasPackages: Bool { !graph.packages.flatMap(\.value).isEmpty }
public var path: AbsolutePath { graph.path }
public var workspace: Workspace { graph.workspace }
public var projects: [AbsolutePath: Project] { graph.projects }
private let graph: ValueGraph
public required init(graph: ValueGraph) {
self.graph = graph
}
public func target(path: AbsolutePath, name: String) -> Target? {
graph.targets[path]?[name]
public func target(path: AbsolutePath, name: String) -> ValueGraphTarget? {
guard let project = graph.projects[path], let target = graph.targets[path]?[name] else { return nil }
return ValueGraphTarget(path: path, target: target, project: project)
}
public func targets(at path: AbsolutePath) -> [Target] {
public func targets(at path: AbsolutePath) -> Set<ValueGraphTarget> {
guard let project = graph.projects[path] else { return Set() }
guard let targets = graph.targets[path] else { return [] }
return Array(targets.values).sorted()
return Set(targets.values.map { ValueGraphTarget(path: path, target: $0, project: project) })
}
public func directTargetDependencies(path: AbsolutePath, name: String) -> [Target] {
public func directTargetDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
guard let dependencies = graph.dependencies[.target(name: name, path: path)] else { return [] }
return dependencies.flatMap { (dependency) -> [Target] in
guard let project = graph.projects[path] else { return Set() }
return Set(dependencies.flatMap { (dependency) -> [ValueGraphTarget] in
guard case let ValueGraphDependency.target(dependencyName, dependencyPath) = dependency else { return [] }
guard let projectDependencies = graph.targets[dependencyPath], let dependencyTarget = projectDependencies[dependencyName] else { return []
}
return [dependencyTarget]
}.sorted()
return [ValueGraphTarget(path: path, target: dependencyTarget, project: project)]
})
}
public func resourceBundleDependencies(path: AbsolutePath, name: String) -> [Target] {
public func resourceBundleDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
guard let target = graph.targets[path]?[name] else { return [] }
guard target.supportsResources else { return [] }
let canHostResources: (ValueGraphDependency) -> Bool = {
self.target(from: $0)?.supportsResources == true
self.target(from: $0)?.target.supportsResources == true
}
let isBundle: (ValueGraphDependency) -> Bool = {
self.target(from: $0)?.product == .bundle
self.target(from: $0)?.target.product == .bundle
}
let bundles = filterDependencies(from: .target(name: name, path: path),
@ -45,39 +55,42 @@ public class ValueGraphTraverser: GraphTraversing {
skip: canHostResources)
let bundleTargets = bundles.compactMap(target(from:))
return bundleTargets.sorted()
return Set(bundleTargets)
}
public func testTargetsDependingOn(path: AbsolutePath, name: String) -> [Target] {
graph.targets[path]?.values
public func testTargetsDependingOn(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
guard let project = graph.projects[path] else { return Set() }
return Set(graph.targets[path]?.values
.filter { $0.product.testsBundle }
.filter { graph.dependencies[.target(name: $0.name, path: path)]?.contains(.target(name: name, path: path)) == true }
.sorted() ?? []
.map { ValueGraphTarget(path: path, target: $0, project: project) } ?? [])
}
public func target(from dependency: ValueGraphDependency) -> Target? {
public func target(from dependency: ValueGraphDependency) -> ValueGraphTarget? {
guard case let ValueGraphDependency.target(name, path) = dependency else {
return nil
}
return graph.targets[path]?[name]
guard let target = graph.targets[path]?[name] else { return nil }
guard let project = graph.projects[path] else { return nil }
return ValueGraphTarget(path: path, target: target, project: project)
}
public func appExtensionDependencies(path: AbsolutePath, name: String) -> [Target] {
public func appExtensionDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
let validProducts: [Product] = [
.appExtension, .stickerPackExtension, .watch2Extension, .messagesExtension,
]
return directTargetDependencies(path: path, name: name)
.filter { validProducts.contains($0.product) }
.sorted()
return Set(directTargetDependencies(path: path, name: name)
.filter { validProducts.contains($0.target.product) })
}
public func appClipsDependency(path: AbsolutePath, name: String) -> Target? {
public func appClipDependencies(path: AbsolutePath, name: String) -> ValueGraphTarget? {
directTargetDependencies(path: path, name: name)
.first { $0.product == .appClip }
.first { $0.target.product == .appClip }
}
public func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
graph.dependencies[.target(name: name, path: path)]?
public func directStaticDependencies(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
Set(graph.dependencies[.target(name: name, path: path)]?
.compactMap { (dependency: ValueGraphDependency) -> (path: AbsolutePath, name: String)? in
guard case let ValueGraphDependency.target(name, path) = dependency else {
return nil
@ -86,8 +99,7 @@ public class ValueGraphTraverser: GraphTraversing {
}
.compactMap { graph.targets[$0.path]?[$0.name] }
.filter { $0.product.isStatic }
.map { .product(target: $0.name, productName: $0.productNameWithExtension) }
.sorted() ?? []
.map { .product(target: $0.name, productName: $0.productNameWithExtension) } ?? [])
}
/// It traverses the depdency graph and returns all the dependencies.
@ -105,15 +117,213 @@ public class ValueGraphTraverser: GraphTraversing {
return references
}
public func embeddableFrameworks(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
guard let target = self.target(path: path, name: name), canEmbedProducts(target: target.target) else { return Set() }
var references: Set<GraphDependencyReference> = Set([])
/// Precompiled frameworks
let precompiledFrameworks = filterDependencies(from: .target(name: name, path: path),
test: isDependencyPrecompiledDynamicAndLinkable,
skip: canDependencyEmbedProducts)
.lazy
.compactMap(dependencyReference)
references.formUnion(precompiledFrameworks)
/// Other targets' frameworks.
let otherTargetFrameworks = filterDependencies(from: .target(name: name, path: path),
test: isDependencyDynamicTarget,
skip: canDependencyEmbedProducts)
.lazy
.compactMap(dependencyReference)
references.formUnion(otherTargetFrameworks)
// Exclude any products embed in unit test host apps
if target.target.product == .unitTests {
if let hostApp = hostApplication(path: path, name: name) {
references.subtract(embeddableFrameworks(path: hostApp.path, name: hostApp.target.name))
} else {
references = Set()
}
}
return references
}
public func linkableDependencies(path: AbsolutePath, name: String) throws -> Set<GraphDependencyReference> {
guard let target = self.target(path: path, name: name) else { return Set() }
var references = Set<GraphDependencyReference>()
// System libraries and frameworks
if target.target.canLinkStaticProducts() {
let transitiveSystemLibraries = transitiveStaticTargets(from: .target(name: name, path: path))
.flatMap { (dependency) -> [GraphDependencyReference] in
let dependencies = self.graph.dependencies[dependency, default: []]
return dependencies.compactMap { dependencyDependency -> GraphDependencyReference? in
guard case let ValueGraphDependency.sdk(_, path, status, source) = dependencyDependency else { return nil }
return .sdk(path: path, status: status, source: source)
}
}
references.formUnion(transitiveSystemLibraries)
}
// AppClip dependencies
if target.target.isAppClip {
let path = try SDKNode.appClip(status: .required).path
references.formUnion([GraphDependencyReference.sdk(path: path,
status: .required,
source: .system)])
}
// Direct system libraries and frameworks
let directSystemLibrariesAndFrameworks = graph.dependencies[.target(name: name, path: path), default: []]
.compactMap { dependency -> GraphDependencyReference? in
guard case let ValueGraphDependency.sdk(_, path, status, source) = dependency else { return nil }
return .sdk(path: path, status: status, source: source)
}
references.formUnion(directSystemLibrariesAndFrameworks)
// Precompiled libraries and frameworks
let precompiledLibrariesAndFrameworks = graph.dependencies[.target(name: name, path: path), default: []]
.lazy
.filter(\.isPrecompiled)
.compactMap(dependencyReference)
references.formUnion(precompiledLibrariesAndFrameworks)
// Static libraries and frameworks / Static libraries' dynamic libraries
if target.target.canLinkStaticProducts() {
var transitiveStaticTargets = self.transitiveStaticTargets(from: .target(name: name, path: path))
// Exclude any static products linked in a host application
if target.target.product == .unitTests {
if let hostApp = hostApplication(path: path, name: name) {
transitiveStaticTargets.subtract(self.transitiveStaticTargets(from: .target(name: hostApp.target.name, path: hostApp.project.path)))
}
}
let transitiveStaticTargetReferences = transitiveStaticTargets.compactMap(dependencyReference)
let staticDependenciesDynamicLibrariesAndFrameworks = transitiveStaticTargets.flatMap { (dependency) -> [GraphDependencyReference] in
self.graph.dependencies[dependency, default: []]
.lazy
.filter(\.isTarget)
.filter(isDependencyDynamicTarget)
.compactMap(dependencyReference)
}
references.formUnion(transitiveStaticTargetReferences + staticDependenciesDynamicLibrariesAndFrameworks)
}
// Link dynamic libraries and frameworks
let dynamicLibrariesAndFrameworks = graph.dependencies[.target(name: name, path: path), default: []]
.filter(or(isDependencyDynamicLibrary, isDependencyFramework))
.compactMap(dependencyReference)
references.formUnion(dynamicLibrariesAndFrameworks)
return references
}
public func copyProductDependencies(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
guard let target = self.target(path: path, name: name) else { return Set() }
var dependencies = Set<GraphDependencyReference>()
if target.target.product.isStatic {
dependencies.formUnion(directStaticDependencies(path: path, name: name))
}
dependencies.formUnion(resourceBundleDependencies(path: path, name: name).map(targetProductReference))
return Set(dependencies)
}
public func librariesPublicHeadersFolders(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
let dependencies = graph.dependencies[.target(name: name, path: path), default: []]
let libraryPublicHeaders = dependencies.compactMap { dependency -> AbsolutePath? in
guard case let ValueGraphDependency.library(_, publicHeaders, _, _, _) = dependency else { return nil }
return publicHeaders
}
return Set(libraryPublicHeaders)
}
public func librariesSearchPaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
let dependencies = graph.dependencies[.target(name: name, path: path), default: []]
let libraryPaths = dependencies.compactMap { dependency -> AbsolutePath? in
guard case let ValueGraphDependency.library(path, _, _, _, _) = dependency else { return nil }
return path
}
return Set(libraryPaths.compactMap { $0.removingLastComponent() })
}
public func librariesSwiftIncludePaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
let dependencies = graph.dependencies[.target(name: name, path: path), default: []]
let librarySwiftModuleMapPaths = dependencies.compactMap { dependency -> AbsolutePath? in
guard case let ValueGraphDependency.library(_, _, _, _, swiftModuleMapPath) = dependency else { return nil }
return swiftModuleMapPath
}
return Set(librarySwiftModuleMapPaths.compactMap { $0.removingLastComponent() })
}
public func runPathSearchPaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
guard let target = target(path: path, name: name),
canEmbedProducts(target: target.target),
target.target.product == .unitTests,
hostApplication(path: path, name: name) == nil
else {
return Set()
}
var references: Set<AbsolutePath> = Set([])
let from = ValueGraphDependency.target(name: name, path: path)
let precompiledFramewoksPaths = filterDependencies(from: from,
test: isDependencyPrecompiledDynamicAndLinkable,
skip: canDependencyEmbedProducts)
.lazy
.compactMap { (dependency: ValueGraphDependency) -> AbsolutePath? in
switch dependency {
case let .xcframework(path, _, _, _): return path
case let .framework(path, _, _, _, _, _, _): return path
case .library: return nil
case .packageProduct: return nil
case .target: return nil
case .sdk: return nil
case .cocoapods: return nil
}
}
.map(\.parentDirectory)
references.formUnion(precompiledFramewoksPaths)
return references
}
public func hostTargetFor(path: AbsolutePath, name: String) -> ValueGraphTarget? {
guard let targets = graph.targets[path] else { return nil }
guard let project = graph.projects[path] else { return nil }
return targets.values.compactMap { (target) -> ValueGraphTarget? in
let dependencies = self.graph.dependencies[.target(name: target.name, path: path), default: Set()]
let dependsOnTarget = dependencies.contains(where: { dependency in
guard case let ValueGraphDependency.target(_name, _path) = dependency else { return false }
return _name == name && _path == path
})
let valueGraphTarget = ValueGraphTarget(path: path, target: target, project: project)
return dependsOnTarget ? valueGraphTarget : nil
}.first
}
// MARK: - Internal
/// The method collects the dependencies that are selected by the provided test closure.
/// The skip closure allows skipping the traversing of a specific dependendency branch.
/// - Parameters:
/// - from: Dependency from which the traverse is done.
/// - test: If the closure returns true, the dependency is included.
/// - skip: If the closure returns false, the traversing logic doesn't traverse the dependencies from that dependency.
public func filterDependencies(from rootDependency: ValueGraphDependency,
test: (ValueGraphDependency) -> Bool = { _ in true },
skip: (ValueGraphDependency) -> Bool = { _ in false }) -> Set<ValueGraphDependency>
func filterDependencies(from rootDependency: ValueGraphDependency,
test: (ValueGraphDependency) -> Bool = { _ in true },
skip: (ValueGraphDependency) -> Bool = { _ in false }) -> Set<ValueGraphDependency>
{
var stack = Stack<ValueGraphDependency>()
@ -150,4 +360,144 @@ public class ValueGraphTraverser: GraphTraversing {
return references
}
func transitiveStaticTargets(from dependency: ValueGraphDependency) -> Set<ValueGraphDependency> {
filterDependencies(from: dependency,
test: isDependencyStaticTarget,
skip: canDependencyLinkStaticProducts)
}
func targetProductReference(target: ValueGraphTarget) -> GraphDependencyReference {
.product(target: target.target.name, productName: target.target.productNameWithExtension)
}
func isDependencyPrecompiledLibrary(dependency: ValueGraphDependency) -> Bool {
switch dependency {
case .xcframework: return true
case .framework: return true
case .library: return true
case .packageProduct: return false
case .target: return false
case .sdk: return false
case .cocoapods: return false
}
}
func isDependencyPrecompiledFramework(dependency: ValueGraphDependency) -> Bool {
switch dependency {
case .xcframework: return true
case .framework: return true
case .library: return false
case .packageProduct: return false
case .target: return false
case .sdk: return false
case .cocoapods: return false
}
}
func isDependencyStaticTarget(dependency: ValueGraphDependency) -> Bool {
guard case let ValueGraphDependency.target(name, path) = dependency,
let target = self.target(path: path, name: name) else { return false }
return target.target.product.isStatic
}
func isDependencyDynamicLibrary(dependency: ValueGraphDependency) -> Bool {
guard case let ValueGraphDependency.target(name, path) = dependency,
let target = self.target(path: path, name: name) else { return false }
return target.target.product == .dynamicLibrary
}
func isDependencyFramework(dependency: ValueGraphDependency) -> Bool {
guard case let ValueGraphDependency.target(name, path) = dependency,
let target = self.target(path: path, name: name) else { return false }
return target.target.product == .framework
}
func isDependencyDynamicTarget(dependency: ValueGraphDependency) -> Bool {
switch dependency {
case .xcframework: return false
case .framework: return false
case .library: return false
case .packageProduct: return false
case let .target(name, path):
guard let target = self.target(path: path, name: name) else { return false }
return target.target.product.isDynamic
case .sdk: return false
case .cocoapods: return false
}
}
func isDependencyPrecompiledDynamicAndLinkable(dependency: ValueGraphDependency) -> Bool {
switch dependency {
case let .xcframework(_, _, _, linking): return linking == .dynamic
case let .framework(_, _, _, _, linking, _, _): return linking == .dynamic
case .library: return false
case .packageProduct: return false
case .target: return false
case .sdk: return false
case .cocoapods: return false
}
}
func canDependencyEmbedProducts(dependency: ValueGraphDependency) -> Bool {
guard case let ValueGraphDependency.target(name, path) = dependency,
let target = self.target(path: path, name: name) else { return false }
return canEmbedProducts(target: target.target)
}
func canDependencyLinkStaticProducts(dependency: ValueGraphDependency) -> Bool {
guard case let ValueGraphDependency.target(name, path) = dependency,
let target = self.target(path: path, name: name) else { return false }
return target.target.canLinkStaticProducts()
}
func hostApplication(path: AbsolutePath, name: String) -> ValueGraphTarget? {
directTargetDependencies(path: path, name: name)
.first(where: { $0.target.product == .app })
}
func canEmbedProducts(target: Target) -> Bool {
let validProducts: [Product] = [
.app,
.unitTests,
.uiTests,
.watch2Extension,
]
return validProducts.contains(target.product)
}
func dependencyReference(dependency: ValueGraphDependency) -> GraphDependencyReference? {
switch dependency {
case .cocoapods:
return nil
case let .framework(path, binaryPath, dsymPath, bcsymbolmapPaths, linking, architectures, isCarthage):
return .framework(path: path,
binaryPath: binaryPath,
isCarthage: isCarthage,
dsymPath: dsymPath,
bcsymbolmapPaths: bcsymbolmapPaths,
linking: linking,
architectures: architectures,
product: (linking == .static) ? .staticFramework : .framework)
case let .library(path, _, linking, architectures, _):
return .library(path: path,
linking: linking,
architectures: architectures,
product: (linking == .static) ? .staticLibrary : .dynamicLibrary)
case .packageProduct:
return nil
case let .sdk(_, path, status, source):
return .sdk(path: path,
status: status,
source: source)
case let .target(name, path):
guard let target = self.target(path: path, name: name) else { return nil }
return .product(target: target.target.name, productName: target.target.productNameWithExtension)
case let .xcframework(path, infoPlist, primaryBinaryPath, _):
return .xcframework(path: path,
infoPlist: infoPlist,
primaryBinaryPath: primaryBinaryPath,
binaryPath: primaryBinaryPath)
}
}
}

View File

@ -27,7 +27,8 @@ public extension GraphDependencyReference {
path: AbsolutePath = "/frameworks/tuist.xcframework",
infoPlist: XCFrameworkInfoPlist = .test(),
primaryBinaryPath: AbsolutePath = "/frameworks/tuist.xcframework/ios-arm64/tuist",
binaryPath: AbsolutePath = "/frameworks/tuist.xcframework/ios-arm64/tuist"
binaryPath: AbsolutePath = "/frameworks/tuist.xcframework/ios-arm64/tuist",
linking _: BinaryLinking = .dynamic
) -> GraphDependencyReference {
GraphDependencyReference.xcframework(path: path,
infoPlist: infoPlist,
@ -36,13 +37,11 @@ public extension GraphDependencyReference {
}
static func testLibrary(path: AbsolutePath = "/libraries/library.a",
binaryPath: AbsolutePath = "/libraries/library.a",
linking: BinaryLinking = .static,
architectures: [BinaryArchitecture] = [BinaryArchitecture.arm64],
product: Product = .staticLibrary) -> GraphDependencyReference
{
GraphDependencyReference.library(path: path,
binaryPath: binaryPath,
linking: linking,
architectures: architectures,
product: product)

View File

@ -3,13 +3,63 @@ import TSCBasic
@testable import TuistCore
final class MockGraphTraverser: GraphTraversing {
var invokedNameGetter = false
var invokedNameGetterCount = 0
var stubbedName: String! = ""
var name: String {
invokedNameGetter = true
invokedNameGetterCount += 1
return stubbedName
}
var invokedHasPackagesGetter = false
var invokedHasPackagesGetterCount = 0
var stubbedHasPackages: Bool! = false
var hasPackages: Bool {
invokedHasPackagesGetter = true
invokedHasPackagesGetterCount += 1
return stubbedHasPackages
}
var invokedPathGetter = false
var invokedPathGetterCount = 0
var stubbedPath: AbsolutePath!
var path: AbsolutePath {
invokedPathGetter = true
invokedPathGetterCount += 1
return stubbedPath
}
var invokedWorkspaceGetter = false
var invokedWorkspaceGetterCount = 0
var stubbedWorkspace: Workspace!
var workspace: Workspace {
invokedWorkspaceGetter = true
invokedWorkspaceGetterCount += 1
return stubbedWorkspace
}
var invokedProjectsGetter = false
var invokedProjectsGetterCount = 0
var stubbedProjects: [AbsolutePath: Project]! = [:]
var projects: [AbsolutePath: Project] {
invokedProjectsGetter = true
invokedProjectsGetterCount += 1
return stubbedProjects
}
var invokedTarget = false
var invokedTargetCount = 0
var invokedTargetParameters: (path: AbsolutePath, name: String)?
var invokedTargetParametersList = [(path: AbsolutePath, name: String)]()
var stubbedTargetResult: Target!
var stubbedTargetResult: ValueGraphTarget!
func target(path: AbsolutePath, name: String) -> Target? {
func target(path: AbsolutePath, name: String) -> ValueGraphTarget? {
invokedTarget = true
invokedTargetCount += 1
invokedTargetParameters = (path, name)
@ -21,9 +71,9 @@ final class MockGraphTraverser: GraphTraversing {
var invokedTargetsCount = 0
var invokedTargetsParameters: (path: AbsolutePath, Void)?
var invokedTargetsParametersList = [(path: AbsolutePath, Void)]()
var stubbedTargetsResult: [Target]! = []
var stubbedTargetsResult: Set<ValueGraphTarget>! = []
func targets(at path: AbsolutePath) -> [Target] {
func targets(at path: AbsolutePath) -> Set<ValueGraphTarget> {
invokedTargets = true
invokedTargetsCount += 1
invokedTargetsParameters = (path, ())
@ -35,9 +85,9 @@ final class MockGraphTraverser: GraphTraversing {
var invokedDirectTargetDependenciesCount = 0
var invokedDirectTargetDependenciesParameters: (path: AbsolutePath, name: String)?
var invokedDirectTargetDependenciesParametersList = [(path: AbsolutePath, name: String)]()
var stubbedDirectTargetDependenciesResult: [Target]! = []
var stubbedDirectTargetDependenciesResult: Set<ValueGraphTarget>! = []
func directTargetDependencies(path: AbsolutePath, name: String) -> [Target] {
func directTargetDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
invokedDirectTargetDependencies = true
invokedDirectTargetDependenciesCount += 1
invokedDirectTargetDependenciesParameters = (path, name)
@ -49,9 +99,9 @@ final class MockGraphTraverser: GraphTraversing {
var invokedAppExtensionDependenciesCount = 0
var invokedAppExtensionDependenciesParameters: (path: AbsolutePath, name: String)?
var invokedAppExtensionDependenciesParametersList = [(path: AbsolutePath, name: String)]()
var stubbedAppExtensionDependenciesResult: [Target]! = []
var stubbedAppExtensionDependenciesResult: Set<ValueGraphTarget>! = []
func appExtensionDependencies(path: AbsolutePath, name: String) -> [Target] {
func appExtensionDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
invokedAppExtensionDependencies = true
invokedAppExtensionDependenciesCount += 1
invokedAppExtensionDependenciesParameters = (path, name)
@ -63,9 +113,9 @@ final class MockGraphTraverser: GraphTraversing {
var invokedResourceBundleDependenciesCount = 0
var invokedResourceBundleDependenciesParameters: (path: AbsolutePath, name: String)?
var invokedResourceBundleDependenciesParametersList = [(path: AbsolutePath, name: String)]()
var stubbedResourceBundleDependenciesResult: [Target]! = []
var stubbedResourceBundleDependenciesResult: Set<ValueGraphTarget>! = []
func resourceBundleDependencies(path: AbsolutePath, name: String) -> [Target] {
func resourceBundleDependencies(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
invokedResourceBundleDependencies = true
invokedResourceBundleDependenciesCount += 1
invokedResourceBundleDependenciesParameters = (path, name)
@ -77,9 +127,9 @@ final class MockGraphTraverser: GraphTraversing {
var invokedTestTargetsDependingOnCount = 0
var invokedTestTargetsDependingOnParameters: (path: AbsolutePath, name: String)?
var invokedTestTargetsDependingOnParametersList = [(path: AbsolutePath, name: String)]()
var stubbedTestTargetsDependingOnResult: [Target]! = []
var stubbedTestTargetsDependingOnResult: Set<ValueGraphTarget>! = []
func testTargetsDependingOn(path: AbsolutePath, name: String) -> [Target] {
func testTargetsDependingOn(path: AbsolutePath, name: String) -> Set<ValueGraphTarget> {
invokedTestTargetsDependingOn = true
invokedTestTargetsDependingOnCount += 1
invokedTestTargetsDependingOnParameters = (path, name)
@ -91,9 +141,9 @@ final class MockGraphTraverser: GraphTraversing {
var invokedDirectStaticDependenciesCount = 0
var invokedDirectStaticDependenciesParameters: (path: AbsolutePath, name: String)?
var invokedDirectStaticDependenciesParametersList = [(path: AbsolutePath, name: String)]()
var stubbedDirectStaticDependenciesResult: [GraphDependencyReference]! = []
var stubbedDirectStaticDependenciesResult: Set<GraphDependencyReference>! = []
func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
func directStaticDependencies(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
invokedDirectStaticDependencies = true
invokedDirectStaticDependenciesCount += 1
invokedDirectStaticDependenciesParameters = (path, name)
@ -101,7 +151,133 @@ final class MockGraphTraverser: GraphTraversing {
return stubbedDirectStaticDependenciesResult
}
func appClipsDependency(path _: AbsolutePath, name _: String) -> Target? {
nil
var invokedAppClipDependencies = false
var invokedAppClipDependenciesCount = 0
var invokedAppClipDependenciesParameters: (path: AbsolutePath, name: String)?
var invokedAppClipDependenciesParametersList = [(path: AbsolutePath, name: String)]()
var stubbedAppClipDependenciesResult: ValueGraphTarget!
func appClipDependencies(path: AbsolutePath, name: String) -> ValueGraphTarget? {
invokedAppClipDependencies = true
invokedAppClipDependenciesCount += 1
invokedAppClipDependenciesParameters = (path, name)
invokedAppClipDependenciesParametersList.append((path, name))
return stubbedAppClipDependenciesResult
}
var invokedEmbeddableFrameworks = false
var invokedEmbeddableFrameworksCount = 0
var invokedEmbeddableFrameworksParameters: (path: AbsolutePath, name: String)?
var invokedEmbeddableFrameworksParametersList = [(path: AbsolutePath, name: String)]()
var stubbedEmbeddableFrameworksResult: Set<GraphDependencyReference>! = []
func embeddableFrameworks(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
invokedEmbeddableFrameworks = true
invokedEmbeddableFrameworksCount += 1
invokedEmbeddableFrameworksParameters = (path, name)
invokedEmbeddableFrameworksParametersList.append((path, name))
return stubbedEmbeddableFrameworksResult
}
var invokedLinkableDependencies = false
var invokedLinkableDependenciesCount = 0
var invokedLinkableDependenciesParameters: (path: AbsolutePath, name: String)?
var invokedLinkableDependenciesParametersList = [(path: AbsolutePath, name: String)]()
var stubbedLinkableDependenciesError: Error?
var stubbedLinkableDependenciesResult: Set<GraphDependencyReference>! = []
func linkableDependencies(path: AbsolutePath, name: String) throws -> Set<GraphDependencyReference> {
invokedLinkableDependencies = true
invokedLinkableDependenciesCount += 1
invokedLinkableDependenciesParameters = (path, name)
invokedLinkableDependenciesParametersList.append((path, name))
if let error = stubbedLinkableDependenciesError {
throw error
}
return stubbedLinkableDependenciesResult
}
var invokedCopyProductDependencies = false
var invokedCopyProductDependenciesCount = 0
var invokedCopyProductDependenciesParameters: (path: AbsolutePath, name: String)?
var invokedCopyProductDependenciesParametersList = [(path: AbsolutePath, name: String)]()
var stubbedCopyProductDependenciesResult: Set<GraphDependencyReference>! = []
func copyProductDependencies(path: AbsolutePath, name: String) -> Set<GraphDependencyReference> {
invokedCopyProductDependencies = true
invokedCopyProductDependenciesCount += 1
invokedCopyProductDependenciesParameters = (path, name)
invokedCopyProductDependenciesParametersList.append((path, name))
return stubbedCopyProductDependenciesResult
}
var invokedLibrariesPublicHeadersFolders = false
var invokedLibrariesPublicHeadersFoldersCount = 0
var invokedLibrariesPublicHeadersFoldersParameters: (path: AbsolutePath, name: String)?
var invokedLibrariesPublicHeadersFoldersParametersList = [(path: AbsolutePath, name: String)]()
var stubbedLibrariesPublicHeadersFoldersResult: Set<AbsolutePath>! = []
func librariesPublicHeadersFolders(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
invokedLibrariesPublicHeadersFolders = true
invokedLibrariesPublicHeadersFoldersCount += 1
invokedLibrariesPublicHeadersFoldersParameters = (path, name)
invokedLibrariesPublicHeadersFoldersParametersList.append((path, name))
return stubbedLibrariesPublicHeadersFoldersResult
}
var invokedLibrariesSearchPaths = false
var invokedLibrariesSearchPathsCount = 0
var invokedLibrariesSearchPathsParameters: (path: AbsolutePath, name: String)?
var invokedLibrariesSearchPathsParametersList = [(path: AbsolutePath, name: String)]()
var stubbedLibrariesSearchPathsResult: Set<AbsolutePath>! = []
func librariesSearchPaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
invokedLibrariesSearchPaths = true
invokedLibrariesSearchPathsCount += 1
invokedLibrariesSearchPathsParameters = (path, name)
invokedLibrariesSearchPathsParametersList.append((path, name))
return stubbedLibrariesSearchPathsResult
}
var invokedLibrariesSwiftIncludePaths = false
var invokedLibrariesSwiftIncludePathsCount = 0
var invokedLibrariesSwiftIncludePathsParameters: (path: AbsolutePath, name: String)?
var invokedLibrariesSwiftIncludePathsParametersList = [(path: AbsolutePath, name: String)]()
var stubbedLibrariesSwiftIncludePathsResult: Set<AbsolutePath>! = []
func librariesSwiftIncludePaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
invokedLibrariesSwiftIncludePaths = true
invokedLibrariesSwiftIncludePathsCount += 1
invokedLibrariesSwiftIncludePathsParameters = (path, name)
invokedLibrariesSwiftIncludePathsParametersList.append((path, name))
return stubbedLibrariesSwiftIncludePathsResult
}
var invokedRunPathSearchPaths = false
var invokedRunPathSearchPathsCount = 0
var invokedRunPathSearchPathsParameters: (path: AbsolutePath, name: String)?
var invokedRunPathSearchPathsParametersList = [(path: AbsolutePath, name: String)]()
var stubbedRunPathSearchPathsResult: Set<AbsolutePath>! = []
func runPathSearchPaths(path: AbsolutePath, name: String) -> Set<AbsolutePath> {
invokedRunPathSearchPaths = true
invokedRunPathSearchPathsCount += 1
invokedRunPathSearchPathsParameters = (path, name)
invokedRunPathSearchPathsParametersList.append((path, name))
return stubbedRunPathSearchPathsResult
}
var invokedHostTargetFor = false
var invokedHostTargetForCount = 0
var invokedHostTargetForParameters: (path: AbsolutePath, name: String)?
var invokedHostTargetForParametersList = [(path: AbsolutePath, name: String)]()
var stubbedHostTargetForResult: ValueGraphTarget!
func hostTargetFor(path: AbsolutePath, name: String) -> ValueGraphTarget? {
invokedHostTargetFor = true
invokedHostTargetForCount += 1
invokedHostTargetForParameters = (path, name)
invokedHostTargetForParametersList.append((path, name))
return stubbedHostTargetForResult
}
}

View File

@ -4,7 +4,8 @@ import TuistCore
public extension ValueGraph {
static func test(name: String = "graph",
path: AbsolutePath,
path: AbsolutePath = .root,
workspace: Workspace = .test(),
projects: [AbsolutePath: Project] = [:],
packages: [AbsolutePath: [String: Package]] = [:],
targets: [AbsolutePath: [String: Target]] = [:],
@ -12,6 +13,7 @@ public extension ValueGraph {
{
ValueGraph(name: name,
path: path,
workspace: workspace,
projects: projects,
packages: packages,
targets: targets,

View File

@ -0,0 +1,76 @@
import Foundation
import TSCBasic
@testable import TuistCore
public extension ValueGraphDependency {
static func testCocoapods(path: AbsolutePath = .root) -> ValueGraphDependency {
ValueGraphDependency.cocoapods(path: path)
}
static func testFramework(path: AbsolutePath = AbsolutePath.root.appending(component: "Test.framework"),
binaryPath: AbsolutePath = AbsolutePath.root.appending(RelativePath("Test.framework/Test")),
dsymPath: AbsolutePath? = nil,
bcsymbolmapPaths: [AbsolutePath] = [],
linking: BinaryLinking = .dynamic,
architectures: [BinaryArchitecture] = [.armv7],
isCarthage: Bool = false) -> ValueGraphDependency
{
ValueGraphDependency.framework(path: path,
binaryPath: binaryPath,
dsymPath: dsymPath,
bcsymbolmapPaths: bcsymbolmapPaths,
linking: linking,
architectures: architectures,
isCarthage: isCarthage)
}
static func testXCFramework(path: AbsolutePath = AbsolutePath.root.appending(RelativePath("Test.xcframework")),
infoPlist: XCFrameworkInfoPlist = .test(),
primaryBinaryPath: AbsolutePath = AbsolutePath.root.appending(RelativePath("Test.xcframework/Test")),
linking: BinaryLinking = .dynamic) -> ValueGraphDependency
{
.xcframework(path: path,
infoPlist: infoPlist,
primaryBinaryPath: primaryBinaryPath,
linking: linking)
}
static func testTarget(name: String = "Test",
path: AbsolutePath = .root) -> ValueGraphDependency
{
.target(name: name,
path: path)
}
static func testSDK(name: String = "XCTest",
path: AbsolutePath = AbsolutePath.root.appending(RelativePath("XCTest.framework")),
status: SDKStatus = .required,
source: SDKSource = .system) -> ValueGraphDependency
{
.sdk(name: name,
path: path,
status: status,
source: source)
}
static func testLibrary(path: AbsolutePath = AbsolutePath.root.appending(RelativePath("libTuist.a")),
publicHeaders: AbsolutePath = AbsolutePath.root.appending(RelativePath("headers")),
linking: BinaryLinking = .dynamic,
architectures: [BinaryArchitecture] = [.armv7],
swiftModuleMap: AbsolutePath? = nil) -> ValueGraphDependency
{
.library(path: path,
publicHeaders: publicHeaders,
linking: linking,
architectures: architectures,
swiftModuleMap: swiftModuleMap)
}
static func testPackageProduct(path: AbsolutePath = .root,
product: String = "Tuist") -> ValueGraphDependency
{
.packageProduct(path: path,
product: product)
}
}

View File

@ -0,0 +1,15 @@
import Foundation
import TSCBasic
@testable import TuistCore
public extension ValueGraphTarget {
static func test(path: AbsolutePath = .root,
target: Target = .test(),
project: Project = .test()) -> ValueGraphTarget
{
ValueGraphTarget(path: path,
target: target,
project: project)
}
}

View File

@ -325,8 +325,10 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
pbxproj: PBXProj,
resourcesBuildPhase: PBXResourcesBuildPhase)
{
let bundles = graphTraverser.resourceBundleDependencies(path: path, name: target.name)
let refs = bundles.compactMap { fileElements.product(target: $0.name) }
let bundles = graphTraverser
.resourceBundleDependencies(path: path, name: target.name)
.sorted()
let refs = bundles.compactMap { fileElements.product(target: $0.target.name) }
refs.forEach {
let pbxBuildFile = PBXBuildFile(file: $0)
@ -342,14 +344,14 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
fileElements: ProjectFileElements,
pbxproj: PBXProj) throws
{
let appExtensions = graphTraverser.appExtensionDependencies(path: path, name: target.name)
let appExtensions = graphTraverser.appExtensionDependencies(path: path, name: target.name).sorted()
guard !appExtensions.isEmpty else { return }
let appExtensionsBuildPhase = PBXCopyFilesBuildPhase(dstSubfolderSpec: .plugins, name: "Embed App Extensions")
pbxproj.add(object: appExtensionsBuildPhase)
pbxTarget.buildPhases.append(appExtensionsBuildPhase)
let refs = appExtensions.compactMap { fileElements.product(target: $0.name) }
let refs = appExtensions.compactMap { fileElements.product(target: $0.target.name) }
refs.forEach {
let pbxBuildFile = PBXBuildFile(file: $0, settings: ["ATTRIBUTES": ["RemoveHeadersOnCopy"]])
@ -366,7 +368,7 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
pbxproj: PBXProj) throws
{
let targetDependencies = graphTraverser.directTargetDependencies(path: path, name: target.name)
let watchApps = targetDependencies.filter { $0.product == .watch2App }
let watchApps = targetDependencies.filter { $0.target.product == .watch2App }
guard !watchApps.isEmpty else { return }
let embedWatchAppBuildPhase = PBXCopyFilesBuildPhase(dstPath: "$(CONTENTS_FOLDER_PATH)/Watch",
@ -375,7 +377,7 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
pbxproj.add(object: embedWatchAppBuildPhase)
pbxTarget.buildPhases.append(embedWatchAppBuildPhase)
let refs = watchApps.compactMap { fileElements.product(target: $0.name) }
let refs = watchApps.compactMap { fileElements.product(target: $0.target.name) }
refs.forEach {
let pbxBuildFile = PBXBuildFile(file: $0, settings: ["ATTRIBUTES": ["RemoveHeadersOnCopy"]])
@ -395,7 +397,7 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
return
}
guard let appClips = graphTraverser.appClipsDependency(path: path, name: target.name) else {
guard let appClips = graphTraverser.appClipDependencies(path: path, name: target.name) else {
return
}
@ -405,7 +407,7 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
pbxproj.add(object: embedAppClipsBuildPhase)
pbxTarget.buildPhases.append(embedAppClipsBuildPhase)
let refs = fileElements.product(target: appClips.name)
let refs = fileElements.product(target: appClips.target.name)
let pbxBuildFile = PBXBuildFile(file: refs, settings: ["ATTRIBUTES": ["RemoveHeadersOnCopy"]])
pbxproj.add(object: pbxBuildFile)

View File

@ -225,22 +225,22 @@ final class ConfigGenerator: ConfigGenerating {
}
let targetDependencies = graphTraverser.directTargetDependencies(path: projectPath, name: target.name)
let appDependency = targetDependencies.first { $0.product.canHostTests() }
let appDependency = targetDependencies.first { $0.target.product.canHostTests() }
guard let app = appDependency else {
return [:]
}
var settings: SettingsDictionary = [:]
settings["TEST_TARGET_NAME"] = .string("\(app.name)")
settings["TEST_TARGET_NAME"] = .string("\(app.target.name)")
if target.product == .unitTests {
var testHostPath = "$(BUILT_PRODUCTS_DIR)/\(app.productNameWithExtension)"
var testHostPath = "$(BUILT_PRODUCTS_DIR)/\(app.target.productNameWithExtension)"
if target.platform == .macOS {
testHostPath += "/Contents/MacOS"
}
settings["TEST_HOST"] = .string("\(testHostPath)/\(app.productName)")
settings["TEST_HOST"] = .string("\(testHostPath)/\(app.target.productName)")
settings["BUNDLE_LOADER"] = "$(TEST_HOST)"
}
@ -287,12 +287,12 @@ final class ConfigGenerator: ConfigGenerating {
}
let targetDependencies = graphTraverser.directTargetDependencies(path: projectPath, name: target.name)
guard let watchExtension = targetDependencies.first(where: { $0.product == .watch2Extension }) else {
guard let watchExtension = targetDependencies.first(where: { $0.target.product == .watch2Extension }) else {
return [:]
}
return [
"IBSC_MODULE": .string(watchExtension.productName),
"IBSC_MODULE": .string(watchExtension.target.productName),
]
}
}

View File

@ -338,7 +338,7 @@ final class LinkGenerator: LinkGenerating {
switch dependency {
case let .framework(path, _, _, _, _, _, _, _):
try addBuildFile(path)
case let .library(path, _, _, _, _):
case let .library(path, _, _, _):
try addBuildFile(path)
case let .xcframework(path, _, _, _):
try addBuildFile(path)

View File

@ -198,7 +198,7 @@ class ProjectFileElements {
try generatePrecompiled(path)
case let .framework(path, _, _, _, _, _, _, _):
try generatePrecompiled(path)
case let .library(path, _, _, _, _):
case let .library(path, _, _, _):
try generatePrecompiled(path)
case let .sdk(sdkNodePath, _, _):
generateSDKFileElement(sdkNodePath: sdkNodePath,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
import Foundation
import TSCBasic
import XCTest
@testable import TuistCore
@testable import TuistSupportTesting
final class ValueGraphDependencyTests: TuistUnitTestCase {
func test_isTarget() {
XCTAssertFalse(ValueGraphDependency.testXCFramework().isTarget)
XCTAssertFalse(ValueGraphDependency.testFramework().isTarget)
XCTAssertFalse(ValueGraphDependency.testLibrary().isTarget)
XCTAssertFalse(ValueGraphDependency.testPackageProduct().isTarget)
XCTAssertTrue(ValueGraphDependency.testTarget().isTarget)
XCTAssertFalse(ValueGraphDependency.testSDK().isTarget)
XCTAssertFalse(ValueGraphDependency.testCocoapods().isTarget)
}
func test_isPrecompiled() {
XCTAssertTrue(ValueGraphDependency.testXCFramework().isPrecompiled)
XCTAssertTrue(ValueGraphDependency.testFramework().isPrecompiled)
XCTAssertTrue(ValueGraphDependency.testLibrary().isPrecompiled)
XCTAssertFalse(ValueGraphDependency.testPackageProduct().isPrecompiled)
XCTAssertFalse(ValueGraphDependency.testTarget().isPrecompiled)
XCTAssertFalse(ValueGraphDependency.testSDK().isPrecompiled)
XCTAssertFalse(ValueGraphDependency.testCocoapods().isPrecompiled)
}
}

View File

@ -0,0 +1,16 @@
import Foundation
import TSCBasic
import XCTest
@testable import TuistCore
@testable import TuistCoreTesting
@testable import TuistSupportTesting
final class ValueGraphTargetTests: TuistUnitTestCase {
func test_comparable() {
XCTAssertTrue(ValueGraphTarget.test(target: Target.test(name: "a")) < ValueGraphTarget.test(target: Target.test(name: "b")))
XCTAssertFalse(ValueGraphTarget.test(target: Target.test(name: "b")) < ValueGraphTarget.test(target: Target.test(name: "a")))
XCTAssertTrue(ValueGraphTarget.test(path: "/a", target: Target.test(name: "a")) < ValueGraphTarget.test(path: "/b", target: Target.test(name: "a")))
XCTAssertFalse(ValueGraphTarget.test(path: "/b", target: Target.test(name: "a")) < ValueGraphTarget.test(path: "/a", target: Target.test(name: "a")))
}
}

View File

@ -106,32 +106,40 @@ final class ValueGraphTests: TuistUnitTestCase {
XCTAssertEqual(valueGraph.dependencies[.target(name: aTarget.name, path: aNode.path)]?
.contains(.sdk(name: xctestNode.name, path: xctestNode.path, status: xctestNode.status, source: xctestNode.source)), true)
// Then: A -> BFramework
XCTAssertEqual(valueGraph.dependencies[.target(name: aTarget.name, path: aNode.path)]?
.contains(.framework(path: bFrameworkNode.path,
XCTAssertEqual(valueGraph.dependencies[.target(name: aTarget.name, path: aNode.path), default: []]
.contains(.framework(path: bFrameworkPath,
binaryPath: bFrameworkNode.binaryPath,
dsymPath: bFrameworkNode.dsymPath,
bcsymbolmapPaths: bFrameworkNode.bcsymbolmapPaths,
linking: bFrameworkNode.linking,
architectures: bFrameworkNode.architectures)), true)
architectures: bFrameworkNode.architectures,
isCarthage: bFrameworkNode.isCarthage)), true)
// Then: A -> Package
XCTAssertEqual(valueGraph.dependencies[.target(name: aTarget.name, path: aNode.path)]?
.contains(.packageProduct(path: packageProduct.path, product: packageProduct.product)), true)
// Then: BFramework -> AFramework
XCTAssertEqual(valueGraph.dependencies[.framework(path: bFrameworkNode.path,
binaryPath: bFrameworkNode.binaryPath,
dsymPath: bFrameworkNode.dsymPath,
bcsymbolmapPaths: bFrameworkNode.bcsymbolmapPaths,
linking: bFrameworkNode.linking,
architectures: bFrameworkNode.architectures)]?
architectures: bFrameworkNode.architectures,
isCarthage: bFrameworkNode.isCarthage), default: []]
.contains(.framework(path: aFrameworkNode.path,
binaryPath: aFrameworkNode.binaryPath,
dsymPath: aFrameworkNode.dsymPath,
bcsymbolmapPaths: aFrameworkNode.bcsymbolmapPaths,
linking: aFrameworkNode.linking,
architectures: aFrameworkNode.architectures)), true)
architectures: aFrameworkNode.architectures,
isCarthage: aFrameworkNode.isCarthage)), true)
// then: AFramework
XCTAssertNotNil(valueGraph.dependencies[.framework(path: aFrameworkNode.path,
binaryPath: aFrameworkNode.binaryPath,
dsymPath: aFrameworkNode.dsymPath,
bcsymbolmapPaths: aFrameworkNode.bcsymbolmapPaths,
linking: aFrameworkNode.linking,
architectures: aFrameworkNode.architectures)])
architectures: aFrameworkNode.architectures,
isCarthage: aFrameworkNode.isCarthage)])
// Then: XCTest
XCTAssertNotNil(valueGraph.dependencies[.sdk(name: xctestNode.name, path: xctestNode.path, status: xctestNode.status, source: xctestNode.source)])

File diff suppressed because it is too large Load Diff