mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This is a tool specifically designed to generate Xcode projects for the Swift repo (as well as a couple of adjacent repos such as LLVM and Clang). It aims to provide a much more user-friendly experience than the CMake Xcode generation (`build-script --xcode`).
173 lines
4.3 KiB
Swift
173 lines
4.3 KiB
Swift
//===--- SchemeGenerator.swift --------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2024 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import Xcodeproj
|
|
|
|
struct Scheme {
|
|
var name: String
|
|
var buildAction: BuildAction
|
|
var runAction: RunAction?
|
|
var replaceExisting: Bool
|
|
|
|
init(_ name: String, replaceExisting: Bool, buildTargets: [BuildTarget]) {
|
|
self.name = name
|
|
self.replaceExisting = replaceExisting
|
|
self.buildAction = .init(targets: buildTargets)
|
|
}
|
|
|
|
init(_ name: String, replaceExisting: Bool, buildTargets: BuildTarget...) {
|
|
self.init(
|
|
name, replaceExisting: replaceExisting, buildTargets: buildTargets
|
|
)
|
|
}
|
|
|
|
mutating func addBuildTarget(
|
|
_ target: Xcode.Target, in path: RelativePath
|
|
) {
|
|
buildAction.targets.append(.init(target, in: path))
|
|
}
|
|
}
|
|
|
|
extension Scheme {
|
|
struct BuildAction {
|
|
var targets: [BuildTarget]
|
|
}
|
|
|
|
struct BuildTarget {
|
|
var name: String
|
|
var container: RelativePath
|
|
|
|
init(_ target: Xcode.Target, in path: RelativePath) {
|
|
self.name = target.name
|
|
self.container = path
|
|
}
|
|
|
|
init(_ name: String, in path: RelativePath) {
|
|
self.name = name
|
|
self.container = path
|
|
}
|
|
}
|
|
|
|
struct RunAction {
|
|
var path: AbsolutePath
|
|
}
|
|
}
|
|
|
|
struct SchemeGenerator {
|
|
let containerDir: AbsolutePath
|
|
let disableAutoCreation: Bool
|
|
|
|
var schemes: [Scheme] = []
|
|
|
|
init(in containerDir: AbsolutePath, disableAutoCreation: Bool = true) {
|
|
self.containerDir = containerDir
|
|
self.disableAutoCreation = disableAutoCreation
|
|
}
|
|
}
|
|
|
|
extension SchemeGenerator {
|
|
mutating func add(_ scheme: Scheme) {
|
|
schemes.append(scheme)
|
|
}
|
|
}
|
|
|
|
extension SchemeGenerator {
|
|
private func disableAutoCreationIfNeeded() throws {
|
|
guard disableAutoCreation else { return }
|
|
|
|
var relPath: RelativePath = "xcshareddata/WorkspaceSettings.xcsettings"
|
|
if containerDir.hasExtension(.xcodeproj) {
|
|
relPath = "project.xcworkspace/\(relPath)"
|
|
}
|
|
let settingsPath = containerDir.appending(relPath)
|
|
|
|
let workspaceSettings = """
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
|
|
<false/>
|
|
</dict>
|
|
</plist>
|
|
"""
|
|
|
|
try settingsPath.write(workspaceSettings)
|
|
log.info("Generated '\(settingsPath)'")
|
|
}
|
|
|
|
private func writeScheme(_ scheme: Scheme) throws {
|
|
let path = containerDir.appending(
|
|
"xcshareddata/xcschemes/\(scheme.name).xcscheme"
|
|
)
|
|
|
|
// Don't overwrite if we haven't been asked to.
|
|
if !scheme.replaceExisting && path.exists {
|
|
return
|
|
}
|
|
|
|
var plist = """
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<Scheme LastUpgradeVersion = "9999" version = "1.3">
|
|
<BuildAction parallelizeBuildables = "YES" buildImplicitDependencies = "YES">
|
|
<BuildActionEntries>
|
|
|
|
"""
|
|
for buildTarget in scheme.buildAction.targets {
|
|
plist += """
|
|
<BuildActionEntry buildForTesting = "YES" buildForRunning = "YES" buildForProfiling = "YES" buildForArchiving = "YES" buildForAnalyzing = "YES">
|
|
<BuildableReference
|
|
BuildableIdentifier = "primary"
|
|
BuildableName = "\(buildTarget.name)"
|
|
BlueprintName = "\(buildTarget.name)"
|
|
ReferencedContainer = "container:\(buildTarget.container)">
|
|
</BuildableReference>
|
|
</BuildActionEntry>
|
|
|
|
"""
|
|
}
|
|
plist += """
|
|
</BuildActionEntries>
|
|
</BuildAction>
|
|
|
|
"""
|
|
|
|
if let runAction = scheme.runAction {
|
|
plist += """
|
|
<LaunchAction>
|
|
<PathRunnable
|
|
FilePath = "\(runAction.path.escaped(addQuotesIfNeeded: false))">
|
|
</PathRunnable>
|
|
</LaunchAction>
|
|
|
|
"""
|
|
}
|
|
|
|
plist += """
|
|
</Scheme>
|
|
"""
|
|
|
|
try path.write(plist)
|
|
log.info("Generated '\(path)'")
|
|
}
|
|
|
|
private func writeSchemes() throws {
|
|
for scheme in schemes {
|
|
try writeScheme(scheme)
|
|
}
|
|
}
|
|
|
|
func write() throws {
|
|
try disableAutoCreationIfNeeded()
|
|
try writeSchemes()
|
|
}
|
|
}
|