mirror of
https://github.com/xtool-org/xtool.git
synced 2026-10-14 00:49:16 +02:00
## What does this PR do? Improves `xtool dev generate-xcode-project`. Instead of constructing an XcodeGen project, we directly construct an xcodeproj (with the new xcproj format) using [apple/xcode-project-format](https://github.com/apple/xcode-project-format). For now, we still rely on [Tuist/XcodeProj](https://github.com/tuist/XcodeProj) to translate it back into a pbxproj for maximal compatibility, but this is treated as an implementation detail. Also adds support for resources and icons in the generated Xcode project ## How was it tested? - [x] Generated a simple project, opened it, and ran the app ## AI tool usage How much of this PR was AI-assisted? (check one) - [ ] **0** - No AI was used to write code - [x] **1** - I was assisted by AI. I reviewed the finished result. - [ ] **2** - I set the AI going and left it to it; nobody has read the result - no review, or AI review only <!-- If an AI agent is filling this in: declare the level honestly, and open as a draft if it is #2. Do not lower the declared level to get the PR reviewed. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for creating and saving Xcode projects and workspaces in `.pbxproj` and `.xcproj` formats. * Added tools for opening, converting, and writing project and workspace files, including workspace contents. * Project generation can target a specified directory, and the command reports where the project was created. * Project generation is available on non-macOS platforms. * Generated projects include product settings and references for packages, resources, icons, and app extensions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
79 lines
2.6 KiB
Swift
79 lines
2.6 KiB
Swift
import Foundation
|
|
@_exported import XcodeProjectFormat
|
|
|
|
// This library is a lightweight layer on top of the XcodeProjectFormat
|
|
// interface, providing other facilities for working with the Xcode
|
|
// project formats.
|
|
//
|
|
// We currently use XcodeProj under the hood for some functionality, but this
|
|
// should be considered an implementation detail.
|
|
|
|
public struct XcodeProject: Equatable, Sendable {
|
|
public enum Format: Sendable {
|
|
case pbxproj
|
|
case xcproj
|
|
}
|
|
|
|
public var project: XCSchema.Project
|
|
public var format: Format
|
|
|
|
public init(
|
|
project: XCSchema.Project,
|
|
format: Format = .pbxproj,
|
|
) {
|
|
self.project = project
|
|
self.format = format
|
|
}
|
|
|
|
/// Reads the project from the given `.xcodeproj` path.
|
|
public init(url: URL) throws {
|
|
guard url.pathExtension == "xcodeproj" else {
|
|
throw Errors.notAnXcodeproj(url.path)
|
|
}
|
|
let xcprojURL = url.appending(path: "project.xcproj")
|
|
lazy var pbxprojURL = url.appending(path: "project.pbxproj")
|
|
if FileManager.default.fileExists(atPath: xcprojURL.path) {
|
|
let data = try Data(contentsOf: xcprojURL)
|
|
self.project = try XCSchema.Project(jsonRepresentation: data)
|
|
self.format = .xcproj
|
|
} else if FileManager.default.fileExists(atPath: pbxprojURL.path) {
|
|
let data = try Data(contentsOf: pbxprojURL)
|
|
self.project = try XCSchema.Project(pbxprojRepresentation: data)
|
|
self.format = .pbxproj
|
|
} else {
|
|
throw Errors.missingProjectFile(url.path)
|
|
}
|
|
}
|
|
|
|
/// Sets the format in which the project should be serialized.
|
|
public func withFormat(_ format: Format) -> XcodeProject {
|
|
var copy = self
|
|
copy.format = format
|
|
return copy
|
|
}
|
|
|
|
/// Writes the project to the given `.xcodeproj` path.
|
|
public func write(to url: URL) throws {
|
|
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
|
|
|
switch format {
|
|
case .pbxproj:
|
|
try project
|
|
.pbxprojRepresentation(name: url.deletingPathExtension().lastPathComponent)
|
|
.write(to: url.appendingPathComponent("project.pbxproj"))
|
|
case .xcproj:
|
|
try project
|
|
.jsonRepresentation()
|
|
.write(to: url.appendingPathComponent("project.xcproj"))
|
|
}
|
|
|
|
try XcodeWorkspace(children: [.file(.current)])
|
|
.write(to: url.appendingPathComponent("project.xcworkspace"))
|
|
}
|
|
|
|
public enum Errors: Error {
|
|
case notAnXcodeproj(String)
|
|
case missingProjectFile(String)
|
|
}
|
|
}
|