diff --git a/crates/moon/src/cli.rs b/crates/moon/src/cli.rs index d47c8e91..32923a90 100644 --- a/crates/moon/src/cli.rs +++ b/crates/moon/src/cli.rs @@ -30,6 +30,7 @@ pub mod info; pub mod mooncake_adapter; pub mod new; mod pre_build; +pub mod query; pub mod run; pub mod shell_completion; pub mod test; @@ -51,6 +52,7 @@ pub use generate_test_driver::*; pub use info::*; use moonbuild::upgrade::UpgradeSubcommand; pub use new::*; +pub use query::*; pub use run::*; pub use shell_completion::*; pub use test::*; @@ -117,6 +119,8 @@ pub enum MoonBuildSubcommands { // Misc Coverage(CoverageSubcommand), GenerateBuildMatrix(GenerateBuildMatrix), + #[clap(hide = true)] + Query(QuerySubcommand), /// Upgrade toolchains Upgrade(UpgradeSubcommand), diff --git a/crates/moon/src/cli/build.rs b/crates/moon/src/cli/build.rs index a5054a66..7ac05e52 100644 --- a/crates/moon/src/cli/build.rs +++ b/crates/moon/src/cli/build.rs @@ -49,6 +49,9 @@ pub struct BuildSubcommand { /// Monitor the file system and automatically build artifacts #[clap(long, short)] pub watch: bool, + + #[clap(long, hide = true)] + pub show_artifacts: bool, } pub fn run_build(cli: &UniversalFlags, cmd: &BuildSubcommand) -> anyhow::Result { @@ -193,5 +196,20 @@ fn run_build_internal( trace::close(); } + if let (Ok(_), true) = (res.as_ref(), cmd.show_artifacts) { + // can't use HashMap because the order of the packages is not guaranteed + // can't use IndexMap because moonc cannot handled ordered map + let mut artifacts = Vec::new(); + for pkg in module + .get_topo_pkgs()? + .iter() + .filter(|pkg| !pkg.is_third_party) + { + let mi = pkg.artifact.with_extension("mi"); + let core = pkg.artifact.with_extension("core"); + artifacts.push((pkg.full_name(), mi, core)); + } + println!("{}", serde_json::to_string(&artifacts).unwrap()); + } res } diff --git a/crates/moon/src/cli/query.rs b/crates/moon/src/cli/query.rs new file mode 100644 index 00000000..920a0cca --- /dev/null +++ b/crates/moon/src/cli/query.rs @@ -0,0 +1,85 @@ +// moon: The build system and package manager for MoonBit. +// Copyright (C) 2024 International Digital Economy Academy +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// +// For inquiries, you can contact us via e-mail at jichuruanjian@idea.edu.cn. + +use moonutil::{cli::UniversalFlags, common::DEP_PATH}; + +use super::BuildFlags; + +#[derive(Debug, clap::Parser, Clone)] +pub struct QuerySubcommand { + #[clap(flatten)] + pub build_flags: BuildFlags, + + pub mod_name: String, +} + +pub fn run_query(_cli: UniversalFlags, cmd: QuerySubcommand) -> anyhow::Result { + let temp_dir = std::env::temp_dir(); + let moon_repl_dir = temp_dir.join("moon_repl"); + + if !moon_repl_dir.exists() { + std::fs::create_dir_all(&moon_repl_dir)?; + } + + let mod_json_path = moon_repl_dir.join("moon.mod.json"); + if !mod_json_path.exists() { + let mod_json_content = r#"{ + "name": "moon/repl", + "version": "0.0.1" + }"#; + std::fs::write(mod_json_path, mod_json_content)?; + } + + let mod_name = cmd.mod_name; + + let moon_path = std::env::current_exe() + .map_or_else(|_| "moon".into(), |x| x.to_string_lossy().into_owned()); + + let moon_add_output = std::process::Command::new(&moon_path) + .arg("add") + .arg(&mod_name) + .arg("--source-dir") + .arg(moon_repl_dir.to_str().unwrap()) + .output()?; + + if !moon_add_output.status.success() { + eprintln!("{}", String::from_utf8_lossy(&moon_add_output.stdout)); + eprintln!("{}", String::from_utf8_lossy(&moon_add_output.stderr)); + anyhow::bail!("Failed to add module {}", mod_name); + } + + let moon_build_output = std::process::Command::new(&moon_path) + .arg("build") + .arg("--source-dir") + .arg( + moon_repl_dir + .join(DEP_PATH) + .join(&mod_name) + .to_str() + .unwrap(), + ) + .arg("--show-artifacts") + .output()?; + + println!("{}", String::from_utf8_lossy(&moon_build_output.stdout)); + if !moon_build_output.status.success() { + anyhow::bail!("Failed to build module {}", mod_name); + } + + Ok(0) +} diff --git a/crates/moon/src/main.rs b/crates/moon/src/main.rs index 9facf45e..36ef3db1 100644 --- a/crates/moon/src/main.rs +++ b/crates/moon/src/main.rs @@ -80,6 +80,7 @@ fn main1() -> anyhow::Result { New(n) => cli::run_new(&flags, n), Publish(p) => cli::mooncake_adapter::publish_cli(flags, p), Package(p) => cli::mooncake_adapter::package_cli(flags, p), + Query(q) => cli::run_query(flags, q), Register(r) => cli::mooncake_adapter::register_cli(flags, r), Remove(r) => cli::remove_cli(flags, r), Run(r) => cli::run_run(&flags, r), diff --git a/crates/moon/tests/test_cases/mod.rs b/crates/moon/tests/test_cases/mod.rs index 44db8451..247b4e6c 100644 --- a/crates/moon/tests/test_cases/mod.rs +++ b/crates/moon/tests/test_cases/mod.rs @@ -7966,3 +7966,19 @@ fn test_run_doc_test() { "#]], ); } + +#[cfg(unix)] +#[test] +fn test_moon_query() { + let dir = TestDir::new("test_filter_pkg_with_test_imports.in"); + + check( + get_stdout(&dir, ["build", "--show-artifacts"]), + // need topological order + expect![[r#" + [["username/hello/lib6","$ROOT/target/wasm-gc/release/build/lib6/lib6.mi","$ROOT/target/wasm-gc/release/build/lib6/lib6.core"],["username/hello/lib7","$ROOT/target/wasm-gc/release/build/lib7/lib7.mi","$ROOT/target/wasm-gc/release/build/lib7/lib7.core"],["username/hello/lib3","$ROOT/target/wasm-gc/release/build/lib3/lib3.mi","$ROOT/target/wasm-gc/release/build/lib3/lib3.core"],["username/hello/lib1","$ROOT/target/wasm-gc/release/build/lib1/lib1.mi","$ROOT/target/wasm-gc/release/build/lib1/lib1.core"],["username/hello/lib5","$ROOT/target/wasm-gc/release/build/lib5/lib5.mi","$ROOT/target/wasm-gc/release/build/lib5/lib5.core"],["username/hello/lib4","$ROOT/target/wasm-gc/release/build/lib4/lib4.mi","$ROOT/target/wasm-gc/release/build/lib4/lib4.core"],["username/hello/lib2","$ROOT/target/wasm-gc/release/build/lib2/lib2.mi","$ROOT/target/wasm-gc/release/build/lib2/lib2.core"],["username/hello/lib","$ROOT/target/wasm-gc/release/build/lib/lib.mi","$ROOT/target/wasm-gc/release/build/lib/lib.core"],["username/hello/main","$ROOT/target/wasm-gc/release/build/main/main.mi","$ROOT/target/wasm-gc/release/build/main/main.core"]] + "#]], + ); + + get_stdout(&dir, ["query", "moonbitlang/x"]); +} diff --git a/crates/moonutil/src/module.rs b/crates/moonutil/src/module.rs index 00751fbb..f42bea6a 100644 --- a/crates/moonutil/src/module.rs +++ b/crates/moonutil/src/module.rs @@ -88,6 +88,46 @@ impl ModuleDB { self.packages.values().find(|it| it.root_path == path) } + pub fn get_topo_pkgs(&self) -> anyhow::Result> { + use petgraph::graph::NodeIndex; + + let mut graph = DiGraph::::new(); + let mut name_to_idx: IndexMap = IndexMap::new(); + let mut idx_to_name = IndexMap::new(); + + for (to_node, pkg) in self.packages.iter() { + if !name_to_idx.contains_key(to_node) { + let to_idx = graph.add_node(to_node.clone()); + name_to_idx.insert(to_node.clone(), to_idx); + idx_to_name.insert(to_idx, to_node.clone()); + } + + let to_idx = name_to_idx[to_node]; + + for dep in pkg.imports.iter() { + let from_node = dep.path.make_full_path(); + if !name_to_idx.contains_key(&from_node) { + let to_idx = graph.add_node(from_node.clone()); + name_to_idx.insert(from_node.clone(), to_idx); + idx_to_name.insert(to_idx, from_node.clone()); + } + let from_idx = name_to_idx[&from_node]; + graph.add_edge(from_idx, to_idx, 0); + } + } + + let topo_pkgs = match petgraph::algo::toposort(&graph, None) { + Ok(res) => res + .into_iter() + .map(|idx| self.get_package_by_name(idx_to_name[&idx].as_str())) + .collect::>(), + Err(cycle) => { + bail!("cyclic dependency detected: {:?}", cycle); + } + }; + Ok(topo_pkgs) + } + pub fn get_package_by_path_mut(&mut self, path: &Path) -> Option<&mut Package> { self.packages .iter_mut()