Files
SideStore/SideStoreApp/Package.swift
2023-03-10 19:32:33 -05:00

634 lines
20 KiB
Swift

// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import Foundation
import os.log
import PackageDescription
// Process enviroment variables.
typealias EnviromentBool = (var: String, default: Bool)
// Possible keys for `env` and their default value
let USE_CARGO = envBool(("USE_CARGO", false))
let USE_CXX_INTEROP = envBool(("USE_CXX_INTEROP", false))
let USE_CXX_MODULES = envBool(("USE_CXX_MODULES", false))
let INHIBIT_UPSTREAM_WARNINGS = envBool(("INHIBIT_UPSTREAM_WARNINGS", true))
let STATIC_LIBRARY = envBool(("STATIC_LIBRARY", false))
let unsafe_flags : [String] = INHIBIT_UPSTREAM_WARNINGS ?
["-w"] :
[String]()
let unsafe_flags_cxx: [String] = INHIBIT_UPSTREAM_WARNINGS ?
["-w", "-Wno-module-import-in-extern-c"] :
["-Wno-module-import-in-extern-c"]
extension Package.Dependency {
/// The combination of all the dependencies for the Package.
enum SideStore {
static let dependencies: [Package.Dependency] =
Packages_SideStoreTeam +
Packages_3rdParty +
Plugins_BuildTools +
Plugins_CICD
}
/// Side Store Team Packages
static let Packages_SideStoreTeam: [Package.Dependency] = [
.github("SideStore/AltSign", from: "1.0.3"),
.github("SideStore/iMobileDevice.swift", from: "1.0.5"),
.github("SideStore/SideKit", from: "0.1.0"),
/// @JoeMatt updated fork for Riley's Roxas
.github("JoeMatt/Roxas", from: "1.2.2"),
]
/// 3rd party Packages
static let Packages_3rdParty: [Package.Dependency] = [
.github("SwiftPackageIndex/SemanticVersion", from: "0.3.5"),
.github("johnxnguyen/Down", branch: "master"),
.github("kean/Nuke", from: "7.0.0"),
.github("kishikawakatsumi/KeychainAccess", from: "4.2.0"),
.github("microsoft/appcenter-sdk-apple", from: "4.2.0"),
.github("sindresorhus/LaunchAtLogin", from: "5.0.0"),
]
static let Plugins_BuildTools: [Package.Dependency] = [
// Plugins
/// IntentBuilder for spm support of intents and Logger injection
.github("JoeMatt/SwiftPMPlugins", from: "1.0.0"),
/// Generate swift files with git head info
.github("elegantchaos/Versionator", from: "1.0.3"),
/// plists from .json, including Info.plist
.github("elegantchaos/InfomaticPlugin", branch: "main").disable
]-?
static let Plugins_CICD: [Package.Dependency] = [
/// Swiftlint
.github("lukepistrol/SwiftLintPlugin", from: "0.2.2").disable,
/// git secrets from env (for adding sensative api keys via CI/CD,
/// ```sh
/// swift package plugin --allow-writing-to-package-directory secret-keys generate
/// ```
/// or using Mint
/// ```sh
/// mint run secret-keys generate
/// ```
.github("simorgh3196/swift-secret-keys", from: "0.0.1").disable,
/// #__ Swift docc generator __
/// `swift package generate-documentation`
/// ## Inline creation
/// ```sh
/// swift package \
/// --allow-writing-to-directory ./docs \
/// generate-documentation \
/// --target MyFramework \
/// --output-path ./docs
/// ```
/// ## Preview:
/// `swift package --disable-sandbox preview-documentation --target MyFramework`
/// [Hosting](https://apple.github.io/swift-docc-plugin/documentation/swiftdoccplugin/)
.github("apple/swift-docc-plugin", from: "1.1.0").disable,
]-?
#if USE_RESULT_BUILDER
// TODO: Make a DSL using a @resultBuilder
@DependencyBuilder
static var Additional_Plugins: [Package.Dependency] {
// Secrets manager using `.env`
github("vdka/SecretsManager.git", from: "1.0.0")
// Generate `PackageBuild` struct with build time info about repo
github("DimaRU/PackageBuildInfo", branch: "master")
/// Plugin for simply updating your Package.swift file consistently and understandably.
github("mackoj/PackageGeneratorPlugin", from: "0.3.0")
/// Plugin for quickly updating your Schemes files
github("mackoj/SchemeGeneratorPlugin", from: "0.5.5")
github("pointfreeco/swift-composable-architecture", from: "0.45.0")
}
#else
static let Additional_Plugins: [Package.Dependency] = [
// Secrets manager using `.env`
github("vdka/SecretsManager.git", from: "1.0.0").disable,
// Generate `PackageBuild` struct with build time info about repo
github("DimaRU/PackageBuildInfo", branch: "master").disable,
/// Plugin for simply updating your `Package.swift` file consistently and understandably.
github("mackoj/PackageGeneratorPlugin", from: "0.3.0").disable,
/// Plugin for quickly updating your `.xcscheme` files
github("mackoj/SchemeGeneratorPlugin", from: "0.5.5").disable,
github("pointfreeco/swift-composable-architecture", from: "0.45.0").disable
].removeNils()
#endif
}
let InfomaticPlugin : Target.PluginUsage = .plugin(name: "InfomaticPlugin", package: "InfomaticPlugin")
let PackageBuildInfoPlugin : Target.PluginUsage = .plugin(name: "PackageBuildInfoPlugin",package: "PackageBuildInfo")
let SafeURLPlugin : Target.PluginUsage = .plugin(name: "SafeURLPlugin", package: "SafeURLPlugin")
let SecretsManagerPlugin : Target.PluginUsage = .plugin(name: "SecretsManagerPlugin", package: "SecretsManager")
let VersionatorPlugin : Target.PluginUsage = .plugin(name: "VersionatorPlugin", package: "Versionator")
let IntentBuilderPlugin : Target.PluginUsage = .plugin(name: "IntentBuilderPlugin", package: "SwiftPMPlugins")
let LoggerPlugin : Target.PluginUsage = .plugin(name: "LoggerPlugin", package: "SwiftPMPlugins")
let commonPlugins: [Target.PluginUsage] = [LoggerPlugin]
// MARK: - 3rd Party Dependencies
let AppCenterAnalytics: Target.Dependency = .product(name: "AppCenterAnalytics", package: "appcenter-sdk-apple")
let AppCenterCrashes : Target.Dependency = .product(name: "AppCenterCrashes", package: "appcenter-sdk-apple")
let CoreCrypto : Target.Dependency = .product(name: "CoreCrypto", package: "AltSign")
let CCoreCrypto : Target.Dependency = .product(name: "CCoreCrypto", package: "AltSign")
let libimobiledevice: Target.Dependency = .product(name: "libimobiledevice", package: "iMobileDevice.swift")
let Roxas : Target.Dependency = .product(name: "Roxas", package: "Roxas")
let RoxasUI : Target.Dependency = .product(name: "RoxasUI", package: "Roxas")
// MARK: - Linking
let frameworksCommon: [LinkerSetting] = [ "Avfoundation",
"CoreData",
"Combine",
"Network",
"UniformTypeIdentifiers" ].map{LinkerSetting.linkedFramework($0)}
let frameworksIOS: [LinkerSetting] = [
"ARKit",
"AudioToolbox",
"AuthenticationServices",
"CoreHaptics",
"Intents",
"IntentsUI",
"MessageUI",
"MobileCoreServices",
"QuickLook",
"SafariServices",
"UserNotifications",
"WidgetKit"
]
.map{LinkerSetting.linkedFramework($0,
.when(platforms: [.iOS, .macCatalyst]))}
// MARK: -- PACKAGE --
let package = Package(
name: "SideStore",
defaultLocalization: "en",
platforms: [
.iOS(.v14),
.tvOS(.v14),
.macCatalyst(.v14),
.macOS(.v12),
],
products: Product.products,
dependencies: Package.Dependency.SideStore.dependencies,
targets: Target.SideStore.targets,
swiftLanguageVersions: [.v5],
cLanguageStandard: .gnu11,
cxxLanguageStandard: .gnucxx14
)
// ----------------
// MARK: - Products
// ----------------
extension Product {
static let products: [Product] = [
// Modules
SideStoreAppKit.0,
SideStoreAppKit.static,
SideStoreAppKit.dynamic,
SideStoreCore.0,
SideStoreCore.static,
SideStoreCore.dynamic,
SideWidgetKit.0,
SideWidgetKit.static,
SideWidgetKit.dynamic,
// `.app` Executables
SideStore_app,
]
// CLI Executables
static let cliProducts: [Product] = [
SideDaemon
]
// SideStoreAppKit
static let SideStoreAppKit = librarySet("SideStoreAppKit")
// SideStoreCore
static let SideStoreCore = librarySet("SideStoreCore")
// SideWidgetKit
static let SideWidgetKit = librarySet("SideWidgetKit")
// SideStore Executable
static let SideStore_app: Product = .executable( name: "SideStore", targets: ["SideStore"])
// SideDaemon Executable
static let SideDaemon : Product = .executable(name: "SideDaemon", targets: ["SideDaemon"])
#if USE_CARGO_BUILD_PLUGIN
// Cargo Plugin (WIP)
static let CargoPlugin: Product = .plugin(name: "CargoPlugin", targets: ["CargoPlugin"])
#endif
}
// ----------------
// MARK: - Targets
// ----------------
extension Target {
enum SideStore { //: Encodable, CaseIterable {
static let linkerSettings: [LinkerSetting] = frameworksCommon + frameworksIOS + [
.linkedFramework("UIKit", .when(platforms: [.iOS, .macCatalyst, .tvOS])),
.linkedLibrary("AppleArchive")
]
}
}
typealias TargetPair = (target: PackageDescription.Target, testTarget: PackageDescription.Target?)
// MARK: - SideStoreAppKit
extension Target.SideStore {
/// All the targets fo be added to `Package(targets:)`
static let targets: [Target] = publicTargets + Internal.targets + pluginTargets
/// __Public Targets__
static let publicTargets: [Target] = [
// SideStoreAppKit
SideStoreAppKit,
// iOS Widget
SideWidgetKit,
// SideStoreCore
SideStoreCore.target,
SideStoreCore.testTarget,
// SideBackup Executable
SideBackup,
// SideDaemon
SideDaemon.target,
SideDaemon.testTarget,
// SidePatcher
SidePatcher.target,
SidePatcher.testTarget,
// App Bundle
Apps.SideStore_app,
].compactMap{$0}
/// __PluginTargets__
static let pluginTargets: [Target] = {
#if USE_CARGO_BUILD_PLUGIN
Cargo.Plugins
#else
[]
#endif
}()
// MARK: - SideStoreAppKit
static let SideStoreAppKit: Target =
.target(
name: "SideStoreAppKit",
dependencies: [
"AltSign",
"Down",
"EmotionalDamage",
"KeychainAccess",
libimobiledevice,
"MiniMuxer",
"Nuke",
Roxas,
RoxasUI,
"SideKit",
"SidePatcher",
"SideStoreCore",
],
resources: [],
linkerSettings: linkerSettings,
plugins: [LoggerPlugin, IntentBuilderPlugin]
)
// MARK: - SideWidgetKit
static let SideWidgetKit: Target =
.target(
name: "SideWidgetKit",
dependencies: [
"SideKit",
"SideStoreAppKit",
"SideStoreCore"
],
plugins: commonPlugins
)
// MARK: - SideStoreCore
static let SideStoreCore: TargetPair = (
.target(
name: "SideStoreCore",
dependencies: [
"AltSign",
"KeychainAccess",
Roxas,
"SemanticVersion"],
plugins: commonPlugins),
.testTarget(
name: "SideStoreCoreTests",
dependencies: ["SideStoreCore", "KeychainAccess", "AltSign", "SemanticVersion", "SideKit"]))
// MARK: - SideDaemon
static let SideDaemon: TargetPair = (
.executableTarget(
name: "SideDaemon",
dependencies: [
"AltSign",
"SideKit",
CoreCrypto,
CCoreCrypto,
.product(name: "LaunchAtLogin", package: "LaunchAtLogin"),
],
plugins: commonPlugins
),
.testTarget(name: "SideDaemonTests", dependencies: ["SideDaemon"]))
// MARK: - SideBackup
static let SideBackup: Target =
.executableTarget(name: "SideBackup", plugins: commonPlugins)
// MARK: - SidePatcher
static let SidePatcher: TargetPair = (
.target(name: "SidePatcher", dependencies: [ RoxasUI ], plugins: commonPlugins),
.testTarget(name: "SidePatcherTests", dependencies: ["SidePatcher"]))
}
// MARK: -- .app Targets
/// This seems to be a new feature for XCode 14.3 beta.
/// `PackageDescription.ProductSetting` has no developer documentation
/// though there's probably info on swift-lang.org
extension Target.SideStore {
// MARK: - SideStore.app
enum Apps: Encodable, CaseIterable {
case _apps(Target)
static var allCases: [Apps] = [_apps(SideStore_app)]
static let SideStore_app: Target = .executableTarget(
name: "SideStore",
dependencies: [ "SideStoreAppKit" ],
exclude: [
"Resources/AltBackup.ipa",
"Resources/Info.info",
"Resources/Info.plist",
"Resources/SideStore.entitlements",
"Resources/tempEnt.plist",
],
resources: [
.copy("Resources/JSON/apps-alpha.json"),
.copy("Resources/JSON/apps.json"),
.process("Resources/Assets"),
.process("Resources/Base.lproj"),
.process("Resources/Settings.bundle"),
.process("Resources/Sounds"),
.process("Resources/Storyboards"),
.process("Resources/XIB")
],
linkerSettings: linkerSettings,
plugins: commonPlugins
)
#if false
static let productSettings: [PackageDescription.ProductSetting] = [
.bundleIdentifier(""),
.bundleVersion(""),
.displayVersion(""),
.teamIdentifier(""),
.iOSAppInfo(
.init(
appIcon: .asset("AppIcon"),
accentColor: .asset("SettingsHighlighted"),
supportedDeviceFamilies: [.phone, .mac],
supportedInterfaceOrientations: [
.portrait,
.landscapeLeft(.when(deviceFamilies: [.mac, .pad])),
.landscapeRight(.when(deviceFamilies: [.mac, .pad]))
],
capabilities: [
.appTransportSecurity(configuration:
.init(
)),
.fileAccess(.userSelectedFiles, mode: .readOnly),
.fileAccess(.downloadsFolder, mode: .readOnly),
.incomingNetworkConnections(.when(deviceFamilies: [.mac, .pad, .phone])),
.localNetwork(purposeString: "", bonjourServiceTypes: ["_altserver._tcp"]),
.outgoingNetworkConnections(),
//.bluetoothAlways(purposeString: ""),
//.calendars(purposeString: ""),
//.camera(purposeString: ""),
//.contacts(purposeString: ""),
//.faceID(purposeString: ""),
//.locationAlwaysAndWhenInUse(purposeString: ""),
],
appCategory: .developerTools,
additionalInfoPlistContentFilePath: "Resources/Info.info")
)
]
#endif
}
}
// MARK: -- Internal Targets
extension Target.SideStore {
enum Internal: Encodable, CaseIterable {
static var allCases: [PackageDescription.Target.SideStore.Internal] = targets.map{._internal($0)}
case _internal(Target)
// MARK: Internal Targets
static let targets: [Target] = [
em_proxy_target,
emotionalDamageTarget.target,
emotionalDamageTarget.testTarget,
minimuxer_target,
miniMuxerSwiftTarget.target,
miniMuxerSwiftTarget.testTarget
].compactMap{$0}
// MARK: em_proxy
private static let em_proxy_target: Target = .binaryTarget(
name: "em_proxy",
url: "https://github.com/SideStore/em_proxy/releases/download/build/em_proxy.xcframework.zip",
checksum: "79f90075b8ff2f47540a5bccf5fb7740905cda63463f833e2505256237df3c1b")
// MARK: - EmotionalDamage (Swift)
private static let emotionalDamageTarget: TargetPair = (
.target(name: "EmotionalDamage", dependencies: ["em_proxy"]),
.testTarget(name: "EmotionalDamageTests", dependencies: ["EmotionalDamage"]))
// MARK: minimuxer
private static let minimuxer_target: Target =
.binaryTarget(name: "minimuxer",
url: "https://github.com/SideStore/minimuxer/releases/download/build/minimuxer.xcframework.zip",
checksum: "aa47182547b60f4f7560bdc0f25ea797c69419765003d16d5039c13b87930ed1")
// MARK: MiniMuxer.Swift
private static let miniMuxerSwiftTarget: TargetPair = (
.target(name: "MiniMuxer", dependencies: ["minimuxer", libimobiledevice]),
.testTarget(name: "MiniMuxerTests",
dependencies: ["MiniMuxer", libimobiledevice]))
}
}
// MARK: - Helpers
/// Utility function to test if a value of enviroment variable is a representation of `true`, as case-insensative word, letter or digit
/// - Parameter key: A key value from an enviroment variable
/// - Returns: `true` if `1`, "on", "true", or "yes" when `.lowercased()`, or `false` if "off", "false", 0, "no". `.lowercased()`
func envBool(_ key: EnviromentBool) -> Bool {
guard let value = ProcessInfo.processInfo.environment[key.`var`]?.lowercased() else { return key.default }
// We check for the opposite style string to be true
// or return the default. That way a junk string will
// always return the default.
if key.default == true {
// Check if a known 'false' type or return 'true'
let falseValues = ["0", "off", "f", "false", "n", "no"]
return !falseValues.contains(value)
} else {
let trueValues = ["1", "on", "t", "true", "y", "yes"]
return trueValues.contains(value)
}
}
/// Utility function to generate 3 of the same target with different types` {_, .static, .dynamic}`
/// - Parameter name: Name of your exported library. Must have target of same name.
/// - Returns: `[Product.Library, Product.Library.Static, Product.Library.Dynamic]`
func librarySet(_ name: String) -> (Product, `static`: Product, `dynamic`: Product) {(
.library(name: name, targets: [name]),
.library(name: "\(name)-Static", type: .static, targets: [name]),
.library(name: "\(name)-Dynamic", type: .dynamic, targets: [name]))}
#if USE_CARGO_BUILD_PLUGIN
enum Cargo: Encodable {
enum Dependencies: Encodable {case _dependencies([Package.Dependency])}
enum Target: Encodable {case _target(Target, test: Target?)}
enum Plugin: Encodable {case _plugin(Target)}
static let Dependencies: Cargo.Dependencies = [
.github("apple/swift-argument-parser.git", from: "1.0.3"),
.github("apple/swift-package-manager.git", branch: "release/5.7"),
.github("apple/swift-tools-support-core.git", branch: "release/5.7"),
]
static let Executable: (Cargo.Target, test: Cargo.Target?) = ._target(
.executableTarget(
name: "Cargo",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SwiftPM-auto", package: "swift-package-manager"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core")
]),
.testTarget(
name: "CargoTests",
dependencies: ["Cargo"],
exclude: [
"swiftlint",
"xcframework"
]
))
static let Plugins: [Cargo.Plugin] = [
.plugin(
name: "CargoPlugin",
capability: .buildTool(),
dependencies: [
"Cargo"
]
),
.plugin(
name: "CargoPlugin-Generate",
capability: .command(
intent: .custom(
verb: "generate-code-from-rust",
description: "Creates .c code from your `rust` code"
),
permissions: [
.writeToPackageDirectory(reason: "This command generates source code")
]
),
dependencies: ["Cargo"]
)].map{._plugin($0)}
}
#endif
extension PackageDescription.Package.Dependency {
var disable: PackageDescription.Package.Dependency? { nil }
static func github(_ repo: String,
from: Version) -> PackageDescription.Package.Dependency {
.package(url: "https://github.com/\(repo).git", from: from)}
static func github(_ repo: String,
exact: Version) -> PackageDescription.Package.Dependency {
.package(url: "https://github.com/\(repo).git", exact: exact)}
static func github(_ repo: String,
branch: String) -> PackageDescription.Package.Dependency {
.package(url: "https://github.com/\(repo).git", branch: branch)}
static func github(_ repo: String,
revision: String) -> PackageDescription.Package.Dependency {
.package(url: "https://github.com/\(repo).git", revision: revision)}
}
postfix operator -?
extension Array where Element == Package.Dependency? {
func removeNils() -> [Package.Dependency] { self.compactMap{$0} }
static postfix func -? (array: Self) -> [Package.Dependency] { array.removeNils() }
}
extension Array where Element == Target? {
func removeNils() -> [Target] { self.compactMap{$0} }
static postfix func -? (array: Self) -> [Target] { array.removeNils() }
}
@resultBuilder
struct DependencyBuilder {
typealias Component = [Package.Dependency]
typealias Expression = Package.Dependency
static func buildExpression(_ element: Expression) -> Component {
return [element]
}
static func buildOptional(_ component: Component?) -> Component {
guard let component = component else { return [] }
return component
}
static func buildEither(first component: Component) -> Component {
return component
}
static func buildEither(second component: Component) -> Component {
return component
}
static func buildArray(_ components: [Component]) -> Component {
return Array(components.joined())
}
static func buildBlock(_ components: Component...) -> Component {
return Array(components.joined())
}
}