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 -->
104 lines
3.0 KiB
Swift
104 lines
3.0 KiB
Swift
import Foundation
|
|
|
|
public struct XcodeWorkspace: Hashable, Sendable {
|
|
public enum Element: Hashable, Sendable {
|
|
case file(Location)
|
|
case group(Group)
|
|
case fileSystemSynchronizedGroup(Group)
|
|
|
|
public var location: Location {
|
|
get {
|
|
switch self {
|
|
case let .file(location):
|
|
location
|
|
case let .group(group):
|
|
group.location
|
|
case let .fileSystemSynchronizedGroup(group):
|
|
group.location
|
|
}
|
|
}
|
|
set {
|
|
switch self {
|
|
case .file:
|
|
self = .file(newValue)
|
|
case .group(var group):
|
|
group.location = newValue
|
|
self = .group(group)
|
|
case .fileSystemSynchronizedGroup(var group):
|
|
group.location = newValue
|
|
self = .fileSystemSynchronizedGroup(group)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct Group: Hashable, Sendable {
|
|
public var location: Location
|
|
public var name: String?
|
|
public var children: [Element]
|
|
|
|
public init(location: Location, name: String?, children: [Element]) {
|
|
self.location = location
|
|
self.name = name
|
|
self.children = children
|
|
}
|
|
}
|
|
|
|
public struct Location: Hashable, Sendable {
|
|
public struct Base: Hashable, Sendable, RawRepresentable {
|
|
public var rawValue: String
|
|
|
|
public init(rawValue: String) {
|
|
self.rawValue = rawValue
|
|
}
|
|
}
|
|
|
|
public var base: Base
|
|
public var path: String
|
|
|
|
public init(base: Base, path: String) {
|
|
self.base = base
|
|
self.path = path
|
|
}
|
|
}
|
|
|
|
public var children: [Element]
|
|
|
|
public init(children: [Element]) {
|
|
self.children = children
|
|
}
|
|
}
|
|
|
|
extension XcodeWorkspace.Location.Base {
|
|
/// Absolute path
|
|
public static let absolute = Self(rawValue: "absolute")
|
|
/// Relative to container
|
|
public static let container = Self(rawValue: "container")
|
|
/// Relative to Developer Directory
|
|
public static let developer = Self(rawValue: "developer")
|
|
/// Relative to group
|
|
public static let group = Self(rawValue: "group")
|
|
/// Single project workspace in xcodeproj directory
|
|
public static let current = Self(rawValue: "self")
|
|
}
|
|
|
|
extension XcodeWorkspace.Location {
|
|
public static var current: Self {
|
|
Self(base: .current, path: "")
|
|
}
|
|
}
|
|
|
|
extension XcodeWorkspace.Location: LosslessStringConvertible, CustomStringConvertible {
|
|
public init?(_ string: String) {
|
|
guard let colon = string.firstIndex(of: ":") else {
|
|
return nil
|
|
}
|
|
self.base = Base(rawValue: String(string[..<colon]))
|
|
self.path = String(string[colon...].dropFirst())
|
|
}
|
|
|
|
public var description: String {
|
|
"\(base.rawValue):\(path)"
|
|
}
|
|
}
|