Make wasm-bindgen parallel/disable log, speed up 2-10x (#3161)

* wip: pull out incr linker intercept and debug=true for wasm for faster builds

* make walrus parallel

* clean up impl
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2024-11-04 18:32:23 -08:00
1 parent e700e00b6a
commit 8b586352a2
7 files changed
+260 -231

No files matched your search

Generated
+178 -171
View File
File diff suppressed because it is too large. Load diff
+5
View File
@@ -245,6 +245,11 @@ tray-icon = "0.19"
incremental = true
debug = 0
[profile.release-max-opt]
inherits = "release"
lto = true
codegen-units = 1
# Disable debug assertions to check the released path of core and other packages, but build without optimizations to keep build times quick
[profile.release-unoptimized]
inherits = "dev"
+4
View File
@@ -61,6 +61,7 @@ syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mu
headers = "0.4.0"
walkdir = "2"
walrus = { workspace = true, features = ["parallel"] }
# tools download
dirs = { workspace = true }
@@ -99,6 +100,9 @@ ansi-to-tui = "6.0"
ansi-to-html = "0.2.1"
ratatui = { version = "0.28.0", features = ["crossterm", "unstable"] }
# disable `log` entirely since `walrus` uses it and is *much* slower with it enableda
log = { version = "0.4", features = ["max_level_off", "release_max_level_off"] }
# link intercept
tempfile = "3.3"
manganis-core = { workspace = true }
+8 -18
View File
@@ -15,6 +15,7 @@ pub(crate) struct AssetManifest {
}
impl AssetManifest {
#[allow(dead_code)]
pub(crate) fn load_from_file(path: &Path) -> anyhow::Result<Self> {
let src = std::fs::read_to_string(path)
.context("Failed to read asset manifest from filesystem")?;
@@ -24,31 +25,20 @@ impl AssetManifest {
/// Fill this manifest with a file object/rlib files, typically extracted from the linker intercepted
pub(crate) fn add_from_object_path(&mut self, path: PathBuf) -> anyhow::Result<()> {
let Some(ext) = path.extension() else {
return Ok(());
};
let Some(ext) = ext.to_str() else {
return Ok(());
};
let data = std::fs::read(path.clone())?;
match ext {
// Parse an unarchived object file
"o" => {
if let Ok(object) = object::File::parse(&*data) {
self.add_from_object_file(&object)?;
}
}
match path.extension().and_then(|ext| ext.to_str()) {
// Parse an rlib as a collection of objects
"rlib" => {
Some("rlib") => {
if let Ok(archive) = object::read::archive::ArchiveFile::parse(&*data) {
self.add_from_archive_file(&archive, &data)?;
}
}
_ => {}
_ => {
if let Ok(object) = object::File::parse(&*data) {
self.add_from_object_file(&object)?;
}
}
}
Ok(())
+56 -38
View File
@@ -6,7 +6,10 @@ use crate::{build::BuildArgs, link::LinkAction};
use crate::{AppBundle, Platform};
use anyhow::Context;
use serde::Deserialize;
use std::{path::PathBuf, process::Stdio};
use std::{
path::{Path, PathBuf},
process::Stdio,
};
use tokio::{io::AsyncBufReadExt, process::Command};
#[derive(Clone, Debug)]
@@ -50,7 +53,7 @@ impl BuildRequest {
pub(crate) async fn build_app(&self) -> Result<BuildArtifacts> {
tracing::debug!("Building app...");
let exe = self.build_cargo().await?;
let assets = self.collect_assets().await?;
let assets = self.collect_assets(&exe).await?;
Ok(BuildArtifacts { exe, assets })
}
@@ -185,7 +188,7 @@ impl BuildRequest {
/// This will execute `dx` with an env var set to force `dx` to operate as a linker, and then
/// traverse the .o and .rlib files rustc passes that new `dx` instance, collecting the link
/// tables marked by manganis and parsing them as a ResourceAsset.
pub(crate) async fn collect_assets(&self) -> anyhow::Result<AssetManifest> {
pub(crate) async fn collect_assets(&self, exe: &Path) -> Result<AssetManifest> {
tracing::debug!("Collecting assets ...");
// If assets are skipped, we don't need to collect them
@@ -193,43 +196,11 @@ impl BuildRequest {
return Ok(AssetManifest::default());
}
// Create a temp file to put the output of the args
// We need to do this since rustc won't actually print the link args to stdout, so we need to
// give `dx` a file to dump its env::args into
let tmp_file = tempfile::NamedTempFile::new()?;
let mut manifest = AssetManifest::default();
// Run `cargo rustc` again, but this time with a custom linker (dx) and an env var to force
// `dx` to act as a linker
//
// This will force `dx` to look through the incremental cache and find the assets from the previous build
Command::new("cargo")
// .env("RUSTFLAGS", self.rust_flags())
.arg("rustc")
.args(self.build_arguments())
.arg("--offline") /* don't use the network, should already be resolved */
.arg("--")
.arg(format!(
"-Clinker={}",
std::env::current_exe()
.unwrap()
.canonicalize()
.unwrap()
.display()
))
.env(
LinkAction::ENV_VAR_NAME,
LinkAction::BuildAssetManifest {
destination: tmp_file.path().to_path_buf(),
}
.to_json(),
)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.await?;
_ = manifest.add_from_object_path(exe.to_path_buf());
// The linker wrote the manifest to the temp file, let's load it!
AssetManifest::load_from_file(tmp_file.path())
Ok(manifest)
}
/// Create a list of arguments for cargo builds
@@ -416,4 +387,51 @@ impl BuildRequest {
/ 3.5) as usize
})
}
/// We used to require traversing incremental artifacts for assets that were included but not
/// directly exposed to the final binary. Now, however, we force APIs to carry items created
/// from asset calls into top-level items such that they *do* get included in the final binary.
///
/// There's a chance that's not actually true, so this function is kept around in case we do
/// need to revert to "deep extraction".
#[allow(unused)]
async fn deep_linker_asset_extract(&self) -> Result<AssetManifest> {
// Create a temp file to put the output of the args
// We need to do this since rustc won't actually print the link args to stdout, so we need to
// give `dx` a file to dump its env::args into
let tmp_file = tempfile::NamedTempFile::new()?;
// Run `cargo rustc` again, but this time with a custom linker (dx) and an env var to force
// `dx` to act as a linker
//
// This will force `dx` to look through the incremental cache and find the assets from the previous build
Command::new("cargo")
// .env("RUSTFLAGS", self.rust_flags())
.arg("rustc")
.args(self.build_arguments())
.arg("--offline") /* don't use the network, should already be resolved */
.arg("--")
.arg(format!(
"-Clinker={}",
std::env::current_exe()
.unwrap()
.canonicalize()
.unwrap()
.display()
))
.env(
LinkAction::ENV_VAR_NAME,
LinkAction::BuildAssetManifest {
destination: tmp_file.path().to_path_buf(),
}
.to_json(),
)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.await?;
// The linker wrote the manifest to the temp file, let's load it!
Ok(AssetManifest::load_from_file(tmp_file.path())?)
}
}
+5 -4
View File
@@ -653,7 +653,8 @@ impl AppBundle {
let input_path = input_path.to_path_buf();
let bindgen_outdir = bindgen_outdir.to_path_buf();
let name = self.build.krate.executable_name().to_string();
let keep_debug = self.build.krate.config.web.wasm_opt.debug || (!self.build.build.release);
let keep_debug = self.build.krate.config.web.wasm_opt.debug;
let reference_types = self.build.krate.config.web.wasm_opt.reference_types;
let start = std::time::Instant::now();
tokio::task::spawn_blocking(move || {
@@ -664,9 +665,9 @@ impl AppBundle {
.debug(keep_debug)
.demangle(keep_debug)
.keep_debug(keep_debug)
.reference_types(true)
.remove_name_section(!keep_debug)
.remove_producers_section(!keep_debug)
.reference_types(reference_types)
.remove_name_section(true)
.remove_producers_section(true)
.out_name(&name)
.generate(&bindgen_outdir)
})
+4
View File
@@ -58,6 +58,10 @@ pub(crate) struct WasmOptConfig {
/// Keep debug symbols in the wasm file
#[serde(default = "false_bool")]
pub(crate) debug: bool,
/// Enable reference types
#[serde(default = "false_bool")]
pub(crate) reference_types: bool,
}
/// The wasm-opt level to use for release web builds [default: 4]