[Dependencies.swift] Generate `DependenciesGraph` for `Carthage` dependencies. (#3043)

* [carthage-graph] fix - typo

* [carthage-graph] add - `Constants.DependenciesDirectory.graphName`

* [carthage-graph] fix - logging

* [carthage-graph] change - improve `app_with_framework_and_tests_and_dependencies` fixture

* [carthage-graph] add - init `DependenciesGraph`

* [carthage-graph] add - unit tests

* [carthage-graph] change - merge `X_Interactor.fetch` and `X_Interactor.update` into `X_Interactor.install`

* [carthage-graph] change - mark `WIP` places

* [carthage-graph] add - `XCTestCase.XCTAssertDecodableEqualToJson`

* [carthage-graph] add - `CarthageVersionFile` model

* [carthage-graph] fix - adjustments after rebasing onto `main`

* [carthage-graph] add - `DependenciesGraphNode`

* [carthage-graph] add - `CarthageGraphGenerator`

* [carthage-graph] add - `TuistCore.DependenciesGraphController`

* [carthage-graph] fix - generating `graph.json`

* [carthage-graph] change - encode `DependenciesGraph` with `prettyPrinted` option

* [carthage-graph] add - supported architectures to `DependenciesGraphNode.xcframework`

* [carthage-graph] add - set `frameworkPath` in `DependenciesGraphNode.xcframework`

* [carthage-graph] change - update documentation

* [carthage-graph] change - update documentation

* [carthage-graph] change - update acceptance tests

* [carthage-graph] fix - run `./fourier format swift --fix`

* [carthage-graph] change - move `DependenciesGraphController` from `TuistCore` to `TuistDependencies`

* [carthage-graph] fix - typos

* [carthage-graph] change - update `CocoaPodsInteracting` api

* [carthage-graph] change - [Carthage] drop support for production of regular frameworks

* [carthage-graph] fix - typos

* [carthage-graph] change - `DependenciesGraph` adjustments

* [carthage-graph] add - `DependenciesGraphControlling.clean`

* [carthage-graph] add - unit tests for `CarthageGraphGenerator`

* [carthage-graph] add - unit tests for `DependenciesGraphController`

* [carthage-graph] add - more unit tests for `DependenciesController`

* [carthage-graph] add - more unit tests to `CarthageInteractor`

* [carthage-graph] change - update documentation

* [carthage-graph] change - update changelog

* [carthage-graph] fix - run `./fourier format swift --fix`

* [carthage-graph] fix - typo

* [carthage-graph] change - `CarthageDependencies` and `SwiftPackageManagerDependencies`  confirm `ExpressibleByArrayLiteral`

* [carthage-graph] change - `compactMapValues` instead of `reduce`

* [carthage-graph] change - save empty graph when no results

* [carthage-graph] fix - acceptance tests

* [carthage-graph] change - update documentation

* [carthage-graph] fix - run `./fourier format swift --fix`

* [carthage-graph] fix - unit tests

* [carthage-graph] fix - acceptance tests

* [carthage-graph] fix - code review remarks

* [carthage-graph] fix - code review remarks

* [carthage-graph] fix - acceptance tests
This commit is contained in:
Kamil Harasimowicz 2021-06-11 14:01:22 +02:00 committed by GitHub
parent 782f50effb
commit bc798f7063
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1839 additions and 732 deletions

View File

@ -7,6 +7,15 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
### Added
- Add `tvTopShelfExtension` and `tvIntentsExtension` target product. [#2793](https://github.com/tuist/tuist/pull/2793) by [@rmnblm](https://github.com/rmnblm)
- The `tuist dependencies` command generates a `graph.json` file for the `Carthage` dependencies. [#3043](https://github.com/tuist/tuist/pull/3043) by [@laxmorek](https://github.com/laxmorek)
### Changed
- **breaking** The `tuist dependencies` command requires the `Carthage` version to be at least `0.37.0`. [#3043](https://github.com/tuist/tuist/pull/3043) by [@laxmorek](https://github.com/laxmorek)
### Removed
- **breaking** Remove the `CarthageDependencies.Options` from the `Dependencies.swift` manifest model. [#3043](https://github.com/tuist/tuist/pull/3043) by [@laxmorek](https://github.com/laxmorek)
### Fixed

View File

@ -5,21 +5,18 @@ public struct CarthageDependencies: Codable, Equatable {
/// List of depedencies that will be installed using Carthage.
public let dependencies: [Dependency]
/// List of options for Carthage installation.
public let options: Set<Options>
/// Creates `CarthageDependencies` instance.
/// - Parameters:
/// - dependencies: List of depedencies that can be installed using Carthage.
/// - options: List of options for Carthage installation.
public static func carthage(
_ dependencies: [Dependency],
options: Set<Options> = []
) -> Self {
.init(
dependencies: dependencies,
options: options
)
/// - Parameter dependencies: List of depedencies that can be installed using Carthage.
public init(_ dependencies: [Dependency]) {
self.dependencies = dependencies
}
}
// MARK: - ExpressibleByArrayLiteral
extension CarthageDependencies: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Dependency...) {
dependencies = elements
}
}
@ -44,17 +41,6 @@ public extension CarthageDependencies {
case branch(String)
case revision(String)
}
/// The options that you can set for Carthage installation.
enum Options: String, Codable, Equatable {
/// When passed, Carthage will produce XCFrameworks instead of regular frameworks.
/// Refers to `--use-xcframeworks` Carthage flag.
/// **Note: It requires Carthage in version at least 0.37.0.**
case useXCFrameworks
/// When passed, Carthage will rebuild dependencies from source instead of using downloaded binaries when possible.
/// Refers to `--no-use-binaries` Carthage flag.
case noUseBinaries
}
}
// MARK: - CarthageDependencies.Dependency: Codable

View File

@ -7,11 +7,15 @@ public struct SwiftPackageManagerDependencies: Codable, Equatable {
/// Creates `SwiftPackageManagerDependencies` instance.
/// - Parameter packages: List of packages that will be installed using Swift Package Manager.
public static func swiftPackageManager(
_ packages: [Package]
) -> Self {
.init(
packages: packages
)
public init(_ packages: [Package]) {
self.packages = packages
}
}
// MARK: - ExpressibleByArrayLiteral
extension SwiftPackageManagerDependencies: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Package...) {
packages = elements
}
}

View File

@ -13,8 +13,6 @@ enum CarthageInteractorError: FatalError, Equatable {
case cartfileResolvedNotFound
/// Thrown when `Carthage/Build` directory cannot be found in temporary directory after Carthage installation.
case buildDirectoryNotFound
/// Thrown when version of Carthage installed in environment does not support XCFrameworks production.
case xcFrameworksProductionNotSupported
/// Error type.
var type: ErrorType {
@ -22,8 +20,7 @@ enum CarthageInteractorError: FatalError, Equatable {
case .cartfileResolvedNotFound,
.buildDirectoryNotFound:
return .bug
case .carthageNotFound,
.xcFrameworksProductionNotSupported:
case .carthageNotFound:
return .abort
}
}
@ -40,11 +37,6 @@ enum CarthageInteractorError: FatalError, Equatable {
return "The Cartfile.resolved lockfile was not found after resolving the dependencies using the Carthage."
case .buildDirectoryNotFound:
return "The Carthage/Build directory was not found after resolving the dependencies using the Carthage."
case .xcFrameworksProductionNotSupported:
return """
The version of Carthage installed in your environment doesn't suppport production of XCFrameworks.
Update the tool or disable XCFrameworks in your Dependencies.swift manifest.
"""
}
}
}
@ -52,27 +44,19 @@ enum CarthageInteractorError: FatalError, Equatable {
// MARK: - Carthage Interacting
public protocol CarthageInteracting {
/// Fetches `Carthage` dependencies.
/// Installs `Carthage` dependencies
/// - Parameters:
/// - dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
/// - dependencies: List of dependencies to fetch using `Carthage`.
/// - platforms: List of platforms for which you want to fetch dependencies.
func fetch(
/// - dependencies: List of dependencies to install using `Carthage`.
/// - platforms: List of platforms for which you want to install dependencies.
/// - shouldUpdate: Indicates whether dependencies should be updated or fetched based on the `Tuist/Lockfiles/Cartfile.resolved` lockfile.
/// - Returns: A graph that represents dependencies installed using `Carthage`.
func install(
dependenciesDirectory: AbsolutePath,
dependencies: CarthageDependencies,
platforms: Set<Platform>
) throws
/// Updates `Carthage` dependencies.
/// - Parameters:
/// - dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
/// - dependencies: List of dependencies to update using `Carthage`.
/// - platforms: List of platforms for which you want to update dependencies.
func update(
dependenciesDirectory: AbsolutePath,
dependencies: CarthageDependencies,
platforms: Set<Platform>
) throws
platforms: Set<Platform>,
shouldUpdate: Bool
) throws -> DependenciesGraph
/// Removes all cached `Carthage` dependencies.
/// - Parameter dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
@ -82,49 +66,66 @@ public protocol CarthageInteracting {
// MARK: - Carthage Interactor
public final class CarthageInteractor: CarthageInteracting {
private let fileHandler: FileHandling
private let carthageController: CarthageControlling
private let carthageGraphGenerator: CarthageGraphGenerating
public init(
fileHandler: FileHandling = FileHandler.shared,
carthageController: CarthageControlling = CarthageController.shared
carthageController: CarthageControlling = CarthageController.shared,
carthageGraphGenerator: CarthageGraphGenerating = CarthageGraphGenerator()
) {
self.fileHandler = fileHandler
self.carthageController = carthageController
self.carthageGraphGenerator = carthageGraphGenerator
}
public func fetch(
public func install(
dependenciesDirectory: AbsolutePath,
dependencies: CarthageDependencies,
platforms: Set<Platform>
) throws {
logger.info("Resolving and fetching Carthage dependencies.", metadata: .section)
platforms: Set<Platform>,
shouldUpdate: Bool
) throws -> DependenciesGraph {
logger.info("Installing Carthage dependencies.", metadata: .subsection)
try install(
dependenciesDirectory: dependenciesDirectory,
dependencies: dependencies,
platforms: platforms,
shouldUpdate: false
)
// check availability of `carthage`
guard carthageController.canUseSystemCarthage() else {
throw CarthageInteractorError.carthageNotFound
}
logger.info("Carthage dependencies resolved and fetched successfully.", metadata: .subsection)
}
// install depedencies and generate dependencies graph
let dependenciesGraph: DependenciesGraph = try FileHandler.shared
.inTemporaryDirectory { temporaryDirectoryPath in
// prepare paths
let pathsProvider = CarthagePathsProvider(
dependenciesDirectory: dependenciesDirectory,
temporaryDirectoryPath: temporaryDirectoryPath
)
public func update(
dependenciesDirectory: AbsolutePath,
dependencies: CarthageDependencies,
platforms: Set<Platform>
) throws {
logger.info("Updating Carthage dependencies.", metadata: .section)
// prepare for installation
try loadDependencies(pathsProvider: pathsProvider, dependencies: dependencies)
try install(
dependenciesDirectory: dependenciesDirectory,
dependencies: dependencies,
platforms: platforms,
shouldUpdate: true
)
// run `Carthage`
if shouldUpdate {
try carthageController.update(
at: temporaryDirectoryPath,
platforms: platforms
)
} else {
try carthageController.bootstrap(
at: temporaryDirectoryPath,
platforms: platforms
)
}
logger.info("Carthage dependencies updated successfully.", metadata: .subsection)
// post installation
try saveDepedencies(pathsProvider: pathsProvider)
// generate dependencies graph
return try carthageGraphGenerator
.generate(at: pathsProvider.temporaryCarthageBuildDirectory)
}
logger.info("Carthage dependencies installed successfully.", metadata: .subsection)
return dependenciesGraph
}
public func clean(dependenciesDirectory: AbsolutePath) throws {
@ -134,63 +135,16 @@ public final class CarthageInteractor: CarthageInteracting {
.appending(component: Constants.DependenciesDirectory.lockfilesDirectoryName)
.appending(component: Constants.DependenciesDirectory.cartfileResolvedName)
try fileHandler.delete(carthageDirectory)
try fileHandler.delete(cartfileResolvedPath)
try FileHandler.shared.delete(carthageDirectory)
try FileHandler.shared.delete(cartfileResolvedPath)
}
// MARK: - Installation
/// Installs given `dependencies` at `dependenciesDirectory`.
private func install(
dependenciesDirectory: AbsolutePath,
dependencies: CarthageDependencies,
platforms: Set<Platform>,
shouldUpdate: Bool
) throws {
// check availability of `carthage`
guard carthageController.canUseSystemCarthage() else {
throw CarthageInteractorError.carthageNotFound
}
// check if XCFrameworks production supported if it is needed
if dependencies.options.contains(.useXCFrameworks), !(try carthageController.isXCFrameworksProductionSupported()) {
throw CarthageInteractorError.xcFrameworksProductionNotSupported
}
try fileHandler.inTemporaryDirectory { temporaryDirectoryPath in
// prepare paths
let pathsProvider = CarthagePathsProvider(
dependenciesDirectory: dependenciesDirectory,
temporaryDirectoryPath: temporaryDirectoryPath
)
// prepare for installation
try loadDependencies(pathsProvider: pathsProvider, dependencies: dependencies)
// run `Carthage`
if shouldUpdate {
try carthageController.update(
at: temporaryDirectoryPath,
platforms: platforms,
options: dependencies.options
)
} else {
try carthageController.bootstrap(
at: temporaryDirectoryPath,
platforms: platforms,
options: dependencies.options
)
}
// post installation
try saveDepedencies(pathsProvider: pathsProvider)
}
}
/// Loads lockfile and dependencies into working directory if they had been saved before.
private func loadDependencies(pathsProvider: CarthagePathsProvider, dependencies: CarthageDependencies) throws {
// copy build directory from previous run if exist
if fileHandler.exists(pathsProvider.destinationCarthageDirectory) {
if FileHandler.shared.exists(pathsProvider.destinationCarthageDirectory) {
try copy(
from: pathsProvider.destinationCarthageDirectory,
to: pathsProvider.temporaryCarthageBuildDirectory
@ -198,7 +152,7 @@ public final class CarthageInteractor: CarthageInteracting {
}
// copy `Cartfile.resolved` directory from previous run if exist
if fileHandler.exists(pathsProvider.destinationCarfileResolvedPath) {
if FileHandler.shared.exists(pathsProvider.destinationCarfileResolvedPath) {
try copy(
from: pathsProvider.destinationCarfileResolvedPath,
to: pathsProvider.temporaryCarfileResolvedPath
@ -208,7 +162,7 @@ public final class CarthageInteractor: CarthageInteracting {
// create `Cartfile`
let cartfileContent = dependencies.cartfileValue()
let cartfilePath = pathsProvider.temporaryDirectoryPath.appending(component: "Cartfile")
try fileHandler.write(cartfileContent, path: cartfilePath, atomically: true)
try FileHandler.shared.write(cartfileContent, path: cartfilePath, atomically: true)
// log
logger.debug("Cartfile:", metadata: .subsection)
@ -218,10 +172,10 @@ public final class CarthageInteractor: CarthageInteracting {
/// Saves lockfile resolved depedencies in `Tuist/Depedencies` directory.
private func saveDepedencies(pathsProvider: CarthagePathsProvider) throws {
// validation
guard fileHandler.exists(pathsProvider.temporaryCarfileResolvedPath) else {
guard FileHandler.shared.exists(pathsProvider.temporaryCarfileResolvedPath) else {
throw CarthageInteractorError.cartfileResolvedNotFound
}
guard fileHandler.exists(pathsProvider.temporaryCarthageBuildDirectory) else {
guard FileHandler.shared.exists(pathsProvider.temporaryCarthageBuildDirectory) else {
throw CarthageInteractorError.buildDirectoryNotFound
}
@ -241,11 +195,11 @@ public final class CarthageInteractor: CarthageInteracting {
// MARK: - Helpers
private func copy(from fromPath: AbsolutePath, to toPath: AbsolutePath) throws {
if fileHandler.exists(toPath) {
try fileHandler.replace(toPath, with: fromPath)
if FileHandler.shared.exists(toPath) {
try FileHandler.shared.replace(toPath, with: fromPath)
} else {
try fileHandler.createFolder(toPath.removingLastComponent())
try fileHandler.copy(from: fromPath, to: toPath)
try FileHandler.shared.createFolder(toPath.removingLastComponent())
try FileHandler.shared.copy(from: fromPath, to: toPath)
}
}
}

View File

@ -0,0 +1,55 @@
import Foundation
import TuistGraph
/// A model that represents the `Carthage` version file.
/// Reference: https://github.com/Carthage/Carthage/blob/master/Documentation/VersionFile.md#version-files
struct CarthageVersionFile: Decodable, Equatable {
enum CodingKeys: String, CodingKey {
case commitish
case iOS
case macOS = "Mac"
case watchOS
case tvOS
}
let commitish: String
let iOS: [Product]?
let macOS: [Product]?
let watchOS: [Product]?
let tvOS: [Product]?
/// Returns all products.
var allProducts: [Product] {
[iOS, macOS, watchOS, tvOS]
.compactMap { $0 }
.flatMap { $0 }
}
}
// MARK: - Models
extension CarthageVersionFile {
struct Product: Decodable, Equatable {
let swiftToolchainVersion: String
let hash: String
let name: String
let container: String
let identifier: String
/// Returns architectures the product is built for.
var architectures: [BinaryArchitecture] {
// example identifier: `ios-arm64_i386_x86_64-simulator`
let identifierComponents = identifier.components(separatedBy: ["-"])
guard identifierComponents.count >= 2 else { return [] }
return identifierComponents[1]
.replacingOccurrences(of: "x86_64", with: "x8664")
.replacingOccurrences(of: "arm64_32", with: "arm6432")
.components(separatedBy: ["_"])
.map { $0 == "x8664" ? "x86_64" : $0 }
.map { $0 == "arm6432" ? "arm64_32" : $0 }
.compactMap { BinaryArchitecture(rawValue: $0) }
}
}
}

View File

@ -11,12 +11,15 @@ enum CarthageControllerError: FatalError, Equatable {
case carthageNotFound
/// Thrown when version of Carthage cannot be determined.
case unrecognizedCarthageVersion
/// Thrown when version of Carthage installed in environment does not support XCFrameworks production.
case xcFrameworksProductionNotSupported(installedVersion: Version)
/// Error type.
var type: ErrorType {
switch self {
case .carthageNotFound,
.unrecognizedCarthageVersion:
.unrecognizedCarthageVersion,
.xcFrameworksProductionNotSupported:
return .abort
}
}
@ -25,9 +28,20 @@ enum CarthageControllerError: FatalError, Equatable {
var description: String {
switch self {
case .carthageNotFound:
return "Carthage was not found in the environment. It's possible that the tool is not installed or hasn't been exposed to your environment." // swiftlint:disable:this line_length
return """
Carthage was not found in the environment.
It's possible that the tool is not installed or hasn't been exposed to your environment.
"""
case .unrecognizedCarthageVersion:
return "Version of Carthage cannot be determined. It's possible that the tool is not installed or hasn't been exposed to your environment." // swiftlint:disable:this line_length
return """
The version of Carthage cannot be determined.
It's possible that the tool is not installed or hasn't been exposed to your environment.
"""
case let .xcFrameworksProductionNotSupported(installedVersion):
return """
The version of Carthage installed in your environment (\(installedVersion.description)) doesn't suppport production of XCFrameworks.
You have to update the tool to at least 0.37.0 version.
"""
}
}
}
@ -42,22 +56,17 @@ public protocol CarthageControlling {
/// Return version of Carthage that is available in the environment.
func carthageVersion() throws -> Version
/// Returns true if version of Carthage available in the environment supports producing XCFrameworks.
func isXCFrameworksProductionSupported() throws -> Bool
/// Checkouts and builds the project's dependencies
/// - Parameters:
/// - path: Directory whose project's dependencies will be installed.
/// - path: Directory where project's dependencies will be installed.
/// - platforms: The platforms to build for.
/// - options: The options for Carthage installation.
func bootstrap(at path: AbsolutePath, platforms: Set<TuistGraph.Platform>, options: Set<CarthageDependencies.Options>) throws
func bootstrap(at path: AbsolutePath, platforms: Set<TuistGraph.Platform>) throws
/// Updates and rebuilds the project's dependencies
/// - Parameters:
/// - path: Directory whose project's dependencies will be installed.
/// - path: Directory where project's dependencies will be installed.
/// - platforms: The platforms to build for.
/// - options: The options for Carthage installation.
func update(at path: AbsolutePath, platforms: Set<TuistGraph.Platform>, options: Set<CarthageDependencies.Options>) throws
func update(at path: AbsolutePath, platforms: Set<TuistGraph.Platform>) throws
}
// MARK: - Carthage Controller
@ -97,19 +106,21 @@ public final class CarthageController: CarthageControlling {
return version
}
public func isXCFrameworksProductionSupported() throws -> Bool {
// Carthage has supported XCFrameworks production since 0.37.0
// More info here: https://github.com/Carthage/Carthage/releases/tag/0.37.0
try carthageVersion() >= Version(0, 37, 0)
}
public func bootstrap(at path: AbsolutePath, platforms: Set<TuistGraph.Platform>) throws {
guard try isXCFrameworksProductionSupported() else {
throw CarthageControllerError.xcFrameworksProductionNotSupported(installedVersion: try carthageVersion())
}
public func bootstrap(at path: AbsolutePath, platforms: Set<TuistGraph.Platform>, options: Set<CarthageDependencies.Options>) throws {
let command = buildCarthageCommand(path: path, platforms: platforms, options: options, subcommand: "bootstrap")
let command = buildCarthageCommand(path: path, platforms: platforms, subcommand: "bootstrap")
try System.shared.run(command)
}
public func update(at path: AbsolutePath, platforms: Set<TuistGraph.Platform>, options: Set<CarthageDependencies.Options>) throws {
let command = buildCarthageCommand(path: path, platforms: platforms, options: options, subcommand: "update")
public func update(at path: AbsolutePath, platforms: Set<TuistGraph.Platform>) throws {
guard try isXCFrameworksProductionSupported() else {
throw CarthageControllerError.xcFrameworksProductionNotSupported(installedVersion: try carthageVersion())
}
let command = buildCarthageCommand(path: path, platforms: platforms, subcommand: "update")
try System.shared.run(command)
}
@ -118,7 +129,6 @@ public final class CarthageController: CarthageControlling {
private func buildCarthageCommand(
path: AbsolutePath,
platforms: Set<TuistGraph.Platform>,
options: Set<CarthageDependencies.Options>,
subcommand: String
) -> [String] {
var commandComponents: [String] = [
@ -138,20 +148,9 @@ public final class CarthageController: CarthageControlling {
]
}
if !options.isEmpty {
commandComponents += options
.map { option in
switch option {
case .useXCFrameworks:
return "--use-xcframeworks"
case .noUseBinaries:
return "--no-use-binaries"
}
}
.sorted()
}
commandComponents += [
"--use-xcframeworks",
"--no-use-binaries",
"--use-netrc",
"--cache-builds",
"--new-resolver",
@ -159,4 +158,10 @@ public final class CarthageController: CarthageControlling {
return commandComponents
}
private func isXCFrameworksProductionSupported() throws -> Bool {
// Carthage has supported XCFrameworks production since 0.37.0
// More info here: https://github.com/Carthage/Carthage/releases/tag/0.37.0
try carthageVersion() >= Version(0, 37, 0)
}
}

View File

@ -0,0 +1,47 @@
import Foundation
import TSCBasic
import TSCUtility
import TuistCore
import TuistGraph
import TuistSupport
/// A protocol that defines an interface to generate the `DependenciesGraph` for the `Carthage` dependencies.
public protocol CarthageGraphGenerating {
/// Generates the `DependenciesGraph` for the `Carthage` dependencies.
/// - Parameter path: The path to the directory that contains the `Carthage/Build` directory where `Carthage` installed dependencies.
func generate(at path: AbsolutePath) throws -> DependenciesGraph
}
public final class CarthageGraphGenerator: CarthageGraphGenerating {
public init() {}
public func generate(at path: AbsolutePath) throws -> DependenciesGraph {
let versionFilePaths = try FileHandler.shared
.contentsOfDirectory(path)
.filter { $0.extension == "version" }
let jsonDecoder = JSONDecoder()
let products = try versionFilePaths
.map { try FileHandler.shared.readFile($0) }
.map { try jsonDecoder.decode(CarthageVersionFile.self, from: $0) }
.flatMap { $0.allProducts }
let thirdPartyDependencies: [String: ThirdPartyDependency] = Dictionary(grouping: products, by: \.name)
.compactMapValues { product in
guard let frameworkName = product.first?.container else { return nil }
let path = AbsolutePath("/")
.appending(components: [
Constants.tuistDirectoryName,
Constants.DependenciesDirectory.name,
Constants.DependenciesDirectory.carthageDirectoryName,
frameworkName,
])
let architectures: Set<BinaryArchitecture> = Set(product.flatMap { $0.architectures })
return .xcframework(path: path, architectures: architectures)
}
return DependenciesGraph(thirdPartyDependencies: thirdPartyDependencies)
}
}

View File

@ -26,13 +26,15 @@ enum CocoaPodsInteractorError: FatalError {
// MARK: - CocoaPods Interacting
public protocol CocoaPodsInteracting {
/// Fetches `CocoaPods` dependencies.
/// - Parameter dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
func fetch(dependenciesDirectory: AbsolutePath) throws
/// Installs `CocoaPods` dependencies.
/// - Parameters:
/// - dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
/// - shouldUpdate: Indicates whether dependencies should be updated or fetched based on the `Tuist/Lockfiles/Podfile.lock` lockfile.
func install(dependenciesDirectory: AbsolutePath, shouldUpdate: Bool) throws
/// Update `CocoaPods` dependencies.
/// Removes all cached `CocoaPods` dependencies.
/// - Parameter dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
func update(dependenciesDirectory: AbsolutePath) throws
func clean(dependenciesDirectory: AbsolutePath) throws
}
// MARK: - Cocoapods Interactor
@ -40,11 +42,11 @@ public protocol CocoaPodsInteracting {
public final class CocoaPodsInteractor: CocoaPodsInteracting {
public init() {}
public func fetch(dependenciesDirectory _: AbsolutePath) throws {
public func install(dependenciesDirectory _: AbsolutePath, shouldUpdate _: Bool) throws {
throw CocoaPodsInteractorError.unimplemented
}
public func update(dependenciesDirectory _: AbsolutePath) throws {
public func clean(dependenciesDirectory _: AbsolutePath) throws {
throw CocoaPodsInteractorError.unimplemented
}
}

View File

@ -35,7 +35,7 @@ enum DependenciesControllerError: FatalError {
/// 4. Generating dependencies graph under `./Tuist/Dependencies/graph.json`.
public protocol DependenciesControlling {
/// Fetches dependencies.
/// - Parameter path: Directory whose project's dependencies will be fetched.
/// - Parameter path: Directory where project's dependencies will be fetched.
/// - Parameter dependencies: List of dependencies to fetch.
/// - Parameter swiftVersion: The specified version of Swift. If `nil` is passed then the environments version will be used.
func fetch(
@ -46,7 +46,7 @@ public protocol DependenciesControlling {
/// Updates dependencies.
/// - Parameters:
/// - path: Directory whose project's dependencies will be updated.
/// - path: Directory where project's dependencies will be updated.
/// - dependencies: List of dependencies to update.
/// - swiftVersion: The specified version of Swift. If `nil` is passed then will use the environments version will be used.
func update(
@ -62,18 +62,54 @@ public final class DependenciesController: DependenciesControlling {
private let carthageInteractor: CarthageInteracting
private let cocoaPodsInteractor: CocoaPodsInteracting
private let swiftPackageManagerInteractor: SwiftPackageManagerInteracting
private let dependenciesGraphController: DependenciesGraphControlling
public init(
carthageInteractor: CarthageInteracting = CarthageInteractor(),
cocoaPodsInteractor: CocoaPodsInteracting = CocoaPodsInteractor(),
swiftPackageManagerInteractor: SwiftPackageManagerInteracting = SwiftPackageManagerInteractor()
swiftPackageManagerInteractor: SwiftPackageManagerInteracting = SwiftPackageManagerInteractor(),
dependenciesGraphController: DependenciesGraphControlling = DependenciesGraphController()
) {
self.carthageInteractor = carthageInteractor
self.cocoaPodsInteractor = cocoaPodsInteractor
self.swiftPackageManagerInteractor = swiftPackageManagerInteractor
self.dependenciesGraphController = dependenciesGraphController
}
public func fetch(at path: AbsolutePath, dependencies: Dependencies, swiftVersion: String?) throws {
public func fetch(
at path: AbsolutePath,
dependencies: Dependencies,
swiftVersion: String?
) throws {
try install(
at: path,
dependencies: dependencies,
shouldUpdate: false,
swiftVersion: swiftVersion
)
}
public func update(
at path: AbsolutePath,
dependencies: Dependencies,
swiftVersion: String?
) throws {
try install(
at: path,
dependencies: dependencies,
shouldUpdate: true,
swiftVersion: swiftVersion
)
}
// MARK: - Helpers
private func install(
at path: AbsolutePath,
dependencies: Dependencies,
shouldUpdate: Bool,
swiftVersion: String?
) throws {
let dependenciesDirectory = path
.appending(component: Constants.tuistDirectoryName)
.appending(component: Constants.DependenciesDirectory.name)
@ -83,55 +119,34 @@ public final class DependenciesController: DependenciesControlling {
throw DependenciesControllerError.noPlatforms
}
var dependenciesGraph = DependenciesGraph(thirdPartyDependencies: [:])
if let carthageDepedencies = dependencies.carthage, !carthageDepedencies.dependencies.isEmpty {
try carthageInteractor.fetch(
dependenciesGraph = try carthageInteractor.install(
dependenciesDirectory: dependenciesDirectory,
dependencies: carthageDepedencies,
platforms: platforms
platforms: platforms,
shouldUpdate: shouldUpdate
)
} else {
try carthageInteractor.clean(dependenciesDirectory: dependenciesDirectory)
}
if let swiftPackageManagerDependencies = dependencies.swiftPackageManager, !swiftPackageManagerDependencies.packages.isEmpty {
try swiftPackageManagerInteractor.fetch(
try swiftPackageManagerInteractor.install(
dependenciesDirectory: dependenciesDirectory,
dependencies: swiftPackageManagerDependencies,
shouldUpdate: shouldUpdate,
swiftToolsVersion: swiftVersion
)
} else {
try swiftPackageManagerInteractor.clean(dependenciesDirectory: dependenciesDirectory)
}
}
public func update(at path: AbsolutePath, dependencies: Dependencies, swiftVersion: String?) throws {
let dependenciesDirectory = path
.appending(component: Constants.tuistDirectoryName)
.appending(component: Constants.DependenciesDirectory.name)
let platforms = dependencies.platforms
guard !platforms.isEmpty else {
throw DependenciesControllerError.noPlatforms
}
if let carthageDepedencies = dependencies.carthage, !carthageDepedencies.dependencies.isEmpty {
try carthageInteractor.update(
dependenciesDirectory: dependenciesDirectory,
dependencies: carthageDepedencies,
platforms: platforms
)
if dependenciesGraph.thirdPartyDependencies.isEmpty {
try dependenciesGraphController.clean(at: path)
} else {
try carthageInteractor.clean(dependenciesDirectory: dependenciesDirectory)
}
if let swiftPackageManagerDependencies = dependencies.swiftPackageManager, !swiftPackageManagerDependencies.packages.isEmpty {
try swiftPackageManagerInteractor.update(
dependenciesDirectory: dependenciesDirectory,
dependencies: swiftPackageManagerDependencies,
swiftToolsVersion: swiftVersion
)
} else {
try swiftPackageManagerInteractor.clean(dependenciesDirectory: dependenciesDirectory)
try dependenciesGraphController.save(dependenciesGraph, to: path)
}
}
}

View File

@ -0,0 +1,92 @@
import Foundation
import TSCBasic
import TuistGraph
import TuistSupport
// MARK: - Dependencies Graph Controller Errors
enum DependenciesGraphControllerError: FatalError, Equatable {
case failedToEncodeDependeniesGraph
var type: ErrorType {
switch self {
case .failedToEncodeDependeniesGraph:
return .bug
}
}
var description: String {
switch self {
case .failedToEncodeDependeniesGraph:
return "Couldn't encode the DependenciesGraph as a JSON file."
}
}
}
// MARK: - Dependencies Graph Controlling
/// A protocol that defines an interface to save and load the `DependenciesGraph` using a `graph.json` file.
public protocol DependenciesGraphControlling {
/// Saves the `DependenciesGraph` as `graph.json`.
/// - Parameters:
/// - dependenciesGraph: A model that will be saved.
/// - path: Directory where project's dependencies graph will be saved.
func save(_ dependenciesGraph: DependenciesGraph, to path: AbsolutePath) throws
/// Loads the `DependenciesGraph` from `graph.json` file.
/// - Parameter path: Directory where project's dependencies graph will be loaded.
func load(at path: AbsolutePath) throws -> DependenciesGraph
/// Removes cached `graph.json`.
/// - Parameter path: Directory where project's dependencies graph was saved.
func clean(at path: AbsolutePath) throws
}
// MARK: - Dependencies Graph Controller
public final class DependenciesGraphController: DependenciesGraphControlling {
public init() {}
public func save(_ dependenciesGraph: DependenciesGraph, to path: AbsolutePath) throws {
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let encodedGraph = try jsonEncoder.encode(dependenciesGraph)
guard let encodedGraphContent = String(data: encodedGraph, encoding: .utf8) else {
throw DependenciesGraphControllerError.failedToEncodeDependeniesGraph
}
let graphPath = self.graphPath(at: path)
try FileHandler.shared.touch(graphPath)
try FileHandler.shared.write(encodedGraphContent, path: graphPath, atomically: true)
}
public func load(at path: AbsolutePath) throws -> DependenciesGraph {
let graphPath = self.graphPath(at: path)
let graphData = try FileHandler.shared.readFile(graphPath)
let jsonDecoder = JSONDecoder()
let decodedGraph = try jsonDecoder.decode(DependenciesGraph.self, from: graphData)
return decodedGraph
}
public func clean(at path: AbsolutePath) throws {
let graphPath = self.graphPath(at: path)
try FileHandler.shared.delete(graphPath)
}
// MARK: - Helpers
private func graphPath(at path: AbsolutePath) -> AbsolutePath {
path
.appending(components: [
Constants.tuistDirectoryName,
Constants.DependenciesDirectory.name,
Constants.DependenciesDirectory.graphName,
])
}
}

View File

@ -33,25 +33,16 @@ enum SwiftPackageManagerInteractorError: FatalError, Equatable {
// MARK: - Swift Package Manager Interacting
public protocol SwiftPackageManagerInteracting {
/// Fetches `Swift Package Manager` dependencies.
/// Installs `Swift Package Manager` dependencies.
/// - Parameters:
/// - dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
/// - dependencies: List of dependencies to fetch using `Swift Package Manager`.
/// - dependencies: List of dependencies to install using `Swift Package Manager`.
/// - shouldUpdate: Indicates whether dependencies should be updated or fetched based on the `Tuist/Lockfiles/Package.resolved` lockfile.
/// - swiftToolsVersion: The version of Swift tools that will be used to resolve dependencies. If `nil` is passed then the environments version will be used.
func fetch(
dependenciesDirectory: AbsolutePath,
dependencies: SwiftPackageManagerDependencies,
swiftToolsVersion: String?
) throws
/// Updates `Swift Package Manager` dependencies.
/// - Parameters:
/// - dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
/// - dependencies: List of dependencies to update using `Swift Package Manager`.
/// - swiftToolsVersion: The version of Swift tools that will be used to resolve dependencies. If `nil` is passed then the environments version will be used.
func update(
func install(
dependenciesDirectory: AbsolutePath,
dependencies: SwiftPackageManagerDependencies,
shouldUpdate: Bool,
swiftToolsVersion: String?
) throws
@ -74,64 +65,15 @@ public final class SwiftPackageManagerInteractor: SwiftPackageManagerInteracting
self.swiftPackageManagerController = swiftPackageManagerController
}
public func fetch(
dependenciesDirectory: AbsolutePath,
dependencies: SwiftPackageManagerDependencies,
swiftToolsVersion: String?
) throws {
logger.warning("Support for Swift Package Manager dependencies is currently being worked on and is not ready to be used yet.")
logger.info("Resolving and fetching Swift Package Manager dependencies.", metadata: .section)
try install(
dependenciesDirectory: dependenciesDirectory,
dependencies: dependencies,
shouldUpdate: false,
swiftToolsVersion: swiftToolsVersion
)
logger.info("Packages resolved and fetched successfully.", metadata: .subsection)
}
public func update(
dependenciesDirectory: AbsolutePath,
dependencies: SwiftPackageManagerDependencies,
swiftToolsVersion: String?
) throws {
logger.warning("Support for Swift Package Manager dependencies is currently being worked on and is not ready to be used yet.")
logger.info("Updating Swift Package Manager dependencies.", metadata: .section)
try install(
dependenciesDirectory: dependenciesDirectory,
dependencies: dependencies,
shouldUpdate: true,
swiftToolsVersion: swiftToolsVersion
)
logger.info("Updating resolved and fetched successfully.", metadata: .subsection)
}
public func clean(dependenciesDirectory: AbsolutePath) throws {
let swiftPackageManagerDirectory = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.swiftPackageManagerDirectoryName)
let packageResolvedPath = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.lockfilesDirectoryName)
.appending(component: Constants.DependenciesDirectory.packageResolvedName)
try fileHandler.delete(swiftPackageManagerDirectory)
try fileHandler.delete(packageResolvedPath)
}
// MARK: - Installation
/// Installs given `dependencies` at `dependenciesDirectory`.
private func install(
public func install(
dependenciesDirectory: AbsolutePath,
dependencies: SwiftPackageManagerDependencies,
shouldUpdate: Bool,
swiftToolsVersion: String?
) throws {
logger.warning("Support for Swift Package Manager dependencies is currently being worked on and is not ready to be used yet.")
logger.info("Installing Swift Package Manager dependencies.", metadata: .subsection)
try fileHandler.inTemporaryDirectory { temporaryDirectoryPath in
// prepare paths
let pathsProvider = SwiftPackageManagerPathsProvider(
@ -152,8 +94,23 @@ public final class SwiftPackageManagerInteractor: SwiftPackageManagerInteracting
// post installation
try saveDepedencies(pathsProvider: pathsProvider)
}
logger.info("Swift Package Manager dependencies installed successfully.", metadata: .subsection)
}
public func clean(dependenciesDirectory: AbsolutePath) throws {
let swiftPackageManagerDirectory = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.swiftPackageManagerDirectoryName)
let packageResolvedPath = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.lockfilesDirectoryName)
.appending(component: Constants.DependenciesDirectory.packageResolvedName)
try fileHandler.delete(swiftPackageManagerDirectory)
try fileHandler.delete(packageResolvedPath)
}
// MARK: - Installation
/// Loads lockfile and dependencies into working directory if they had been saved before.
private func loadDependencies(
pathsProvider: SwiftPackageManagerPathsProvider,

View File

@ -1,33 +1,23 @@
import TSCBasic
import TuistGraph
import TuistGraphTesting
@testable import TuistDependencies
public final class MockCarthageInteractor: CarthageInteracting {
public init() {}
var invokedFetch = false
var fetchStub: ((AbsolutePath, CarthageDependencies, Set<Platform>) throws -> Void)?
var invokedInstall = false
var installStub: ((AbsolutePath, CarthageDependencies, Set<Platform>, Bool) throws -> DependenciesGraph)?
public func fetch(
public func install(
dependenciesDirectory: AbsolutePath,
dependencies: CarthageDependencies,
platforms: Set<Platform>
) throws {
invokedFetch = true
try fetchStub?(dependenciesDirectory, dependencies, platforms)
}
var invokedUpdate = false
var updateStub: ((AbsolutePath, CarthageDependencies, Set<Platform>) throws -> Void)?
public func update(
dependenciesDirectory: AbsolutePath,
dependencies: CarthageDependencies,
platforms: Set<Platform>
) throws {
invokedUpdate = true
try updateStub?(dependenciesDirectory, dependencies, platforms)
platforms: Set<Platform>,
shouldUpdate: Bool
) throws -> DependenciesGraph {
invokedInstall = true
return try installStub?(dependenciesDirectory, dependencies, platforms, shouldUpdate) ?? .test()
}
var invokedClean = false

View File

@ -0,0 +1,654 @@
import Foundation
import TSCBasic
@testable import TuistDependencies
extension CarthageVersionFile {
static func test(
commitish: String = "",
iOS: [Product] = [],
macOS: [Product] = [],
watchOS: [Product] = [],
tvOS: [Product] = []
) -> Self {
.init(
commitish: commitish,
iOS: iOS,
macOS: macOS,
watchOS: watchOS,
tvOS: tvOS
)
}
static var testAlamofire: Self {
.init(
commitish: "5.4.3",
iOS: [
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "5fbbffeccfee11c3d48840b59111c9483f985e01a53109e920cf60a79df743cb",
name: "Alamofire",
container: "Alamofire.xcframework",
identifier: "ios-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "615afccd6819b4d613bf80375d08a39b15beea9e00698b8f3a83d35fb4e7be1c",
name: "Alamofire",
container: "Alamofire.xcframework",
identifier: "ios-arm64_armv7"
),
],
macOS: [
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "57f5c800334d5f7a1c46285e1e00fd9e26abaf836dbcec92578b69403dd69596",
name: "Alamofire",
container: "Alamofire.xcframework",
identifier: "macos-arm64_x86_64"
),
],
watchOS: [
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "ce13aaa785ffa2c3c16ba88b8ab54e97bac5ba0a41a5ac22d9552a84100b07dc",
name: "Alamofire",
container: "Alamofire.xcframework",
identifier: "watchos-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "54293baccc33dc9f91018a4ec9253f3b17faa0c62fe3eef973835a76bc1357c9",
name: "Alamofire",
container: "Alamofire.xcframework",
identifier: "watchos-arm64_32_armv7k"
),
],
tvOS: [
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "bf2734287d14a558d4b739727ebe5f9f9a1f6ed2aeb0c5781b633b8bcac37d70",
name: "Alamofire",
container: "Alamofire.xcframework",
identifier: "tvos-arm64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "0494fd475a6c62575d810bf50c8c3d09a5b3e5cc192d6f88005e45ff718bf503",
name: "Alamofire",
container: "Alamofire.xcframework",
identifier: "tvos-arm64_x86_64-simulator"
),
]
)
}
static var testRxSwift: Self {
.init(
commitish: "6.2.0",
iOS: [
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "5cb314834422a56915d9404d12e072600665eeba5815b89ca547032eaa7b372e",
name: "RxBlocking",
container: "RxBlocking.xcframework",
identifier: "ios-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "4ed2e9c1871c5338a481fa0b73cf7a71e92ded5f7477292e116742c543431101",
name: "RxBlocking",
container: "RxBlocking.xcframework",
identifier: "ios-arm64_armv7"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "5c1719d1c61658eddba8809440b809fb23ab64e24f196db24797627683fd5485",
name: "RxCocoa",
container: "RxCocoa.xcframework",
identifier: "ios-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "c3492a3348d7a20396c185dbee479fa19f601b77f0f627608ff67cc029e06e3c",
name: "RxCocoa",
container: "RxCocoa.xcframework",
identifier: "ios-arm64_armv7"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "3725a226c968c7331363377e4e4e2d8b218ae27391ea815263c840e5a66da76a",
name: "RxRelay",
container: "RxRelay.xcframework",
identifier: "ios-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "4dbc43707ed1bde34abec38f4cf1e903604a20dcd4937130e27922ad6f98caac",
name: "RxRelay",
container: "RxRelay.xcframework",
identifier: "ios-arm64_armv7"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "d2e8ba83ea1e99dc8a22fcc94650e6d8f915293fd811ef1d9a34a3ccb84d4d93",
name: "RxSwift",
container: "RxSwift.xcframework",
identifier: "ios-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "0c2f64086afd5835576d820cd9671b42659a7612afb42d44149757b93d39119c",
name: "RxSwift",
container: "RxSwift.xcframework",
identifier: "ios-arm64_armv7"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "8a2f2e875b174a68c1330791d12e65119f912ad660fd08c14c831a9e6ecd7cfb",
name: "RxTest",
container: "RxTest.xcframework",
identifier: "ios-arm64_armv7"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "6b863d8e43e0831195b03fc0bcb8f84d7d652b0565966f9a890839c45365bf61",
name: "RxTest",
container: "RxTest.xcframework",
identifier: "ios-arm64_i386_x86_64-simulator"
),
],
macOS: [
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "86b1f3a3476db7b35180336876c2731a49b546899d647ab99d52909a6635c883",
name: "RxBlocking",
container: "RxBlocking.xcframework",
identifier: "macos-arm64_x86_64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "f7b73b8a44fd2992b330e6c9d044be3873aa9195cb98990dd041abc86622b359",
name: "RxCocoa",
container: "RxCocoa.xcframework",
identifier: "macos-arm64_x86_64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "55ce7c5d0f4fe9df7a609d23174dd4ae62a66333f30051d880285f58967ef415",
name: "RxRelay",
container: "RxRelay.xcframework",
identifier: "macos-arm64_x86_64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "a9ff6e3d6213ea912c3136173af678bd4bb1840057ce88c0b451b30962ccb0bd",
name: "RxSwift",
container: "RxSwift.xcframework",
identifier: "macos-arm64_x86_64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "3a81b7ea565c01a2663cb3d970bc8411c13f43c092bdb515432d49fc12ea3c72",
name: "RxTest",
container: "RxTest.xcframework",
identifier: "macos-arm64_x86_64"
),
],
watchOS: [
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "fa387e94a430ae1a2185b00d2e71d7c837adf11646ba036fa0800b08ed3db154",
name: "RxBlocking",
container: "RxBlocking.xcframework",
identifier: "watchos-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "c6c6e1483df5fa04a8192c1bd4a3eb9f8d2db46d4b77659aa05e0102548c072d",
name: "RxBlocking",
container: "RxBlocking.xcframework",
identifier: "watchos-arm64_32_armv7k"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "e614a6a4c2cfb6547753381d3638302e8955ad21893cb5d2f6e07b46946dbe36",
name: "RxCocoa",
container: "RxCocoa.xcframework",
identifier: "watchos-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "69ffb2f2502a30e7b4bcd5258fd39b97fdc9c81a2e9e49d8770703dd4c07e0ee",
name: "RxCocoa",
container: "RxCocoa.xcframework",
identifier: "watchos-arm64_32_armv7k"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "79091465303a53417f13caa34c6f5c16713d1888f2c7620a2790574d772bc6c2",
name: "RxRelay",
container: "RxRelay.xcframework",
identifier: "watchos-arm64_32_armv7k"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "d9c5f13754933b994beaded1c5ff0edde45efb86ca13aebc08bae7e567727c18",
name: "RxRelay",
container: "RxRelay.xcframework",
identifier: "watchos-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "2cb6d5c3c02b778610b0ce1d8af2ff5f69fca80cf2c6382da4f14fb936735689",
name: "RxSwift",
container: "RxSwift.xcframework",
identifier: "watchos-arm64_i386_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "f65a096e38c6940c1b1fdbc5c24f11e8d8af9cafb219723b1e4e2041da6c81c0",
name: "RxSwift",
container: "RxSwift.xcframework",
identifier: "watchos-arm64_32_armv7k"
),
],
tvOS: [
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "acc2a71bb7bc9c27a0ec95385f048a0040d88bab44deddcb9a1a3b61320c4e6f",
name: "RxBlocking",
container: "RxBlocking.xcframework",
identifier: "tvos-arm64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "12c36aea3712976f34d25e0337ba8f6393b76ecfc3e1351a2ab3023c99018b33",
name: "RxBlocking",
container: "RxBlocking.xcframework",
identifier: "tvos-arm64_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "4420a6279e55e2c5c3f4b6aa7567eda56aa81a162315b028d6ac7b5689266ef3",
name: "RxCocoa",
container: "RxCocoa.xcframework",
identifier: "tvos-arm64_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "a4f7428701da909fb88bdcda602f1a9d1526de20914702dc1519565aa41135eb",
name: "RxCocoa",
container: "RxCocoa.xcframework",
identifier: "tvos-arm64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "c312b7732b52838109e93404db33a8693a5f86384c6de054d7878bd98f64f780",
name: "RxRelay",
container: "RxRelay.xcframework",
identifier: "tvos-arm64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "b73a59d4a73bfdffb3c382588ee48fc74a3d0c5f4fe87242a0588a597ac289d0",
name: "RxRelay",
container: "RxRelay.xcframework",
identifier: "tvos-arm64_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "e4d95e41cdc6a5e624f1dfc649935a93e6f9eb6a979ace621a8afbd4e9ea6389",
name: "RxSwift",
container: "RxSwift.xcframework",
identifier: "tvos-arm64_x86_64-simulator"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "9665ba98bde33d0fb8e77d0f70a2950421104b555942375d15515d6c63585eac",
name: "RxSwift",
container: "RxSwift.xcframework",
identifier: "tvos-arm64"
),
.init(
swiftToolchainVersion: "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
hash: "6e281b30953f1c8db38d9f1652926e3466188a633cd16bf74339715d931759ec",
name: "RxTest",
container: "RxTest.xcframework",
identifier: "tvos-arm64_x86_64-simulator"
),
]
)
}
}
extension CarthageVersionFile {
/// A snapshot of `.Alamofire.version` file
/// that was generated by `Carthage` in` `0.37.0` version
/// using `carthage bootstrap --platform iOS,macOS,tvOS,watchOS --use-xcframeworks --no-use-binaries --use-netrc --cache-builds --new-resolver` command
static var testAlamofireJson: String {
"""
{
"Mac" : [
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "57f5c800334d5f7a1c46285e1e00fd9e26abaf836dbcec92578b69403dd69596",
"name" : "Alamofire",
"container" : "Alamofire.xcframework",
"identifier" : "macos-arm64_x86_64"
}
],
"watchOS" : [
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "ce13aaa785ffa2c3c16ba88b8ab54e97bac5ba0a41a5ac22d9552a84100b07dc",
"name" : "Alamofire",
"container" : "Alamofire.xcframework",
"identifier" : "watchos-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "54293baccc33dc9f91018a4ec9253f3b17faa0c62fe3eef973835a76bc1357c9",
"name" : "Alamofire",
"container" : "Alamofire.xcframework",
"identifier" : "watchos-arm64_32_armv7k"
}
],
"tvOS" : [
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "bf2734287d14a558d4b739727ebe5f9f9a1f6ed2aeb0c5781b633b8bcac37d70",
"name" : "Alamofire",
"container" : "Alamofire.xcframework",
"identifier" : "tvos-arm64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "0494fd475a6c62575d810bf50c8c3d09a5b3e5cc192d6f88005e45ff718bf503",
"name" : "Alamofire",
"container" : "Alamofire.xcframework",
"identifier" : "tvos-arm64_x86_64-simulator"
}
],
"commitish" : "5.4.3",
"iOS" : [
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "5fbbffeccfee11c3d48840b59111c9483f985e01a53109e920cf60a79df743cb",
"name" : "Alamofire",
"container" : "Alamofire.xcframework",
"identifier" : "ios-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "615afccd6819b4d613bf80375d08a39b15beea9e00698b8f3a83d35fb4e7be1c",
"name" : "Alamofire",
"container" : "Alamofire.xcframework",
"identifier" : "ios-arm64_armv7"
}
]
}
"""
}
/// A snapshot of `.RxSwift.version` file
/// that was generated by `Carthage` in` `0.37.0` version
/// using `carthage bootstrap --platform iOS,macOS,tvOS,watchOS --use-xcframeworks --no-use-binaries --use-netrc --cache-builds --new-resolver` command
static var testRxSwiftJson: String {
"""
{
"Mac" : [
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "86b1f3a3476db7b35180336876c2731a49b546899d647ab99d52909a6635c883",
"name" : "RxBlocking",
"container" : "RxBlocking.xcframework",
"identifier" : "macos-arm64_x86_64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "f7b73b8a44fd2992b330e6c9d044be3873aa9195cb98990dd041abc86622b359",
"name" : "RxCocoa",
"container" : "RxCocoa.xcframework",
"identifier" : "macos-arm64_x86_64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "55ce7c5d0f4fe9df7a609d23174dd4ae62a66333f30051d880285f58967ef415",
"name" : "RxRelay",
"container" : "RxRelay.xcframework",
"identifier" : "macos-arm64_x86_64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "a9ff6e3d6213ea912c3136173af678bd4bb1840057ce88c0b451b30962ccb0bd",
"name" : "RxSwift",
"container" : "RxSwift.xcframework",
"identifier" : "macos-arm64_x86_64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "3a81b7ea565c01a2663cb3d970bc8411c13f43c092bdb515432d49fc12ea3c72",
"name" : "RxTest",
"container" : "RxTest.xcframework",
"identifier" : "macos-arm64_x86_64"
}
],
"watchOS" : [
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "fa387e94a430ae1a2185b00d2e71d7c837adf11646ba036fa0800b08ed3db154",
"name" : "RxBlocking",
"container" : "RxBlocking.xcframework",
"identifier" : "watchos-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "c6c6e1483df5fa04a8192c1bd4a3eb9f8d2db46d4b77659aa05e0102548c072d",
"name" : "RxBlocking",
"container" : "RxBlocking.xcframework",
"identifier" : "watchos-arm64_32_armv7k"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "e614a6a4c2cfb6547753381d3638302e8955ad21893cb5d2f6e07b46946dbe36",
"name" : "RxCocoa",
"container" : "RxCocoa.xcframework",
"identifier" : "watchos-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "69ffb2f2502a30e7b4bcd5258fd39b97fdc9c81a2e9e49d8770703dd4c07e0ee",
"name" : "RxCocoa",
"container" : "RxCocoa.xcframework",
"identifier" : "watchos-arm64_32_armv7k"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "79091465303a53417f13caa34c6f5c16713d1888f2c7620a2790574d772bc6c2",
"name" : "RxRelay",
"container" : "RxRelay.xcframework",
"identifier" : "watchos-arm64_32_armv7k"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "d9c5f13754933b994beaded1c5ff0edde45efb86ca13aebc08bae7e567727c18",
"name" : "RxRelay",
"container" : "RxRelay.xcframework",
"identifier" : "watchos-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "2cb6d5c3c02b778610b0ce1d8af2ff5f69fca80cf2c6382da4f14fb936735689",
"name" : "RxSwift",
"container" : "RxSwift.xcframework",
"identifier" : "watchos-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "f65a096e38c6940c1b1fdbc5c24f11e8d8af9cafb219723b1e4e2041da6c81c0",
"name" : "RxSwift",
"container" : "RxSwift.xcframework",
"identifier" : "watchos-arm64_32_armv7k"
}
],
"tvOS" : [
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "acc2a71bb7bc9c27a0ec95385f048a0040d88bab44deddcb9a1a3b61320c4e6f",
"name" : "RxBlocking",
"container" : "RxBlocking.xcframework",
"identifier" : "tvos-arm64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "12c36aea3712976f34d25e0337ba8f6393b76ecfc3e1351a2ab3023c99018b33",
"name" : "RxBlocking",
"container" : "RxBlocking.xcframework",
"identifier" : "tvos-arm64_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "4420a6279e55e2c5c3f4b6aa7567eda56aa81a162315b028d6ac7b5689266ef3",
"name" : "RxCocoa",
"container" : "RxCocoa.xcframework",
"identifier" : "tvos-arm64_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "a4f7428701da909fb88bdcda602f1a9d1526de20914702dc1519565aa41135eb",
"name" : "RxCocoa",
"container" : "RxCocoa.xcframework",
"identifier" : "tvos-arm64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "c312b7732b52838109e93404db33a8693a5f86384c6de054d7878bd98f64f780",
"name" : "RxRelay",
"container" : "RxRelay.xcframework",
"identifier" : "tvos-arm64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "b73a59d4a73bfdffb3c382588ee48fc74a3d0c5f4fe87242a0588a597ac289d0",
"name" : "RxRelay",
"container" : "RxRelay.xcframework",
"identifier" : "tvos-arm64_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "e4d95e41cdc6a5e624f1dfc649935a93e6f9eb6a979ace621a8afbd4e9ea6389",
"name" : "RxSwift",
"container" : "RxSwift.xcframework",
"identifier" : "tvos-arm64_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "9665ba98bde33d0fb8e77d0f70a2950421104b555942375d15515d6c63585eac",
"name" : "RxSwift",
"container" : "RxSwift.xcframework",
"identifier" : "tvos-arm64"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "6e281b30953f1c8db38d9f1652926e3466188a633cd16bf74339715d931759ec",
"name" : "RxTest",
"container" : "RxTest.xcframework",
"identifier" : "tvos-arm64_x86_64-simulator"
}
],
"commitish" : "6.2.0",
"iOS" : [
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "5cb314834422a56915d9404d12e072600665eeba5815b89ca547032eaa7b372e",
"name" : "RxBlocking",
"container" : "RxBlocking.xcframework",
"identifier" : "ios-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "4ed2e9c1871c5338a481fa0b73cf7a71e92ded5f7477292e116742c543431101",
"name" : "RxBlocking",
"container" : "RxBlocking.xcframework",
"identifier" : "ios-arm64_armv7"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "5c1719d1c61658eddba8809440b809fb23ab64e24f196db24797627683fd5485",
"name" : "RxCocoa",
"container" : "RxCocoa.xcframework",
"identifier" : "ios-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "c3492a3348d7a20396c185dbee479fa19f601b77f0f627608ff67cc029e06e3c",
"name" : "RxCocoa",
"container" : "RxCocoa.xcframework",
"identifier" : "ios-arm64_armv7"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "3725a226c968c7331363377e4e4e2d8b218ae27391ea815263c840e5a66da76a",
"name" : "RxRelay",
"container" : "RxRelay.xcframework",
"identifier" : "ios-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "4dbc43707ed1bde34abec38f4cf1e903604a20dcd4937130e27922ad6f98caac",
"name" : "RxRelay",
"container" : "RxRelay.xcframework",
"identifier" : "ios-arm64_armv7"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "d2e8ba83ea1e99dc8a22fcc94650e6d8f915293fd811ef1d9a34a3ccb84d4d93",
"name" : "RxSwift",
"container" : "RxSwift.xcframework",
"identifier" : "ios-arm64_i386_x86_64-simulator"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "0c2f64086afd5835576d820cd9671b42659a7612afb42d44149757b93d39119c",
"name" : "RxSwift",
"container" : "RxSwift.xcframework",
"identifier" : "ios-arm64_armv7"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "8a2f2e875b174a68c1330791d12e65119f912ad660fd08c14c831a9e6ecd7cfb",
"name" : "RxTest",
"container" : "RxTest.xcframework",
"identifier" : "ios-arm64_armv7"
},
{
"swiftToolchainVersion" : "5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)",
"hash" : "6b863d8e43e0831195b03fc0bcb8f84d7d652b0565966f9a890839c45365bf61",
"name" : "RxTest",
"container" : "RxTest.xcframework",
"identifier" : "ios-arm64_i386_x86_64-simulator"
}
]
}
"""
}
}
extension CarthageVersionFile.Product {
static func test(
swiftToolchainVersion: String = "",
hash: String = "",
name: String = "",
container: String = "",
identifier: String = ""
) -> Self {
.init(
swiftToolchainVersion: swiftToolchainVersion,
hash: hash,
name: name,
container: container,
identifier: identifier
)
}
}

View File

@ -32,26 +32,24 @@ public final class MockCarthageController: CarthageControlling {
}
var invokedBootstrap = false
var bootstrapStub: ((AbsolutePath, Set<TuistGraph.Platform>, Set<CarthageDependencies.Options>) throws -> Void)?
var bootstrapStub: ((AbsolutePath, Set<TuistGraph.Platform>) throws -> Void)?
public func bootstrap(
at path: AbsolutePath,
platforms: Set<TuistGraph.Platform>,
options: Set<CarthageDependencies.Options>
platforms: Set<TuistGraph.Platform>
) throws {
invokedBootstrap = true
try bootstrapStub?(path, platforms, options)
try bootstrapStub?(path, platforms)
}
var invokedUpdate = false
var updateStub: ((AbsolutePath, Set<TuistGraph.Platform>, Set<CarthageDependencies.Options>) throws -> Void)?
var updateStub: ((AbsolutePath, Set<TuistGraph.Platform>) throws -> Void)?
public func update(
at path: AbsolutePath,
platforms: Set<TuistGraph.Platform>,
options: Set<CarthageDependencies.Options>
platforms: Set<TuistGraph.Platform>
) throws {
invokedUpdate = true
try updateStub?(path, platforms, options)
try updateStub?(path, platforms)
}
}

View File

@ -0,0 +1,17 @@
import TSCBasic
import TSCUtility
import TuistGraph
@testable import TuistDependencies
public final class MockCarthageGraphGenerator: CarthageGraphGenerating {
public init() {}
var invokedGenerate = false
var generateStub: ((AbsolutePath) throws -> DependenciesGraph)?
public func generate(at path: AbsolutePath) throws -> DependenciesGraph {
invokedGenerate = true
return try generateStub?(path) ?? .test()
}
}

View File

@ -6,19 +6,22 @@ import TuistCore
public final class MockCocoaPodsInteractor: CocoaPodsInteracting {
public init() {}
var invokedFetch = false
var fetchStub: ((AbsolutePath) throws -> Void)?
var invokedInstall = false
var installStub: ((AbsolutePath, Bool) throws -> Void)?
public func fetch(dependenciesDirectory: AbsolutePath) throws {
invokedFetch = true
try fetchStub?(dependenciesDirectory)
public func install(
dependenciesDirectory: AbsolutePath,
shouldUpdate: Bool
) throws {
invokedInstall = true
try installStub?(dependenciesDirectory, shouldUpdate)
}
var invokedUpdate = false
var updateStub: ((AbsolutePath) throws -> Void)?
var invokedClean = false
var cleanStub: ((AbsolutePath) throws -> Void)?
public func update(dependenciesDirectory: AbsolutePath) throws {
invokedUpdate = true
try updateStub?(dependenciesDirectory)
public func clean(dependenciesDirectory: AbsolutePath) throws {
invokedClean = true
try cleanStub?(dependenciesDirectory)
}
}

View File

@ -0,0 +1,33 @@
import Foundation
import TSCBasic
import TuistGraph
@testable import TuistDependencies
public final class MockDependenciesGraphController: DependenciesGraphControlling {
public init() {}
var invokedSave = false
var saveStub: ((DependenciesGraph, AbsolutePath) throws -> Void)?
public func save(_ dependenciesGraph: DependenciesGraph, to path: AbsolutePath) throws {
invokedSave = true
try saveStub?(dependenciesGraph, path)
}
var invokedLoad = false
var loadStub: ((AbsolutePath) throws -> DependenciesGraph)?
public func load(at path: AbsolutePath) throws -> DependenciesGraph {
invokedLoad = true
return try loadStub?(path) ?? .test()
}
var invokedClean = false
var cleanStub: ((AbsolutePath) throws -> Void)?
public func clean(at path: AbsolutePath) throws {
invokedClean = true
try cleanStub?(path)
}
}

View File

@ -6,28 +6,17 @@ import TuistGraph
public final class MockSwiftPackageManagerInteractor: SwiftPackageManagerInteracting {
public init() {}
var invokedFetch = false
var fetchStub: ((AbsolutePath, SwiftPackageManagerDependencies, String?) throws -> Void)?
var invokedInstall = false
var installStub: ((AbsolutePath, SwiftPackageManagerDependencies, Bool, String?) throws -> Void)?
public func fetch(
public func install(
dependenciesDirectory: AbsolutePath,
dependencies: SwiftPackageManagerDependencies,
shouldUpdate: Bool,
swiftToolsVersion: String?
) throws {
invokedFetch = true
try fetchStub?(dependenciesDirectory, dependencies, swiftToolsVersion)
}
var invokedUpdate = false
var updateStub: ((AbsolutePath, SwiftPackageManagerDependencies, String?) throws -> Void)?
public func update(
dependenciesDirectory: AbsolutePath,
dependencies: SwiftPackageManagerDependencies,
swiftToolsVersion: String?
) throws {
invokedUpdate = true
try updateStub?(dependenciesDirectory, dependencies, swiftToolsVersion)
invokedInstall = true
try installStub?(dependenciesDirectory, dependencies, shouldUpdate, swiftToolsVersion)
}
var invokedClean = false

View File

@ -0,0 +1,16 @@
import Foundation
import TSCBasic
/// A directed acyclic graph (DAG) that Tuist uses to represent the third party dependency tree.
public struct DependenciesGraph: Equatable, Codable {
/// A dictionary where the keys are the names of dependencies,
/// and the values are the dependencies themselves.
public var thirdPartyDependencies: [String: ThirdPartyDependency]
/// Create an instance of `DependenciesGraph` model.
public init(
thirdPartyDependencies: [String: ThirdPartyDependency]
) {
self.thirdPartyDependencies = thirdPartyDependencies
}
}

View File

@ -0,0 +1,43 @@
import Foundation
import TSCBasic
// A enum containing information about third party dependency.
public enum ThirdPartyDependency: Hashable, Equatable, Codable {
/// A dependency that represents a pre-compiled .xcframework.
case xcframework(path: AbsolutePath, architectures: Set<BinaryArchitecture>)
}
// MARK: - Codable
extension ThirdPartyDependency {
private enum Kind: String, Codable {
case xcframework
}
private enum CodingKeys: String, CodingKey {
case kind
case path
case architectures
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let kind = try container.decode(Kind.self, forKey: .kind)
switch kind {
case .xcframework:
let path = try container.decode(AbsolutePath.self, forKey: .path)
let architectures = try container.decode(Set<BinaryArchitecture>.self, forKey: .architectures)
self = .xcframework(path: path, architectures: architectures)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .xcframework(path, architectures):
try container.encode(Kind.xcframework, forKey: .kind)
try container.encode(path, forKey: .path)
try container.encode(architectures, forKey: .architectures)
}
}
}

View File

@ -5,19 +5,13 @@ public struct CarthageDependencies: Equatable {
/// List of depedencies that can be installed using Carthage.
public let dependencies: [Dependency]
/// List of options for Carthage installation.
public let options: Set<Options>
/// Initializes a new `CarthageDependencies` instance.
/// - Parameters:
/// - dependencies: List of depedencies that can be installed using Carthage.
/// - options: List of options for Carthage installation.
public init(
_ dependencies: [Dependency],
options: Set<Options>
_ dependencies: [Dependency]
) {
self.dependencies = dependencies
self.options = options
}
/// Returns `Cartfile` representation.
@ -77,12 +71,3 @@ public extension CarthageDependencies {
}
}
}
// MARK: - CarthageDependencies.Options
public extension CarthageDependencies {
enum Options: Equatable {
case useXCFrameworks
case noUseBinaries
}
}

View File

@ -1,7 +1,7 @@
import Foundation
import TSCBasic
/// An directed acyclic graph (DAG) that Tuist uses to represent the dependency tree.
/// A directed acyclic graph (DAG) that Tuist uses to represent the dependency tree.
public struct ValueGraph: Equatable, Codable {
/// The name of the graph
public var name: String

View File

@ -0,0 +1,35 @@
import Foundation
import TSCBasic
import TuistGraph
public extension DependenciesGraph {
/// A snapshot of `graph.json` file.
static var testJson: String {
"""
{
"thirdPartyDependencies" : {
"RxSwift" : {
"kind" : "xcframework",
"path" : "/Tuist/Dependencies/Carthage/RxSwift.xcframework",
"architectures" : [
"arm64_32",
"x86_64",
"armv7",
"armv7k",
"arm64",
"i386"
]
}
}
}
"""
}
static func test(
thirdPartyDependencies: [String: ThirdPartyDependency] = [:]
) -> Self {
.init(
thirdPartyDependencies: thirdPartyDependencies
)
}
}

View File

@ -0,0 +1,12 @@
import Foundation
import TSCBasic
import TuistGraph
public extension ThirdPartyDependency {
static func testXCFramework(
path: AbsolutePath = AbsolutePath.root.appending(RelativePath("Test.xcframework")),
architectures: Set<BinaryArchitecture> = []
) -> Self {
return .xcframework(path: path, architectures: architectures)
}
}

View File

@ -9,9 +9,8 @@ extension TuistGraph.CarthageDependencies {
/// Creates `TuistGraph.CarthageDependencies` instance from `ProjectDescription.CarthageDependencies` instance.
static func from(manifest: ProjectDescription.CarthageDependencies) throws -> Self {
let dependencies = manifest.dependencies.map { TuistGraph.CarthageDependencies.Dependency.from(manifest: $0) }
let options = manifest.options.map { TuistGraph.CarthageDependencies.Options.from(manifest: $0) }
return .init(dependencies, options: Set(options))
return .init(dependencies)
}
}
@ -46,15 +45,3 @@ extension TuistGraph.CarthageDependencies.Requirement {
}
}
}
extension TuistGraph.CarthageDependencies.Options {
/// Creates `TuistGraph.CarthageDependencies.Options` instance from `ProjectDescription.CarthageDependencies.Options` instance.
static func from(manifest: ProjectDescription.CarthageDependencies.Options) -> Self {
switch manifest {
case .useXCFrameworks:
return .useXCFrameworks
case .noUseBinaries:
return .noUseBinaries
}
}
}

View File

@ -32,6 +32,7 @@ public enum Constants {
public enum DependenciesDirectory {
public static let name = "Dependencies"
public static let graphName = "graph.json"
public static let lockfilesDirectoryName = "Lockfiles"
public static let cartfileResolvedName = "Cartfile.resolved"
public static let packageResolvedName = "Package.resolved"

View File

@ -140,6 +140,30 @@ public extension XCTestCase {
}
}
/// Asserts that a `json` object decoded as a `T` type is equal to an `expected` value.
func XCTAssertDecodableEqualToJson<C: Decodable & Equatable>(_ json: String, _ expected: C, file: StaticString = #file, line: UInt = #line) {
guard let jsonData = json.data(using: .utf8) else {
XCTFail("Invalid JSON.", file: file, line: line)
return
}
let decoder = JSONDecoder()
let decoded = XCTTry(try decoder.decode(C.self, from: jsonData), file: file, line: line)
let errorString = """
The JSON-decoded object doesn't match the expected value:
Given
=======
\(decoded)
Expected
=======
\(expected)
"""
XCTAssertEqual(decoded, expected, errorString, file: file, line: line)
}
func XCTEmpty<S>(_ array: [S], file: StaticString = #file, line: UInt = #line) {
XCTAssertTrue(array.isEmpty, "Expected array to be empty but it's not. It contains the following elements: \(array)", file: file, line: line)
}

View File

@ -5,16 +5,10 @@ import XCTest
final class CarthageDependenciesTests: XCTestCase {
func test_carthageDependencies_codable() throws {
let subject: CarthageDependencies = .carthage(
[
.github(path: "Dependency/Dependency", requirement: .revision("xyz")),
.git(path: "Git/Git", requirement: .atLeast("1.2.3")),
],
options: [
.useXCFrameworks,
.noUseBinaries,
]
)
let subject: CarthageDependencies = [
.github(path: "Dependency/Dependency", requirement: .revision("xyz")),
.git(path: "Git/Git", requirement: .atLeast("1.2.3")),
]
XCTAssertCodable(subject)
}
@ -61,16 +55,4 @@ final class CarthageDependenciesTests: XCTestCase {
let subject: CarthageDependencies.Requirement = .revision("revision")
XCTAssertCodable(subject)
}
// MARK: - Carthage Options tests
func test_carthageOptions_useXCFrameworks_codable() throws {
let subject: CarthageDependencies.Options = .useXCFrameworks
XCTAssertCodable(subject)
}
func test_carthageOptions_noUseBinaries_codable() throws {
let subject: CarthageDependencies.Options = .noUseBinaries
XCTAssertCodable(subject)
}
}

View File

@ -6,22 +6,14 @@ import XCTest
final class DependenciesTests: XCTestCase {
func test_dependencies_codable() throws {
let subject = Dependencies(
carthage: .carthage(
[
.github(path: "Dependency1/Dependency1", requirement: .branch("BranchName")),
.git(path: "Dependency2/Dependency2", requirement: .upToNext("1.2.3")),
],
options: [
.useXCFrameworks,
.noUseBinaries,
]
),
swiftPackageManager: .swiftPackageManager(
[
.local(path: "Path/Path"),
.remote(url: "Dependency3/Dependency3", requirement: .exact("4.5.6")),
]
),
carthage: [
.github(path: "Dependency1/Dependency1", requirement: .branch("BranchName")),
.git(path: "Dependency2/Dependency2", requirement: .upToNext("1.2.3")),
],
swiftPackageManager: [
.local(path: "Path/Path"),
.remote(url: "Dependency3/Dependency3", requirement: .exact("4.5.6")),
],
platforms: [.iOS, .macOS, .tvOS, .watchOS]
)
XCTAssertCodable(subject)

View File

@ -5,12 +5,10 @@ import XCTest
final class SwiftPackageManagerDependenciesTests: XCTestCase {
func test_swiftPackageManagerDependencies_codable() {
let subject: SwiftPackageManagerDependencies = .swiftPackageManager(
[
.local(path: "Path/Path"),
.remote(url: "Dependency3/Dependency3", requirement: .exact("4.5.6")),
]
)
let subject: SwiftPackageManagerDependencies = [
.local(path: "Path/Path"),
.remote(url: "Dependency3/Dependency3", requirement: .exact("4.5.6")),
]
XCTAssertCodable(subject)
}
}

View File

@ -11,24 +11,28 @@ import XCTest
final class CarthageInteractorTests: TuistUnitTestCase {
private var subject: CarthageInteractor!
private var carthageController: MockCarthageController!
private var carthageGraphGenerator: MockCarthageGraphGenerator!
override func setUp() {
super.setUp()
carthageController = MockCarthageController()
carthageGraphGenerator = MockCarthageGraphGenerator()
subject = CarthageInteractor(
carthageController: carthageController
carthageController: carthageController,
carthageGraphGenerator: carthageGraphGenerator
)
}
override func tearDown() {
carthageController = nil
carthageGraphGenerator = nil
subject = nil
super.tearDown()
}
func test_fetch() throws {
func test_install_when_shouldNotBeUpdated() throws {
// Given
carthageController.canUseSystemCarthageStub = { true }
@ -41,30 +45,35 @@ final class CarthageInteractorTests: TuistUnitTestCase {
.appending(component: Constants.DependenciesDirectory.carthageDirectoryName)
let platforms: Set<Platform> = [.iOS, .watchOS, .macOS, .tvOS]
let options = Set<CarthageDependencies.Options>([])
let stubbedDependencies = CarthageDependencies(
[
.github(path: "Moya", requirement: .exact("1.1.1")),
],
options: options
]
)
carthageController.bootstrapStub = { arg0, arg1, arg2 in
carthageController.bootstrapStub = { arg0, arg1 in
XCTAssertEqual(arg0, try self.temporaryPath())
XCTAssertEqual(arg1, platforms)
XCTAssertEqual(arg2, options)
try self.simulateCarthageOutput(at: arg0)
}
carthageGraphGenerator.generateStub = { arg0 in
XCTAssertEqual(arg0, try self.temporaryPath().appending(components: "Carthage", "Build"))
return .test()
}
// When
try subject.fetch(
let got = try subject.install(
dependenciesDirectory: dependenciesDirectory,
dependencies: stubbedDependencies,
platforms: platforms
platforms: platforms,
shouldUpdate: false
)
// Then
XCTAssertEqual(got, .test())
XCTAssertTrue(carthageGraphGenerator.invokedGenerate)
try XCTAssertDirectoryContentEqual(
dependenciesDirectory,
[
@ -121,59 +130,7 @@ final class CarthageInteractorTests: TuistUnitTestCase {
)
}
func test_fetch_throws_when_carthageUnavailableInEnvironment() throws {
// Given
carthageController.canUseSystemCarthageStub = { false }
let rootPath = try TemporaryDirectory(removeTreeOnDeinit: true).path
let dependenciesDirectory = rootPath
.appending(component: Constants.DependenciesDirectory.name)
let dependencies = CarthageDependencies(
[
.github(path: "Moya", requirement: .exact("1.1.1")),
],
options: []
)
let platforms: Set<Platform> = [.iOS]
// When / Then
XCTAssertThrowsSpecific(
try subject.fetch(
dependenciesDirectory: dependenciesDirectory,
dependencies: dependencies,
platforms: platforms
),
CarthageInteractorError.carthageNotFound
)
}
func test_fetch_throws_when_xcFrameworkdProductionUnsupported_and_useXCFrameworksSpecifiedInOptions() throws {
// Given
carthageController.canUseSystemCarthageStub = { true }
carthageController.isXCFrameworksProductionSupportedStub = { false }
let rootPath = try TemporaryDirectory(removeTreeOnDeinit: true).path
let dependenciesDirectory = rootPath
.appending(component: Constants.DependenciesDirectory.name)
let dependencies = CarthageDependencies(
[
.github(path: "Moya", requirement: .exact("1.1.1")),
],
options: [.useXCFrameworks]
)
let platforms: Set<Platform> = [.iOS]
XCTAssertThrowsSpecific(
try subject.fetch(
dependenciesDirectory: dependenciesDirectory,
dependencies: dependencies,
platforms: platforms
),
CarthageInteractorError.xcFrameworksProductionNotSupported
)
}
func test_update() throws {
func test_install_when_shouldBeUpdated() throws {
// Given
carthageController.canUseSystemCarthageStub = { true }
@ -186,30 +143,35 @@ final class CarthageInteractorTests: TuistUnitTestCase {
.appending(component: Constants.DependenciesDirectory.carthageDirectoryName)
let platforms: Set<Platform> = [.iOS, .watchOS, .macOS, .tvOS]
let options = Set<CarthageDependencies.Options>([])
let stubbedDependencies = CarthageDependencies(
[
.github(path: "Moya", requirement: .exact("1.1.1")),
],
options: options
]
)
carthageController.updateStub = { arg0, arg1, arg2 in
carthageController.updateStub = { arg0, arg1 in
XCTAssertEqual(arg0, try self.temporaryPath())
XCTAssertEqual(arg1, platforms)
XCTAssertEqual(arg2, options)
try self.simulateCarthageOutput(at: arg0)
}
carthageGraphGenerator.generateStub = { arg0 in
XCTAssertEqual(arg0, try self.temporaryPath().appending(components: "Carthage", "Build"))
return .test()
}
// When
try subject.update(
let got = try subject.install(
dependenciesDirectory: dependenciesDirectory,
dependencies: stubbedDependencies,
platforms: platforms
platforms: platforms,
shouldUpdate: true
)
// Then
XCTAssertEqual(got, .test())
XCTAssertTrue(carthageGraphGenerator.invokedGenerate)
try XCTAssertDirectoryContentEqual(
dependenciesDirectory,
[
@ -266,7 +228,7 @@ final class CarthageInteractorTests: TuistUnitTestCase {
)
}
func test_update_throws_when_carthageUnavailableInEnvironment() throws {
func test_install_throws_when_carthageUnavailableInEnvironment() throws {
// Given
carthageController.canUseSystemCarthageStub = { false }
@ -276,48 +238,22 @@ final class CarthageInteractorTests: TuistUnitTestCase {
let dependencies = CarthageDependencies(
[
.github(path: "Moya", requirement: .exact("1.1.1")),
],
options: []
]
)
let platforms: Set<Platform> = [.iOS]
// When / Then
XCTAssertThrowsSpecific(
try subject.update(
try subject.install(
dependenciesDirectory: dependenciesDirectory,
dependencies: dependencies,
platforms: platforms
platforms: platforms,
shouldUpdate: true
),
CarthageInteractorError.carthageNotFound
)
}
func test_update_throws_when_xcFrameworkdProductionUnsupported_and_useXCFrameworksSpecifiedInOptions() throws {
// Given
carthageController.canUseSystemCarthageStub = { true }
carthageController.isXCFrameworksProductionSupportedStub = { false }
let rootPath = try TemporaryDirectory(removeTreeOnDeinit: true).path
let dependenciesDirectory = rootPath
.appending(component: Constants.DependenciesDirectory.name)
let dependencies = CarthageDependencies(
[
.github(path: "Moya", requirement: .exact("1.1.1")),
],
options: [.useXCFrameworks]
)
let platforms: Set<Platform> = [.iOS]
XCTAssertThrowsSpecific(
try subject.update(
dependenciesDirectory: dependenciesDirectory,
dependencies: dependencies,
platforms: platforms
),
CarthageInteractorError.xcFrameworksProductionNotSupported
)
}
func test_clean() throws {
// Given
let rootPath = try temporaryPath()

View File

@ -0,0 +1,94 @@
import TSCBasic
import TSCUtility
import TuistCore
import TuistGraph
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistDependenciesTesting
@testable import TuistSupportTesting
final class CarthageVersionFileTests: TuistUnitTestCase {
func test_codable_alamofire() {
// Given
let json = CarthageVersionFile.testAlamofireJson
let expected = CarthageVersionFile.testAlamofire
// When / Then
XCTAssertDecodableEqualToJson(json, expected)
}
func test_codable_rxSwift() {
// Given
let json = CarthageVersionFile.testRxSwiftJson
let expected = CarthageVersionFile.testRxSwift
// When / Then
XCTAssertDecodableEqualToJson(json, expected)
}
func test_allProducts() {
// Given
let iOSProduct = CarthageVersionFile.Product.test(name: "iOS")
let macOSProduct = CarthageVersionFile.Product.test(name: "macOS")
let watchOSProduct = CarthageVersionFile.Product.test(name: "watchOS")
let tvOSProduct = CarthageVersionFile.Product.test(name: "tvOS")
let subject = CarthageVersionFile.test(
iOS: [iOSProduct],
macOS: [macOSProduct],
watchOS: [watchOSProduct],
tvOS: [tvOSProduct]
)
// When
let got = subject.allProducts
// Then
let expected: [CarthageVersionFile.Product] = [iOSProduct, macOSProduct, watchOSProduct, tvOSProduct]
XCTAssertEqual(got, expected)
}
func test_product_architectures_arm64_x8664() {
// Given
let subject: CarthageVersionFile.Product = .test(
identifier: "macos-arm64_x86_64"
)
// When
let got = subject.architectures
// Then
let expected: [BinaryArchitecture] = [.arm64, .x8664]
XCTAssertEqual(got, expected)
}
func test_product_architectures_arm64_i386_x8664() {
// Given
let subject: CarthageVersionFile.Product = .test(
identifier: "ios-arm64_i386_x86_64-simulator"
)
// When
let got = subject.architectures
// Then
let expected: [BinaryArchitecture] = [.arm64, .i386, .x8664]
XCTAssertEqual(got, expected)
}
func test_product_architectures_arm6432_armv7k() {
// Given
let subject: CarthageVersionFile.Product = .test(
identifier: "watchos-arm64_32_armv7k"
)
// When
let got = subject.architectures
// Then
let expected: [BinaryArchitecture] = [.arm6432, .armv7k]
XCTAssertEqual(got, expected)
}
}

View File

@ -55,41 +55,31 @@ final class CarthageControllerTests: TuistUnitTestCase {
XCTAssertEqual(try subject.carthageVersion(), Version(0, 37, 0))
}
func test_isXCFrameworksProductionSupported_notSupported() {
// Given
system.stubs["/usr/bin/env carthage version"] = (stderror: nil, stdout: "0.36.1", exitstatus: 0)
// When / Then
XCTAssertFalse(try subject.isXCFrameworksProductionSupported())
}
func test_isXCFrameworksProductionSupported_supported() {
func test_bootstrap() throws {
// Given
system.stubs["/usr/bin/env carthage version"] = (stderror: nil, stdout: "0.37.0", exitstatus: 0)
// When / Then
XCTAssertTrue(try subject.isXCFrameworksProductionSupported())
}
func test_bootstrap() throws {
// Given
let path = try temporaryPath()
system.succeedCommand([
"carthage",
"bootstrap",
"--project-directory",
path.pathString,
"--use-xcframeworks",
"--no-use-binaries",
"--use-netrc",
"--cache-builds",
"--new-resolver",
])
// When / Then
XCTAssertNoThrow(try subject.bootstrap(at: path, platforms: [], options: []))
XCTAssertNoThrow(try subject.bootstrap(at: path, platforms: []))
}
func test_bootstrap_with_platforms() throws {
// Given
system.stubs["/usr/bin/env carthage version"] = (stderror: nil, stdout: "0.37.0", exitstatus: 0)
let path = try temporaryPath()
system.succeedCommand([
"carthage",
@ -98,17 +88,22 @@ final class CarthageControllerTests: TuistUnitTestCase {
path.pathString,
"--platform",
"iOS",
"--use-xcframeworks",
"--no-use-binaries",
"--use-netrc",
"--cache-builds",
"--new-resolver",
])
// When / Then
XCTAssertNoThrow(try subject.bootstrap(at: path, platforms: [.iOS], options: []))
XCTAssertNoThrow(try subject.bootstrap(at: path, platforms: [.iOS]))
}
func test_bootstrap_with_platforms_and_options() throws {
func test_bootstrap_with_platforms_throws_when_xcFrameworkdProductionUnsupported() throws {
// Given
let carthageVersion = Version("0.36.0")
system.stubs["/usr/bin/env carthage version"] = (stderror: nil, stdout: carthageVersion.description, exitstatus: 0)
let path = try temporaryPath()
system.succeedCommand([
"carthage",
@ -117,36 +112,45 @@ final class CarthageControllerTests: TuistUnitTestCase {
path.pathString,
"--platform",
"iOS",
"--no-use-binaries",
"--use-xcframeworks",
"--no-use-binaries",
"--use-netrc",
"--cache-builds",
"--new-resolver",
])
// When / Then
XCTAssertNoThrow(try subject.bootstrap(at: path, platforms: [.iOS], options: [.noUseBinaries, .useXCFrameworks]))
XCTAssertThrowsSpecific(
try subject.bootstrap(at: path, platforms: [.iOS]),
CarthageControllerError.xcFrameworksProductionNotSupported(installedVersion: carthageVersion)
)
}
func test_update() throws {
// Given
system.stubs["/usr/bin/env carthage version"] = (stderror: nil, stdout: "0.37.0", exitstatus: 0)
let path = try temporaryPath()
system.succeedCommand([
"carthage",
"update",
"--project-directory",
path.pathString,
"--use-xcframeworks",
"--no-use-binaries",
"--use-netrc",
"--cache-builds",
"--new-resolver",
])
// When / Then
XCTAssertNoThrow(try subject.update(at: path, platforms: [], options: []))
XCTAssertNoThrow(try subject.update(at: path, platforms: []))
}
func test_update_with_platforms() throws {
// Given
system.stubs["/usr/bin/env carthage version"] = (stderror: nil, stdout: "0.37.0", exitstatus: 0)
let path = try temporaryPath()
system.succeedCommand([
"carthage",
@ -155,17 +159,22 @@ final class CarthageControllerTests: TuistUnitTestCase {
path.pathString,
"--platform",
"iOS",
"--use-xcframeworks",
"--no-use-binaries",
"--use-netrc",
"--cache-builds",
"--new-resolver",
])
// When / Then
XCTAssertNoThrow(try subject.update(at: path, platforms: [.iOS], options: []))
XCTAssertNoThrow(try subject.update(at: path, platforms: [.iOS]))
}
func test_update_with_platforms_and_options() throws {
func test_update_with_platforms_throws_when_xcFrameworkdProductionUnsupported() throws {
// Given
let carthageVersion = Version("0.36.0")
system.stubs["/usr/bin/env carthage version"] = (stderror: nil, stdout: carthageVersion.description, exitstatus: 0)
let path = try temporaryPath()
system.succeedCommand([
"carthage",
@ -174,14 +183,17 @@ final class CarthageControllerTests: TuistUnitTestCase {
path.pathString,
"--platform",
"iOS",
"--no-use-binaries",
"--use-xcframeworks",
"--no-use-binaries",
"--use-netrc",
"--cache-builds",
"--new-resolver",
])
// When / Then
XCTAssertNoThrow(try subject.update(at: path, platforms: [.iOS], options: [.noUseBinaries, .useXCFrameworks]))
XCTAssertThrowsSpecific(
try subject.bootstrap(at: path, platforms: [.iOS]),
CarthageControllerError.xcFrameworksProductionNotSupported(installedVersion: carthageVersion)
)
}
}

View File

@ -0,0 +1,74 @@
import TSCBasic
import TSCUtility
import TuistCore
import TuistGraph
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistDependenciesTesting
@testable import TuistSupportTesting
final class CarthageGraphGeneratorTests: TuistUnitTestCase {
private var subject: CarthageGraphGenerator!
override func setUp() {
super.setUp()
subject = CarthageGraphGenerator()
}
override func tearDown() {
super.tearDown()
subject = nil
}
func test_generate() throws {
// Given
let path = try temporaryPath()
let rxSwiftVersionFilePath = path.appending(component: ".RxSwift.version")
try fileHandler.touch(rxSwiftVersionFilePath)
try fileHandler.write(CarthageVersionFile.testRxSwiftJson, path: rxSwiftVersionFilePath, atomically: true)
let alamofireVersionFilePath = path.appending(component: ".Alamofire.version")
try fileHandler.touch(alamofireVersionFilePath)
try fileHandler.write(CarthageVersionFile.testAlamofireJson, path: alamofireVersionFilePath, atomically: true)
// When
let got = try subject.generate(at: path)
// Then
let expected = DependenciesGraph(
thirdPartyDependencies: [
"RxSwift": .xcframework(
path: "/Tuist/Dependencies/Carthage/RxSwift.xcframework",
architectures: [.armv7k, .arm64, .i386, .x8664, .armv7, .arm6432]
),
"RxCocoa": .xcframework(
path: "/Tuist/Dependencies/Carthage/RxCocoa.xcframework",
architectures: [.armv7, .armv7k, .arm6432, .x8664, .i386, .arm64]
),
"RxRelay": .xcframework(
path: "/Tuist/Dependencies/Carthage/RxRelay.xcframework",
architectures: [.armv7k, .i386, .x8664, .armv7, .arm6432, .arm64]
),
"RxTest": .xcframework(
path: "/Tuist/Dependencies/Carthage/RxTest.xcframework",
architectures: [.i386, .x8664, .arm64, .armv7]
),
"RxBlocking": .xcframework(
path: "/Tuist/Dependencies/Carthage/RxBlocking.xcframework",
architectures: [.arm64, .armv7k, .x8664, .arm6432, .i386, .armv7]
),
"Alamofire": .xcframework(
path: "/Tuist/Dependencies/Carthage/Alamofire.xcframework",
architectures: [.x8664, .armv7k, .i386, .arm64, .armv7, .arm6432]
),
]
)
XCTAssertEqual(got, expected)
}
}

View File

@ -18,19 +18,19 @@ final class CocoaPodsInteractorTests: TuistUnitTestCase {
super.tearDown()
}
func test_fetch() throws {
func test_install_unimplemented() throws {
// Given
let stubbedPath = try temporaryPath()
// When/Then
XCTAssertThrowsSpecific(try subject.fetch(dependenciesDirectory: stubbedPath), CocoaPodsInteractorError.unimplemented)
XCTAssertThrowsSpecific(try subject.install(dependenciesDirectory: stubbedPath, shouldUpdate: false), CocoaPodsInteractorError.unimplemented)
}
func test_update() throws {
func test_clean_unimplemented() throws {
// Given
let stubbedPath = try temporaryPath()
// When/Then
XCTAssertThrowsSpecific(try subject.update(dependenciesDirectory: stubbedPath), CocoaPodsInteractorError.unimplemented)
XCTAssertThrowsSpecific(try subject.clean(dependenciesDirectory: stubbedPath), CocoaPodsInteractorError.unimplemented)
}
}

View File

@ -1,5 +1,4 @@
import TSCBasic
import TuistCore
import TuistGraph
import TuistSupport
import XCTest
@ -14,6 +13,7 @@ final class DependenciesControllerTests: TuistUnitTestCase {
private var carthageInteractor: MockCarthageInteractor!
private var cocoaPodsInteractor: MockCocoaPodsInteractor!
private var swiftPackageManagerInteractor: MockSwiftPackageManagerInteractor!
private var dependenciesGraphController: MockDependenciesGraphController!
override func setUp() {
super.setUp()
@ -21,11 +21,13 @@ final class DependenciesControllerTests: TuistUnitTestCase {
carthageInteractor = MockCarthageInteractor()
cocoaPodsInteractor = MockCocoaPodsInteractor()
swiftPackageManagerInteractor = MockSwiftPackageManagerInteractor()
dependenciesGraphController = MockDependenciesGraphController()
subject = DependenciesController(
carthageInteractor: carthageInteractor,
cocoaPodsInteractor: cocoaPodsInteractor,
swiftPackageManagerInteractor: swiftPackageManagerInteractor
swiftPackageManagerInteractor: swiftPackageManagerInteractor,
dependenciesGraphController: dependenciesGraphController
)
}
@ -35,6 +37,7 @@ final class DependenciesControllerTests: TuistUnitTestCase {
carthageInteractor = nil
cocoaPodsInteractor = nil
swiftPackageManagerInteractor = nil
dependenciesGraphController = nil
super.tearDown()
}
@ -53,10 +56,6 @@ final class DependenciesControllerTests: TuistUnitTestCase {
[
.github(path: "Moya", requirement: .exact("1.1.1")),
.github(path: "RxSwift", requirement: .exact("2.0.0")),
],
options: [
.useXCFrameworks,
.noUseBinaries,
]
)
let dependencies = Dependencies(
@ -64,11 +63,19 @@ final class DependenciesControllerTests: TuistUnitTestCase {
swiftPackageManager: nil,
platforms: platforms
)
let graph: DependenciesGraph = .test(thirdPartyDependencies: ["Name": .testXCFramework()])
carthageInteractor.fetchStub = { arg0, arg1, arg2 in
carthageInteractor.installStub = { arg0, arg1, arg2, arg3 in
XCTAssertEqual(arg0, dependenciesDirectoryPath)
XCTAssertEqual(arg1, carthageDependencies)
XCTAssertEqual(arg2, platforms)
XCTAssertFalse(arg3)
return graph
}
dependenciesGraphController.saveStub = { arg0, arg1 in
XCTAssertEqual(graph, arg0)
XCTAssertEqual(rootPath, arg1)
}
// When
@ -76,15 +83,16 @@ final class DependenciesControllerTests: TuistUnitTestCase {
// Then
XCTAssertFalse(carthageInteractor.invokedClean)
XCTAssertFalse(carthageInteractor.invokedUpdate)
XCTAssertTrue(carthageInteractor.invokedFetch)
XCTAssertTrue(carthageInteractor.invokedInstall)
XCTAssertTrue(swiftPackageManagerInteractor.invokedClean)
XCTAssertFalse(swiftPackageManagerInteractor.invokedUpdate)
XCTAssertFalse(swiftPackageManagerInteractor.invokedFetch)
XCTAssertFalse(swiftPackageManagerInteractor.invokedInstall)
XCTAssertFalse(cocoaPodsInteractor.invokedFetch)
XCTAssertFalse(cocoaPodsInteractor.invokedUpdate)
XCTAssertFalse(cocoaPodsInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedInstall)
XCTAssertTrue(dependenciesGraphController.invokedSave)
XCTAssertFalse(dependenciesGraphController.invokedClean)
}
func test_fetch_swiftPackageManger() throws {
@ -108,10 +116,15 @@ final class DependenciesControllerTests: TuistUnitTestCase {
)
let swiftVersion = "5.4.0"
swiftPackageManagerInteractor.fetchStub = { arg0, arg1, arg2 in
swiftPackageManagerInteractor.installStub = { arg0, arg1, arg2, arg3 in
XCTAssertEqual(arg0, dependenciesDirectoryPath)
XCTAssertEqual(arg1, swiftPackageManagerDependencies)
XCTAssertEqual(arg2, swiftVersion)
XCTAssertFalse(arg2)
XCTAssertEqual(arg3, swiftVersion)
}
dependenciesGraphController.saveStub = { arg0, arg1 in
XCTAssertEqual(.test(), arg0)
XCTAssertEqual(rootPath, arg1)
}
// When
@ -119,15 +132,16 @@ final class DependenciesControllerTests: TuistUnitTestCase {
// Then
XCTAssertFalse(swiftPackageManagerInteractor.invokedClean)
XCTAssertFalse(swiftPackageManagerInteractor.invokedUpdate)
XCTAssertTrue(swiftPackageManagerInteractor.invokedFetch)
XCTAssertTrue(swiftPackageManagerInteractor.invokedInstall)
XCTAssertTrue(carthageInteractor.invokedClean)
XCTAssertFalse(carthageInteractor.invokedUpdate)
XCTAssertFalse(carthageInteractor.invokedFetch)
XCTAssertFalse(carthageInteractor.invokedInstall)
XCTAssertFalse(cocoaPodsInteractor.invokedFetch)
XCTAssertFalse(cocoaPodsInteractor.invokedUpdate)
XCTAssertFalse(cocoaPodsInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedInstall)
XCTAssertFalse(dependenciesGraphController.invokedSave)
XCTAssertTrue(dependenciesGraphController.invokedClean)
}
func test_fetch_carthage_swiftPackageManger() throws {
@ -142,10 +156,6 @@ final class DependenciesControllerTests: TuistUnitTestCase {
[
.github(path: "Moya", requirement: .exact("1.1.1")),
.github(path: "RxSwift", requirement: .exact("2.0.0")),
],
options: [
.useXCFrameworks,
.noUseBinaries,
]
)
let swiftPackageManagerDependencies = SwiftPackageManagerDependencies(
@ -160,32 +170,42 @@ final class DependenciesControllerTests: TuistUnitTestCase {
platforms: platforms
)
let swiftVersion = "5.4.0"
let graph: DependenciesGraph = .test(thirdPartyDependencies: ["Name": .testXCFramework()])
carthageInteractor.fetchStub = { arg0, arg1, arg2 in
carthageInteractor.installStub = { arg0, arg1, arg2, arg3 in
XCTAssertEqual(arg0, dependenciesDirectoryPath)
XCTAssertEqual(arg1, carthageDependencies)
XCTAssertEqual(arg2, platforms)
XCTAssertFalse(arg3)
return graph
}
swiftPackageManagerInteractor.fetchStub = { arg0, arg1, arg2 in
swiftPackageManagerInteractor.installStub = { arg0, arg1, arg2, arg3 in
XCTAssertEqual(arg0, dependenciesDirectoryPath)
XCTAssertEqual(arg1, swiftPackageManagerDependencies)
XCTAssertEqual(arg2, swiftVersion)
XCTAssertFalse(arg2)
XCTAssertEqual(arg3, swiftVersion)
}
dependenciesGraphController.saveStub = { arg0, arg1 in
XCTAssertEqual(graph, arg0)
XCTAssertEqual(rootPath, arg1)
}
// When
try subject.fetch(at: rootPath, dependencies: dependencies, swiftVersion: swiftVersion)
// Then
XCTAssertTrue(carthageInteractor.invokedFetch)
XCTAssertFalse(carthageInteractor.invokedUpdate)
XCTAssertTrue(carthageInteractor.invokedInstall)
XCTAssertFalse(carthageInteractor.invokedClean)
XCTAssertTrue(swiftPackageManagerInteractor.invokedFetch)
XCTAssertFalse(swiftPackageManagerInteractor.invokedUpdate)
XCTAssertTrue(swiftPackageManagerInteractor.invokedInstall)
XCTAssertFalse(swiftPackageManagerInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedFetch)
XCTAssertFalse(cocoaPodsInteractor.invokedUpdate)
XCTAssertFalse(cocoaPodsInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedInstall)
XCTAssertTrue(dependenciesGraphController.invokedSave)
XCTAssertFalse(dependenciesGraphController.invokedClean)
}
func test_fetch_throws_when_noPlatforms() throws {
@ -193,7 +213,7 @@ final class DependenciesControllerTests: TuistUnitTestCase {
let rootPath = try temporaryPath()
let dependencies = Dependencies(
carthage: .init([], options: []),
carthage: .init([]),
swiftPackageManager: .init([]),
platforms: []
)
@ -210,7 +230,7 @@ final class DependenciesControllerTests: TuistUnitTestCase {
let rootPath = try temporaryPath()
let dependencies = Dependencies(
carthage: .init([], options: []),
carthage: .init([]),
swiftPackageManager: .init([]),
platforms: [.iOS]
)
@ -219,16 +239,17 @@ final class DependenciesControllerTests: TuistUnitTestCase {
try subject.fetch(at: rootPath, dependencies: dependencies, swiftVersion: nil)
// Then
XCTAssertFalse(carthageInteractor.invokedFetch)
XCTAssertFalse(carthageInteractor.invokedUpdate)
XCTAssertFalse(carthageInteractor.invokedInstall)
XCTAssertTrue(carthageInteractor.invokedClean)
XCTAssertFalse(swiftPackageManagerInteractor.invokedFetch)
XCTAssertFalse(swiftPackageManagerInteractor.invokedUpdate)
XCTAssertFalse(swiftPackageManagerInteractor.invokedInstall)
XCTAssertTrue(swiftPackageManagerInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedFetch)
XCTAssertFalse(cocoaPodsInteractor.invokedUpdate)
XCTAssertFalse(cocoaPodsInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedInstall)
XCTAssertFalse(dependenciesGraphController.invokedSave)
XCTAssertTrue(dependenciesGraphController.invokedClean)
}
// MARK: - Update
@ -245,10 +266,6 @@ final class DependenciesControllerTests: TuistUnitTestCase {
[
.github(path: "Moya", requirement: .exact("1.1.1")),
.github(path: "RxSwift", requirement: .exact("2.0.0")),
],
options: [
.useXCFrameworks,
.noUseBinaries,
]
)
let dependencies = Dependencies(
@ -256,11 +273,19 @@ final class DependenciesControllerTests: TuistUnitTestCase {
swiftPackageManager: nil,
platforms: platforms
)
let graph: DependenciesGraph = .test(thirdPartyDependencies: ["Name": .testXCFramework()])
carthageInteractor.updateStub = { arg0, arg1, arg2 in
carthageInteractor.installStub = { arg0, arg1, arg2, arg3 in
XCTAssertEqual(arg0, dependenciesDirectoryPath)
XCTAssertEqual(arg1, carthageDependencies)
XCTAssertEqual(arg2, platforms)
XCTAssertTrue(arg3)
return graph
}
dependenciesGraphController.saveStub = { arg0, arg1 in
XCTAssertEqual(graph, arg0)
XCTAssertEqual(rootPath, arg1)
}
// When
@ -268,15 +293,16 @@ final class DependenciesControllerTests: TuistUnitTestCase {
// Then
XCTAssertFalse(carthageInteractor.invokedClean)
XCTAssertFalse(carthageInteractor.invokedFetch)
XCTAssertTrue(carthageInteractor.invokedUpdate)
XCTAssertTrue(carthageInteractor.invokedInstall)
XCTAssertTrue(swiftPackageManagerInteractor.invokedClean)
XCTAssertFalse(swiftPackageManagerInteractor.invokedUpdate)
XCTAssertFalse(swiftPackageManagerInteractor.invokedFetch)
XCTAssertFalse(swiftPackageManagerInteractor.invokedInstall)
XCTAssertFalse(cocoaPodsInteractor.invokedFetch)
XCTAssertFalse(cocoaPodsInteractor.invokedUpdate)
XCTAssertFalse(cocoaPodsInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedInstall)
XCTAssertTrue(dependenciesGraphController.invokedSave)
XCTAssertFalse(dependenciesGraphController.invokedClean)
}
func test_update_swiftPackageManger() throws {
@ -300,10 +326,15 @@ final class DependenciesControllerTests: TuistUnitTestCase {
)
let swiftVersion = "5.4.0"
swiftPackageManagerInteractor.fetchStub = { arg0, arg1, arg2 in
swiftPackageManagerInteractor.installStub = { arg0, arg1, arg2, arg3 in
XCTAssertEqual(arg0, dependenciesDirectoryPath)
XCTAssertEqual(arg1, swiftPackageManagerDependencies)
XCTAssertEqual(arg2, swiftVersion)
XCTAssertTrue(arg2)
XCTAssertEqual(arg3, swiftVersion)
}
dependenciesGraphController.saveStub = { arg0, arg1 in
XCTAssertEqual(.test(), arg0)
XCTAssertEqual(rootPath, arg1)
}
// When
@ -311,15 +342,16 @@ final class DependenciesControllerTests: TuistUnitTestCase {
// Then
XCTAssertFalse(swiftPackageManagerInteractor.invokedClean)
XCTAssertFalse(swiftPackageManagerInteractor.invokedFetch)
XCTAssertTrue(swiftPackageManagerInteractor.invokedUpdate)
XCTAssertTrue(swiftPackageManagerInteractor.invokedInstall)
XCTAssertTrue(carthageInteractor.invokedClean)
XCTAssertFalse(carthageInteractor.invokedUpdate)
XCTAssertFalse(carthageInteractor.invokedFetch)
XCTAssertFalse(carthageInteractor.invokedInstall)
XCTAssertFalse(cocoaPodsInteractor.invokedFetch)
XCTAssertFalse(cocoaPodsInteractor.invokedUpdate)
XCTAssertFalse(cocoaPodsInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedInstall)
XCTAssertFalse(dependenciesGraphController.invokedSave)
XCTAssertTrue(dependenciesGraphController.invokedClean)
}
func test_update_carthage_swiftPackageManger() throws {
@ -334,10 +366,6 @@ final class DependenciesControllerTests: TuistUnitTestCase {
[
.github(path: "Moya", requirement: .exact("1.1.1")),
.github(path: "RxSwift", requirement: .exact("2.0.0")),
],
options: [
.useXCFrameworks,
.noUseBinaries,
]
)
let swiftPackageManagerDependencies = SwiftPackageManagerDependencies(
@ -352,16 +380,25 @@ final class DependenciesControllerTests: TuistUnitTestCase {
platforms: platforms
)
let swiftVersion = "5.4.0"
let graph: DependenciesGraph = .test(thirdPartyDependencies: ["Name": .testXCFramework()])
carthageInteractor.updateStub = { arg0, arg1, arg2 in
carthageInteractor.installStub = { arg0, arg1, arg2, arg3 in
XCTAssertEqual(arg0, dependenciesDirectoryPath)
XCTAssertEqual(arg1, carthageDependencies)
XCTAssertEqual(arg2, platforms)
XCTAssertTrue(arg3)
return graph
}
swiftPackageManagerInteractor.fetchStub = { arg0, arg1, arg2 in
swiftPackageManagerInteractor.installStub = { arg0, arg1, arg2, arg3 in
XCTAssertEqual(arg0, dependenciesDirectoryPath)
XCTAssertEqual(arg1, swiftPackageManagerDependencies)
XCTAssertEqual(arg2, swiftVersion)
XCTAssertTrue(arg2)
XCTAssertEqual(arg3, swiftVersion)
}
dependenciesGraphController.saveStub = { arg0, arg1 in
XCTAssertEqual(graph, arg0)
XCTAssertEqual(rootPath, arg1)
}
// When
@ -369,15 +406,16 @@ final class DependenciesControllerTests: TuistUnitTestCase {
// Then
XCTAssertFalse(carthageInteractor.invokedClean)
XCTAssertFalse(carthageInteractor.invokedFetch)
XCTAssertTrue(carthageInteractor.invokedUpdate)
XCTAssertTrue(carthageInteractor.invokedInstall)
XCTAssertFalse(swiftPackageManagerInteractor.invokedClean)
XCTAssertFalse(swiftPackageManagerInteractor.invokedFetch)
XCTAssertTrue(swiftPackageManagerInteractor.invokedUpdate)
XCTAssertTrue(swiftPackageManagerInteractor.invokedInstall)
XCTAssertFalse(cocoaPodsInteractor.invokedFetch)
XCTAssertFalse(cocoaPodsInteractor.invokedUpdate)
XCTAssertFalse(cocoaPodsInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedInstall)
XCTAssertTrue(dependenciesGraphController.invokedSave)
XCTAssertFalse(dependenciesGraphController.invokedClean)
}
func test_update_throws_when_noPlatforms() throws {
@ -385,7 +423,7 @@ final class DependenciesControllerTests: TuistUnitTestCase {
let rootPath = try temporaryPath()
let dependencies = Dependencies(
carthage: .init([], options: []),
carthage: .init([]),
swiftPackageManager: .init([]),
platforms: []
)
@ -402,7 +440,7 @@ final class DependenciesControllerTests: TuistUnitTestCase {
let rootPath = try temporaryPath()
let dependencies = Dependencies(
carthage: .init([], options: []),
carthage: .init([]),
swiftPackageManager: .init([]),
platforms: [.iOS]
)
@ -411,15 +449,16 @@ final class DependenciesControllerTests: TuistUnitTestCase {
try subject.update(at: rootPath, dependencies: dependencies, swiftVersion: nil)
// Then
XCTAssertFalse(carthageInteractor.invokedFetch)
XCTAssertFalse(carthageInteractor.invokedUpdate)
XCTAssertFalse(carthageInteractor.invokedInstall)
XCTAssertTrue(carthageInteractor.invokedClean)
XCTAssertFalse(swiftPackageManagerInteractor.invokedFetch)
XCTAssertFalse(swiftPackageManagerInteractor.invokedUpdate)
XCTAssertFalse(swiftPackageManagerInteractor.invokedInstall)
XCTAssertTrue(swiftPackageManagerInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedFetch)
XCTAssertFalse(cocoaPodsInteractor.invokedUpdate)
XCTAssertFalse(cocoaPodsInteractor.invokedClean)
XCTAssertFalse(cocoaPodsInteractor.invokedInstall)
XCTAssertFalse(dependenciesGraphController.invokedSave)
XCTAssertTrue(dependenciesGraphController.invokedClean)
}
}

View File

@ -0,0 +1,76 @@
import Foundation
import TSCBasic
import TuistGraph
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistDependenciesTesting
@testable import TuistGraphTesting
@testable import TuistSupportTesting
public final class DependenciesGraphControllerTests: TuistUnitTestCase {
private var subject: DependenciesGraphController!
override public func setUp() {
super.setUp()
subject = DependenciesGraphController()
}
override public func tearDown() {
super.tearDown()
subject = nil
}
func test_save() throws {
// Given
let root = try temporaryPath()
let graph = DependenciesGraph.test()
// When
try subject.save(graph, to: root)
// Then
let graphPath = root.appending(components: "Tuist", "Dependencies", "graph.json")
XCTAssertTrue(fileHandler.exists(graphPath))
}
func test_load() throws {
// Given
let root = try temporaryPath()
let graphPath = root.appending(components: "Tuist", "Dependencies", "graph.json")
try fileHandler.touch(graphPath)
try fileHandler.write(DependenciesGraph.testJson, path: graphPath, atomically: true)
// When
let got = try subject.load(at: root)
// Then
let expected = DependenciesGraph.test(
thirdPartyDependencies: [
"RxSwift": .xcframework(
path: "/Tuist/Dependencies/Carthage/RxSwift.xcframework",
architectures: [.arm6432, .x8664, .armv7, .armv7k, .arm64, .i386]
),
]
)
XCTAssertEqual(got, expected)
}
func test_clean() throws {
// Given
let root = try temporaryPath()
let graphPath = root.appending(components: "Tuist", "Dependencies", "graph.json")
try fileHandler.touch(graphPath)
// When
try subject.clean(at: root)
// Then
XCTAssertFalse(fileHandler.exists(graphPath))
}
}

View File

@ -28,7 +28,7 @@ final class SwiftPackageManagerInteractorTests: TuistUnitTestCase {
super.tearDown()
}
func test_fetch() throws {
func test_install_when_shouldNotBeUpdated() throws {
// Given
let rootPath = try TemporaryDirectory(removeTreeOnDeinit: true).path
let dependenciesDirectory = rootPath
@ -54,9 +54,10 @@ final class SwiftPackageManagerInteractorTests: TuistUnitTestCase {
}
// When
try subject.fetch(
try subject.install(
dependenciesDirectory: dependenciesDirectory,
dependencies: depedencies,
shouldUpdate: false,
swiftToolsVersion: nil
)
@ -90,7 +91,7 @@ final class SwiftPackageManagerInteractorTests: TuistUnitTestCase {
XCTAssertFalse(swiftPackageManagerController.invokedUpdate)
}
func test_fetch_with_swiftToolsVersion() throws {
func test_install_when_shouldNotBeUpdated_and_swiftToolsVersionPassed() throws {
// Given
let rootPath = try TemporaryDirectory(removeTreeOnDeinit: true).path
let dependenciesDirectory = rootPath
@ -117,9 +118,10 @@ final class SwiftPackageManagerInteractorTests: TuistUnitTestCase {
}
// When
try subject.fetch(
try subject.install(
dependenciesDirectory: dependenciesDirectory,
dependencies: depedencies,
shouldUpdate: false,
swiftToolsVersion: swiftToolsVersion
)
@ -153,7 +155,7 @@ final class SwiftPackageManagerInteractorTests: TuistUnitTestCase {
XCTAssertFalse(swiftPackageManagerController.invokedUpdate)
}
func test_update() throws {
func test_install_when_shouldBeUpdated() throws {
// Given
let rootPath = try TemporaryDirectory(removeTreeOnDeinit: true).path
let dependenciesDirectory = rootPath
@ -179,9 +181,10 @@ final class SwiftPackageManagerInteractorTests: TuistUnitTestCase {
}
// When
try subject.update(
try subject.install(
dependenciesDirectory: dependenciesDirectory,
dependencies: depedencies,
shouldUpdate: true,
swiftToolsVersion: nil
)

View File

@ -0,0 +1,15 @@
import Foundation
import XCTest
@testable import TuistGraph
@testable import TuistSupportTesting
final class DependenciesGraphTests: TuistUnitTestCase {
func test_codable_xcframework() {
// Given
let subject = DependenciesGraph.test()
// Then
XCTAssertCodable(subject)
}
}

View File

@ -0,0 +1,17 @@
import Foundation
import XCTest
@testable import TuistGraph
@testable import TuistSupportTesting
final class ThirdPartyDependencyTests: TuistUnitTestCase {
func test_codable_xcframework() {
// Given
let subject = ThirdPartyDependency.testXCFramework(
architectures: [.arm64, .i386, .arm6432]
)
// Then
XCTAssertCodable(subject)
}
}

View File

@ -9,8 +9,7 @@ final class CarthageDependenciesTests: TuistUnitTestCase {
let carthageDependencies: CarthageDependencies = .init(
[
.github(path: "Dependency/Dependency", requirement: .exact("1.1.1")),
],
options: []
]
)
let expected = """
github "Dependency/Dependency" == 1.1.1
@ -34,8 +33,7 @@ final class CarthageDependenciesTests: TuistUnitTestCase {
.github(path: "XYZ/Bar", requirement: .upToNext("1.1.1")),
.binary(path: "https://my.domain.com/release/MyFramework.json", requirement: .upToNext("1.0.1")),
.binary(path: "file:///some/local/path/MyFramework.json", requirement: .atLeast("1.1.0")),
],
options: []
]
)
let expected = """
github "Dependency/Dependency" == 2.1.1

View File

@ -49,8 +49,7 @@ final class DependenciesFetchServiceTests: TuistUnitTestCase {
carthage: .init(
[
.github(path: "Dependency1", requirement: .exact("1.1.1")),
],
options: []
]
),
swiftPackageManager: .init(
[

View File

@ -49,8 +49,7 @@ final class DependenciesUpdateServiceTests: TuistUnitTestCase {
carthage: .init(
[
.git(path: "Dependency1", requirement: .exact("1.1.1")),
],
options: []
]
),
swiftPackageManager: .init(
[

View File

@ -37,19 +37,14 @@ final class DependenciesModelLoaderTests: TuistUnitTestCase {
manifestLoader.loadDependenciesStub = { _ in
Dependencies(
carthage: .carthage(
[
.github(path: "Dependency1", requirement: .exact("1.1.1")),
.git(path: "Dependency1", requirement: .exact("2.3.4")),
],
options: [.useXCFrameworks, .noUseBinaries]
),
swiftPackageManager: .swiftPackageManager(
[
.local(path: Path(localSwiftPackagePath.pathString)),
.remote(url: "RemoteUrl.com", requirement: .exact("1.2.3")),
]
),
carthage: [
.github(path: "Dependency1", requirement: .exact("1.1.1")),
.git(path: "Dependency1", requirement: .exact("2.3.4")),
],
swiftPackageManager: [
.local(path: Path(localSwiftPackagePath.pathString)),
.remote(url: "RemoteUrl.com", requirement: .exact("1.2.3")),
],
platforms: [.iOS, .macOS]
)
}
@ -63,8 +58,7 @@ final class DependenciesModelLoaderTests: TuistUnitTestCase {
[
.github(path: "Dependency1", requirement: .exact("1.1.1")),
.git(path: "Dependency1", requirement: .exact("2.3.4")),
],
options: [.useXCFrameworks, .noUseBinaries]
]
),
swiftPackageManager: .init(
[

View File

@ -17,20 +17,15 @@ final class DependenciesManifestMapperTests: TuistUnitTestCase {
let generatorPaths = GeneratorPaths(manifestDirectory: temporaryPath)
let manifest: ProjectDescription.Dependencies = Dependencies(
carthage: .carthage(
[
.github(path: "Dependency1", requirement: .exact("1.1.1")),
.git(path: "Dependency.git", requirement: .branch("BranchName")),
.binary(path: "DependencyXYZ", requirement: .atLeast("2.3.1")),
],
options: [.useXCFrameworks, .noUseBinaries]
),
swiftPackageManager: .swiftPackageManager(
[
.local(path: .init(localPackagePath.pathString)),
.remote(url: "RemotePackage.com", requirement: .exact("1.2.3")),
]
),
carthage: [
.github(path: "Dependency1", requirement: .exact("1.1.1")),
.git(path: "Dependency.git", requirement: .branch("BranchName")),
.binary(path: "DependencyXYZ", requirement: .atLeast("2.3.1")),
],
swiftPackageManager: [
.local(path: .init(localPackagePath.pathString)),
.remote(url: "RemotePackage.com", requirement: .exact("1.2.3")),
],
platforms: [.iOS, .macOS, .tvOS]
)
@ -44,8 +39,7 @@ final class DependenciesManifestMapperTests: TuistUnitTestCase {
.github(path: "Dependency1", requirement: .exact("1.1.1")),
.git(path: "Dependency.git", requirement: .branch("BranchName")),
.binary(path: "DependencyXYZ", requirement: .atLeast("2.3.1")),
],
options: [.useXCFrameworks, .noUseBinaries]
]
),
swiftPackageManager: .init(
[

View File

@ -45,12 +45,9 @@ The snippet below shows an example `Dependencies.swift` manifest file:
import ProjectDescription
let dependencies = Dependencies(
carthage: .carthage(
[
.github(path: "Alamofire/Alamofire", requirement: .exact("5.0.4"))
],
options: [.useXCFrameworks, .noUseBinaries]
),
carthage: [
.github(path: "Alamofire/Alamofire", requirement: .exact("5.0.4"))
],
swiftPackageManager: nil, // work in progress, pass `nil`
platforms: [.iOS]
)
@ -68,13 +65,14 @@ The folder structure below shows how Tuist organizes dependencies:
Tuist
|- Dependencies.swift # Manifest
|- Dependencies
|- graph.json # coming soon
|- graph.json
|- Lockfiles
|- Carthage.resolved
|- Package.resolved
|- Podfile.lock # coming soon
|- Carthage # stores content of `Carthage/Build` directory generated by `Carthage`
|- Alamofire.xcframework
|- .Alamofire.version
|- SwiftPackageManager # stores content of `.build/` directory generated by `Swift Package Manager`
|- artifacts
|- checkouts

View File

@ -14,15 +14,9 @@ Learn how to get started with `Dependencies.swift` [here](/guides/third-party-de
import ProjectDescription
let dependencies = Dependencies(
carthage: .carthage(
[
.github(path: "Alamofire/Alamofire", requirement: .exact("5.0.4"))
],
options: [
.useXCFrameworks,
.noUseBinaries,
]
),
carthage: [
.github(path: "Alamofire/Alamofire", requirement: .exact("5.0.4"))
],
swiftPackageManager: nil, // work in progress, pass `nil`
platforms: [.iOS]
)
@ -45,26 +39,16 @@ Contains the description of a dependency that can be installed using Carthage.
| Property | Description | Type | Required | Default |
| -------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------ | -------- | ------- |
| `dependencies` | List of depedencies that will be installed using Carthage. | [`[CarthageDependencies.Dependency]`](#carthage-dependencies-dependency) | Yes | |
| `options` | List of options for Carthage installation. | [`Set<CarthageDependencies.Options>`](#carthage-dependencies-options) | No | `[]` |
### CarthageDependencies Dependency
Specifies origin of Carthage dependency.
| Case | Description |
| ------------------------------ | ---------------------------------------------------------------------- |
| ------------------------------ | ---------------------------------------------------------------------- | --- |
| `.github(String, Requirement)` | GitHub repositories (both GitHub.com and GitHub Enterprise). |
| `.git(String, Requirement)` | Other Git repositories. |
| `.binary(String, Requirement)` | Dependencies that are only available as compiled binary `.framework`s. |
### CarthageDependencies Options
The options that you can set for Carthage installation.
| Case | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `.useXCFrameworks` | When passed, Carthage will produce XCFrameworks instead of regular frameworks. Refers to `--use-xcframeworks` Carthage flag. **Note: It requires Carthage in version at least 0.37.0.** |
| `.noUseBinaries` | When passed, Carthage will rebuild dependencies from source instead of using downloaded binaries when possible. Refers to `--no-use-binaries` Carthage flag. |
| `.binary(String, Requirement)` | Dependencies that are only available as compiled binary `.framework`s. | |
### SwiftPackageManagerDependencies

View File

@ -5,9 +5,10 @@ Feature: Install dependencies Tuist.
And I have a working directory
Then I copy the fixture app_with_framework_and_tests_and_dependencies into the working directory
Then tuist fetches dependencies
Then a directory Tuist/Dependencies/Carthage/Alamofire.xcframework exists
Then a file Tuist/Dependencies/Carthage/.Alamofire.version exists
Then a directory Tuist/Dependencies/Carthage/RxSwift.xcframework exists
Then a file Tuist/Dependencies/Carthage/.RxSwift.version exists
Then a file Tuist/Dependencies/Lockfiles/Cartfile.resolved exists
Then a directory Tuist/Dependencies/SwiftPackageManager/checkouts/SnapKit exists
Then a file Tuist/Dependencies/Lockfiles/Package.resolved exists
Then a file Tuist/Dependencies/graph.json exists

View File

@ -5,8 +5,9 @@ Feature: Install dependencies Tuist.
And I have a working directory
Then I copy the fixture app_with_framework_and_tests_and_dependencies into the working directory
Then tuist updates dependencies
Then a directory Tuist/Dependencies/Carthage/Alamofire.xcframework exists
Then a file Tuist/Dependencies/Carthage/.Alamofire.version exists
Then a directory Tuist/Dependencies/Carthage/RxSwift.xcframework exists
Then a file Tuist/Dependencies/Carthage/.RxSwift.version exists
Then a file Tuist/Dependencies/Lockfiles/Cartfile.resolved exists
Then a directory Tuist/Dependencies/SwiftPackageManager/checkouts/SnapKit exists
Then a file Tuist/Dependencies/Lockfiles/Package.resolved exists
Then a file Tuist/Dependencies/Lockfiles/Package.resolved exists
Then a file Tuist/Dependencies/graph.json exists

View File

@ -1,19 +1,11 @@
import ProjectDescription
let dependencies = Dependencies(
carthage: .carthage(
[
.github(path: "Alamofire/Alamofire", requirement: .exact("5.0.4"))
],
options: [
.useXCFrameworks,
.noUseBinaries,
]
),
swiftPackageManager: .swiftPackageManager(
[
.package(url: "https://github.com/SnapKit/SnapKit.git", .upToNextMajor(from: "5.0.1"))
]
),
carthage: [
.github(path: "ReactiveX/RxSwift", requirement: .exact("5.1.2")),
],
swiftPackageManager: [
.package(url: "https://github.com/SnapKit/SnapKit.git", .upToNextMajor(from: "5.0.1"))
],
platforms: [.iOS]
)

View File

@ -1,17 +1,13 @@
import ProjectDescription
let dependencies = Dependencies(
carthage: .carthage(
[
.git(path: "https://github.com/Alamofire/Alamofire", requirement: .exact("5.0.4")),
.git(path: "https://github.com/Swinject/Swinject", requirement: .exact("2.7.1"))
]
),
swiftPackageManager: .swiftPackageManager(
[
.package(url: "https://github.com/SnapKit/SnapKit.git", .upToNextMajor(from: "5.0.1")),
.package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("6.1.0"))
]
),
carthage: [
.git(path: "https://github.com/Alamofire/Alamofire", requirement: .exact("5.0.4")),
.git(path: "https://github.com/Swinject/Swinject", requirement: .exact("2.7.1"))
],
swiftPackageManager: [
.package(url: "https://github.com/SnapKit/SnapKit.git", .upToNextMajor(from: "5.0.1")),
.package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("6.1.0"))
],
platforms: [.iOS]
)