Created
July 21, 2023 20:30
-
-
Save leogdion/ad5ca07da41fe470b0f9cb721c22507c to your computer and use it in GitHub Desktop.
Swift DSL for Package.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// swift-tools-version: 5.9 | |
// The swift-tools-version declares the minimum version of Swift required to build this package. | |
// add this to beginning of your Package.swift | |
import PackageDescription | |
// MARK: SupportedPlatform | |
protocol SupportedPlatformable { | |
var platforms: [SupportedPlatform] { get } | |
} | |
protocol SupportedPlatformBody: SupportedPlatformable { | |
@SupportedPlatformBuilder | |
var body: [SupportedPlatform] { get } | |
} | |
extension SupportedPlatform: SupportedPlatformable { | |
var platforms: [SupportedPlatform] { | |
[self] | |
} | |
} | |
extension SupportedPlatformBody { | |
var platforms: [SupportedPlatform] { | |
self.body | |
} | |
} | |
@resultBuilder | |
enum SupportedPlatformBuilder { | |
static func buildPartialBlock(first: SupportedPlatformable) -> [SupportedPlatform] { | |
first.platforms | |
} | |
static func buildPartialBlock(accumulated: [SupportedPlatform], next: SupportedPlatformable) -> [SupportedPlatform] { | |
accumulated + next.platforms | |
} | |
} | |
// MARK: Products | |
protocol Productable { | |
var products: [Product] { get } | |
} | |
enum ProductType { | |
case library | |
case executable | |
} | |
protocol ProductSpecs: Productable { | |
var name: String { get } | |
var targetDependencies: [String] { get } | |
var type: ProductType { get } | |
} | |
extension ProductSpecs { | |
var name: String { | |
"\(Self.self)" | |
} | |
var targetDependencies: [String] { | |
[self.name] | |
} | |
var type: ProductType { | |
.library | |
} | |
var products: [Product] { | |
switch self.type { | |
case .executable: | |
[Product.executable(name: name, targets: targetDependencies)] | |
case .library: | |
[Product.library(name: name, targets: targetDependencies)] | |
} | |
} | |
} | |
protocol ProductBody: Productable { | |
@ProductBuilder | |
var body: [Product] { get } | |
} | |
extension Product: Productable { | |
var products: [Product] { | |
[self] | |
} | |
} | |
extension ProductBody { | |
var products: [Product] { | |
self.body | |
} | |
} | |
@resultBuilder | |
enum ProductBuilder { | |
static func buildPartialBlock(first: Productable) -> [Product] { | |
first.products | |
} | |
static func buildPartialBlock(accumulated: [Product], next: Productable) -> [Product] { | |
accumulated + next.products | |
} | |
} | |
// MARK: Dependencies | |
protocol PackageDependency { | |
var packageDependencies: [Package.Dependency] { get } | |
} | |
extension Package.Dependency: PackageDependency { | |
var packageDependencies: [Package.Dependency] { | |
[self] | |
} | |
} | |
extension PackageDependency { | |
static func package(url: String, from version: PackageDescription.Version) -> PackageDependency { | |
Package.Dependency.package(url: url, from: version) | |
} | |
} | |
protocol PackageDependencySpec: PackageDependency { | |
var dependency: Package.Dependency { get } | |
} | |
extension PackageDependencySpec { | |
var packageDependencies: [Package.Dependency] { | |
[self.dependency] | |
} | |
} | |
@resultBuilder | |
enum PackageDependencyBuilder { | |
static func package(url: String, from version: PackageDescription.Version) -> PackageDependency { | |
Package.Dependency.package(url: url, from: version) | |
} | |
static func buildPartialBlock(first: PackageDependency) -> [Package.Dependency] { | |
first.packageDependencies | |
} | |
static func buildPartialBlock( | |
accumulated: [Package.Dependency], | |
next: PackageDependency | |
) -> [Package.Dependency] { | |
accumulated + next.packageDependencies | |
} | |
} | |
// MARK: TargetDependencies | |
protocol TargetDependency { | |
var targetDependency: [Target.Dependency] { get } | |
} | |
extension Target.Dependency: TargetDependency { | |
var targetDependency: [Target.Dependency] { | |
[self] | |
} | |
} | |
extension String { | |
var packageName : String? { | |
self.split(separator: "/").last?.split(separator: ".").first.map(String.init) | |
} | |
} | |
extension PackageDependencySpec where Self: TargetDependency { | |
var productName: String { | |
"\(Self.self)" | |
} | |
var targetDependency: [Target.Dependency] { | |
switch self.dependency.kind { | |
case let .sourceControl(name: name, location: location, requirement: requirement): | |
let packageName = name ?? location.packageName | |
return [ | |
Target.Dependency.product(name: productName, package: packageName) | |
] | |
@unknown default: | |
return [ | |
Target.Dependency.byName(name: productName) | |
] | |
} | |
} | |
} | |
@resultBuilder | |
enum TargetDependencyBuilder { | |
static func buildPartialBlock(first: TargetDependency) -> [Target.Dependency] { | |
first.targetDependency | |
} | |
static func buildPartialBlock(accumulated: [Target.Dependency], next: TargetDependency) -> [Target.Dependency] { | |
accumulated + next.targetDependency | |
} | |
} | |
// MARK: Targets | |
protocol Targetable: TargetDependency { | |
var targets: [Target] { get } | |
} | |
extension Targetable { | |
var targetDependency: [Target.Dependency] { | |
targets.map { target in | |
.target(name: target.name) | |
} | |
} | |
} | |
protocol TargetSpecs: Targetable { | |
var targetName: String { get } | |
@TargetDependencyBuilder | |
var dependencies: [Target.Dependency] { get } | |
} | |
extension TargetSpecs { | |
var targetName: String { | |
"\(Self.self)" | |
} | |
var dependencies: [Target.Dependency] { | |
[] | |
} | |
var targets: [Target] { | |
[.target(name: self.targetName, dependencies: self.dependencies)] | |
} | |
} | |
protocol TargetBody: Targetable { | |
@TargetBuilder | |
var body: [Target] { get } | |
} | |
extension Target: Targetable { | |
var targets: [Target] { | |
[self] | |
} | |
} | |
extension TargetBody { | |
var targets: [Target] { | |
self.body | |
} | |
} | |
@resultBuilder | |
enum TargetBuilder { | |
static func buildPartialBlock(first: Targetable) -> [Target] { | |
first.targets | |
} | |
static func buildPartialBlock(accumulated: [Target], next: Targetable) -> [Target] { | |
accumulated + next.targets | |
} | |
} | |
// MARK: Package | |
extension LanguageTag { | |
static let english: LanguageTag = "en" | |
} | |
extension Package { | |
convenience init( | |
name: String, | |
defaultLocalization: LanguageTag, | |
@SupportedPlatformBuilder platforms: @escaping () -> [SupportedPlatform], | |
@ProductBuilder products: () -> [Product], | |
@PackageDependencyBuilder dependencies: () -> [Package.Dependency], | |
@TargetBuilder targets: () -> [Target] | |
) { | |
self.init( | |
name: name, | |
defaultLocalization: defaultLocalization, | |
platforms: platforms(), | |
products: products(), | |
dependencies: dependencies(), | |
targets: targets() | |
) | |
} | |
} | |
// example code from Bushel: | |
// MARK: - Bushel | |
//// MARK: Supported Platforms | |
// | |
//struct WWDC2023: SupportedPlatformBody { | |
// var body: [SupportedPlatform] { | |
// SupportedPlatform.macOS(.v14) | |
// SupportedPlatform.iOS(.v17) | |
// SupportedPlatform.watchOS(.v10) | |
// SupportedPlatform.tvOS(.v17) | |
// } | |
//} | |
// | |
//// MARK: Dependencies | |
// | |
//struct FelinePine: PackageDependencySpec, TargetDependency { | |
// var dependency: PackageDescription.Package.Dependency { | |
// .package(url: "https://github.com/brightdigit/FelinePine.git", from: "0.1.0-alpha.3") | |
// } | |
//} | |
// | |
//struct ArgumentParser: PackageDependencySpec, TargetDependency { | |
// var dependency: PackageDescription.Package.Dependency { | |
// .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.0") | |
// } | |
//} | |
// | |
//struct BushelCore: TargetSpecs {} | |
// | |
//struct BushelLocalization: TargetSpecs {} | |
// | |
//struct BushelUT: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelCore() | |
// } | |
//} | |
// | |
//struct BushelLogging: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// FelinePine() | |
// } | |
//} | |
// | |
//struct BushelDataCore: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLogging() | |
// } | |
//} | |
// | |
//struct BushelLibraryData: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLibrary() | |
// BushelLogging() | |
// BushelDataCore() | |
// } | |
//} | |
// | |
//struct BushelLibrary: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLogging() | |
// BushelCore() | |
// } | |
//} | |
// | |
//struct BushelLibraryViews: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLibrary() | |
// BushelLibraryData() | |
// BushelLogging() | |
// BushelUT() | |
// BushelViewsCore() | |
// } | |
//} | |
// | |
//struct BushelMachine: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelCore() | |
// BushelLogging() | |
// } | |
//} | |
// | |
//struct BushelMachineData: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelDataCore() | |
// BushelMachine() | |
// BushelLogging() | |
// } | |
//} | |
// | |
//struct BushelMachineViews: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelMachineData() | |
// BushelLogging() | |
// BushelUT() | |
// BushelLocalization() | |
// BushelViewsCore() | |
// } | |
//} | |
// | |
//struct BushelViewsCore: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLogging() | |
// BushelUT() | |
// } | |
//} | |
// | |
//struct BushelViews: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLibraryViews() | |
// BushelMachineViews() | |
// BushelSettingsViews() | |
// } | |
//} | |
// | |
//struct BushelData: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLibraryData() | |
// BushelMachineData() | |
// } | |
//} | |
// | |
//struct BushelVirtualization: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLibraryMacOS() | |
// BushelMachineMacOS() | |
// } | |
//} | |
// | |
// | |
//struct BushelSettingsViews: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelData() | |
// BushelLocalization() | |
// } | |
//} | |
// | |
//struct BushelLibraryMacOS: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLibrary() | |
// BushelMacOSCore() | |
// } | |
//} | |
// | |
//struct BushelMachineMacOS: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelMachine() | |
// BushelMacOSCore() | |
// } | |
//} | |
// | |
//struct BushelMacOSCore: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelCore() | |
// } | |
//} | |
// | |
//struct BushelArgs: TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// ArgumentParser() | |
// } | |
//} | |
// | |
//struct BushelCommand: TargetSpecs, ProductSpecs { | |
// var targetName: String { | |
// "bushel" | |
// } | |
// | |
// var dependencies: [Target.Dependency] { | |
// BushelArgs() | |
// } | |
// | |
// var type: ProductType { | |
// .executable | |
// } | |
//} | |
// | |
//struct BushelLibraryApp: ProductSpecs, TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelLibraryViews() | |
// BushelLibraryMacOS() | |
// } | |
//} | |
// | |
// | |
//struct BushelSettingsApp: ProductSpecs, TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelSettingsViews() | |
// } | |
//} | |
// | |
// | |
//struct BushelMachineApp: TargetSpecs, ProductSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelMachineViews() | |
// BushelMachineMacOS() | |
// } | |
//} | |
// | |
// | |
//struct BushelApp: ProductSpecs, TargetSpecs { | |
// var dependencies: [Target.Dependency] { | |
// BushelViews() | |
// BushelVirtualization() | |
// BushelMachine() | |
// BushelLibrary() | |
// BushelData() | |
// } | |
//} | |
// | |
// | |
//let package = Package( | |
// name: "BushelKit", | |
// defaultLocalization: .english | |
//) { | |
// WWDC2023() | |
//} products: { | |
// BushelLibraryApp() | |
// BushelMachineApp() | |
// BushelSettingsApp() | |
// BushelApp() | |
//} dependencies: { | |
// FelinePine() | |
// ArgumentParser() | |
//} targets: { | |
// BushelCore() | |
// BushelUT() | |
// BushelLocalization() | |
// BushelLogging() | |
// | |
// BushelArgs() | |
// BushelCommand() | |
// | |
// BushelDataCore() | |
// BushelViewsCore() | |
// | |
// BushelMacOSCore() | |
// BushelLibraryMacOS() | |
// BushelMachineMacOS() | |
// BushelVirtualization() | |
// | |
// BushelLibrary() | |
// BushelLibraryData() | |
// BushelLibraryViews() | |
// BushelLibraryApp() | |
// | |
// BushelMachine() | |
// BushelMachineData() | |
// BushelMachineViews() | |
// BushelMachineApp() | |
// | |
// BushelSettingsViews() | |
// BushelSettingsApp() | |
// | |
// BushelData() | |
// BushelViews() | |
// | |
// BushelApp() | |
//} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment