Add separate Builder class, use WasmTransformer (#131)

Resolves #127.

* Add separate Builder class, use WasmTransformer

* Don't apply the transformer in non-browser environments

* Fix macOS Swift 5.2 build failure

* Fix Package.swift errors

* Fix cyclic dependency in `Package.swift`

* Bump WasmTransformer requirement to 0.0.1

* Rename `case wasmer` to `case other`
This commit is contained in:
Max Desiatov 2020-10-21 08:48:03 +01:00 committed by GitHub
parent 703078dd83
commit 08956baf1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 136 additions and 14 deletions

5
.vscode/tasks.json vendored
View File

@ -33,6 +33,11 @@
"type": "shell",
"command": "swift build && cd TestApp && ../.build/debug/carton dev"
},
{
"label": "build release and run dev",
"type": "shell",
"command": "swift build -c release && cd TestApp && ../.build/release/carton dev"
},
{
"label": "build and run dev on 9090 port",
"type": "shell",

View File

@ -163,6 +163,15 @@
"version": "4.32.0"
}
},
{
"package": "WasmTransformer",
"repositoryURL": "https://github.com/swiftwasm/WasmTransformer",
"state": {
"branch": null,
"revision": "c7cb17309372e57c9f75f7abd5c7323d667eaddc",
"version": "0.0.1"
}
},
{
"package": "websocket-kit",
"repositoryURL": "https://github.com/vapor/websocket-kit.git",

View File

@ -26,6 +26,7 @@ let package = Package(
.package(url: "https://github.com/vapor/vapor.git", from: "4.29.3"),
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.1.0"),
.package(url: "https://github.com/JohnSundell/Splash.git", from: "0.14.0"),
.package(url: "https://github.com/swiftwasm/WasmTransformer", .upToNextMinor(from: "0.0.1")),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module
@ -51,6 +52,7 @@ let package = Package(
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
"CartonHelpers",
openCombineProduct,
"WasmTransformer",
]
),
.target(

View File

@ -20,6 +20,7 @@ let package = Package(
.package(url: "https://github.com/vapor/vapor.git", from: "4.29.3"),
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.1.0"),
.package(url: "https://github.com/JohnSundell/Splash.git", from: "0.14.0"),
.package(url: "https://github.com/swiftwasm/WasmTransformer", .upToNextMinor(from: "0.0.1")),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module
@ -45,6 +46,7 @@ let package = Package(
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
"CartonHelpers",
"OpenCombine",
"WasmTransformer",
]
),
.target(

View File

@ -21,7 +21,7 @@ import OpenCombine
#endif
import TSCBasic
extension Subscribers.Completion {
public extension Subscribers.Completion {
var result: Result<(), Failure> {
switch self {
case let .failure(error):

View File

@ -0,0 +1,100 @@
// Copyright 2020 Carton contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import CartonHelpers
#if canImport(Combine)
import Combine
#else
import OpenCombine
#endif
import Foundation
import TSCBasic
import WasmTransformer
public final class Builder {
public enum Environment {
case other
case browser
}
public let mainWasmPath: AbsolutePath
private var currentProcess: ProcessRunner?
private let arguments: [String]
private let environment: Environment
private let terminal: InteractiveWriter
private let fileSystem: FileSystem
private var subscription: AnyCancellable?
public init(
arguments: [String],
mainWasmPath: AbsolutePath,
environment: Environment = .browser,
_ fileSystem: FileSystem,
_ terminal: InteractiveWriter
) {
self.arguments = arguments
self.mainWasmPath = mainWasmPath
self.environment = environment
self.terminal = terminal
self.fileSystem = fileSystem
}
public func run() -> AnyPublisher<String, Error> {
let buildStarted = Date()
let process = ProcessRunner(arguments, loadingMessage: "Compiling...", terminal)
currentProcess = process
return process
.publisher
.handleEvents(receiveCompletion: { [weak self] in
guard case .finished = $0, let self = self else { return }
self.terminal.logLookup(
"`swift build` completed in ",
String(format: "%.2f seconds", abs(buildStarted.timeIntervalSinceNow))
)
guard self.environment == .browser else { return }
// FIXME: errors from these `try` expressions should be recoverable, not sure how to
// do that in `handleEvents`, and `flatMap` doesnt' fit here as we need to track
// publisher completion.
// swiftlint:disable force_try
let binary = try! self.fileSystem.readFileContents(self.mainWasmPath)
let loweringStarted = Date()
let loweredBinary = try! lowerI64Imports(binary.contents)
self.terminal.logLookup(
"Binary transformation for Safari compatibility completed in ",
String(format: "%.2f seconds", abs(loweringStarted.timeIntervalSinceNow))
)
try! self.fileSystem.writeFileContents(self.mainWasmPath, bytes: .init(loweredBinary))
// swiftlint:enable force_try
})
.eraseToAnyPublisher()
}
public func runAndWaitUntilFinished() throws {
try await { completion in
subscription = run()
.sink(
receiveCompletion: { completion($0.result) },
receiveValue: { _ in }
)
}
}
}

View File

@ -225,10 +225,10 @@ public final class Toolchain {
"--enable-test-discovery", "--destination", destination ?? inferDestinationPath().pathString,
]
try ProcessRunner(builderArguments, loadingMessage: "Compiling...", terminal)
.waitUntilFinished()
try Builder(arguments: builderArguments, mainWasmPath: mainWasmPath, fileSystem, terminal)
.runAndWaitUntilFinished()
guard localFileSystem.exists(mainWasmPath) else {
guard fileSystem.exists(mainWasmPath) else {
terminal.write(
"Failed to build the main executable binary, fix the build errors and restart\n",
inColor: .red
@ -257,9 +257,16 @@ public final class Toolchain {
"-Xswiftc", "-color-diagnostics",
]
try ProcessRunner(builderArguments, terminal).waitUntilFinished()
try Builder(
arguments: builderArguments,
mainWasmPath: testBundlePath,
environment: .other,
fileSystem,
terminal
)
.runAndWaitUntilFinished()
guard localFileSystem.exists(testBundlePath) else {
guard fileSystem.exists(testBundlePath) else {
terminal.write(
"Failed to build the test bundle, fix the build errors and restart\n",
inColor: .red

View File

@ -84,9 +84,8 @@ struct Dev: ParsableCommand {
let sources = try paths.flatMap { try localFileSystem.traverseRecursively($0) }
try Server(
builderArguments: arguments,
builder: Builder(arguments: arguments, mainWasmPath: mainWasmPath, localFileSystem, terminal),
pathsToWatch: sources,
mainWasmPath: mainWasmPath,
customIndexContent: HTML.readCustomIndexPage(at: customIndexPage, on: localFileSystem),
// swiftlint:disable:next force_try
package: try! toolchain.package.get(),

View File

@ -37,15 +37,13 @@ final class Server {
private var connections = Set<WebSocket>()
private var subscriptions = [AnyCancellable]()
private let watcher: Watcher
private var builder: ProcessRunner?
private let app: Application
private let localURL: String
private let skipAutoOpen: Bool
init(
builderArguments: [String],
builder: Builder,
pathsToWatch: [AbsolutePath],
mainWasmPath: AbsolutePath,
customIndexContent: String?,
package: SwiftToolchain.Package,
verbose: Bool,
@ -63,7 +61,7 @@ final class Server {
app = Application(env)
app.configure(
port: port,
mainWasmPath: mainWasmPath,
mainWasmPath: builder.mainWasmPath,
customIndexContent: customIndexContent,
package: package,
onWebSocketOpen: { [weak self] in
@ -85,8 +83,8 @@ final class Server {
for change in changes.map(\.pathString) {
terminal.write("- \(change)\n", inColor: .cyan)
}
return ProcessRunner(builderArguments, terminal)
.publisher
return builder
.run()
.handleEvents(receiveCompletion: { [weak self] in
guard case .finished = $0, let self = self else { return }