From 41ccf6f1ccf236820fcec6628bc7f2d626f41492 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Penna Date: Fri, 5 Jun 2026 07:05:09 -0700 Subject: [PATCH 1/3] [build] Fix nanvix_binaries build failure On a non-English Windows (e.g. French), running build.bat panicked in the nanvix_binaries build script with 'certutil output not UTF-8'. certutil_sha256() in src/backends/nanvix/binaries/build.rs read certutil's stdout via String::from_utf8(...).expect(...). On localized Windows, certutil emits its header/footer lines (e.g. 'Hachage SHA256 de ...', '... s'est terminee correctement.') in the console's OEM code page (CP850), not UTF-8. Bytes such as 0xA0, 0x92 and 0xE9 are invalid UTF-8, so the strict conversion panicked even though the SHA256 hash line itself is pure ASCII. Use String::from_utf8_lossy so the localized header/footer no longer aborts the build, and locate the hash by scanning for the 64-character hex line instead of relying on a fixed line index. This keeps hash extraction locale-independent and robust to certutil output variations. Fixes #498 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/backends/nanvix/binaries/build.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/backends/nanvix/binaries/build.rs b/src/backends/nanvix/binaries/build.rs index 6b2e6621..11d90fa2 100644 --- a/src/backends/nanvix/binaries/build.rs +++ b/src/backends/nanvix/binaries/build.rs @@ -436,14 +436,19 @@ fn certutil_sha256(path: &Path) -> String { // SHA256 hash of : // // CertUtil: -hashfile command completed successfully. - let stdout = String::from_utf8(output.stdout).expect("certutil output not UTF-8"); + // + // Use a lossy conversion because the localized header/footer lines are + // emitted in the console's OEM code page (e.g. CP850 on French Windows), + // not UTF-8 -- a strict `from_utf8` would panic on those bytes. The hash + // line itself is pure ASCII hex, so it survives the lossy conversion. We + // locate the hash by scanning for a 64-character hex line rather than + // relying on a fixed line index, which keeps this locale-independent. + let stdout = String::from_utf8_lossy(&output.stdout); stdout .lines() - .nth(1) + .map(|line| line.trim().replace(' ', "").to_lowercase()) + .find(|line| line.len() == 64 && line.bytes().all(|b| b.is_ascii_hexdigit())) .unwrap_or_else(|| panic!("nanvix_binaries: unexpected certutil output: {}", stdout)) - .trim() - .replace(' ', "") - .to_lowercase() } fn verify_checksums(binaries: &[&str], bin_dir: &Path, checksums: &HashMap) { From 3839dc44cace2c2d8bfaaaa6a97da8017ade6948 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Penna Date: Fri, 5 Jun 2026 07:13:40 -0700 Subject: [PATCH 2/3] [build] Gate nanvix_binaries download Running build.bat without --with-microvm still downloaded the NanVix release assets and verified them with certutil. This was the trigger for the locale crash in issue #498, and it forced network + hashing work on every build even when the micro-VM backend was not requested. Root cause: nanvix_binaries is an unconditional workspace member, so a plain `cargo build` (which builds all default members) compiled it and ran its build script. The script gated only on the target OS, never on whether the micro-VM backend was actually being built. The --with-microvm flag merely added `--features microvm` (gating the nanvix_runner link and the SDK copy step) and never reached the download path. Gate the expensive work behind a new `microvm` feature on the crate: - nanvix_binaries: add a `microvm = []` feature. build.rs now early-returns when CARGO_FEATURE_MICROVM is unset, emitting only NANVIX_BIN_DIR (still required because lib.rs references it via env!) and the rerun directive. No download, no certutil, no snapshot generation. - wxc / lxc: their `microvm` features now enable `nanvix_binaries/microvm` (instead of just pulling the optional dep), so the download path runs exactly when --with-microvm / --features microvm is requested. build.bat needs no change: it already passes --features microvm, which now turns on the crate's gate. The crate is still compiled on a default build (cheap lib + early-returning build script); only the network/hashing work is skipped. Verified: - `cargo build -p nanvix_binaries` (no feature): compiles, no download/certutil. - `cargo build -p wxc --features microvm`: build script runs full path (binaries verified, snapshots checked), confirming CARGO_FEATURE_MICROVM propagates via nanvix_binaries/microvm. - `cargo tree`: nanvix_binaries absent by default, present with the feature. - cargo fmt --check and cargo clippy -- -D warnings clean on touched crates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/backends/nanvix/binaries/Cargo.toml | 8 ++++++++ src/backends/nanvix/binaries/build.rs | 17 +++++++++++++++++ src/core/lxc/Cargo.toml | 2 +- src/core/wxc/Cargo.toml | 2 +- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/backends/nanvix/binaries/Cargo.toml b/src/backends/nanvix/binaries/Cargo.toml index d526b990..01e7ee52 100644 --- a/src/backends/nanvix/binaries/Cargo.toml +++ b/src/backends/nanvix/binaries/Cargo.toml @@ -8,6 +8,14 @@ links = "nanvix_binaries" [lib] path = "src/lib.rs" +[features] +# Gates the build script's expensive work (downloading NanVix release assets +# and verifying their checksums). OFF by default so a plain `cargo build` — +# which still compiles this crate as a workspace member — performs no network +# or hashing work. Enabled transitively via the `microvm` feature of `wxc` and +# `lxc`. +microvm = [] + [dependencies] nanvix_common = { path = "../common" } diff --git a/src/backends/nanvix/binaries/build.rs b/src/backends/nanvix/binaries/build.rs index 11d90fa2..ff407355 100644 --- a/src/backends/nanvix/binaries/build.rs +++ b/src/backends/nanvix/binaries/build.rs @@ -34,6 +34,23 @@ use std::process::Command; use nanvix_common::{github_download_url, load_checksums, load_json, ReleaseConfig, RepoConfig}; fn main() { + // The expensive work in this build script — downloading NanVix release + // assets and verifying their checksums via `certutil` — is only needed + // when the micro-VM backend is actually being built. Gate it behind this + // crate's `microvm` feature so that a default `cargo build` (which still + // compiles this crate as a workspace member) performs no network or hashing + // work. `wxc` and `lxc` enable `nanvix_binaries/microvm` through their own + // `microvm` features. + // + // `NANVIX_BIN_DIR` must still be emitted in every configuration because + // `lib.rs` references it via `env!`. + if std::env::var_os("CARGO_FEATURE_MICROVM").is_none() { + let out_dir = std::env::var("OUT_DIR").unwrap(); + println!("cargo:rustc-env=NANVIX_BIN_DIR={}", out_dir); + println!("cargo:rerun-if-changed=build.rs"); + return; + } + // Check the TARGET platform (not host). NanVix binaries are only needed when // the output binary will run on Windows or Linux with KVM. let target = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); diff --git a/src/core/lxc/Cargo.toml b/src/core/lxc/Cargo.toml index b065b9d8..cd5b1126 100644 --- a/src/core/lxc/Cargo.toml +++ b/src/core/lxc/Cargo.toml @@ -9,7 +9,7 @@ path = "src/main.rs" [features] hyperlight = ["dep:hyperlight_common", "hyperlight_common/hyperlight"] -microvm = ["dep:nanvix_binaries", "dep:nanvix_runner"] +microvm = ["nanvix_binaries/microvm", "dep:nanvix_runner"] [build-dependencies] mxc_build_common.workspace = true diff --git a/src/core/wxc/Cargo.toml b/src/core/wxc/Cargo.toml index 686869d4..e9231c2f 100644 --- a/src/core/wxc/Cargo.toml +++ b/src/core/wxc/Cargo.toml @@ -34,7 +34,7 @@ nanvix_common = { path = "../../backends/nanvix/common" } [features] default = [] -microvm = ["nanvix_binaries", "dep:nanvix_runner"] +microvm = ["nanvix_binaries/microvm", "dep:nanvix_runner"] wslc = ["dep:wslc_common", "wslc_common/link-wslcsdk"] hyperlight = ["dep:hyperlight_common", "hyperlight_common/hyperlight"] isolation_session = [ From 93b37a7fcc5e7bde9c818bf9288f895e37248d79 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Penna Date: Fri, 5 Jun 2026 07:36:55 -0700 Subject: [PATCH 3/3] [build] Address PR review: explicit dep + rerun trigger Apply the three suggestions from the PR #499 review: - nanvix_binaries/build.rs: emit `cargo:rerun-if-env-changed=CARGO_FEATURE_MICROVM` so toggling the `microvm` feature between builds re-runs the script instead of reusing stale output (NANVIX_BIN_DIR and whether the download/verify path runs). - wxc / lxc Cargo.toml: enable the optional `nanvix_binaries` dependency explicitly via `dep:nanvix_binaries` alongside `nanvix_binaries/microvm`, matching the `dep:`-prefixed style used by the other optional-dep features (wslc, hyperlight). No behavioral change: `nanvix_binaries/microvm` already pulled in the optional dep, and Cargo already re-runs build scripts on feature-set changes; these make the wiring explicit and guard against stale build-script output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/backends/nanvix/binaries/build.rs | 7 +++++++ src/core/lxc/Cargo.toml | 2 +- src/core/wxc/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/backends/nanvix/binaries/build.rs b/src/backends/nanvix/binaries/build.rs index ff407355..df6a0a52 100644 --- a/src/backends/nanvix/binaries/build.rs +++ b/src/backends/nanvix/binaries/build.rs @@ -34,6 +34,13 @@ use std::process::Command; use nanvix_common::{github_download_url, load_checksums, load_json, ReleaseConfig, RepoConfig}; fn main() { + // The build script's output (`NANVIX_BIN_DIR` and whether the download / + // verify path runs) depends on the `microvm` feature, surfaced here as the + // `CARGO_FEATURE_MICROVM` env var. Declare it as a rerun trigger so toggling + // the feature between builds re-runs this script instead of reusing stale + // output. + println!("cargo:rerun-if-env-changed=CARGO_FEATURE_MICROVM"); + // The expensive work in this build script — downloading NanVix release // assets and verifying their checksums via `certutil` — is only needed // when the micro-VM backend is actually being built. Gate it behind this diff --git a/src/core/lxc/Cargo.toml b/src/core/lxc/Cargo.toml index cd5b1126..acb20506 100644 --- a/src/core/lxc/Cargo.toml +++ b/src/core/lxc/Cargo.toml @@ -9,7 +9,7 @@ path = "src/main.rs" [features] hyperlight = ["dep:hyperlight_common", "hyperlight_common/hyperlight"] -microvm = ["nanvix_binaries/microvm", "dep:nanvix_runner"] +microvm = ["dep:nanvix_binaries", "nanvix_binaries/microvm", "dep:nanvix_runner"] [build-dependencies] mxc_build_common.workspace = true diff --git a/src/core/wxc/Cargo.toml b/src/core/wxc/Cargo.toml index e9231c2f..e76b6800 100644 --- a/src/core/wxc/Cargo.toml +++ b/src/core/wxc/Cargo.toml @@ -34,7 +34,7 @@ nanvix_common = { path = "../../backends/nanvix/common" } [features] default = [] -microvm = ["nanvix_binaries/microvm", "dep:nanvix_runner"] +microvm = ["dep:nanvix_binaries", "nanvix_binaries/microvm", "dep:nanvix_runner"] wslc = ["dep:wslc_common", "wslc_common/link-wslcsdk"] hyperlight = ["dep:hyperlight_common", "hyperlight_common/hyperlight"] isolation_session = [