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 -->
85 lines
2.7 KiB
Swift
85 lines
2.7 KiB
Swift
import Foundation
|
|
private import XcodeProj
|
|
|
|
extension XcodeWorkspace {
|
|
public init(url: URL) throws {
|
|
let workspace = try XCWorkspace(pathString: url.path)
|
|
self.init(workspace.data)
|
|
}
|
|
|
|
public func dataRepresentation() throws -> Data {
|
|
try xcWorkspaceData.dataRepresentation() ?? Data()
|
|
}
|
|
|
|
public func write(to url: URL) throws {
|
|
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
|
try self.dataRepresentation()
|
|
.write(to: url.appendingPathComponent("contents.xcworkspacedata"))
|
|
}
|
|
}
|
|
|
|
// MARK: - Conversion
|
|
|
|
extension XcodeWorkspace {
|
|
fileprivate init(_ xcWorkspaceData: XCWorkspaceData) {
|
|
self.init(children: xcWorkspaceData.children.map { .init(from: $0) })
|
|
}
|
|
|
|
fileprivate var xcWorkspaceData: XCWorkspaceData {
|
|
XCWorkspaceData(children: children.map(\.xcWorkspaceDataElement))
|
|
}
|
|
}
|
|
|
|
extension XcodeWorkspace.Element {
|
|
fileprivate init(from xcWorkspaceDataElement: XCWorkspaceDataElement) {
|
|
switch xcWorkspaceDataElement {
|
|
case .file(let file):
|
|
self = .file(.init(file.location))
|
|
case .group(let group):
|
|
self = .group(.init(group))
|
|
case .fileSystemSynchronizedGroup(let group):
|
|
self = .fileSystemSynchronizedGroup(.init(group))
|
|
}
|
|
}
|
|
|
|
fileprivate var xcWorkspaceDataElement: XCWorkspaceDataElement {
|
|
switch self {
|
|
case .file(let location):
|
|
.file(.init(location: location.xcWorkspaceLocation))
|
|
case .group(let group):
|
|
.group(group.xcWorkspaceDataElement)
|
|
case .fileSystemSynchronizedGroup(let group):
|
|
.fileSystemSynchronizedGroup(group.xcWorkspaceDataElement)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension XcodeWorkspace.Location {
|
|
fileprivate init(_ xcWorkspaceDataElementLocation: XCWorkspaceDataElementLocationType) {
|
|
self.base = .init(rawValue: xcWorkspaceDataElementLocation.schema)
|
|
self.path = xcWorkspaceDataElementLocation.path
|
|
}
|
|
|
|
fileprivate var xcWorkspaceLocation: XCWorkspaceDataElementLocationType {
|
|
.other(base.rawValue, path)
|
|
}
|
|
}
|
|
|
|
extension XcodeWorkspace.Group {
|
|
fileprivate init(_ xcWorkspaceDataGroup: XCWorkspaceDataGroup) {
|
|
self.init(
|
|
location: .init(xcWorkspaceDataGroup.location),
|
|
name: xcWorkspaceDataGroup.name,
|
|
children: xcWorkspaceDataGroup.children.map { .init(from: $0) }
|
|
)
|
|
}
|
|
|
|
fileprivate var xcWorkspaceDataElement: XCWorkspaceDataGroup {
|
|
.init(
|
|
location: self.location.xcWorkspaceLocation,
|
|
name: name,
|
|
children: children.map(\.xcWorkspaceDataElement)
|
|
)
|
|
}
|
|
}
|