diff --git a/README.md b/README.md index 2b8674b7..d44a77d3 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Options: -v, --verbose Increase verbosity --trace Trace the execution of the program --dry-run Do not actually run the command + --build-graph generate build graph -h, --help Print help ``` diff --git a/changes.md b/changes.md index 75d105ac..59e4f476 100644 --- a/changes.md +++ b/changes.md @@ -1,2 +1,4 @@ -- check latest toolchain version before moon upgrade and add a `--force` flag to force upgrade \ No newline at end of file +- check latest toolchain version before moon upgrade and add a `--force` flag to force upgrade + +- add `--build-graph` flag to generate build graph for build | check | test | bundle diff --git a/crates/moon/src/cli/build.rs b/crates/moon/src/cli/build.rs index 1c916e67..e8c654c5 100644 --- a/crates/moon/src/cli/build.rs +++ b/crates/moon/src/cli/build.rs @@ -130,6 +130,7 @@ fn run_build_internal( run_mode, quiet: cli.quiet, verbose: cli.verbose, + build_graph: cli.build_graph, ..Default::default() }; diff --git a/crates/moon/src/cli/check.rs b/crates/moon/src/cli/check.rs index 15183a3b..ee286fea 100644 --- a/crates/moon/src/cli/check.rs +++ b/crates/moon/src/cli/check.rs @@ -149,6 +149,7 @@ fn run_check_internal( quiet: cli.quiet, verbose: cli.verbose, output_json: cmd.output_json, + build_graph: cli.build_graph, ..Default::default() }; diff --git a/crates/moon/src/cli/fmt.rs b/crates/moon/src/cli/fmt.rs index 0dbb80d4..2cb8a540 100644 --- a/crates/moon/src/cli/fmt.rs +++ b/crates/moon/src/cli/fmt.rs @@ -61,6 +61,7 @@ pub fn run_fmt(cli: &UniversalFlags, cmd: FmtSubcommand) -> anyhow::Result sort_input: cmd.sort_input, run_mode, fmt_opt: Some(FmtOpt { check: cmd.check }), + build_graph: cli.build_graph, ..Default::default() }; diff --git a/crates/moon/src/cli/run.rs b/crates/moon/src/cli/run.rs index aff94f90..c0472bce 100644 --- a/crates/moon/src/cli/run.rs +++ b/crates/moon/src/cli/run.rs @@ -264,6 +264,7 @@ pub fn run_run_internal(cli: &UniversalFlags, cmd: RunSubcommand) -> anyhow::Res args: cmd.args.clone(), quiet: true, verbose: cli.verbose, + build_graph: cli.build_graph, ..Default::default() }; diff --git a/crates/moon/src/cli/test.rs b/crates/moon/src/cli/test.rs index 922dbb25..ebd1df83 100644 --- a/crates/moon/src/cli/test.rs +++ b/crates/moon/src/cli/test.rs @@ -188,6 +188,7 @@ fn run_test_internal( quiet: true, verbose: cli.verbose, no_parallelize: cmd.no_parallelize, + build_graph: cli.build_graph, ..Default::default() }; diff --git a/crates/moon/tests/test_cases/mod.rs b/crates/moon/tests/test_cases/mod.rs index 008b2068..383db79e 100644 --- a/crates/moon/tests/test_cases/mod.rs +++ b/crates/moon/tests/test_cases/mod.rs @@ -437,6 +437,7 @@ fn test_moon_help() { -v, --verbose Increase verbosity --trace Trace the execution of the program --dry-run Do not actually run the command + --build-graph generate build graph -h, --help Print help "#]], ); @@ -3502,18 +3503,18 @@ fn test_deny_warn() { &get_stderr_on_success_with_args_and_replace_dir(&dir, ["check", "--sort-input"]), expect![[r#" Warning: [2000] - ╭─[$ROOT/lib/hello.mbt:14:3] + ╭─[$ROOT/lib/hello.mbt:13:3] │ - 14 │ alert_2(); + 13 │ alert_1(); │ ───┬─── - │ ╰───── Warning (Alert alert_2): alert_2 + │ ╰───── Warning (Alert alert_1): alert_1 ────╯ Warning: [2000] - ╭─[$ROOT/lib/hello.mbt:13:3] + ╭─[$ROOT/lib/hello.mbt:14:3] │ - 13 │ alert_1(); + 14 │ alert_2(); │ ───┬─── - │ ╰───── Warning (Alert alert_1): alert_1 + │ ╰───── Warning (Alert alert_2): alert_2 ────╯ Warning: [1002] ╭─[$ROOT/lib/hello.mbt:4:7] @@ -3547,20 +3548,12 @@ fn test_deny_warn() { "#]], ); - let out = snapbox::cmd::Command::new(moon_bin()) - .current_dir(&dir) - .args(["check", "--deny-warn", "--sort-input"]) - .assert() - .failure() - .get_output() - .stdout - .to_owned(); - - let s = std::str::from_utf8(&out).unwrap().to_string(); - - assert!(s.contains( - "failed: moonc check -error-format json -w @a -alert @all-raise-throw-unsafe+deprecated" - )); + check( + &get_err_stdout_with_args_and_replace_dir(&dir, ["check", "--deny-warn", "--sort-input"]), + expect![[r#" + failed: moonc check -error-format json -w @a -alert @all-raise-throw-unsafe+deprecated $ROOT/lib/hello.mbt -o $ROOT/target/wasm-gc/release/check/lib/lib.mi -pkg username/hello/lib -std-path $MOON_HOME/lib/core/target/wasm-gc/release/bundle -pkg-sources username/hello/lib:$ROOT/lib -target wasm-gc + "#]], + ); check( &get_stderr_on_success_with_args_and_replace_dir(&dir, ["build", "--sort-input"]), @@ -3611,19 +3604,11 @@ fn test_deny_warn() { "#]], ); - let out = snapbox::cmd::Command::new(moon_bin()) - .current_dir(&dir) - .args(["build", "--deny-warn", "--sort-input"]) - .assert() - .failure() - .get_output() - .stdout - .to_owned(); - - let s = std::str::from_utf8(&out).unwrap().to_string(); - - assert!( - s.contains("failed: moonc build-package -error-format json -w @a -alert @all-raise-throw-unsafe+deprecated") + check( + &get_err_stdout_with_args_and_replace_dir(&dir, ["build", "--deny-warn", "--sort-input"]), + expect![[r#" + failed: moonc build-package -error-format json -w @a -alert @all-raise-throw-unsafe+deprecated $ROOT/lib/hello.mbt -o $ROOT/target/wasm-gc/release/build/lib/lib.core -pkg username/hello/lib -std-path $MOON_HOME/lib/core/target/wasm-gc/release/bundle -pkg-sources username/hello/lib:$ROOT/lib -target wasm-gc + "#]], ); } diff --git a/crates/moon/tests/test_cases/moon_build_package.in/moon.test b/crates/moon/tests/test_cases/moon_build_package.in/moon.test index 02deeca4..c567cf31 100644 --- a/crates/moon/tests/test_cases/moon_build_package.in/moon.test +++ b/crates/moon/tests/test_cases/moon_build_package.in/moon.test @@ -2,11 +2,31 @@ moonc build-package ./src/lib/hello.mbt -o ./target/wasm-gc/release/build/lib/lib.core -pkg username/hello/lib -std-path $MOON_HOME/lib/core/target/wasm-gc/release/bundle -pkg-sources username/hello/lib:./src/lib -target wasm-gc moonc build-package ./src/top.mbt -o ./target/wasm-gc/release/build/hello.core -pkg username/hello -std-path $MOON_HOME/lib/core/target/wasm-gc/release/bundle -i ./target/wasm-gc/release/build/lib/lib.mi:lib -pkg-sources username/hello:./src -target wasm-gc - $ moon build + $ moon build --build-graph --sort-input + generated build graph: ${WORK_DIR}/target/wasm-gc/release/build/build_graph.dot Finished. moon: ran 2 tasks, now up to date $ xls ./target/wasm-gc/release/build/lib/ lib.core lib.mi $ xls ./target/wasm-gc/release/build/ - .moon-lock build.moon_db build.output hello.core hello.mi lib moon.db + .moon-lock build.moon_db build.output build_graph.dot hello.core hello.mi lib moon.db + $ xcat ./target/wasm-gc/release/build/build_graph.dot + digraph BuildGraph { + "./target/wasm-gc/release/build/hello.core" [shape=box, style=filled, fillcolor=black, fontcolor=white]; + "./target/wasm-gc/release/build/hello.mi" [shape=box, color=black, ]; + "./src/top.mbt" [shape=box, color=black, ]; + "./target/wasm-gc/release/build/lib/lib.mi" [shape=box, color=black, ]; + "./target/wasm-gc/release/build/lib/lib.core" [shape=box, style=filled, fillcolor=black, fontcolor=white]; + "./src/lib/hello.mbt" [shape=box, color=black, ]; + "build-package: username/hello" [shape=ellipse]; + "./src/top.mbt" -> "build-package: username/hello"; + "./target/wasm-gc/release/build/lib/lib.mi" -> "build-package: username/hello"; + "build-package: username/hello" -> "./target/wasm-gc/release/build/hello.core"; + "build-package: username/hello" -> "./target/wasm-gc/release/build/hello.mi"; + "build-package: username/hello/lib" [shape=ellipse]; + "./src/lib/hello.mbt" -> "build-package: username/hello/lib"; + "build-package: username/hello/lib" -> "./target/wasm-gc/release/build/lib/lib.core"; + "build-package: username/hello/lib" -> "./target/wasm-gc/release/build/lib/lib.mi"; + } + diff --git a/crates/moonbuild/src/entry.rs b/crates/moonbuild/src/entry.rs index 0030349c..8aad2192 100644 --- a/crates/moonbuild/src/entry.rs +++ b/crates/moonbuild/src/entry.rs @@ -20,8 +20,11 @@ use indexmap::IndexMap; use moonutil::module::ModuleDB; use moonutil::package::Package; use moonutil::path::PathComponent; +use n2::graph::FileId; +use n2::load::State; use n2::progress::{DumbConsoleProgress, FancyConsoleProgress, Progress}; use n2::terminal; +use std::collections::HashSet; use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::atomic::AtomicBool; @@ -107,6 +110,10 @@ pub fn n2_run_interface( }); }; + if moonbuild_opt.build_graph { + vis_build_graph(&state, moonbuild_opt); + } + let mut progress = create_progress_console(Some(Box::new(render_and_catch))); let options = work::Options { parallelism: default_parallelism()?, @@ -164,6 +171,66 @@ pub fn n2_run_interface( Ok(res) } +fn vis_build_graph(state: &State, moonbuild_opt: &MoonbuildOpt) { + let path = moonbuild_opt.target_dir.join("build_graph.dot"); + let source_dir = moonbuild_opt.source_dir.display().to_string(); + + let graph = &state.graph; + let files = &graph.files; + let builds = &graph.builds; + let default_artifact = state + .default + .clone() + .into_iter() + .collect::>(); + + let mut dot = String::from("digraph BuildGraph {\n"); + + for file_id in files.all_ids() { + let file_name = &files.by_id[file_id].name.replace(&source_dir, "."); + // mark the file if it's the default artifact that we really want + let (style, fontcolor) = if default_artifact.contains(&file_id) { + ("style=filled, fillcolor=black", "fontcolor=white") + } else { + ("color=black", "") + }; + dot.push_str(&format!( + " \"{}\" [shape=box, {}, {}];\n", + file_name, style, fontcolor + )); + } + + let default_desc = "missing description".to_string(); + for build in builds.iter() { + let build_desc = build + .desc + .as_ref() + .unwrap_or(&default_desc) + .replace(&source_dir, "."); + dot.push_str(&format!(" \"{}\" [shape=ellipse];\n", build_desc)); + + for &input_id in build.ins.ids.iter() { + let input_file_name = &files.by_id[input_id].name.replace(&source_dir, "."); + dot.push_str(&format!( + " \"{}\" -> \"{}\";\n", + input_file_name, build_desc + )); + } + + for &output_id in build.outs() { + let output_file_name = &files.by_id[output_id].name.replace(&source_dir, "."); + dot.push_str(&format!( + " \"{}\" -> \"{}\";\n", + build_desc, output_file_name + )); + } + } + + dot.push_str("}\n"); + std::fs::write(&path, dot).expect("Unable to write dot file"); + eprintln!("generated build graph: {}", path.display()); +} + pub fn run_check( moonc_opt: &MooncOpt, moonbuild_opt: &MoonbuildOpt, diff --git a/crates/moonbuild/src/fmt.rs b/crates/moonbuild/src/fmt.rs index e2afc71c..a06b8006 100644 --- a/crates/moonbuild/src/fmt.rs +++ b/crates/moonbuild/src/fmt.rs @@ -164,6 +164,7 @@ fn gen_inplace_fmt_command(graph: &mut n2graph::Graph, item: &FmtItem) -> (Build .arg(&item.phony_out) .build(); build.cmdline = Some(command); + build.desc = Some(format!("moonfmt {}", item.input)); (build, output_id) } @@ -264,6 +265,7 @@ fn gen_fmt_to_command(graph: &mut n2graph::Graph, item: &FmtItem) -> (Build, Fil .arg(&item.output) .build(); build.cmdline = Some(command); + build.desc = Some(format!("moonfmt {}", item.input)); (build, output_id) } diff --git a/crates/moonbuild/src/gen/gen_build.rs b/crates/moonbuild/src/gen/gen_build.rs index b9ae1f79..24181e0a 100644 --- a/crates/moonbuild/src/gen/gen_build.rs +++ b/crates/moonbuild/src/gen/gen_build.rs @@ -278,6 +278,7 @@ pub fn gen_build_command( .build(); log::debug!("Command: {}", command); build.cmdline = Some(command); + build.desc = Some(format!("build-package: {}", item.package_full_name)); (build, core_output_id) } @@ -414,6 +415,7 @@ pub fn gen_link_command( .build(); log::debug!("Command: {}", command); build.cmdline = Some(command); + build.desc = Some(format!("link-core: {}", item.package_full_name)); (build, artifact_id) } diff --git a/crates/moonbuild/src/gen/gen_bundle.rs b/crates/moonbuild/src/gen/gen_bundle.rs index 927a1858..97eeea95 100644 --- a/crates/moonbuild/src/gen/gen_bundle.rs +++ b/crates/moonbuild/src/gen/gen_bundle.rs @@ -235,6 +235,7 @@ pub fn gen_build_command( .build(); log::debug!("Command: {}", command); build.cmdline = Some(command); + build.desc = Some(format!("build-package {}", item.package_full_name)); build } @@ -283,6 +284,7 @@ fn gen_bundle_all( log::debug!("Command: {}", command); build.cmdline = Some(command); + build.desc = Some(format!("bundle-core {}", bundle_all.name)); build } diff --git a/crates/moonbuild/src/gen/gen_check.rs b/crates/moonbuild/src/gen/gen_check.rs index 893a782b..ee800daa 100644 --- a/crates/moonbuild/src/gen/gen_check.rs +++ b/crates/moonbuild/src/gen/gen_check.rs @@ -26,7 +26,7 @@ use moonutil::package::Package; use std::path::{Path, PathBuf}; use std::rc::Rc; -use moonutil::common::{MoonbuildOpt, MooncOpt, MOON_PKG_JSON}; +use moonutil::common::{get_desc_name, MoonbuildOpt, MooncOpt, MOON_PKG_JSON}; use n2::graph::{self as n2graph, Build, BuildIns, BuildOuts, FileLoc}; use n2::load::State; use n2::smallmap::SmallMap; @@ -350,6 +350,10 @@ pub fn gen_check_command( .build(); log::debug!("Command: {}", command); build.cmdline = Some(command); + build.desc = Some(format!( + "check: {}", + get_desc_name(&item.package_full_name, &item.mi_out) + )); build } diff --git a/crates/moonbuild/src/gen/gen_runtest.rs b/crates/moonbuild/src/gen/gen_runtest.rs index 7d9a81c8..439e0f51 100644 --- a/crates/moonbuild/src/gen/gen_runtest.rs +++ b/crates/moonbuild/src/gen/gen_runtest.rs @@ -18,7 +18,7 @@ use anyhow::{bail, Ok}; use colored::Colorize; -use moonutil::common::{DriverKind, GeneratedTestDriver, MOONBITLANG_CORE}; +use moonutil::common::{get_desc_name, DriverKind, GeneratedTestDriver, MOONBITLANG_CORE}; use moonutil::module::ModuleDB; use moonutil::package::Package; @@ -801,6 +801,10 @@ pub fn gen_runtest_build_command( .build(); log::debug!("Command: {}", command); build.cmdline = Some(command); + build.desc = Some(format!( + "build-package: {}", + get_desc_name(&item.package_full_name, &item.core_out) + )); build } @@ -884,6 +888,10 @@ pub fn gen_runtest_link_command( .build(); log::debug!("Command: {}", command); build.cmdline = Some(command); + build.desc = Some(format!( + "link-core: {}", + get_desc_name(&item.package_full_name, &item.out) + )); (build, artifact_id) } @@ -978,5 +986,10 @@ fn gen_generate_test_driver_command( .build(); build.cmdline = Some(command); + build.desc = Some(format!( + "gen-test-driver: {}_{}_test", + item.package_name, + item.driver_kind.to_string() + )); build } diff --git a/crates/moonutil/src/cli.rs b/crates/moonutil/src/cli.rs index 80af9aeb..db3a7de8 100644 --- a/crates/moonutil/src/cli.rs +++ b/crates/moonutil/src/cli.rs @@ -49,4 +49,8 @@ pub struct UniversalFlags { /// Do not actually run the command #[clap(long, global = true)] pub dry_run: bool, + + /// generate build graph + #[clap(long, global = true, conflicts_with = "dry_run")] + pub build_graph: bool, } diff --git a/crates/moonutil/src/common.rs b/crates/moonutil/src/common.rs index 0006ec73..95161cef 100644 --- a/crates/moonutil/src/common.rs +++ b/crates/moonutil/src/common.rs @@ -355,6 +355,7 @@ pub struct MoonbuildOpt { pub quiet: bool, pub output_json: bool, pub no_parallelize: bool, + pub build_graph: bool, } #[derive(Debug, Clone, Default)] @@ -797,3 +798,13 @@ pub fn line_col_to_byte_idx( )?)?; Some(usize::from(offset)) } + +pub fn get_desc_name(package_name: &str, artifact: &str) -> String { + if artifact.contains("internal_test") { + format!("{}_{}", package_name, "internal_test") + } else if artifact.contains("whitebox_test") { + format!("{}_{}", package_name, "whitebox_test") + } else { + package_name.to_string() + } +} diff --git a/docs/manual-zh/src/tutorial.md b/docs/manual-zh/src/tutorial.md index eee79e6b..726f167e 100644 --- a/docs/manual-zh/src/tutorial.md +++ b/docs/manual-zh/src/tutorial.md @@ -48,6 +48,7 @@ -v, --verbose Increase verbosity --trace Trace the execution of the program --dry-run Do not actually run the command + --build-graph generate build graph -h, --help Print help ``` diff --git a/docs/manual/src/tutorial.md b/docs/manual/src/tutorial.md index 2b63d15f..9c4f4a30 100644 --- a/docs/manual/src/tutorial.md +++ b/docs/manual/src/tutorial.md @@ -48,6 +48,7 @@ Before you begin with this tutorial, make sure you have installed the following: -v, --verbose Increase verbosity --trace Trace the execution of the program --dry-run Do not actually run the command + --build-graph generate build graph -h, --help Print help ```