Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persists the generated Edit project in the user's .cache directory #6056

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
5 changes: 5 additions & 0 deletions Sources/TuistCore/Cache/CacheCategory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public enum CacheCategory: String, CaseIterable, RawRepresentable {

/// The manifests cache
case manifests

/// The edit projects cache
case editProjects

public var directoryName: String {
switch self {
Expand All @@ -22,6 +25,8 @@ public enum CacheCategory: String, CaseIterable, RawRepresentable {
return "ProjectDescriptionHelpers"
case .manifests:
return "Manifests"
case .editProjects:
return "EditProjects"
}
}
}
4 changes: 1 addition & 3 deletions Sources/TuistGenerator/Linter/EnvironmentLinter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ public class EnvironmentLinter: EnvironmentLinting {
/// - Returns: An array with a linting issue if the selected version is not compatible.
/// - Throws: An error if there's an error obtaining the selected Xcode version.
func lintXcodeVersion(config: Config) throws -> [LintingIssue] {
guard let xcode = try XcodeController.shared.selected() else {
return []
}
let xcode = try XcodeController.shared.selected()

let version = xcode.infoPlist.version

Expand Down
5 changes: 2 additions & 3 deletions Sources/TuistKit/ProjectEditor/ProjectEditorMapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,8 @@ final class ProjectEditorMapper: ProjectEditorMapping {
let helperAndPluginDependencies = helperTargetDependencies + editablePluginTargetDependencies

let packagesTarget: Target? = try {
guard let packageManifestPath,
let xcode = try XcodeController.shared.selected()
else { return nil }
let xcode = try XcodeController.shared.selected()
guard let packageManifestPath else { return nil }
let packageVersion = try swiftPackageManagerController.getToolsVersion(at: packageManifestPath.parentDirectory)

var packagesSettings = targetBaseSettings(
Expand Down
43 changes: 19 additions & 24 deletions Sources/TuistKit/Services/EditService.swift
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we currently don't have tests for this service but can we add some?

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import TSCBasic
import TuistCore
import TuistGenerator
import TuistGraph
import TuistLoader
Expand Down Expand Up @@ -30,21 +31,22 @@ final class EditService {
private let configLoader: ConfigLoading
private let pluginService: PluginServicing
private let signalHandler: SignalHandling

private static var temporaryDirectory: AbsolutePath?
private let cacheDirectoryProviderFactory: CacheDirectoriesProviderFactoring

init(
projectEditor: ProjectEditing = ProjectEditor(),
opener: Opening = Opener(),
configLoader: ConfigLoading = ConfigLoader(manifestLoader: ManifestLoader()),
pluginService: PluginServicing = PluginService(),
signalHandler: SignalHandling = SignalHandler()
signalHandler: SignalHandling = SignalHandler(),
cacheDirectoryProviderFactory: CacheDirectoriesProviderFactoring = CacheDirectoriesProviderFactory()
) {
self.projectEditor = projectEditor
self.opener = opener
self.configLoader = configLoader
self.pluginService = pluginService
self.signalHandler = signalHandler
self.cacheDirectoryProviderFactory = cacheDirectoryProviderFactory
}

func run(
Expand All @@ -56,27 +58,20 @@ final class EditService {
let plugins = await loadPlugins(at: path)

if !permanent {
try withTemporaryDirectory(removeTreeOnDeinit: true) { generationDirectory in
EditService.temporaryDirectory = generationDirectory

signalHandler.trap { _ in
try? EditService.temporaryDirectory.map(FileHandler.shared.delete)
exit(0)
}

guard let selectedXcode = try XcodeController.shared.selected() else {
throw EditServiceError.xcodeNotSelected
}

let workspacePath = try projectEditor.edit(
at: path,
in: generationDirectory,
onlyCurrentDirectory: onlyCurrentDirectory,
plugins: plugins
)
logger.pretty("Opening Xcode to edit the project. Press \(.keystroke("CTRL + C")) once you are done editing")
try opener.open(path: workspacePath, application: selectedXcode.path, wait: true)
}
let pathHash = try XcodeProjectPathHasher.hashString(for: path.pathString)
let cacheDirectory = try cacheDirectoryProviderFactory.cacheDirectories()
let generationDirectory = try cacheDirectory.tuistCacheDirectory(for: .editProjects).appending(component: "\(pathHash)")

let selectedXcode = try XcodeController.shared.selected()

let workspacePath = try projectEditor.edit(
at: path,
in: generationDirectory,
onlyCurrentDirectory: onlyCurrentDirectory,
plugins: plugins
)
logger.pretty("Opening Xcode to edit the project. Press \(.keystroke("CTRL + C")) once you are done editing")
try opener.open(path: workspacePath, application: selectedXcode.path, wait: true)
} else {
let workspacePath = try projectEditor.edit(
at: path,
Expand Down
4 changes: 2 additions & 2 deletions Sources/TuistSupport/Utils/DerivedDataLocator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public final class DerivedDataLocator: DerivedDataLocating {
// This is taken from XCLogParser, from Spotify, at:
// https://github.com/spotify/XCLogParser/blob/master/Sources/XcodeHasher/XcodeHasher.swift

enum XcodeProjectPathHasher {
public enum XcodeProjectPathHasher {
enum HashingError: Error {
case invalidPartitioning
}

static func hashString(for path: String) throws -> String {
public static func hashString(for path: String) throws -> String {
// Initialize a 28 `String` array since we can't initialize empty `Character`s.
var result = Array(repeating: "", count: 28)

Expand Down
16 changes: 7 additions & 9 deletions Sources/TuistSupport/Xcode/XcodeController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public protocol XcodeControlling {
///
/// - Returns: Selected Xcode.
/// - Throws: An error if it can't be obtained.
func selected() throws -> Xcode?
func selected() throws -> Xcode

/// Returns version of the selected Xcode. Uses `selected()` from `XcodeControlling`
///
Expand All @@ -32,29 +32,29 @@ public class XcodeController: XcodeControlling {
///
/// - Returns: Selected Xcode.
/// - Throws: An error if it can't be obtained.
public func selected() throws -> Xcode? {
public func selected() throws -> Xcode {
// Return cached value if available
if let cached = selectedXcode {
return cached
}

// e.g. /Applications/Xcode.app/Contents/Developer
guard let path = try? System.shared.capture(["xcode-select", "-p"]).spm_chomp() else {
return nil
throw XcodeVersionError.noXcode
}

let xcode = try Xcode.read(path: try AbsolutePath(validating: path).parentDirectory.parentDirectory)
selectedXcode = xcode
return xcode
}

enum XcodeVersionError: FatalError {
public enum XcodeVersionError: FatalError {
case noXcode
case noVersion

var type: ErrorType { .abort }
public var type: ErrorType { .abort }

var description: String {
public var description: String {
switch self {
case .noXcode:
return "Could not find Xcode"
Expand All @@ -65,9 +65,7 @@ public class XcodeController: XcodeControlling {
}

public func selectedVersion() throws -> Version {
guard let xcode = try selected() else {
throw XcodeVersionError.noXcode
}
let xcode = try selected()

guard let version = Version(unformattedString: xcode.infoPlist.version) else {
throw XcodeVersionError.noXcode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ public final class MockXcodeController: XcodeControlling {
public var selectedStub: Result<Xcode, Error>?
public var selectedVersionStub: Result<Version, Error> = .success(Version(0, 0, 0))

public func selected() throws -> Xcode? {
guard let selectedStub else { return nil }
public func selected() throws -> Xcode {
guard let selectedStub else { throw XcodeController.XcodeVersionError.noXcode }

switch selectedStub {
case let .failure(error): throw error
Expand Down