diff --git a/Package.swift b/Package.swift index 72e5eb14..6053f470 100644 --- a/Package.swift +++ b/Package.swift @@ -1,18 +1,54 @@ // swift-tools-version:5.7 +import Foundation import PackageDescription +let environment = ProcessInfo.processInfo.environment +let slim = environment["SWIFT_BUNDLER_SLIM"] == "1" +let requireBuilderAPI = environment["SWIFT_BUNDLER_REQUIRE_BUILDER_API"] == "1" + let package = Package( name: "swift-bundler", platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .macCatalyst(.v13)], - products: [ + products: [], + dependencies: [], + targets: [] +) + +// Builder API +if !slim || requireBuilderAPI { + package.products += [ + .library(name: "SwiftBundlerBuilders", targets: ["SwiftBundlerBuilders"]) + ] + + package.dependencies += [ + .package(url: "https://github.com/gregcotten/AsyncProcess", from: "0.0.5") + ] + + package.targets += [ + .target( + name: "SwiftBundlerBuilders", + dependencies: [ + .product( + name: "ProcessSpawnSync", + package: "AsyncProcess", + condition: .when(platforms: [.linux]) + ) + ] + ) + ] +} + +// Rest of products/targets/dependencies +if !slim { + package.products += [ .executable(name: "swift-bundler", targets: ["swift-bundler"]), .library(name: "SwiftBundler", targets: ["SwiftBundler"]), .library(name: "SwiftBundlerRuntime", targets: ["SwiftBundlerRuntime"]), - .library(name: "SwiftBundlerBuilders", targets: ["SwiftBundlerBuilders"]), .plugin(name: "SwiftBundlerCommandPlugin", targets: ["SwiftBundlerCommandPlugin"]), - ], - dependencies: [ + ] + + package.dependencies += [ .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"), .package(url: "https://github.com/apple/swift-log", from: "1.5.4"), .package(url: "https://github.com/pointfreeco/swift-parsing", .upToNextMinor(from: "0.13.0")), @@ -32,14 +68,14 @@ let package = Package( .package(url: "https://github.com/apple/swift-crypto", from: "3.10.0"), .package(url: "https://github.com/CoreOffice/XMLCoder", from: "0.17.1"), .package(url: "https://github.com/adam-fowler/async-collections.git", from: "0.1.0"), - .package(url: "https://github.com/gregcotten/AsyncProcess", from: "0.0.5"), // File watcher dependencies .package(url: "https://github.com/sersoft-gmbh/swift-inotify", "0.4.0"..<"0.5.0"), .package(url: "https://github.com/apple/swift-system", from: "1.2.0"), .package(url: "https://github.com/apple/swift-async-algorithms", from: "1.0.3"), - ], - targets: [ + ] + + package.targets += [ .executableTarget(name: "swift-bundler", dependencies: ["SwiftBundler"]), .target( name: "SwiftBundler", @@ -125,17 +161,6 @@ let package = Package( ] ), - .target( - name: "SwiftBundlerBuilders", - dependencies: [ - .product( - name: "ProcessSpawnSync", - package: "AsyncProcess", - condition: .when(platforms: [.linux]) - ) - ] - ), - .target( name: "HotReloadingProtocol", dependencies: [ @@ -186,4 +211,4 @@ let package = Package( ] ), ] -) +} diff --git a/Sources/SwiftBundler/Bundler/ProjectBuilder.swift b/Sources/SwiftBundler/Bundler/ProjectBuilder.swift index c30f93e9..c38bf86b 100644 --- a/Sources/SwiftBundler/Bundler/ProjectBuilder.swift +++ b/Sources/SwiftBundler/Bundler/ProjectBuilder.swift @@ -464,9 +464,21 @@ enum ProjectBuilder { isGUIExecutable: false ) + // Let Swift Bundler know that we only need the builder API. This + // greatly reduces the length of the dependency resolution phase for + // clean builds (which is the bottleneck for single-file builders), + // and greatly improves incremental build performance on Windows where + // package trees with lots of files seem to result in pretty terrible + // incremental build performance. + let environment = [ + "SWIFT_BUNDLER_SLIM": "1", + "SWIFT_BUNDLER_REQUIRE_BUILDER_API": "1", + ] + return await SwiftPackageManager.build( product: builderProductName, - buildContext: buildContext + buildContext: buildContext, + additionalEnvironmentVariables: environment ).andThen { _ in await SwiftPackageManager.getProductsDirectory(buildContext) }.mapError { error in diff --git a/Sources/SwiftBundler/Bundler/SwiftPackageManager/SwiftPackageManager.swift b/Sources/SwiftBundler/Bundler/SwiftPackageManager/SwiftPackageManager.swift index f46dadd9..1ebcd21b 100644 --- a/Sources/SwiftBundler/Bundler/SwiftPackageManager/SwiftPackageManager.swift +++ b/Sources/SwiftBundler/Bundler/SwiftPackageManager/SwiftPackageManager.swift @@ -79,10 +79,12 @@ enum SwiftPackageManager { /// - Parameters: /// - product: The product to build. /// - buildContext: The context to build in. + /// - additionalEnvironmentVariables to add to SwiftPM CLI invocations. /// - Returns: If an error occurs, returns a failure. static func build( product: String, - buildContext: BuildContext + buildContext: BuildContext, + additionalEnvironmentVariables: [String: String] = [:] ) async -> Result { return await createBuildArguments( product: product, @@ -91,6 +93,7 @@ enum SwiftPackageManager { let process = Process.create( "swift", arguments: arguments, + environment: additionalEnvironmentVariables, directory: buildContext.genericContext.projectDirectory, runSilentlyWhenNotVerbose: false ) diff --git a/Sources/SwiftBundler/Commands/BundleCommand.swift b/Sources/SwiftBundler/Commands/BundleCommand.swift index a2238473..4c9721e1 100644 --- a/Sources/SwiftBundler/Commands/BundleCommand.swift +++ b/Sources/SwiftBundler/Commands/BundleCommand.swift @@ -607,11 +607,13 @@ struct BundleCommand: ErrorHandledCommand { } let dependenciesScratchDirectory = outputDirectory / "projects" + var dependencyContext = buildContext.genericContext + dependencyContext.scratchDirectory = dependenciesScratchDirectory let dependencies = try await ProjectBuilder.buildDependencies( appConfiguration.dependencies, packageConfiguration: configuration, - context: buildContext.genericContext, + context: dependencyContext, appName: appName, dryRun: skipBuild ).unwrap()