Implement resources copying in `carton bundle` (#109)

* Define `resourcesPath` on `SwiftToolchain.Package`

* Implement resources copying in `carton bundle`

* Remove redundant newline in log output
This commit is contained in:
Max Desiatov 2020-09-20 09:46:49 +01:00 committed by GitHub
parent b2aa6fd62c
commit 1e49463d64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 17 deletions

View File

@ -21,9 +21,14 @@ private extension String {
}
public extension TerminalController {
func logLookup<T: CustomStringConvertible>(_ description: String, _ target: T) {
func logLookup<T>(_ description: String, _ target: T, newline: Bool = false)
where T: CustomStringConvertible
{
write(description)
write("\(target)\n", inColor: .cyan, bold: true)
if newline {
write("\n")
}
}
func clearWindow() {

View File

@ -49,6 +49,10 @@ public struct Package: Codable {
self = try JSONDecoder().decode(Package.self, from: output)
}
public func resourcesPath(for target: Target) -> String {
"\(name)_\(target.name).resources"
}
}
struct ProductType: Codable {

View File

@ -92,10 +92,11 @@ public final class Toolchain {
package = Result { try Package(with: swiftPath, terminal) }
}
private func inferBinPath() throws -> AbsolutePath {
private func inferBinPath(isRelease: Bool) throws -> AbsolutePath {
guard
let output = try processStringOutput([
swiftPath.pathString, "build", "--triple", "wasm32-unknown-wasi", "--show-bin-path",
swiftPath.pathString, "build", "-c", isRelease ? "release" : "debug",
"--triple", "wasm32-unknown-wasi", "--show-bin-path",
])?.components(separatedBy: CharacterSet.newlines),
let binPath = output.first
else { fatalError("failed to decode UTF8 output of the `swift build` invocation") }
@ -210,7 +211,7 @@ public final class Toolchain {
)
}
let binPath = try inferBinPath()
let binPath = try inferBinPath(isRelease: isRelease)
let mainWasmPath = binPath.appending(component: product)
terminal.logLookup("- development binary to serve: ", mainWasmPath.pathString)
@ -238,7 +239,7 @@ public final class Toolchain {
/// Returns an absolute path to the resulting test bundle
public func buildTestBundle(isRelease: Bool) throws -> AbsolutePath {
let package = try self.package.get()
let binPath = try inferBinPath()
let binPath = try inferBinPath(isRelease: isRelease)
let testBundlePath = binPath.appending(component: "\(package.name)PackageTests.xctest")
terminal.logLookup("- test bundle to run: ", testBundlePath.pathString)

View File

@ -55,32 +55,53 @@ struct Bundle: ParsableCommand {
)
try terminal.logLookup(
"Right after building the main binary size is ",
localFileSystem.humanReadableFileSize(mainWasmPath)
localFileSystem.humanReadableFileSize(mainWasmPath),
newline: true
)
try ProcessRunner(["wasm-strip", mainWasmPath.pathString], terminal).waitUntilFinished()
try terminal.logLookup(
"After applying `wasm-strip` the main binary size is ",
localFileSystem.humanReadableFileSize(mainWasmPath)
localFileSystem.humanReadableFileSize(mainWasmPath),
newline: true
)
let bundleDir = AbsolutePath(localFileSystem.currentWorkingDirectory!, "Bundle")
try localFileSystem.removeFileTree(bundleDir)
try localFileSystem.createDirectory(bundleDir)
let optimizedPath = AbsolutePath(bundleDir, "main.wasm")
let bundleDirectory = AbsolutePath(localFileSystem.currentWorkingDirectory!, "Bundle")
try localFileSystem.removeFileTree(bundleDirectory)
try localFileSystem.createDirectory(bundleDirectory)
let optimizedPath = AbsolutePath(bundleDirectory, "main.wasm")
try ProcessRunner(
["wasm-opt", "-Os", mainWasmPath.pathString, "-o", optimizedPath.pathString],
terminal
).waitUntilFinished()
try terminal.logLookup(
"After applying `wasm-opt` the main binary size is ",
localFileSystem.humanReadableFileSize(optimizedPath)
localFileSystem.humanReadableFileSize(optimizedPath),
newline: true
)
try copyToBundle(
terminal: terminal,
optimizedPath: optimizedPath,
buildDirectory: mainWasmPath.parentDirectory,
bundleDirectory: bundleDirectory,
toolchain: toolchain
)
terminal.write("Bundle generation finished successfully\n", inColor: .green, bold: true)
}
func copyToBundle(
terminal: TerminalController,
optimizedPath: AbsolutePath,
buildDirectory: AbsolutePath,
bundleDirectory: AbsolutePath,
toolchain: Toolchain
) throws {
// Rename the final binary to use a part of its hash to bust browsers and CDN caches.
let optimizedHash = try localFileSystem.readFileContents(optimizedPath).hexSHA256.prefix(16)
let mainModuleName = "\(optimizedHash).wasm"
let mainModulePath = AbsolutePath(bundleDir, mainModuleName)
let mainModulePath = AbsolutePath(bundleDirectory, mainModuleName)
try localFileSystem.move(from: optimizedPath, to: mainModulePath)
// Copy the bundle entrypoint, point to the binary, and give it a cachebuster name.
@ -95,18 +116,27 @@ struct Bundle: ParsableCommand {
)
let entrypointName = "\(entrypoint.hexSHA256.prefix(16)).js"
try localFileSystem.writeFileContents(
AbsolutePath(bundleDir, entrypointName),
AbsolutePath(bundleDirectory, entrypointName),
bytes: entrypoint
)
try localFileSystem.writeFileContents(
AbsolutePath(bundleDir, "index.html"),
AbsolutePath(bundleDirectory, "index.html"),
bytes: ByteString(encodingAsUTF8: HTML.indexPage(
customContent: HTML.readCustomIndexPage(at: customIndexPage, on: localFileSystem),
entrypointName: entrypointName
))
)
terminal.write("\nBundle generation finished successfully\n", inColor: .green, bold: true)
let package = try toolchain.package.get()
for target in package.targets where target.type == .regular && !target.resources.isEmpty {
let targetPath = package.resourcesPath(for: target)
let resourcesPath = buildDirectory.appending(component: targetPath)
let targetDirectory = bundleDirectory.appending(component: targetPath)
guard localFileSystem.exists(resourcesPath) else { continue }
terminal.logLookup("Copying resources to ", targetDirectory)
try localFileSystem.copy(from: resourcesPath, to: targetDirectory)
}
}
}

View File

@ -48,7 +48,7 @@ extension Application {
let buildDirectory = mainWasmPath.parentDirectory
for target in package.targets where target.type == .regular && !target.resources.isEmpty {
let resourcesPath = "\(package.name)_\(target.name).resources"
let resourcesPath = package.resourcesPath(for: target)
get(.constant(resourcesPath), "**") {
$0.eventLoop.makeSucceededFuture($0.fileio.streamFile(at: AbsolutePath(
buildDirectory.appending(component: resourcesPath),