Chimera - `carthage` support. (#2060)

* [carthage] Init `DependencyManaging`

* [carthage] add `InstallMethod`

* [carthage] rename `InstallDependencies` -> `InstallDependenciesMethod`

* [carthage] Renaming `*Manger`s -> `*Interactor`s

* [carthage] stub `Dependencies`

* [carthage] add `CarthageCommandBuilder`

* [carthage] add `CartfileContentBuilder`

* [carthage] start using raw strings in `CartfileContentBuilder`

* [carthage] run `carthage` using `tuist dependencies`

* [carthage] copy frameworks and `Cartfile.resolved` to `Tuist/Dependencies/*`

* [carthage] add `DependenciesDirectoryController`

* [carthage] add new TODO warning

* [carthage] add `app_with_framework_and_tests_and_dependencies`

* [carthage] WIP: reading dependencies from `Dependencies.swift`

* [carthage] WIP: removing `ProjectDescription` from `TuistDependencies`

* [carthage] add `DependenciesModelLoader`

* [carthage] add `TuistCore.Dependencies` model

* [carthage] determine platforms from `carthage` run

* [carthage] add `CartfileContentBuilderError`

* [carthage] add `CarfileContentBuilderTests`

* [carthage] add `CarthageCommandBuilderTests`

* [carthage] add `CocoapodsInteractorTests`, `SPMInteractorTests`

* [carthage] remove fixed `warning`

* [carthage] add `CartfileResolvedInteractor`, `CartfileResolvedInteractorTests`

* [carthage] add `CarthageFrameworksInteractor`, `CarthageFrameworksInteractorTests`

* [carthage] add `CarthageInteractorTests`

* [carthage] mark `DependenciesCommand` as `EXPERIMENTAL`

* [carthage] add `MockCarthageInteractor`, `MockCocoapodsInteractor`,  `MockSPMInteractor`

* [carthage] add `DependenciesControllerTests`

* [carthage] fix fixture

* [carthage] fix saving frameworks, fix unit tests

* [carthage] add acceptance tests - `dependencies.feature`

* [carthage] `bundle exec rake style_correct`

* [carthage] fix `SwiftFormat` warning

* [carthage] enable `dependencies` acceptance test

* [carthage] change - grouping the files inside `TuistDependencies`

* [carthage] change - description of `CartfileContentBuilderError.rangeRequirementNotSupported`

* [carthage] remove - `carthage` via `bundler`

* [carthage] update - refactor `CarfileContentBuilderTests`

* [carthage] change - refactor `CarthageCommandBuilder` into `CarthageCommandGenerator`

* [carthage] change - refactor `CartfileContentBuilder` into `CartfileContentGenerator`

* [carthage] fix - typos

* [carthage] remove - `Info.plist` from `fixture/app_with_framework_and_tests_and_dependencies`

* [carthage] fix - typo

* [carthage] remove - warning comment

* [carthage] remove - unnecessary code

* [carthage] add - logging to `DependenciesController`

* [carthage] fix - typo

* [carthage] change - move `CarthageDependency.toString()` to `CarthageDependency.cartfileValue()`

* [carthage] add - `CarthageDependencyTests`

* [carthage] add - `CartfileContentGenerator.validate`

* [carthage] change - pass tuist directory path into `CarthageInteractor`

* [carthage] remove - `CartfileResolvedInteractor`

* [carthage] change - WIP: refactor `CarthageFrameworksInteractor`

* [carthage] change - WIP: refactor `CarthageFrameworksInteractor`

* [carthage] changed - refactor `CarthageFrameworksInteractor`

* [carthage] `bundle exec rake style_correct`

* [carthage] add - `CarthageFrameworksInteractorTests.test_save_only_one_platform`

* [carthage] `bundle exec rake style_correct`

* [carthage] add - `--use-netrc` flag

* [carthage] fix - `CarthageInteractorError.carthageNotFound` description.

* [carthage] add - `Platform.carthageDirectory`

* [carthage] change - remove unused code

* [carthage] remove - `TuistDependencies.Graph`

* [carthage] add - `CarthageVersionFilesInteractor`

* [carthage] add - `CarthageVersionFilesInteracting.loadVersionFiles`

* [carthage] add - cache `Carthage/Build` directory

* [carthage] change - new structure of `Tuist/Dependencies` directory

* [carthage] change - refactor `CarthageInteractor`

* [carthage] fix - deleting unnecessary frameworks

* [carthage] fix - acceptance tests

* [carthage] change - refactor `Dependencies.swift`

* [carthage] change - update changelog

* [carthage] fix - run `bundle exec rake style_correct`

* [carthage] fix - swiftlint violation

* [carthage] delete - `CarthageFrameworksInteractor`

* [carthage] fix - acceptance tests

* [carthage] fix - typos

* [carthage] remove - copy `Cartfile.resolved` from previous run if exist

* [carthage] change - refactor `Dependencies` model

* [carthage] `bundle exec rake style_correct`

* [carthage] add - `Dependency.CarthageOrigin`

* [carthage] remove - `InstallDependenciesMethod`

* [carthage] change - `CarthageInteractor.PathProvider` -> `CarthagePathsProvider`

* [carthage] change - update `Gitignore.stencil` with `Tuist/Dependencies`

* [carthage] add - documentation

* [carthage] fix - changelog after rebase

* [carthage] run `bundle exec rake style_correct`

* [carthage] fix - `app_with_framework_and_tests_and_dependencies` fixture
This commit is contained in:
Kamil Harasimowicz 2020-12-29 13:00:40 +01:00 committed by GitHub
parent f805ba0533
commit 56a72d653b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 1941 additions and 226 deletions

View File

@ -89,6 +89,7 @@ jobs:
'cache-xcframeworks',
'cache-frameworks',
'precompiled',
'dependencies',
]
steps:
- uses: actions/checkout@v1

View File

@ -11,6 +11,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
### Added
- Support for `Carthage` dependencies in `Dependencies.swift` [#2060](https://github.com/tuist/tuist/pull/2060) by [@laxmorek](https://github.com/laxmorek).
- Fourier CLI tool to automate development tasks [#2196](https://github.com/tuist/tuist/pull/2196) by @pepibumur](https://github.com/pepibumur).
- Support `.s` source files [#2199](https://github.com/tuist/tuist/pull/2199) by[ @dcvz](https://github.com/dcvz).

View File

@ -0,0 +1,14 @@
import Foundation
/// A `Dependencies` manifest allows for defining external dependencies for Tuist.
public struct Dependencies: Codable, Equatable {
/// List of dependencies.
public let dependencies: [Dependency]
/// Initializes a new `Dependencies` manifest instance.
/// - Parameter dependencies: List of dependencies.
public init(_ dependencies: [Dependency] = []) {
self.dependencies = dependencies
dumpIfNeeded(self)
}
}

View File

@ -1,51 +1,177 @@
import Foundation
/// Dependency contains the description of any kind of dependency of your Xcode project.
public struct Dependency: Codable, Equatable {
/// Name of the dependency
let name: String
/// Type of requirement for the given dependency
let requirement: Dependency.Requirement
/// Dependecy manager used to retrieve the dependecy
public let manager: Dependency.Manager
/// Set of platforms for the given dependency
public let platforms: Set<Platform>
public init(name: String,
requirement: Dependency.Requirement,
manager: Dependency.Manager,
platforms: [Platform])
{
self.name = name
self.requirement = requirement
self.manager = manager
self.platforms = Set(platforms)
/// Contains the description of external dependency that can by installed using Tuist.
public enum Dependency: Codable, Equatable {
/// Origin of the Carthage dependency
public enum CarthageOrigin: Codable, Equatable {
/// Mimics `github` keyword from `Cartfile`. GitHub repositories (both GitHub.com and GitHub Enterprise).
case github(path: String)
/// Mimics `git` keyword from `Cartfile`. Other Git repositories.
case git(path: String)
/// Mimics `binary` keyword from `Cartfile`. Dependencies that are only available as compiled binary `.frameworks`.
case binary(path: String)
}
/// Carthage dependency initailizer
/// - Parameter name: Name of the dependency
/// - Parameter requirement: Type of requirement for the given dependency
/// - Returns Dependency
public static func carthage(name: String,
requirement: Dependency.Requirement,
platforms: [Platform]) -> Dependency
{
Dependency(name: name, requirement: requirement, manager: .carthage, platforms: platforms)
/// Requirement for the Carthage dependency
public enum CarthageRequirement: Codable, Equatable {
/// Mimics `== 1.0` from `Cartfile`.
case exact(Version)
/// Mimics `~> 1.0` from `Cartfile`.
case upToNext(Version)
/// Mimics `>= 1.0` from `Cartfile`.
case atLeast(Version)
/// Mimics `"branch"` from `Cartfile`.
case branch(String)
/// Mimics `"revision"` from `Cartfile`.
case revision(String)
}
public static func == (lhs: Dependency, rhs: Dependency) -> Bool {
lhs.name == rhs.name && lhs.requirement == rhs.requirement
/// Contains the description of dependency that can by installed using Carthage. More: https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md
case carthage(origin: CarthageOrigin, requirement: CarthageRequirement, platforms: Set<Platform>)
}
// MARK: - Dependency: Codable
extension Dependency {
private enum Kind: String, Codable {
case carthage
}
private enum CodingKeys: String, CodingKey {
case kind
case origin
case requirement
case platforms
}
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 .carthage:
let origin = try container.decode(CarthageOrigin.self, forKey: .origin)
let requirement = try container.decode(CarthageRequirement.self, forKey: .requirement)
let platforms = try container.decode(Set<Platform>.self, forKey: .platforms)
self = .carthage(origin: origin, requirement: requirement, platforms: platforms)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .carthage(origin, requirement, platforms):
try container.encode(Kind.carthage, forKey: .kind)
try container.encode(origin, forKey: .origin)
try container.encode(requirement, forKey: .requirement)
try container.encode(platforms, forKey: .platforms)
}
}
}
public struct Dependencies: Codable, Equatable {
public let dependencies: [Dependency]
// MARK: - Dependency.CarthageRequirement: Codable
public init(_ dependencies: [Dependency]) {
self.dependencies = dependencies
dumpIfNeeded(self)
extension Dependency.CarthageRequirement {
private enum Kind: String, Codable {
case exact
case upToNext
case atLeast
case branch
case revision
}
private enum CodingKeys: String, CodingKey {
case kind
case version
case branch
case revision
}
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 .exact:
let version = try container.decode(Version.self, forKey: .version)
self = .exact(version)
case .upToNext:
let version = try container.decode(Version.self, forKey: .version)
self = .upToNext(version)
case .atLeast:
let version = try container.decode(Version.self, forKey: .version)
self = .atLeast(version)
case .branch:
let branch = try container.decode(String.self, forKey: .branch)
self = .branch(branch)
case .revision:
let revision = try container.decode(String.self, forKey: .revision)
self = .revision(revision)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .exact(version):
try container.encode(Kind.exact, forKey: .kind)
try container.encode(version, forKey: .version)
case let .upToNext(version):
try container.encode(Kind.upToNext, forKey: .kind)
try container.encode(version, forKey: .version)
case let .atLeast(version):
try container.encode(Kind.atLeast, forKey: .kind)
try container.encode(version, forKey: .version)
case let .branch(branch):
try container.encode(Kind.branch, forKey: .kind)
try container.encode(branch, forKey: .branch)
case let .revision(revision):
try container.encode(Kind.revision, forKey: .kind)
try container.encode(revision, forKey: .revision)
}
}
}
// MARK: - Dependency.CarthageOrigin: Codable
extension Dependency.CarthageOrigin {
private enum Kind: String, Codable {
case github
case git
case binary
}
private enum CodingKeys: String, CodingKey {
case kind
case path
}
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 .github:
let path = try container.decode(String.self, forKey: .path)
self = .github(path: path)
case .git:
let path = try container.decode(String.self, forKey: .path)
self = .git(path: path)
case .binary:
let path = try container.decode(String.self, forKey: .path)
self = .binary(path: path)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .github(path):
try container.encode(Kind.github, forKey: .kind)
try container.encode(path, forKey: .path)
case let .git(path):
try container.encode(Kind.git, forKey: .kind)
try container.encode(path, forKey: .path)
case let .binary(path):
try container.encode(Kind.binary, forKey: .kind)
try container.encode(path, forKey: .path)
}
}
}

View File

@ -1,9 +0,0 @@
import Foundation
public extension Dependency {
enum Manager: String, Codable, Equatable {
case carthage
// case spm
// case cocoapods
}
}

View File

@ -1,30 +0,0 @@
import Foundation
// The idea of Requirement comes from SPM PackageRequirement
// https://github.com/apple/swift-package-manager/blob/main/Sources/PackageDescription/PackageRequirement.swift
// This could be further extended for more cases
/// DependencyRequiement describes which dependency the project needs.
public extension Dependency {
enum Requirement: Codable, Equatable {
case exact(Version)
// case range(Range<Version>)
// case branch(String)
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self = .exact(try container.decode(Version.self, forKey: .exact))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if case let .exact(version) = self {
try container.encode(version, forKey: .exact)
}
}
enum CodingKeys: String, CodingKey {
case exact
}
}
}

View File

@ -1,13 +1,85 @@
import Foundation
import TuistSupport
// MARK: - Carthage Dependency
/// CarthageDependency contains the description of a dependency to be fetched with Carthage.
struct CarthageDependency {
/// Name of the dependency
let name: String
public struct CarthageDependency: Equatable {
/// Origin of the dependency
public let origin: Origin
/// Type of requirement for the given dependency
let requirement: Requirement
public let requirement: Requirement
/// Set of platforms for the given dependency
let platforms: Set<Platform>
public let platforms: Set<Platform>
/// Initializes the carthage dependency with its attributes.
///
/// - Parameters:
/// - origin: Origin of the dependency
/// - requirement: Type of requirement for the given dependency
/// - platforms: Set of platforms for the given dependency
public init(
origin: Origin,
requirement: Requirement,
platforms: Set<Platform>
) {
self.origin = origin
self.requirement = requirement
self.platforms = platforms
}
/// Returns `Cartfile` representation.
public var cartfileValue: String {
origin.cartfileValue + " " + requirement.cartfileValue
}
}
public extension CarthageDependency {
enum Origin: Equatable {
case github(path: String)
case git(path: String)
case binary(path: String)
/// Returns `Cartfile` representation.
public var cartfileValue: String {
switch self {
case let .github(path):
return #"github "\#(path)""#
case let .git(path):
return #"git "\#(path)""#
case let .binary(path):
return #"binary "\#(path)""#
}
}
}
}
// MARK: - Requirement
public extension CarthageDependency {
enum Requirement: Equatable {
case exact(String)
case upToNext(String)
case atLeast(String)
case branch(String)
case revision(String)
/// Returns `Cartfile` representation.
public var cartfileValue: String {
switch self {
case let .exact(version):
return "== \(version)"
case let .upToNext(version):
return "~> \(version)"
case let .atLeast(version):
return ">= \(version)"
case let .branch(branch):
return #""\#(branch)""#
case let .revision(revision):
return #""\#(revision)""#
}
}
}
}

View File

@ -0,0 +1,11 @@
import Foundation
public struct Dependencies: Equatable {
public let carthageDependencies: [CarthageDependency]
public init(
carthageDependencies: [CarthageDependency]
) {
self.carthageDependencies = carthageDependencies
}
}

View File

@ -110,4 +110,14 @@ extension Platform {
default: return nil
}
}
/// Returns the directory name whose Carthage uses to save frameworks.
public var carthageDirectory: String {
switch self {
case .iOS, .watchOS, .tvOS:
return caseValue
case .macOS:
return "Mac"
}
}
}

View File

@ -0,0 +1,187 @@
import RxBlocking
import TSCBasic
import TuistCore
import TuistSupport
// MARK: - Carthage Interactor Errors
enum CarthageInteractorError: FatalError, Equatable {
/// Thrown when Carthage cannot be found.
case carthageNotFound
/// Thrown when Carfile cannont be found in temporary directory after Carthage installation.
case cartfileNotFound
/// Thrown when `Carthage/Build` directory cannont be found in temporary directory after Carthage installation.
case buildDirectoryNotFound
/// Error type.
var type: ErrorType {
switch self {
case .carthageNotFound, .cartfileNotFound, .buildDirectoryNotFound:
return .abort
}
}
/// Error description.
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."
case .cartfileNotFound:
return "Cartfile was not found after Carthage installation."
case .buildDirectoryNotFound:
return "Carthage/Build directory was not found after Carthage installation."
}
}
}
// MARK: - Carthage Interacting
public protocol CarthageInteracting {
/// Fetches `Carthage` dependencies.
/// - Parameter dependenciesDirectory: The path to the directory that contains the `Tuist/Dependencies/` directory.
/// - Parameter method: Installation method.
/// - Parameter dependencies: List of dependencies to intall using `Carthage`.
func fetch(dependenciesDirectory: AbsolutePath, dependencies: [CarthageDependency]) throws
}
// MARK: - Carthage Interactor
public final class CarthageInteractor: CarthageInteracting {
private let fileHandler: FileHandling
private let carthageCommandGenerator: CarthageCommandGenerating
private let cartfileContentGenerator: CartfileContentGenerating
public init(
fileHandler: FileHandling = FileHandler.shared,
carthageCommandGenerator: CarthageCommandGenerating = CarthageCommandGenerator(),
cartfileContentGenerator: CartfileContentGenerating = CartfileContentGenerator()
) {
self.fileHandler = fileHandler
self.carthageCommandGenerator = carthageCommandGenerator
self.cartfileContentGenerator = cartfileContentGenerator
}
public func fetch(dependenciesDirectory: AbsolutePath, dependencies: [CarthageDependency]) throws {
logger.info("We are starting to fetch the Carthage dependencies.", metadata: .section)
// check availability of `carthage`
guard canUseSystemCarthage() else {
throw CarthageInteractorError.carthageNotFound
}
// determine platforms
let platforms: Set<Platform> = dependencies
.reduce(Set<Platform>()) { platforms, dependency in platforms.union(dependency.platforms) }
try fileHandler.inTemporaryDirectory { temporaryDirectoryPath in
// prepare paths
let pathsProvider = CarthagePathsProvider(dependenciesDirectory: dependenciesDirectory, temporaryDirectoryPath: temporaryDirectoryPath)
// prepare for installation
try prepareForInstallation(pathsProvider: pathsProvider, dependencies: dependencies)
// create `carthage` shell command
let command = carthageCommandGenerator.command(path: temporaryDirectoryPath, platforms: platforms)
// run `carthage`
try System.shared.runAndPrint(command)
// post intallation actions
try postInstallationActions(pathsProvider: pathsProvider)
}
logger.info("Carthage dependencies were fetched successfully.", metadata: .success)
}
// MARK: - Installation
private func prepareForInstallation(pathsProvider: CarthagePathsProvider, dependencies: [CarthageDependency]) throws {
// copy build directory from previous run if exist
if fileHandler.exists(pathsProvider.destinationCarthageDirectory) {
try copyDirectory(from: pathsProvider.destinationCarthageDirectory, to: pathsProvider.temporaryCarthageBuildDirectory)
}
// create `Cartfile`
let cartfileContent = cartfileContentGenerator.cartfileContent(for: dependencies)
let cartfilePath = pathsProvider.temporaryDirectoryPath.appending(component: "Cartfile")
try fileHandler.write(cartfileContent, path: cartfilePath, atomically: true)
}
private func postInstallationActions(pathsProvider: CarthagePathsProvider) throws {
// validation
guard fileHandler.exists(pathsProvider.temporaryCarfileResolvedPath) else {
throw CarthageInteractorError.cartfileNotFound
}
guard fileHandler.exists(pathsProvider.temporaryCarthageBuildDirectory) else {
throw CarthageInteractorError.buildDirectoryNotFound
}
// save `Cartfile.resolved`
try copyFile(from: pathsProvider.temporaryCarfileResolvedPath, to: pathsProvider.destinationCarfileResolvedPath)
// save build directory
try copyDirectory(from: pathsProvider.temporaryCarthageBuildDirectory, to: pathsProvider.destinationCarthageDirectory)
}
// MARK: - Helpers
private func copyFile(from fromPath: AbsolutePath, to toPath: AbsolutePath) throws {
try fileHandler.createFolder(toPath.removingLastComponent())
if fileHandler.exists(toPath) {
try fileHandler.replace(toPath, with: fromPath)
} else {
try fileHandler.copy(from: fromPath, to: toPath)
}
}
private func copyDirectory(from fromPath: AbsolutePath, to toPath: AbsolutePath) throws {
try fileHandler.createFolder(toPath.removingLastComponent())
if fileHandler.exists(toPath) {
try fileHandler.delete(toPath)
}
try fileHandler.copy(from: fromPath, to: toPath)
}
/// Returns true if Carthage is avaiable in the environment.
/// - Returns: True if Carthege is available globally in the system.
private func canUseSystemCarthage() -> Bool {
do {
_ = try System.shared.which("carthage")
return true
} catch {
return false
}
}
}
// MARK: - Models
private struct CarthagePathsProvider {
let dependenciesDirectory: AbsolutePath
let temporaryDirectoryPath: AbsolutePath
let destinationCarfileResolvedPath: AbsolutePath
let destinationCarthageDirectory: AbsolutePath
let temporaryCarfileResolvedPath: AbsolutePath
let temporaryCarthageBuildDirectory: AbsolutePath
init(dependenciesDirectory: AbsolutePath, temporaryDirectoryPath: AbsolutePath) {
self.dependenciesDirectory = dependenciesDirectory
self.temporaryDirectoryPath = temporaryDirectoryPath
destinationCarfileResolvedPath = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.lockfilesDirectoryName)
.appending(component: Constants.DependenciesDirectory.cartfileResolvedName)
destinationCarthageDirectory = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.carthageDirectoryName)
temporaryCarfileResolvedPath = temporaryDirectoryPath
.appending(component: Constants.DependenciesDirectory.cartfileResolvedName)
temporaryCarthageBuildDirectory = temporaryDirectoryPath
.appending(component: Constants.DependenciesDirectory.carthageDirectoryName)
.appending(component: "Build")
}
}

View File

@ -0,0 +1,23 @@
import TSCBasic
import TuistCore
import TuistSupport
// MARK: - Cartfile Content Generating
public protocol CartfileContentGenerating {
/// Generates content for `Cartfile`.
/// - Parameter dependencies: The dependencies whose will be installed.
func cartfileContent(for dependencies: [CarthageDependency]) -> String
}
// MARK: - Cartfile Content Generator
public final class CartfileContentGenerator: CartfileContentGenerating {
public init() {}
public func cartfileContent(for dependencies: [CarthageDependency]) -> String {
dependencies
.map(\.cartfileValue)
.joined(separator: "\n")
}
}

View File

@ -0,0 +1,51 @@
import TSCBasic
import TuistCore
import TuistSupport
// MARK: - Carthage Command Generating
public protocol CarthageCommandGenerating {
/// Builds `Carthage` command.
/// - Parameters:
/// - path: Directory whose project's dependencies will be installed.
/// - platforms: The platforms to build for.
func command(path: AbsolutePath, platforms: Set<Platform>?) -> [String]
}
// MARK: - Carthage Command Generator
public final class CarthageCommandGenerator: CarthageCommandGenerating {
public init() {}
public func command(path: AbsolutePath, platforms: Set<Platform>?) -> [String] {
var commandComponents: [String] = []
commandComponents.append("carthage")
commandComponents.append("bootstrap")
// Project Directory
commandComponents.append("--project-directory")
commandComponents.append(path.pathString)
// Platforms
if let platforms = platforms, !platforms.isEmpty {
commandComponents.append("--platform")
commandComponents.append(
platforms
.map(\.caseValue)
.joined(separator: ",")
)
}
// Flags
commandComponents.append("--use-netrc")
commandComponents.append("--cache-builds")
commandComponents.append("--new-resolver")
// Return
return commandComponents
}
}

View File

@ -0,0 +1,42 @@
import TSCBasic
import TuistSupport
// MARK: - CocoaPods Interactor Errors
enum CocoaPodsInteractorError: FatalError {
case unimplemented
/// Error type.
public var type: ErrorType {
switch self {
case .unimplemented:
return .abort
}
}
/// Description.
public var description: String {
switch self {
case .unimplemented:
return "CocoaPods is not supported yet. It's being worked on and it'll be available soon."
}
}
}
// 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
}
// MARK: - Cocoapods Interactor
public final class CocoaPodsInteractor: CocoaPodsInteracting {
public init() {}
public func fetch(dependenciesDirectory _: AbsolutePath) throws {
throw CocoaPodsInteractorError.unimplemented
}
}

View File

@ -1,59 +1,43 @@
import TSCBasic
import TuistCore
import TuistSupport
// MARK: - Dependencies Controller Errors
public enum DependenciesControllerError: FatalError {
case unimplemented
/// Error type.
public var type: ErrorType {
switch self {
case .unimplemented:
return .abort
}
}
/// Description.
public var description: String {
switch self {
case .unimplemented:
return "A standard approach for managing third-party dependencies is being worked on and it'll be available soon."
}
}
}
// MARK: - Dependencies Controlling
/// `DependenciesControlling` controls:
/// 1. Fetching/updating dependencies defined in `./Tuist/Dependencies.swift` by running appropriate dependencies managers (`Cocoapods`, `Carthage`, `SPM`).
/// 2. Compiling fetched/updated depedencies into `.framework.`/`.xcframework.`.
/// 3. Saving compiled frameworks uder `./Tuist/Dependencies/*`.
/// 3. Saving compiled frameworks under `./Tuist/Dependencies/*`.
/// 4. Generating dependencies graph under `./Tuist/Dependencies/graph.json`.
public protocol DependenciesControlling {
/// Fetches dependencies.
/// - Parameter path: Directory whose project's dependencies will be fetched.
func fetch(at path: AbsolutePath) throws
/// Updates dependencies.
/// - Parameter path: Directory whose project's dependencies will be updated.
func update(at path: AbsolutePath) throws
/// - Parameter path: Directory whose project's dependencies will be installed.
/// - Parameter dependencies: List of dependencies to intall.
func fetch(at path: AbsolutePath, dependencies: Dependencies) throws
}
// MARK: - Dependencies Controller
public final class DependenciesController: DependenciesControlling {
public init() {}
private let carthageInteractor: CarthageInteracting
private let cocoaPodsInteractor: CocoaPodsInteracting
private let swiftPackageManagerInteractor: SwiftPackageManagerInteracting
public func fetch(at _: AbsolutePath) throws {
logger.notice("Start fetching depednencies.")
// TODO: implement me!
throw DependenciesControllerError.unimplemented
public init(
carthageInteractor: CarthageInteracting = CarthageInteractor(),
cocoaPodsInteractor: CocoaPodsInteracting = CocoaPodsInteractor(),
swiftPackageManagerInteractor: SwiftPackageManagerInteracting = SwiftPackageManagerInteractor()
) {
self.carthageInteractor = carthageInteractor
self.cocoaPodsInteractor = cocoaPodsInteractor
self.swiftPackageManagerInteractor = swiftPackageManagerInteractor
}
public func update(at _: AbsolutePath) throws {
logger.notice("Start updating depednencies.")
public func fetch(at path: AbsolutePath, dependencies: Dependencies) throws {
let dependenciesDirectory = path
.appending(component: Constants.tuistDirectoryName)
.appending(component: Constants.DependenciesDirectory.name)
// TODO: implement me!
throw DependenciesControllerError.unimplemented
try carthageInteractor.fetch(dependenciesDirectory: dependenciesDirectory, dependencies: dependencies.carthageDependencies)
}
}

View File

@ -0,0 +1,42 @@
import TSCBasic
import TuistSupport
// MARK: - Swift Package Manager Interactor Error
public enum SwiftPackageManagerInteractorError: FatalError {
case unimplemented
/// Error type.
public var type: ErrorType {
switch self {
case .unimplemented:
return .abort
}
}
/// Description.
public var description: String {
switch self {
case .unimplemented:
return "Swift Package Manager is not supported yet. It's being worked on and it'll be available soon."
}
}
}
// MARK: - Swift Package Manager Interacting
public protocol SwiftPackageManagerInteracting {
/// Fetches `Swift Package Manager` dependencies.
/// - Parameter dependenciesDirectoryPath: The path to the directory that contains the `Tuist/Dependencies/` directory.
func fetch(dependenciesDirectory: AbsolutePath) throws
}
// MARK: - Swift Package Manager Interactor
public final class SwiftPackageManagerInteractor: SwiftPackageManagerInteracting {
public init() {}
public func fetch(dependenciesDirectory _: AbsolutePath) throws {
throw SwiftPackageManagerInteractorError.unimplemented
}
}

View File

@ -0,0 +1,26 @@
import TSCBasic
import TuistCore
@testable import TuistDependencies
public final class MockCartfileContentGenerator: CartfileContentGenerating {
public init() {}
var invokedCartfileContent = false
var invokedCartfileContentCount = 0
var invokedCartfileContentParameters: [CarthageDependency]?
var invokedCartfileContentParametersList = [[CarthageDependency]]()
var cartfileContentStub: (([CarthageDependency]) -> String)?
public func cartfileContent(for dependencies: [CarthageDependency]) -> String {
invokedCartfileContent = true
invokedCartfileContentCount += 1
invokedCartfileContentParameters = dependencies
invokedCartfileContentParametersList.append(dependencies)
if let stub = cartfileContentStub {
return stub(dependencies)
} else {
return ""
}
}
}

View File

@ -0,0 +1,26 @@
import TSCBasic
import TuistCore
@testable import TuistDependencies
public final class MockCarthageCommandGenerator: CarthageCommandGenerating {
public init() {}
var invokedCommand = false
var invokedCommandCount = 0
var invokedCommandParameters: (path: AbsolutePath, platforms: Set<Platform>?)?
var invokedCommandParametersList = [(path: AbsolutePath, platforms: Set<Platform>?)]()
var commandStub: ((AbsolutePath, Set<Platform>?) -> [String])?
public func command(path: AbsolutePath, platforms: Set<Platform>?) -> [String] {
invokedCommand = true
invokedCommandCount += 1
invokedCommandParameters = (path, platforms)
invokedCommandParametersList.append((path, platforms))
if let stub = commandStub {
return stub(path, platforms)
} else {
return []
}
}
}

View File

@ -0,0 +1,24 @@
import TSCBasic
import TuistCore
@testable import TuistDependencies
public final class MockCarthageInteractor: CarthageInteracting {
public init() {}
var invokedFetch = false
var invokedFetchCount = 0
var invokedFetchParameters: (dependenciesDirectory: AbsolutePath, dependencies: [CarthageDependency])?
var invokedFetchParametersList = [(dependenciesDirectory: AbsolutePath, dependencies: [CarthageDependency])]()
var stubbedFetchError: Error?
public func fetch(dependenciesDirectory: AbsolutePath, dependencies: [CarthageDependency]) throws {
invokedFetch = true
invokedFetchCount += 1
invokedFetchParameters = (dependenciesDirectory, dependencies)
invokedFetchParametersList.append((dependenciesDirectory, dependencies))
if let error = stubbedFetchError {
throw error
}
}
}

View File

@ -0,0 +1,24 @@
import TSCBasic
import TuistCore
@testable import TuistDependencies
public final class MockCocoaPodsInteractor: CocoaPodsInteracting {
public init() {}
var invokedFetch = false
var invokedFetchCount = 0
var invokedFetchParameters: AbsolutePath?
var invokedFetchParametersList = [AbsolutePath]()
var stubbedFetchError: Error?
public func fetch(dependenciesDirectory: AbsolutePath) throws {
invokedFetch = true
invokedFetchCount += 1
invokedFetchParameters = dependenciesDirectory
invokedFetchParametersList.append(dependenciesDirectory)
if let error = stubbedFetchError {
throw error
}
}
}

View File

@ -1,4 +1,6 @@
import TSCBasic
import TuistCore
@testable import TuistDependencies
public final class MockDependenciesController: DependenciesControlling {
@ -6,33 +8,17 @@ public final class MockDependenciesController: DependenciesControlling {
var invokedFetch = false
var invokedFetchCount = 0
var invokedFetchParameters: AbsolutePath?
var invokedFetchParametersList = [AbsolutePath]()
var invokedFetchParameters: (path: AbsolutePath, dependencies: Dependencies)?
var invokedFetchParametersList = [(path: AbsolutePath, dependencies: Dependencies)]()
var stubbedFetchError: Error?
public func fetch(at path: AbsolutePath) throws {
public func fetch(at path: AbsolutePath, dependencies: Dependencies) throws {
invokedFetch = true
invokedFetchCount += 1
invokedFetchParameters = path
invokedFetchParametersList.append(path)
invokedFetchParameters = (path, dependencies)
invokedFetchParametersList.append((path, dependencies))
if let error = stubbedFetchError {
throw error
}
}
var invokedUpdate = false
var invokedUpdateCount = 0
var invokedUpdateParameters: AbsolutePath?
var invokedUpdateParametersList = [AbsolutePath]()
var stubbedUpdateError: Error?
public func update(at path: AbsolutePath) throws {
invokedUpdate = true
invokedUpdateCount += 1
invokedUpdateParameters = path
invokedUpdateParametersList.append(path)
if let error = stubbedUpdateError {
throw error
}
}
}

View File

@ -0,0 +1,24 @@
import TSCBasic
import TuistCore
@testable import TuistDependencies
public final class MockSwiftPackageManagerInteractor: SwiftPackageManagerInteracting {
public init() {}
var invokedFetch = false
var invokedFetchCount = 0
var invokedFetchParameters: AbsolutePath?
var invokedFetchParametersList = [AbsolutePath]()
var stubbedFetchError: Error?
public func fetch(dependenciesDirectory: AbsolutePath) throws {
invokedFetch = true
invokedFetchCount += 1
invokedFetchParameters = dependenciesDirectory
invokedFetchParametersList.append(dependenciesDirectory)
if let error = stubbedFetchError {
throw error
}
}
}

View File

@ -5,7 +5,7 @@ import TSCBasic
struct DependenciesCommand: ParsableCommand {
static var configuration: CommandConfiguration {
CommandConfiguration(commandName: "dependencies",
abstract: "A set of commands for project's dependencies managment.",
abstract: "[Experimental] A set of commands for project's dependencies managment.",
subcommands: [
DependenciesFetchCommand.self,
DependenciesUpdateCommand.self,

View File

@ -2,19 +2,28 @@ import Foundation
import TSCBasic
import TuistCore
import TuistDependencies
import TuistLoader
import TuistSupport
final class DependenciesFetchService {
private let dependenciesController: DependenciesControlling
private let dependenciesModelLoader: DependenciesModelLoading
init(dependenciesController: DependenciesControlling = DependenciesController()) {
init(dependenciesController: DependenciesControlling = DependenciesController(),
dependenciesModelLoader: DependenciesModelLoading = DependenciesModelLoader())
{
self.dependenciesController = dependenciesController
self.dependenciesModelLoader = dependenciesModelLoader
}
func run(path: String?) throws {
logger.info("We are starting to fetch/update the dependencies.", metadata: .section)
let path = self.path(path)
try dependenciesController.fetch(at: path)
logger.notice("Successfully fetched dependencies", metadata: .success)
let dependencies = try dependenciesModelLoader.loadDependencies(at: path)
try dependenciesController.fetch(at: path, dependencies: dependencies)
logger.info("Dependencies were fetched successfully.", metadata: .success)
}
// MARK: - Helpers

View File

@ -2,28 +2,45 @@ import Foundation
import TSCBasic
import TuistCore
import TuistDependencies
import TuistLoader
import TuistSupport
final class DependenciesUpdateService {
private let dependenciesController: DependenciesControlling
// MARK: - DependenciesUpdateServiceError
init(dependenciesController: DependenciesControlling = DependenciesController()) {
self.dependenciesController = dependenciesController
enum DependenciesUpdateServiceError: FatalError {
case unimplemented
/// Error type.
public var type: ErrorType {
switch self {
case .unimplemented:
return .abort
}
}
func run(path: String?) throws {
let path = self.path(path)
try dependenciesController.update(at: path)
logger.notice("Successfully updated dependencies", metadata: .success)
}
// MARK: - Helpers
private func path(_ path: String?) -> AbsolutePath {
if let path = path {
return AbsolutePath(path, relativeTo: FileHandler.shared.currentPath)
} else {
return FileHandler.shared.currentPath
/// Description.
public var description: String {
switch self {
case .unimplemented:
return "Updating dependencies is not yet supported. It's being worked on and it'll be available soon."
}
}
}
// MARK: - DependenciesUpdateService
final class DependenciesUpdateService {
private let dependenciesController: DependenciesControlling
private let dependenciesModelLoader: DependenciesModelLoading
init(dependenciesController: DependenciesControlling = DependenciesController(),
dependenciesModelLoader: DependenciesModelLoading = DependenciesModelLoader())
{
self.dependenciesController = dependenciesController
self.dependenciesModelLoader = dependenciesModelLoader
}
func run(path _: String?) throws {
throw DependenciesUpdateServiceError.unimplemented
}
}

View File

@ -0,0 +1,27 @@
import Foundation
import ProjectDescription
import TSCBasic
import TuistCore
import TuistSupport
/// Entity responsible for providing dependencies model
public protocol DependenciesModelLoading {
/// Load array of Carthage Dependency models at the specified path.
/// - Parameter path: The absolute path for the dependency models to load.
/// - Returns: The Dependencies loaded from the specified path.
/// - Throws: Error encountered during the loading process (e.g. Missing Dependencies file).
func loadDependencies(at path: AbsolutePath) throws -> TuistCore.Dependencies
}
public class DependenciesModelLoader: DependenciesModelLoading {
private let manifestLoader: ManifestLoading
public init(manifestLoader: ManifestLoading = ManifestLoader()) {
self.manifestLoader = manifestLoader
}
public func loadDependencies(at path: AbsolutePath) throws -> TuistCore.Dependencies {
let manifest = try manifestLoader.loadDependencies(at: path)
return try TuistCore.Dependencies.from(manifest: manifest)
}
}

View File

@ -156,14 +156,13 @@ public class ManifestLoader: ManifestLoading {
}
public func loadDependencies(at path: AbsolutePath) throws -> ProjectDescription.Dependencies {
let dependencyPath = path.appending(component: Manifest.dependencies.fileName(path))
let dependencyPath = path.appending(components: Constants.tuistDirectoryName, Manifest.dependencies.fileName(path))
guard FileHandler.shared.exists(dependencyPath) else {
throw ManifestLoaderError.manifestNotFound(.dependencies, path)
}
let dependenciesData = try loadManifestData(at: dependencyPath)
let decoder = JSONDecoder()
return try decoder.decode(Dependencies.self, from: dependenciesData)
}

View File

@ -0,0 +1,35 @@
import Foundation
import ProjectDescription
import TSCBasic
import TuistCore
import TuistSupport
extension TuistCore.CarthageDependency.Origin {
static func from(manifest: ProjectDescription.Dependency.CarthageOrigin) throws -> Self {
switch manifest {
case let .github(path):
return .github(path: path)
case let .git(path):
return .git(path: path)
case let .binary(path):
return .binary(path: path)
}
}
}
extension TuistCore.CarthageDependency.Requirement {
static func from(manifest: ProjectDescription.Dependency.CarthageRequirement) throws -> Self {
switch manifest {
case let .exact(version):
return .exact(version.description)
case let .upToNext(version):
return .upToNext(version.description)
case let .atLeast(version):
return .atLeast(version.description)
case let .branch(branch):
return .branch(branch)
case let .revision(revision):
return .revision(revision)
}
}
}

View File

@ -0,0 +1,21 @@
import Foundation
import ProjectDescription
import TSCBasic
import TuistCore
import TuistSupport
extension TuistCore.Dependencies {
static func from(manifest: ProjectDescription.Dependencies) throws -> Self {
let carthageDependencies = try manifest.dependencies.reduce(into: [CarthageDependency]()) { result, dependency in
switch dependency {
case let .carthage(origin, requirement, platforms):
let origin = try TuistCore.CarthageDependency.Origin.from(manifest: origin)
let requirement = try TuistCore.CarthageDependency.Requirement.from(manifest: requirement)
let platforms = try platforms.map { try TuistCore.Platform.from(manifest: $0) }
result.append(CarthageDependency(origin: origin, requirement: requirement, platforms: Set(platforms)))
}
}
return Self(carthageDependencies: carthageDependencies)
}
}

View File

@ -0,0 +1,28 @@
import TSCBasic
import TuistCore
import TuistSupport
@testable import TuistLoader
public class MockDependenciesModelLoader: DependenciesModelLoading {
public init() {}
var invokedLoadDependencies = false
var invokedLoadDependenciesCount = 0
var invokedLoadDependenciesParameters: AbsolutePath?
var invokedLoadDependenciesParemetersList = [AbsolutePath]()
var loadDependenciesStub: ((AbsolutePath) throws -> Dependencies)?
public func loadDependencies(at path: AbsolutePath) throws -> Dependencies {
invokedLoadDependencies = true
invokedLoadDependenciesCount += 1
invokedLoadDependenciesParameters = path
invokedLoadDependenciesParemetersList.append(path)
if let stub = loadDependenciesStub {
return try stub(path)
} else {
return Dependencies(carthageDependencies: [])
}
}
}

View File

@ -164,9 +164,7 @@ extension Arguments {
}
extension Dependencies {
public static func test(name: String = "Any Dependency",
requirement: Dependency.Requirement = .exact("1.4.0")) -> Dependencies
{
Dependencies([.carthage(name: name, requirement: requirement, platforms: [.iOS])])
public static func test(dependencies: [Dependency] = []) -> Dependencies {
Dependencies(dependencies)
}
}

View File

@ -27,6 +27,13 @@ public struct Constants {
public static let swiftDoc = "swift-doc"
}
public struct DependenciesDirectory {
public static let name = "Dependencies"
public static let lockfilesDirectoryName = "Lockfiles"
public static let cartfileResolvedName = "Cartfile.resolved"
public static let carthageDirectoryName = "Carthage"
}
public struct DerivedDirectory {
public static let name = "Derived"
public static let infoPlists = "InfoPlists"

View File

@ -65,3 +65,6 @@ DerivedData/
### Tuist derived files ###
graph.dot
Derived/
### Tuist managed dependencies ###
Tuist/Dependencies

View File

@ -0,0 +1,14 @@
import Foundation
import XCTest
@testable import ProjectDescription
final class DependenciesTests: XCTestCase {
func test_dependencies_codable() throws {
let subject = Dependencies([
.carthage(origin: .github(path: "Dependency1/Dependency1"), requirement: .branch("BranchName"), platforms: [.iOS]),
.carthage(origin: .git(path: "Dependency2/Dependency2"), requirement: .upToNext("1.2.3"), platforms: [.tvOS, .macOS]),
])
XCTAssertCodable(subject)
}
}

View File

@ -0,0 +1,55 @@
import Foundation
import XCTest
@testable import ProjectDescription
final class DependencyTests: XCTestCase {
func test_dependency_carthage_codable() throws {
let subject: Dependency = .carthage(origin: .github(path: "Dependency/Dependency"), requirement: .revision("xyz"), platforms: [.iOS, .macOS, .tvOS, .watchOS])
XCTAssertCodable(subject)
}
// MARK: - Carthage Origin tests
func test_carthageOrigin_github_codable() throws {
let subject: Dependency.CarthageOrigin = .github(path: "Path/Path")
XCTAssertCodable(subject)
}
func test_carthageOrigin_git_codable() throws {
let subject: Dependency.CarthageOrigin = .git(path: "Git/Git")
XCTAssertCodable(subject)
}
func test_carthageOrigin_binary_codable() throws {
let subject: Dependency.CarthageOrigin = .binary(path: "file:///some/Path/MyFramework.json")
XCTAssertCodable(subject)
}
// MARK: - Carthage Requirement tests
func test_carthageRequirement_exact_codable() throws {
let subject: Dependency.CarthageRequirement = .exact("1.0.0")
XCTAssertCodable(subject)
}
func test_carthageRequirement_upToNext_codable() throws {
let subject: Dependency.CarthageRequirement = .upToNext("3.2.0")
XCTAssertCodable(subject)
}
func test_carthageRequirement_atLeast_codable() throws {
let subject: Dependency.CarthageRequirement = .atLeast("3.2.0")
XCTAssertCodable(subject)
}
func test_carthageRequirement_branch_codable() throws {
let subject: Dependency.CarthageRequirement = .branch("branch")
XCTAssertCodable(subject)
}
func test_carthageRequirement_revision_codable() throws {
let subject: Dependency.CarthageRequirement = .revision("revision")
XCTAssertCodable(subject)
}
}

View File

@ -0,0 +1,119 @@
import Foundation
import XCTest
@testable import TuistCore
@testable import TuistSupportTesting
final class CarthageDependencyTests: TuistTestCase {
func test_cartfileValue_github_exact() throws {
// Given
let dependency = CarthageDependency(origin: .github(path: "Alamofire/Alamofire"), requirement: .exact("1.2.3"), platforms: [.iOS])
let expected = #"github "Alamofire/Alamofire" == 1.2.3"#
// When
let got = dependency.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
// MARK: - CarthageDependency.Origin tests
func test_origin_cartfileValue_github() {
// Given
let origin: CarthageDependency.Origin = .github(path: "Alamofire/Alamofire")
let expected = #"github "Alamofire/Alamofire""#
// When
let got = origin.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
func test_origin_cartfileValue_git() {
// Given
let origin: CarthageDependency.Origin = .git(path: "https://enterprise.local/desktop/git-error-translations2.git")
let expected = #"git "https://enterprise.local/desktop/git-error-translations2.git""#
// When
let got = origin.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
func test_origin_cartfileValue_binary() {
// Given
let origin: CarthageDependency.Origin = .binary(path: "file:///some/local/path/MyFramework.json")
let expected = #"binary "file:///some/local/path/MyFramework.json""#
// When
let got = origin.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
// MARK: - CarthageDependency.Requirement tests
func test_requirement_cartfileValue_exact() {
// Given
let origin: CarthageDependency.Requirement = .exact("1.2.3")
let expected = #"== 1.2.3"#
// When
let got = origin.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
func test_requirement_cartfileValue_upToNext() {
// Given
let origin: CarthageDependency.Requirement = .upToNext("3.2.3")
let expected = #"~> 3.2.3"#
// When
let got = origin.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
func test_requirement_cartfileValue_atLeast() {
// Given
let origin: CarthageDependency.Requirement = .atLeast("1.2.1")
let expected = #">= 1.2.1"#
// When
let got = origin.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
func test_requirement_cartfileValue_branch() {
// Given
let origin: CarthageDependency.Requirement = .branch("develop")
let expected = #""develop""#
// When
let got = origin.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
func test_requirement_cartfileValue_revision() {
// Given
let origin: CarthageDependency.Requirement = .revision("1234567898765432qwerty")
let expected = #""1234567898765432qwerty""#
// When
let got = origin.cartfileValue
// Then
XCTAssertEqual(got, expected)
}
}

View File

@ -36,6 +36,13 @@ final class PlatformTests: XCTestCase {
XCTAssertTrue(Platform.tvOS.hasSimulators)
}
func test_carthageDirectory() {
XCTAssertEqual(Platform.tvOS.carthageDirectory, "tvOS")
XCTAssertEqual(Platform.iOS.carthageDirectory, "iOS")
XCTAssertEqual(Platform.watchOS.carthageDirectory, "watchOS")
XCTAssertEqual(Platform.macOS.carthageDirectory, "Mac")
}
func test_xcodeSdkRootPath() {
// Given
let platforms: [Platform] = [

View File

@ -0,0 +1,169 @@
import TSCBasic
import TuistCore
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistDependenciesTesting
@testable import TuistSupportTesting
final class CarthageInteractorTests: TuistUnitTestCase {
private var subject: CarthageInteractor!
private var fileHandlerMock: MockFileHandler!
private var carthageCommandGenerator: MockCarthageCommandGenerator!
private var cartfileContentGenerator: MockCartfileContentGenerator!
private var temporaryDirectoryPath: AbsolutePath!
override func setUp() {
super.setUp()
do {
temporaryDirectoryPath = try TemporaryDirectory(removeTreeOnDeinit: true).path
} catch {
XCTFail("Failed to setup TemporaryDirectory")
}
fileHandlerMock = MockFileHandler(temporaryDirectory: { self.temporaryDirectoryPath })
carthageCommandGenerator = MockCarthageCommandGenerator()
cartfileContentGenerator = MockCartfileContentGenerator()
subject = CarthageInteractor(fileHandler: fileHandlerMock,
carthageCommandGenerator: carthageCommandGenerator,
cartfileContentGenerator: cartfileContentGenerator)
}
override func tearDown() {
fileHandlerMock = nil
carthageCommandGenerator = nil
cartfileContentGenerator = nil
temporaryDirectoryPath = nil
subject = nil
super.tearDown()
}
func test_fetch_all_for_platforms() throws {
// Given
let rootPath = try temporaryPath()
let temporaryDependenciesDirectory = temporaryDirectoryPath
.appending(component: Constants.DependenciesDirectory.carthageDirectoryName)
.appending(component: "Build")
let dependenciesDirectory = rootPath
.appending(component: Constants.DependenciesDirectory.name)
try fileHandler.touch(temporaryDirectoryPath.appending(components: Constants.DependenciesDirectory.cartfileResolvedName))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "iOS", "Moya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "iOS", "ReactiveMoya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "iOS", "RxMoya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "Mac", "Moya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "Mac", "ReactiveMoya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "Mac", "RxMoya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "watchOS", "Moya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "watchOS", "ReactiveMoya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "watchOS", "RxMoya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "tvOS", "Moya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "tvOS", "ReactiveMoya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "tvOS", "RxMoya.framework", "Info.plist"))
let stubbedDependencies = [
CarthageDependency(origin: .github(path: "Moya"), requirement: .exact("1.1.1"), platforms: [.iOS]),
]
let stubbedCommand = ["carthage", "bootstrap", "--project-directory", temporaryDirectoryPath.pathString, "--platform iOS", "--cache-builds", "--new-resolver"]
carthageCommandGenerator.commandStub = { _, _ in stubbedCommand }
system.whichStub = { _ in "1.0.0" }
system.succeedCommand(stubbedCommand)
// When
try subject.fetch(dependenciesDirectory: dependenciesDirectory, dependencies: stubbedDependencies)
// Then
let expectedCartfileResolvedPath = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.lockfilesDirectoryName)
.appending(component: Constants.DependenciesDirectory.cartfileResolvedName)
let expectedCarthageDirectory = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.carthageDirectoryName)
XCTAssertTrue(fileHandler.exists(expectedCartfileResolvedPath))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "iOS", "Moya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "iOS", "ReactiveMoya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "iOS", "RxMoya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "Mac", "Moya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "Mac", "ReactiveMoya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "Mac", "RxMoya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "watchOS", "Moya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "watchOS", "ReactiveMoya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "watchOS", "RxMoya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "tvOS", "Moya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "tvOS", "ReactiveMoya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "tvOS", "RxMoya.framework", "Info.plist")))
XCTAssertTrue(carthageCommandGenerator.invokedCommand)
XCTAssertEqual(carthageCommandGenerator.invokedCommandParameters?.path, temporaryDirectoryPath)
XCTAssertEqual(carthageCommandGenerator.invokedCommandParameters?.platforms, [.iOS])
XCTAssertTrue(cartfileContentGenerator.invokedCartfileContent)
XCTAssertEqual(cartfileContentGenerator.invokedCartfileContentParameters, stubbedDependencies)
}
func test_fetch_only_one_platform() throws {
// Given
let rootPath = try temporaryPath()
let temporaryDependenciesDirectory = temporaryDirectoryPath
.appending(component: Constants.DependenciesDirectory.carthageDirectoryName)
.appending(component: "Build")
let dependenciesDirectory = rootPath
.appending(component: Constants.DependenciesDirectory.name)
try fileHandler.touch(temporaryDirectoryPath.appending(components: Constants.DependenciesDirectory.cartfileResolvedName))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "iOS", "Moya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "iOS", "ReactiveMoya.framework", "Info.plist"))
try fileHandler.touch(temporaryDependenciesDirectory.appending(components: "iOS", "RxMoya.framework", "Info.plist"))
let stubbedDependencies = [
CarthageDependency(origin: .github(path: "Moya"), requirement: .exact("1.1.1"), platforms: [.iOS]),
]
let stubbedCommand = ["carthage", "bootstrap", "--project-directory", temporaryDirectoryPath.pathString, "--platform iOS", "--cache-builds", "--new-resolver"]
carthageCommandGenerator.commandStub = { _, _ in stubbedCommand }
system.whichStub = { _ in "1.0.0" }
system.succeedCommand(stubbedCommand)
// When
try subject.fetch(dependenciesDirectory: dependenciesDirectory, dependencies: stubbedDependencies)
// Then
let expectedCartfileResolvedPath = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.lockfilesDirectoryName)
.appending(component: Constants.DependenciesDirectory.cartfileResolvedName)
let expectedCarthageDirectory = dependenciesDirectory
.appending(component: Constants.DependenciesDirectory.carthageDirectoryName)
XCTAssertTrue(fileHandler.exists(expectedCartfileResolvedPath))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "iOS", "Moya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "iOS", "ReactiveMoya.framework", "Info.plist")))
XCTAssertTrue(fileHandler.exists(expectedCarthageDirectory.appending(components: "iOS", "RxMoya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "Mac", "Moya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "Mac", "ReactiveMoya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "Mac", "RxMoya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "watchOS", "Moya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "watchOS", "ReactiveMoya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "watchOS", "RxMoya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "tvOS", "Moya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "tvOS", "ReactiveMoya.framework", "Info.plist")))
XCTAssertFalse(fileHandler.exists(expectedCarthageDirectory.appending(components: "tvOS", "RxMoya.framework", "Info.plist")))
XCTAssertTrue(carthageCommandGenerator.invokedCommand)
XCTAssertEqual(carthageCommandGenerator.invokedCommandParameters?.path, temporaryDirectoryPath)
XCTAssertEqual(carthageCommandGenerator.invokedCommandParameters?.platforms, [.iOS])
XCTAssertTrue(cartfileContentGenerator.invokedCartfileContent)
XCTAssertEqual(cartfileContentGenerator.invokedCartfileContentParameters, stubbedDependencies)
}
}

View File

@ -0,0 +1,77 @@
import TuistCore
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistSupportTesting
final class CartfileContentGenetatorTests: TuistUnitTestCase {
private var subject: CartfileContentGenerator!
override func setUp() {
super.setUp()
subject = CartfileContentGenerator()
}
override func tearDown() {
subject = nil
super.tearDown()
}
func test_build_no_dependencies() throws {
// Given
let dependencies: [CarthageDependency] = []
let expected = """
"""
// When
let got = subject.cartfileContent(for: dependencies)
// Then
XCTAssertEqual(got, expected)
}
func test_build_single_dependency() throws {
// Given
let dependencies: [CarthageDependency] = [
.init(origin: .github(path: "Dependency/Dependency"), requirement: .exact("1.1.1"), platforms: [.iOS]),
]
let expected = """
github "Dependency/Dependency" == 1.1.1
"""
// When
let got = subject.cartfileContent(for: dependencies)
// Then
XCTAssertEqual(got, expected)
}
func test_build_multiple_dependencies() throws {
// Given
let dependencies: [CarthageDependency] = [
.init(origin: .github(path: "Dependency/Dependency"), requirement: .exact("2.1.1"), platforms: [.iOS]),
.init(origin: .github(path: "XYZ/Foo"), requirement: .revision("revision"), platforms: [.tvOS, .macOS]),
.init(origin: .git(path: "Foo/Bar"), requirement: .atLeast("1.0.1"), platforms: [.iOS]),
.init(origin: .github(path: "Qwerty/bar"), requirement: .branch("develop"), platforms: [.watchOS]),
.init(origin: .github(path: "XYZ/Bar"), requirement: .upToNext("1.1.1"), platforms: [.iOS]),
.init(origin: .binary(path: "https://my.domain.com/release/MyFramework.json"), requirement: .upToNext("1.0.1"), platforms: [.iOS]),
.init(origin: .binary(path: "file:///some/local/path/MyFramework.json"), requirement: .atLeast("1.1.0"), platforms: [.iOS]),
]
let expected = """
github "Dependency/Dependency" == 2.1.1
github "XYZ/Foo" "revision"
git "Foo/Bar" >= 1.0.1
github "Qwerty/bar" "develop"
github "XYZ/Bar" ~> 1.1.1
binary "https://my.domain.com/release/MyFramework.json" ~> 1.0.1
binary "file:///some/local/path/MyFramework.json" >= 1.1.0
"""
// When
let got = subject.cartfileContent(for: dependencies)
// Then
XCTAssertEqual(got, expected)
}
}

View File

@ -0,0 +1,48 @@
import TuistCore
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistSupportTesting
final class CarthageCommandGeneratorTests: TuistUnitTestCase {
private var subject: CarthageCommandGenerator!
override func setUp() {
super.setUp()
subject = CarthageCommandGenerator()
}
override func tearDown() {
subject = nil
super.tearDown()
}
func test_command() throws {
// Given
let stubbedPath = try temporaryPath()
let expected = "carthage bootstrap --project-directory \(stubbedPath.pathString) --use-netrc --cache-builds --new-resolver"
// When
let got = subject
.command(path: stubbedPath, platforms: nil)
.joined(separator: " ")
// Then
XCTAssertEqual(got, expected)
}
func test_command_with_platforms() throws {
// Given
let stubbedPath = try temporaryPath()
let expected = "carthage bootstrap --project-directory \(stubbedPath.pathString) --platform iOS --use-netrc --cache-builds --new-resolver"
// When
let got = subject
.command(path: stubbedPath, platforms: [.iOS])
.joined(separator: " ")
// Then
XCTAssertEqual(got, expected)
}
}

View File

@ -0,0 +1,28 @@
import TuistCore
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistSupportTesting
final class CocoaPodsInteractorTests: TuistUnitTestCase {
private var subject: CocoaPodsInteractor!
override func setUp() {
super.setUp()
subject = CocoaPodsInteractor()
}
override func tearDown() {
subject = nil
super.tearDown()
}
func test_fetch() throws {
// Given
let stubbedPath = try temporaryPath()
// When/Then
XCTAssertThrowsSpecific(try subject.fetch(dependenciesDirectory: stubbedPath), CocoaPodsInteractorError.unimplemented)
}
}

View File

@ -0,0 +1,60 @@
import TSCBasic
import TuistCore
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistDependenciesTesting
@testable import TuistSupportTesting
final class DependenciesControllerTests: TuistUnitTestCase {
private var subject: DependenciesController!
private var carthageInteractor: MockCarthageInteractor!
private var cocoaPodsInteractor: MockCocoaPodsInteractor!
private var swiftPackageManagerInteractor: MockSwiftPackageManagerInteractor!
override func setUp() {
super.setUp()
carthageInteractor = MockCarthageInteractor()
cocoaPodsInteractor = MockCocoaPodsInteractor()
swiftPackageManagerInteractor = MockSwiftPackageManagerInteractor()
subject = DependenciesController(carthageInteractor: carthageInteractor,
cocoaPodsInteractor: cocoaPodsInteractor,
swiftPackageManagerInteractor: swiftPackageManagerInteractor)
}
override func tearDown() {
subject = nil
carthageInteractor = nil
cocoaPodsInteractor = nil
swiftPackageManagerInteractor = nil
super.tearDown()
}
func test_fetch() throws {
// Given
let rootPath = try temporaryPath()
let dependenciesDirectoryPath = rootPath
.appending(component: Constants.tuistDirectoryName)
.appending(component: Constants.DependenciesDirectory.name)
let stubbedCarthageDependencies = [
CarthageDependency(origin: .github(path: "Moya"), requirement: .exact("1.1.1"), platforms: [.iOS]),
CarthageDependency(origin: .github(path: "RxSwift"), requirement: .exact("2.0.0"), platforms: [.iOS]),
]
let stubbedDependencies = Dependencies(carthageDependencies: stubbedCarthageDependencies)
// When
try subject.fetch(at: rootPath, dependencies: stubbedDependencies)
// Then
XCTAssertTrue(carthageInteractor.invokedFetch)
XCTAssertEqual(carthageInteractor.invokedFetchParameters?.dependenciesDirectory, dependenciesDirectoryPath)
XCTAssertEqual(carthageInteractor.invokedFetchParameters?.dependencies, stubbedCarthageDependencies)
}
}

View File

@ -1,18 +0,0 @@
import Foundation
import XCTest
@testable import TuistDependencies
@testable import TuistSupportTesting
final class EmptyTests: TuistUnitTestCase {
func test_empty() {
// Given
// When
// Then
XCTAssertTrue(true)
}
}
#warning("Remove this file when you will start adding files to this target.")

View File

@ -0,0 +1,28 @@
import TuistCore
import TuistSupport
import XCTest
@testable import TuistDependencies
@testable import TuistSupportTesting
final class SwiftPackageManagerInteractorTests: TuistUnitTestCase {
private var subject: SwiftPackageManagerInteractor!
override func setUp() {
super.setUp()
subject = SwiftPackageManagerInteractor()
}
override func tearDown() {
subject = nil
super.tearDown()
}
func test_fetch() throws {
// Given
let stubbedPath = try temporaryPath()
// When/Then
XCTAssertThrowsSpecific(try subject.fetch(dependenciesDirectory: stubbedPath), SwiftPackageManagerInteractorError.unimplemented)
}
}

View File

@ -6,10 +6,12 @@ import XCTest
@testable import TuistCoreTesting
@testable import TuistDependenciesTesting
@testable import TuistKit
@testable import TuistLoaderTesting
@testable import TuistSupportTesting
final class DependenciesFetchServiceTests: TuistUnitTestCase {
private var dependenciesController: MockDependenciesController!
private var dependenciesModelLoader: MockDependenciesModelLoader!
private var subject: DependenciesFetchService!
@ -17,40 +19,42 @@ final class DependenciesFetchServiceTests: TuistUnitTestCase {
super.setUp()
dependenciesController = MockDependenciesController()
dependenciesModelLoader = MockDependenciesModelLoader()
subject = DependenciesFetchService(dependenciesController: dependenciesController)
subject = DependenciesFetchService(dependenciesController: dependenciesController,
dependenciesModelLoader: dependenciesModelLoader)
}
override func tearDown() {
subject = nil
dependenciesController = nil
dependenciesModelLoader = nil
super.tearDown()
}
func test_run() throws {
// Given
let path = try temporaryPath()
let stubbedPath = try temporaryPath()
let stubbedDependencies = Dependencies(
carthageDependencies: [
CarthageDependency(origin: .github(path: "Dependency1"), requirement: .exact("1.1.1"), platforms: [.iOS, .macOS]),
]
)
dependenciesModelLoader.loadDependenciesStub = { _ in stubbedDependencies }
// When
try subject.run(path: path.pathString)
try subject.run(path: stubbedPath.pathString)
// Then
XCTAssertTrue(dependenciesController.invokedFetch)
XCTAssertEqual(dependenciesController.invokedFetchCount, 1)
XCTAssertEqual(dependenciesController.invokedFetchParameters, path)
XCTAssertEqual(dependenciesController.invokedFetchParameters?.path, stubbedPath)
XCTAssertEqual(dependenciesController.invokedFetchParameters?.dependencies, stubbedDependencies)
XCTAssertFalse(dependenciesController.invokedUpdate)
}
func test_run_thorws_an_error() throws {
// Given
let path = try temporaryPath()
let error = TestError("Failed fetching!")
dependenciesController.stubbedFetchError = error
// When/Then
XCTAssertThrowsSpecific(try subject.run(path: path.pathString), error)
XCTAssertTrue(dependenciesModelLoader.invokedLoadDependencies)
XCTAssertEqual(dependenciesModelLoader.invokedLoadDependenciesCount, 1)
XCTAssertEqual(dependenciesModelLoader.invokedLoadDependenciesParameters, stubbedPath)
}
}

View File

@ -6,10 +6,12 @@ import XCTest
@testable import TuistCoreTesting
@testable import TuistDependenciesTesting
@testable import TuistKit
@testable import TuistLoaderTesting
@testable import TuistSupportTesting
final class DependenciesUpdateServiceTests: TuistUnitTestCase {
private var dependenciesController: MockDependenciesController!
private var dependenciesModelLoader: MockDependenciesModelLoader!
private var subject: DependenciesUpdateService!
@ -17,40 +19,32 @@ final class DependenciesUpdateServiceTests: TuistUnitTestCase {
super.setUp()
dependenciesController = MockDependenciesController()
dependenciesModelLoader = MockDependenciesModelLoader()
subject = DependenciesUpdateService(dependenciesController: dependenciesController)
subject = DependenciesUpdateService(dependenciesController: dependenciesController,
dependenciesModelLoader: dependenciesModelLoader)
}
override func tearDown() {
subject = nil
dependenciesController = nil
dependenciesModelLoader = nil
super.tearDown()
}
func test_run() throws {
// Given
let path = try temporaryPath()
// When
try subject.run(path: path.pathString)
// Then
XCTAssertTrue(dependenciesController.invokedUpdate)
XCTAssertEqual(dependenciesController.invokedUpdateCount, 1)
XCTAssertEqual(dependenciesController.invokedUpdateParameters, path)
XCTAssertFalse(dependenciesController.invokedFetch)
}
func test_run_thorws_an_error() throws {
// Given
let path = try temporaryPath()
let error = TestError("Failed fetching!")
dependenciesController.stubbedUpdateError = error
let stubbedPath = try temporaryPath()
let stubbedDependencies = Dependencies(
carthageDependencies: [
CarthageDependency(origin: .github(path: "Dependency1"), requirement: .exact("1.1.1"), platforms: [.iOS, .macOS]),
]
)
dependenciesModelLoader.loadDependenciesStub = { _ in stubbedDependencies }
// When/Then
XCTAssertThrowsSpecific(try subject.run(path: path.pathString), error)
XCTAssertThrowsSpecific(try subject.run(path: stubbedPath.pathString), DependenciesUpdateServiceError.unimplemented)
}
}

View File

@ -0,0 +1,53 @@
import Foundation
import TSCBasic
import TuistCore
import TuistSupport
import XCTest
@testable import ProjectDescription
@testable import TuistLoader
@testable import TuistLoaderTesting
@testable import TuistSupportTesting
final class DependenciesModelLoaderTests: TuistUnitTestCase {
private var manifestLoader: MockManifestLoader!
private var subject: DependenciesModelLoader!
override func setUp() {
super.setUp()
manifestLoader = MockManifestLoader()
subject = DependenciesModelLoader(manifestLoader: manifestLoader)
}
override func tearDown() {
subject = nil
manifestLoader = nil
super.tearDown()
}
func test_loadDependencies() throws {
// Given
let stubbedPath = try temporaryPath()
manifestLoader.loadDependenciesStub = { _ in
Dependencies([
.carthage(origin: .github(path: "Dependency1"), requirement: .exact("1.1.1"), platforms: [.iOS]),
.carthage(origin: .git(path: "Dependency1"), requirement: .exact("2.3.4"), platforms: [.macOS, .tvOS]),
])
}
// When
let model = try subject.loadDependencies(at: stubbedPath)
// Then
let expectedCarthageModels: [TuistCore.CarthageDependency] = [
CarthageDependency(origin: .github(path: "Dependency1"), requirement: .exact("1.1.1"), platforms: Set([.iOS])),
CarthageDependency(origin: .git(path: "Dependency1"), requirement: .exact("2.3.4"), platforms: Set([.macOS, .tvOS])),
]
let expectedDependenciesModel = TuistCore.Dependencies(carthageDependencies: expectedCarthageModels)
XCTAssertEqual(model, expectedDependenciesModel)
}
}

View File

@ -0,0 +1,30 @@
import Foundation
import ProjectDescription
import TSCBasic
import TuistCore
import TuistSupport
import XCTest
@testable import TuistLoader
@testable import TuistSupportTesting
final class DependenciesManifestMapperTests: TuistUnitTestCase {
func test_dependencies() throws {
// Given
let manifest: ProjectDescription.Dependencies = Dependencies([
.carthage(origin: .github(path: "Dependency1"), requirement: .exact("1.1.1"), platforms: [.iOS]),
.carthage(origin: .git(path: "Dependency.git"), requirement: .branch("BranchName"), platforms: [.macOS]),
.carthage(origin: .binary(path: "DependencyXYZ"), requirement: .atLeast("2.3.1"), platforms: [.tvOS]),
])
// When
let model = try TuistCore.Dependencies.from(manifest: manifest)
// Then
XCTAssertEqual(model.carthageDependencies, [
TuistCore.CarthageDependency(origin: .github(path: "Dependency1"), requirement: .exact("1.1.1"), platforms: Set([.iOS])),
TuistCore.CarthageDependency(origin: .git(path: "Dependency.git"), requirement: .branch("BranchName"), platforms: Set([.macOS])),
TuistCore.CarthageDependency(origin: .binary(path: "DependencyXYZ"), requirement: .atLeast("2.3.1"), platforms: Set([.tvOS])),
])
}
}

View File

@ -0,0 +1,55 @@
import Foundation
import ProjectDescription
import TSCBasic
import TuistCore
import TuistSupport
import XCTest
@testable import TuistLoader
@testable import TuistSupportTesting
final class PlatformManifestMapperTests: TuistUnitTestCase {
func test_platform_iOS() throws {
// Given
let manifest: ProjectDescription.Platform = .iOS
// When
let model = try TuistCore.Platform.from(manifest: manifest)
// Then
XCTAssertEqual(model, .iOS)
}
func test_platform_tvOS() throws {
// Given
let manifest: ProjectDescription.Platform = .tvOS
// When
let model = try TuistCore.Platform.from(manifest: manifest)
// Then
XCTAssertEqual(model, .tvOS)
}
func test_platform_macOS() throws {
// Given
let manifest: ProjectDescription.Platform = .macOS
// When
let model = try TuistCore.Platform.from(manifest: manifest)
// Then
XCTAssertEqual(model, .macOS)
}
func test_platform_watchOS() throws {
// Given
let manifest: ProjectDescription.Platform = .watchOS
// When
let model = try TuistCore.Platform.from(manifest: manifest)
// Then
XCTAssertEqual(model, .watchOS)
}
}

View File

@ -0,0 +1,11 @@
Feature: Install dependencies Tuist.
Scenario: The project is an application with framework and tests and Dependencies.swift (app_with_framework_and_tests_and_dependencies)
Given that tuist is available
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/Mac/Alamofire.framework exists
Then a file Tuist/Dependencies/Carthage/.Alamofire.version exists
Then a file Tuist/Dependencies/Lockfiles/Cartfile.resolved exists

View File

@ -0,0 +1,9 @@
Then(/tuist fetches dependencies/) do
out, err, status = Open3.capture3("swift", "run", "tuist", "dependencies", "fetch", "--path", @dir)
flunk(err) unless status.success?
end
Then(/tuist updates dependencies/) do
out, err, status = Open3.capture3("swift", "run", "tuist", "dependencies", "update", "--path", @dir)
flunk(err) unless status.success?
end

View File

@ -119,7 +119,12 @@ end
Then(/^a file (.+) exists$/) do |file|
file_path = File.join(@dir, file)
assert(File.exist?(file_path), "#{file_path} does not exist")
assert(File.file?(file_path), "#{file_path} does not exist")
end
Then(/^a directory (.+) exists$/) do |directory|
directory_path = File.join(@dir, directory)
assert(Dir.exist?(directory_path), "#{directory_path} does not exist")
end
Then("the product {string} with destination {string} contains the appClip {string} with architecture {string}") do |product, destination, app_clip, architecture|

View File

@ -0,0 +1,66 @@
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Xcode ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
### Projects ###
*.xcodeproj
*.xcworkspace
### Tuist managed dependencies ###
Tuist/Dependencies

View File

@ -0,0 +1,22 @@
import Framework
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var framework: FrameworkClass = FrameworkClass()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = UIViewController()
viewController.view.backgroundColor = .white
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
func hello() -> String {
return "AppDelegate.hello()"
}
}

View File

@ -0,0 +1,14 @@
import Foundation
import XCTest
@testable import App
final class AppTests: XCTestCase {
func testHello() {
let sut = AppDelegate()
XCTAssertEqual("AppDelegate.hello()", sut.hello())
}
}

View File

@ -0,0 +1,5 @@
import Foundation
public class FrameworkClass {
public init() {}
}

View File

@ -0,0 +1,11 @@
import Foundation
import XCTest
@testable import Framework
final class FrameworkTests: XCTestCase {
var subject: FrameworkClass = FrameworkClass()
func testExample() {
print("Framework tests example")
}
}

View File

@ -0,0 +1,56 @@
import ProjectDescription
let project = Project(
name: "App",
targets: [
Target(
name: "App",
platform: .iOS,
product: .app,
bundleId: "io.tuist.app",
infoPlist: .default,
sources: "App/**",
dependencies: [
.target(name: "Framework"),
]
),
Target(
name: "AppTests",
platform: .iOS,
product: .unitTests,
bundleId: "io.tuist.appTests",
infoPlist: .default,
sources: "AppTests/**",
dependencies: [
.target(name: "App"),
]
),
Target(
name: "Framework",
platform: .iOS,
product: .framework,
bundleId: "io.tuist.framework",
infoPlist: .default,
sources: "Framework/**",
dependencies: [
]
),
Target(
name: "FrameworkTests",
platform: .iOS,
product: .unitTests,
bundleId: "io.tuist.frameworkTests",
infoPlist: .default,
sources: "FrameworkTests/**",
dependencies: [
.target(name: "Framework"),
]
),
],
schemes: [
Scheme(
name: "AppCustomScheme",
buildAction: BuildAction(targets: [TargetReference("App")])
)
]
)

View File

@ -0,0 +1,6 @@
import ProjectDescription
let config = Config(
generationOptions: [
]
)

View File

@ -0,0 +1,5 @@
import ProjectDescription
let dependencies = Dependencies([
.carthage(origin: .github(path: "Alamofire/Alamofire"), requirement: .exact("5.0.4"), platforms: [.macOS])
])

View File

@ -1,5 +1,8 @@
import ProjectDescription
let dependencies = Dependencies([
.carthage(name: "Alamofire", requirement: .exact("5.3.0"), platforms: [.iOS]),
])
let dependencies = Dependencies(
carthage: [
.init(name: "Alamofire/Alamofire", requirement: .exact("5.0.4"), platforms: [.macOS]),
.init(name: "Swinject/Swinject", requirement: .exact("2.7.1"), platforms: [.macOS]),
]
)