Skip to content

Commit 8ba24b8

Browse files
Version 1.0.0: SwiftPM Plugin Support
See CHANGELOG.md
1 parent 33ef59d commit 8ba24b8

File tree

113 files changed

+9035
-3102
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+9035
-3102
lines changed

CHANGELOG.md

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,53 @@
1+
# 1.0.0 (XX XXX 2024)
2+
3+
**Breaking changes:**
4+
5+
`carton` CLI is now slimmed down to be a SwiftPM Plugin.
6+
This means that you can now use `carton` by just declaring it as a dependency in your `Package.swift` file.
7+
8+
```swift
9+
dependencies: [
10+
.package(url: "https://github.com/swiftwasm/carton", from: "1.0.0"),
11+
],
12+
```
13+
14+
Each `carton` subcommand is now split into a separate SwiftPM plugin.
15+
16+
| Old command | New command |
17+
| --------------- | ------------------------- |
18+
| `carton dev` | `swift run carton dev` |
19+
| `carton test` | `swift run carton test` |
20+
| `carton bundle` | `swift run carton bundle` |
21+
22+
Also `carton` no longer supports the following features:
23+
24+
- `carton init` command (use `swift package init --type executable` instead)
25+
26+
**Internal changes:**
27+
28+
- Reduce build time by removing unnecessary dependencies: 96.97s -> 25.26s
29+
- No longer directly depend on SwiftPM as a library. This means that `carton` no longer has to be updated when SwiftPM is updated (hopefully).
30+
31+
Our new SwiftPM plugin oriented architecture is illustrated in the following diagram:
32+
33+
```mermaid
34+
sequenceDiagram
35+
participant SwiftRunCarton as swift run carton
36+
participant SwiftPM
37+
participant CartonDevPlugin as carton-dev Plugin
38+
participant CartonFrontend
39+
SwiftRunCarton->>SwiftPM: exec
40+
SwiftPM->>CartonDevPlugin: spawn
41+
CartonDevPlugin->>SwiftPM: Build product
42+
SwiftPM->>CartonDevPlugin: Build artifacts
43+
CartonDevPlugin->>CartonFrontend: spawn
44+
note right of CartonDevPlugin: Establish IPC
45+
CartonFrontend->>CartonDevPlugin: File changed
46+
CartonDevPlugin->>SwiftPM: Build product
47+
SwiftPM->>CartonDevPlugin: Build artifacts
48+
CartonDevPlugin->>CartonFrontend: Reload browsers
49+
```
50+
151
# 0.20.1 (25 Jan 2024)
252

353
This release fixes a bug in `carton test` where it reports missing `sock_accept` syscall.
@@ -12,10 +62,9 @@ This release adds SwiftWasm 5.9 toolchain support.
1262
- Add Swift 5.9 to Build Action by @STREGA in https://github.com/swiftwasm/carton/pull/409
1363
- Swift 5.9 toolchain & macOS Sonoma beta by @furby-tm in https://github.com/swiftwasm/carton/pull/402
1464
- Add 5.9 support by @STREGA in https://github.com/swiftwasm/carton/pull/412
15-
- Stop bothering WASI apps including unimplemented syscalls by @kateinoigakukun in https://github.com/swiftwasm/carton/pull/415
65+
- Stop bothering WASI apps including unimplemented syscalls by @kateinoigakukun in https://github.com/swiftwasm/carton/pull/415
1666
- Update default toolchain version to 5.9.1 by @kateinoigakukun in https://github.com/swiftwasm/carton/pull/416
1767

18-
1968
# 0.19.1 (9 May 2023)
2069

2170
This release fixes the wrong toolchain version installed in docker image.
@@ -32,7 +81,6 @@ This release adds SwiftWasm 5.8 toolchain support.
3281
- Support jammy and amazonlinux2 for toolchain install by @kateinoigakukun in https://github.com/swiftwasm/carton/pull/397
3382
- Update default toolchain version to 5.8 channel snapshot by @kateinoigakukun in https://github.com/swiftwasm/carton/pull/398
3483

35-
3684
# 0.18.0 (3 April 2023)
3785

3886
This release adds an extra size stripping optimization.

Package.swift

Lines changed: 68 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,105 +5,126 @@ import PackageDescription
55

66
let package = Package(
77
name: "carton",
8-
platforms: [.macOS("10.15.4")],
8+
platforms: [.macOS(.v13)],
99
products: [
1010
.library(name: "SwiftToolchain", targets: ["SwiftToolchain"]),
1111
.library(name: "CartonHelpers", targets: ["CartonHelpers"]),
1212
.library(name: "CartonKit", targets: ["CartonKit"]),
1313
.library(name: "CartonCLI", targets: ["CartonCLI"]),
14-
.executable(name: "carton", targets: ["Carton"]),
14+
.executable(name: "carton", targets: ["carton"]),
1515
.executable(name: "carton-release", targets: ["carton-release"]),
16+
.plugin(name: "CartonBundle", targets: ["CartonBundle"]),
17+
.plugin(name: "CartonTest", targets: ["CartonTest"]),
18+
.plugin(name: "CartonDev", targets: ["CartonDev"]),
19+
.executable(name: "carton-plugin-helper", targets: ["carton-plugin-helper"]),
1620
],
1721
dependencies: [
18-
.package(
19-
url: "https://github.com/swift-server/async-http-client.git",
20-
from: "1.8.1"
21-
),
22+
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.4"),
2223
.package(
2324
url: "https://github.com/apple/swift-argument-parser.git",
24-
.upToNextMinor(from: "1.2.3")
25+
.upToNextMinor(from: "1.3.0")
2526
),
2627
.package(url: "https://github.com/apple/swift-nio.git", from: "2.34.0"),
27-
.package(
28-
url: "https://github.com/apple/swift-package-manager.git",
29-
branch: "release/5.9"
30-
),
31-
.package(
32-
url: "https://github.com/apple/swift-tools-support-core.git",
33-
branch: "release/5.9"
34-
),
35-
.package(url: "https://github.com/vapor/vapor.git", from: "4.57.1"),
36-
.package(url: "https://github.com/apple/swift-crypto.git", from: "2.2.0"),
37-
.package(url: "https://github.com/JohnSundell/Splash.git", from: "0.16.0"),
3828
.package(
3929
url: "https://github.com/swiftwasm/WasmTransformer",
4030
.upToNextMinor(from: "0.5.0")
4131
),
4232
],
4333
targets: [
44-
// Targets are the basic building blocks of a package. A target can define a module
45-
// or a test suite. Targets can depend on other targets in this package, and on
46-
// products in packages which this package depends on.
4734
.executableTarget(
48-
name: "Carton",
35+
name: "carton",
36+
dependencies: [
37+
"SwiftToolchain",
38+
"CartonHelpers",
39+
]
40+
),
41+
.executableTarget(
42+
name: "CartonFrontend",
4943
dependencies: [
5044
"CartonCLI",
5145
]
5246
),
47+
.plugin(
48+
name: "CartonBundle",
49+
capability: .command(
50+
intent: .custom(
51+
verb: "carton-bundle",
52+
description: "Produces an optimized app bundle for distribution."
53+
)
54+
),
55+
dependencies: ["CartonFrontend"],
56+
exclude: ["CartonPluginShared/README.md"]
57+
),
58+
.plugin(
59+
name: "CartonTest",
60+
capability: .command(
61+
intent: .custom(
62+
verb: "carton-test",
63+
description: "Run the tests in a WASI environment."
64+
)
65+
),
66+
dependencies: ["CartonFrontend"],
67+
exclude: ["CartonPluginShared/README.md"]
68+
),
69+
.plugin(
70+
name: "CartonDev",
71+
capability: .command(
72+
intent: .custom(
73+
verb: "carton-dev",
74+
description: "Watch the current directory, host the app, rebuild on change."
75+
)
76+
),
77+
dependencies: ["CartonFrontend"],
78+
exclude: ["CartonPluginShared/README.md"]
79+
),
80+
.executableTarget(name: "carton-plugin-helper"),
5381
.target(
5482
name: "CartonCLI",
55-
dependencies: ["CartonKit"]
83+
dependencies: [
84+
.product(name: "Logging", package: "swift-log"),
85+
"CartonKit",
86+
]
5687
),
5788
.target(
5889
name: "CartonKit",
5990
dependencies: [
60-
.product(name: "AsyncHTTPClient", package: "async-http-client"),
61-
.product(name: "Crypto", package: "swift-crypto"),
62-
.product(name: "Vapor", package: "vapor"),
91+
.product(name: "NIOWebSocket", package: "swift-nio"),
92+
.product(name: "NIOHTTP1", package: "swift-nio"),
93+
.product(name: "NIO", package: "swift-nio"),
94+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
6395
"CartonHelpers",
64-
"SwiftToolchain",
6596
"WebDriverClient",
66-
]
97+
"WasmTransformer",
98+
],
99+
exclude: ["Utilities/README.md"]
67100
),
68101
.target(
69102
name: "SwiftToolchain",
70103
dependencies: [
71-
.product(name: "AsyncHTTPClient", package: "async-http-client"),
72-
.product(name: "NIOFoundationCompat", package: "swift-nio"),
73-
.product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"),
74104
"CartonHelpers",
75-
"WasmTransformer",
76-
]
105+
],
106+
exclude: ["Utilities/README.md"]
77107
),
78108
.target(
79109
name: "CartonHelpers",
80-
dependencies: [
81-
.product(name: "AsyncHTTPClient", package: "async-http-client"),
82-
.product(name: "ArgumentParser", package: "swift-argument-parser"),
83-
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
84-
"Splash",
85-
"WasmTransformer",
86-
]
110+
dependencies: [],
111+
exclude: ["Basics/README.md"]
87112
),
88-
.target(name: "WebDriverClient", dependencies: [
89-
.product(name: "AsyncHTTPClient", package: "async-http-client"),
90-
.product(name: "NIOFoundationCompat", package: "swift-nio"),
91-
]),
113+
.target(name: "WebDriverClient", dependencies: []),
92114
// This target is used only for release automation tasks and
93115
// should not be installed by `carton` users.
94116
.executableTarget(
95117
name: "carton-release",
96118
dependencies: [
97119
.product(name: "ArgumentParser", package: "swift-argument-parser"),
98-
.product(name: "AsyncHTTPClient", package: "async-http-client"),
99-
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
100120
"CartonHelpers",
121+
"WasmTransformer",
101122
]
102123
),
103124
.testTarget(
104125
name: "CartonTests",
105126
dependencies: [
106-
"Carton",
127+
"CartonFrontend",
107128
"CartonHelpers",
108129
.product(name: "ArgumentParser", package: "swift-argument-parser"),
109130
]
@@ -113,8 +134,6 @@ let package = Package(
113134
dependencies: [
114135
"CartonCLI",
115136
.product(name: "ArgumentParser", package: "swift-argument-parser"),
116-
.product(name: "AsyncHTTPClient", package: "async-http-client"),
117-
.product(name: "TSCTestSupport", package: "swift-tools-support-core"),
118137
]
119138
),
120139
.testTarget(name: "WebDriverClientTests", dependencies: ["WebDriverClient"]),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../CartonPluginShared

Plugins/CartonBundle/Plugin.swift

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2024 Carton contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
import PackagePlugin
17+
18+
@main
19+
struct CartonBundlePlugin: CommandPlugin {
20+
21+
struct Options {
22+
var product: String?
23+
var outputDir: String?
24+
var debug: Bool
25+
26+
static func parse(from extractor: inout ArgumentExtractor) -> Options {
27+
let product = extractor.extractOption(named: "product").last
28+
let outputDir = extractor.extractOption(named: "output").last
29+
let debug = extractor.extractFlag(named: "debug")
30+
return Options(product: product, outputDir: outputDir, debug: debug != 0)
31+
}
32+
}
33+
34+
func performCommand(context: PluginContext, arguments: [String]) async throws {
35+
try checkSwiftVersion()
36+
try checkHelpFlag(arguments, subcommand: "bundle", context: context)
37+
38+
var extractor = ArgumentExtractor(arguments)
39+
let options = Options.parse(from: &extractor)
40+
41+
let productName = try options.product ?? deriveDefaultProduct(package: context.package)
42+
43+
// Build products
44+
let parameters = PackageManager.BuildParameters(
45+
configuration: options.debug ? .debug : .release,
46+
logging: .verbose
47+
)
48+
print("Building \"\(productName)\"")
49+
let build = try self.packageManager.build(.product(productName), parameters: parameters)
50+
51+
guard build.succeeded else {
52+
print(build.logText)
53+
exit(1)
54+
}
55+
56+
guard let product = try context.package.products(named: [productName]).first else {
57+
throw CartonPluginError("Failed to find product named \"\(productName)\"")
58+
}
59+
guard let executableProduct = product as? ExecutableProduct else {
60+
throw CartonPluginError(
61+
"Product type of \"\(productName)\" is not supported. Only executable products are supported."
62+
)
63+
}
64+
65+
let productArtifact = try build.findWasmArtifact(for: productName)
66+
67+
let resourcesPaths = deriveResourcesPaths(
68+
productArtifactPath: productArtifact.path,
69+
sourceTargets: executableProduct.targets,
70+
package: context.package
71+
)
72+
73+
let bundleDirectory =
74+
options.outputDir ?? context.pluginWorkDirectory.appending(subpath: "Bundle").string
75+
let frontendArguments =
76+
["bundle", productArtifact.path.string, "--output", bundleDirectory]
77+
+ resourcesPaths.flatMap {
78+
["--resources", $0.string]
79+
} + extractor.remainingArguments
80+
let frontend = try makeCartonFrontendProcess(context: context, arguments: frontendArguments)
81+
frontend.forwardTerminationSignals()
82+
try frontend.run()
83+
frontend.waitUntilExit()
84+
if frontend.terminationStatus == 0 {
85+
print("Bundle written in \(bundleDirectory)")
86+
}
87+
frontend.checkNonZeroExit()
88+
}
89+
}

Plugins/CartonDev/CartonPluginShared

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../CartonPluginShared

0 commit comments

Comments
 (0)