diff --git a/.cargo/config.toml b/.cargo/config.toml index c7818ca..698b57a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,6 @@ [env] CFLAGS_wasm32_unknown_unknown = "-I wasm-sysroot" AR = 'llvm-ar' + +[alias] +xtask = "run -p xtask --" diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 15f4d71..7fdd2c7 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -17,13 +17,11 @@ jobs: - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Build ic10lsp - run: | - cd ic10lsp_wasm - wasm-pack build + run: | + cargo xtask build -p ic10lsp_wasm --release - name: Build ic10emu - run: | - cd ic10emu_wasm - wasm-pack build + run: | + cargo xtask build -p ic10emu_wasm --release - name: Build Page run: | cd www @@ -52,4 +50,4 @@ jobs: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 - + diff --git a/Cargo.lock b/Cargo.lock index f7424d9..8339f26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1839,3 +1839,11 @@ name = "windows_x86_64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "clap", + "thiserror", +] diff --git a/Cargo.toml b/Cargo.toml index 356cfc5..c0d0a8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,14 @@ [workspace] - -members = ["ic10lsp_wasm", "ic10emu_wasm", "ic10emu"] +members = ["ic10lsp_wasm", "ic10emu_wasm", "ic10emu", "xtask"] resolver = "2" + [workspace.package] version = "0.1.0" edition = "2021" - [profile.release] # Tell `rustc` to optimize for small code size. opt-level = "s" -lto = true \ No newline at end of file +lto = true +[profile.dev] +opt-level = 1 diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..bc66fb2 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xtask" +version.workspace = true +edition.workspace = true + +[dependencies] +clap = { version = "4.5.4", features = ["derive", "env"] } +thiserror = "1.0.58" diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..7617f7e --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,142 @@ +use std::process::Command; + +use clap::{ArgMatches, Parser, Subcommand}; + +/// Helper program to start ic10emu and website. +/// +/// Can be invoked as `cargo xtask ` +#[derive(Debug, Parser)] +#[command(bin_name = "cargo xtask")] +struct Args { + /// Package manager to use + #[arg(long, global = true, default_value = "pnpm")] + manager: String, + /// wasm-pack executable + #[arg(long, global = true, default_value = "wasm-pack")] + wasm_pack: String, + #[command(subcommand)] + task: Task, +} + +const PACKAGES: &[&str] = &["ic10lsp_wasm", "ic10emu_wasm"]; + +#[derive(Debug, Subcommand)] +enum Task { + /// Build the packages + Build { + /// Build in release mode + #[arg(long)] + release: bool, + /// Packages to build + #[arg(long, short = 'p', default_values = PACKAGES)] + packages: Vec, + /// Additional arguments to pass to wasm-pack, use another `--` to pass to cargo build + #[arg(last = true, default_values = ["--","-q"])] + rest: Vec, + }, + /// Start the server + /// + /// This does not build the packages, use `build` first + Start {}, +} + +#[derive(thiserror::Error)] +enum Error { + #[error("building package {0} failed. Command: `{1}` Status code {2}")] + BuildFailed(String, String, std::process::ExitStatus), + #[error("failed to run command `{0}`")] + Command(String, #[source] std::io::Error), + #[error("IO failed {0}")] + Io(String, #[source] std::io::Error), +} + +impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use std::error::Error; + use std::fmt::*; + write!(f, "Error: {}", self)?; + let mut err: &dyn Error = self; + while let Some(cause) = err.source() { + write!(f, "\nCaused by: ")?; + Display::fmt(&cause, f)?; + err = cause; + } + Ok(()) + } +} + +fn main() -> Result<(), Error> { + let args = Args::parse(); + let workspace = { + let out = Command::new("cargo") + .arg("metadata") + .arg("--no-deps") + .arg("--format-version=1") + .output() + .map_err(|e| Error::Command("cargo metadata".to_string(), e))?; + let s = std::str::from_utf8(&out.stdout).unwrap(); + let Some((_, s)) = s.split_once(r#"workspace_root":""#) else { + panic!("couldn't find workspace root"); + }; + let Some((path, _)) = s.split_once("\",") else { + panic!("couldn't find workspace root"); + }; + std::path::PathBuf::from(path) + }; + match &args.task { + Task::Build { + release, + packages, + rest, + } => { + build(&args, packages, *release, &workspace, rest)?; + } + Task::Start {} => { + eprintln!("Starting server"); + let mut cmd = Command::new(&args.manager); + cmd.current_dir(&workspace.join("www")); + cmd.args(["run", "start"]).status().map_err(|e| { + Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e) + })?; + } + } + Ok(()) +} + +fn build + std::fmt::Debug + std::fmt::Display>( + args: &Args, + packages: &[P], + release: bool, + workspace: &std::path::Path, + rest: &[std::ffi::OsString], +) -> Result<(), Error> { + if packages.is_empty() { + panic!("no package(s) specified") + } + eprintln!("Building packages: {:?}, release: {}", packages, release); + for package in packages { + eprintln!("Building package: {}", package); + let mut cmd = Command::new(&args.wasm_pack); + cmd.current_dir(workspace); + cmd.arg("build"); + if release { + cmd.arg("--release"); + } else { + cmd.arg("--dev"); + } + cmd.arg(package); + let status = cmd + .status() + .map_err(|e| Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e))?; + if status.success() { + eprintln!("{} built successfully", package); + } else { + return Err(Error::BuildFailed( + package.to_string(), + format!("{cmd:?}"), + status, + )); + } + } + Ok(()) +}