Skip to content

Commit

Permalink
Implement wasm-pack subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
aDogCalledSpot committed Jan 17, 2024
1 parent bab7732 commit e40cfa6
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 19 deletions.
2 changes: 1 addition & 1 deletion src/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
fs,
metadata::PackageId,
regex_vec::{RegexVec, RegexVecBuilder},
term,
term, wasm_target_dir,
};

pub(crate) fn run(args: &mut Args) -> Result<()> {
Expand Down
23 changes: 19 additions & 4 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,10 @@ impl Args {
}
}
match subcommand {
Subcommand::None | Subcommand::Nextest { .. } | Subcommand::NextestArchive => {}
Subcommand::None
| Subcommand::Nextest { .. }
| Subcommand::NextestArchive
| Subcommand::WasmPack => {}
Subcommand::Test => {
if no_run {
unexpected("--no-run", subcommand)?;
Expand Down Expand Up @@ -591,7 +594,8 @@ impl Args {
| Subcommand::Test
| Subcommand::Run
| Subcommand::Nextest { .. }
| Subcommand::NextestArchive => {}
| Subcommand::NextestArchive
| Subcommand::WasmPack => {}
_ => {
if !bin.is_empty() {
unexpected("--bin", subcommand)?;
Expand Down Expand Up @@ -619,6 +623,7 @@ impl Args {
| Subcommand::Run
| Subcommand::Nextest { .. }
| Subcommand::NextestArchive
| Subcommand::WasmPack
| Subcommand::ShowEnv => {}
_ => {
if no_cfg_coverage {
Expand All @@ -634,7 +639,8 @@ impl Args {
| Subcommand::Test
| Subcommand::Nextest { .. }
| Subcommand::NextestArchive
| Subcommand::Clean => {}
| Subcommand::Clean
| Subcommand::WasmPack => {}
_ => {
if workspace {
unexpected("--workspace", subcommand)?;
Expand Down Expand Up @@ -930,6 +936,9 @@ pub(crate) enum Subcommand {
/// Build and archive tests with cargo nextest
NextestArchive,

/// Run tests with wasm-pack
WasmPack,

// internal (unstable)
Demangle,
}
Expand All @@ -946,7 +955,10 @@ static CARGO_LLVM_COV_NEXTEST_ARCHIVE_USAGE: &str =

impl Subcommand {
fn can_passthrough(subcommand: Self) -> bool {
matches!(subcommand, Self::Test | Self::Run | Self::Nextest { .. } | Self::NextestArchive)
matches!(
subcommand,
Self::Test | Self::Run | Self::Nextest { .. } | Self::NextestArchive | Self::WasmPack
)
}

fn help_text(subcommand: Self) -> &'static str {
Expand All @@ -959,6 +971,7 @@ impl Subcommand {
Self::ShowEnv => CARGO_LLVM_COV_SHOW_ENV_USAGE,
Self::Nextest { .. } => CARGO_LLVM_COV_NEXTEST_USAGE,
Self::NextestArchive => CARGO_LLVM_COV_NEXTEST_ARCHIVE_USAGE,
Self::WasmPack => todo!(),
Self::Demangle => "", // internal API
}
}
Expand All @@ -973,6 +986,7 @@ impl Subcommand {
Self::ShowEnv => "show-env",
Self::Nextest { .. } => "nextest",
Self::NextestArchive => "nextest-archive",
Self::WasmPack => "wasm-pack",
Self::Demangle => "demangle",
}
}
Expand All @@ -994,6 +1008,7 @@ impl FromStr for Subcommand {
"show-env" => Ok(Self::ShowEnv),
"nextest" => Ok(Self::Nextest { archive_file: false }),
"nextest-archive" => Ok(Self::NextestArchive),
"wasm-pack" => Ok(Self::WasmPack),
"demangle" => Ok(Self::Demangle),
_ => bail!("unrecognized subcommand {s}"),
}
Expand Down
125 changes: 112 additions & 13 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{
collections::{BTreeSet, HashMap},
ffi::{OsStr, OsString},
io::{self, BufRead, Write},
path::Path,
path::{Path, PathBuf},
time::SystemTime,
};

Expand Down Expand Up @@ -105,6 +105,15 @@ fn try_main() -> Result<()> {
create_dirs(cx)?;
archive_nextest(cx)?;
}
Subcommand::WasmPack => {
let cx = &Context::new(args)?;
clean::clean_partial(cx)?;
create_dirs(cx)?;
wasm_pack_test(cx)?;
if !cx.args.cov.no_report {
generate_report(cx)?;
}
}
Subcommand::None | Subcommand::Test => {
let cx = &Context::new(args)?;
clean::clean_partial(cx)?;
Expand Down Expand Up @@ -487,6 +496,72 @@ fn run_run(cx: &Context) -> Result<()> {
Ok(())
}

// Sanitizes the crate name so we know which .ll file to compile
fn crate_name_to_llvm_name(crate_name: &str) -> String {
crate_name.replace('-', "_")
}

fn compile_ll_file(cx: &Context, prefix: &str) -> Result<()> {
// There are multiple ll files generated per crate, but only one wasm which has
// an ll file with the same file stem sitting next to it.
// That's the ll file we want.
let path = wasm_target_dir(&cx.ws);
let glob = glob::glob(
Utf8Path::new(&glob::Pattern::escape(&path.to_string()))
.join(format!("{prefix}-*.wasm"))
.as_str(),
)?;
let Some(newest) = glob
.filter_map(Result::ok)
.max_by_key(|p| std::fs::metadata(p).unwrap().created().unwrap())
else {
return Ok(());
};

let stem = newest.file_stem().unwrap().to_str().unwrap();
let src = format!("{path}/{stem}.ll");
let dst = format!("{path}/{stem}.o");

let mut cmd = cmd!("clang");
cmd.arg(src);
cmd.arg("-Wno-override-module");
cmd.arg("-c");
cmd.arg("-o");
cmd.arg(dst);
cmd.run()?;
Ok(())
}

fn compile_ll_files(cx: &Context) -> Result<()> {
for id in &cx.ws.metadata.workspace_members {
let prefix = crate_name_to_llvm_name(&cx.ws.metadata.packages[&id].name);
compile_ll_file(cx, &prefix)?;
}
Ok(())
}

fn wasm_profraw_prefix(ws: &Workspace) -> String {
format!("{}-", ws.name)
}

fn wasm_pack_test(cx: &Context) -> Result<()> {
let mut cmd = cmd!("wasm-pack");
cmd.arg("test");
cmd.arg("--coverage");
cmd.args(["--profraw-out", &cx.ws.target_dir.to_string()]);
cmd.args(["--profraw-prefix", &wasm_profraw_prefix(&cx.ws)]);
cmd.args(cx.args.cargo_args.clone());
cmd.args(["--target-dir", cx.ws.target_dir.as_ref()]);

// Emit llvm-ir to obtain debug info (https://github.com/hknio/code-coverage-for-webassembly)
cmd.env("RUSTFLAGS", "-Cinstrument-coverage -Zno-profiler-runtime --emit=llvm-ir");
cmd.run()?;

compile_ll_files(cx)?;

Ok(())
}

fn stdout_to_stderr(cx: &Context, cargo: &mut ProcessBuilder) {
if cx.args.cov.no_report
|| cx.args.cov.output_dir.is_some()
Expand All @@ -500,9 +575,17 @@ fn stdout_to_stderr(cx: &Context, cargo: &mut ProcessBuilder) {
}

fn generate_report(cx: &Context) -> Result<()> {
merge_profraw(cx).context("failed to merge profile data")?;
let profraws = merge_profraw(cx).context("failed to merge profile data")?;

let object_files = object_files(cx).context("failed to collect object files")?;
let mut object_files = object_files(cx).context("failed to collect object files")?;
object_files.append(&mut wasm_object_files(&cx.ws, &profraws));
if object_files.is_empty() {
warn!(
"not found object files (this may occur if \
show-env subcommand is used incorrectly (see docs or other warnings), or unsupported \
commands such as nextest archive are used",
);
}
let ignore_filename_regex = ignore_filename_regex(cx);
let format = Format::from_args(cx);
format
Expand Down Expand Up @@ -604,6 +687,10 @@ fn generate_report(cx: &Context) -> Result<()> {
Ok(())
}

fn wasm_target_dir(ws: &Workspace) -> Utf8PathBuf {
format!("{}/wasm32-unknown-unknown/debug/deps", ws.target_dir).into()
}

fn open_report(cx: &Context, path: &Utf8Path) -> Result<()> {
match &cx.ws.config.doc.browser {
Some(browser) => {
Expand All @@ -618,7 +705,7 @@ fn open_report(cx: &Context, path: &Utf8Path) -> Result<()> {
Ok(())
}

fn merge_profraw(cx: &Context) -> Result<()> {
fn merge_profraw(cx: &Context) -> Result<Vec<PathBuf>> {
// Convert raw profile data.
let profraw_files = glob::glob(
Utf8Path::new(&glob::Pattern::escape(cx.ws.target_dir.as_str()))
Expand All @@ -635,7 +722,7 @@ fn merge_profraw(cx: &Context) -> Result<()> {
);
}
let mut input_files = String::new();
for path in profraw_files {
for path in &profraw_files {
input_files.push_str(
path.to_str().with_context(|| format!("{path:?} contains invalid utf-8 data"))?,
);
Expand All @@ -659,7 +746,7 @@ fn merge_profraw(cx: &Context) -> Result<()> {
status!("Running", "{cmd}");
}
cmd.stdout_to_stderr().run()?;
Ok(())
Ok(profraw_files)
}

fn object_files(cx: &Context) -> Result<Vec<OsString>> {
Expand Down Expand Up @@ -800,15 +887,26 @@ fn object_files(cx: &Context) -> Result<Vec<OsString>> {

// This sort is necessary to make the result of `llvm-cov show` match between macos and linux.
files.sort_unstable();
Ok(files)
}

if files.is_empty() {
warn!(
"not found object files (searched directories: {searched_dir}); this may occur if \
show-env subcommand is used incorrectly (see docs or other warnings), or unsupported \
commands such as nextest archive are used",
);
fn wasm_object_files(ws: &Workspace, profraws: &[PathBuf]) -> Vec<OsString> {
let mut ret = Vec::new();
for file in profraws {
let fname = file.file_name().unwrap().to_str().unwrap();
let prefix = format!("{}wbg-tmp-", wasm_profraw_prefix(ws));
let Some(stem): Option<PathBuf> = fname
.strip_prefix(&prefix)
.and_then(|s| s.strip_suffix(".wasm.profraw"))
.map(|s| s.into())
else {
// Didn't match prefix or suffix
continue;
};
let obj = wasm_target_dir(ws).join(format!("{}.o", stem.display()));
ret.push(obj.as_os_str().to_owned())
}
Ok(files)
ret
}

struct Targets {
Expand Down Expand Up @@ -944,6 +1042,7 @@ impl Format {
cmd.arg("-ignore-filename-regex");
cmd.arg(ignore_filename_regex);
}
cmd.args(["--sources", "."]);

match self {
Self::Text | Self::Html => {
Expand Down
3 changes: 2 additions & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,8 @@ fn invalid_arg() {
));
}
}
if !matches!(subcommand, "" | "test" | "run" | "nextest" | "nextest-archive") {
if !matches!(subcommand, "" | "test" | "run" | "nextest" | "nextest-archive" | "wasm-pack")
{
for arg in [
"--bin=v",
"--example=v",
Expand Down

0 comments on commit e40cfa6

Please sign in to comment.