Skip to content

nvx-rs: break the host module into finer-grained modules #896

Open
0 of 3 issues completed
Open

Description

Goal. Break nvx::host (nvx-rs/src/host/) into finer-grained modules, in three phases: make the dependency graph between its children acyclic and guard it, split its oversized files, and, optionally, group its children into layered subsystems. No phase changes behavior or the public API.

Today (dev 6af32ba0):

  • nvx::host (feature host) has 17 direct children (nvx-rs/src/host/mod.rs:48-67): artifacts, cli (feature cli), filesystem, instance, launch, network, nvxs, platform, process, python (pub(crate)), report, sandbox, seal, session, store, supervisor and sys (private, Unix), besides the test-only test_support (mod.rs:68-69).

  • Its 89 files hold about 29.9k production lines and 25.3k test lines, counting test-only files and inline #[cfg(test)] modules as tests.

  • The production references between those children form one strongly connected component of 8: artifacts, filesystem, launch, platform, process, sandbox, store and supervisor. The rest is acyclic. A reference is a crate::, super:: or self:: path in a use tree or an expression, or one of the crate-root re-exports crate::Artifacts, crate::guest_path and crate::resolve_guest_path (nvx-rs/src/lib.rs:138-141), outside comments and cfg(test) code. nvx-rs: make the host module's dependency graph acyclic #897 lists the three back-edges.

  • The largest files, in production lines, without their inline tests:

    File Production lines Inline test lines
    filesystem.rs 2,041 1,685
    platform/windows.rs 1,468 358
    sandbox/mount.rs 1,190 0
    instance.rs 1,036 212
    cli/sandbox/lifecycle.rs 996 118
    store/mod.rs 987 0
    cli/sandbox/mod.rs 941 0
    network/mod.rs 928 988
    artifacts.rs 919 727
    sandbox/plan.rs 853 0

PRs, in order:

Issue Delivers Needs
#897 An acyclic graph between host's children: three items move down, and a guard test keeps it acyclic Nothing
#898 The oversized files, split into directory modules along their sections #897: the artifacts split includes the helpers that #897 moves there, and its guard protects the layering
#899 Host's children, grouped into private, layered subsystems that re-export them at today's paths #897, preferably #898, and a decision whether its test-name changes are acceptable

Invariants. Every phase keeps them. Together they are the contract of "no behavioral change":

  1. The public API is identical. The crate is published (nvx-rs/Cargo.toml:12-14: publish = ["crates-io"], links = "nvx"), so every public path under nvx:: keeps resolving, through pub use re-exports where items move. Downstream code imports from every public child but seal: aci_edge_sandboxes/ (which also re-exports the crate as aci_edge_sandboxes::nvx, aci_edge_sandboxes/src/lib.rs:62), tests/runtime-adapter/, nvx-rs/tests/ and nvx-rs/src/bin/ use nvx::host::{artifacts, cli::main, filesystem, instance, launch, network::policy, nvxs, platform::{SUPPORTED, Transport, process_start_time}, process, report, sandbox::{distro, mount, scratch}, session, store, supervisor::{Inherited, Launched, VmmCommand, spawn}} and nvx::{Artifacts, guest_path, resolve_guest_path}. Check: a diff of the public surface from rustdoc JSON (RUSTC_BOOTSTRAP=1 cargo rustdoc --lib --all-features -- -Z unstable-options --output-format json), walking reachable paths, re-exports, inherent methods and trait impls, for x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc, the targets that docs.rs documents (Cargo.toml:18-20), with zero differences.
  2. Test names are stable, unless a phase says otherwise. Two references select tests by name: FAMILIES in scripts/nvx_rs_coverage.py:80-82 selects the Python golden tests by host::python::golden:: (also in doc/ci.md:1694-1696), and the artifact-bundling step of .github/actions/check-nvx-rs/action.yml:73-98 runs cargo test --locked --lib --features bundled artifacts, a name filter. The inputs of .github/workflows/nvx-rs-mutants.yml:12-24 give src/host/cli/sandbox/state.rs and host::sandbox:: as examples.
  3. Host code stays under nvx-rs/src/host/. SOURCE_PATTERN (scripts/nvx_tools/build_constants.py:214-230) treats every other file under nvx-rs/src/, except testing/, the binaries other than the guest's, and test modules, as an input of the guest executables, so host code elsewhere would change the guest source identity.
  4. Unsafe code stays confined. host/mod.rs:46 denies it. Only sys (Unix, mod.rs:65-67), platform::windows (platform/mod.rs:22-25), seal::windows (seal.rs:32-34) and python::windows (python/windows.rs:7) allow it, besides four test modules (cli/session/tests.rs:988, sandbox/distro/tests.rs:138, sandbox/scratch/tests.rs:133, store/lifecycle/tests.rs:840).
  5. The cfg matrix builds: Linux and Windows, on which the nvx-rs job of .github/workflows/ci.yml:78-94 runs check-nvx-rs; other Unix hosts, through platform::other, which CI checks for macOS (aarch64-apple-darwin, action.yml:118-129); and the features host, cli, testing, guest, bundled and serde, and --no-default-features (action.yml:42-69).
  6. Intra-doc links keep their targets. A link written with super:: can retarget silently when its file moves: [launch](super::launch) in supervisor/child.rs:166 names the function supervisor::launch, but would name the module host::launch from a file in host/, and cargo doc with -D warnings (action.yml:100-106) would not notice. Moved links use crate:: paths.
  7. Nothing depends on source locations at run time. Host uses no module_path!, file!, include_str! or include_bytes!, and its test fixtures resolve from CARGO_MANIFEST_DIR (artifacts.rs:1030 and 1130, python/golden.rs:17). Moving code changes test names and source paths, not behavior.

Not in scope:

  • Promoting host modules to the crate root, or into crates of their own: the guest source identity (invariant 3), the published crate's links = "nvx", and the crate-private python and sys, which would need public exposure.
  • Merging the private-file helpers that instance.rs:84-205 and store/files.rs duplicate (io_error, private_options, write_private, remove_if_present, …). Those of instance include public API, those of the store are store-private, and their semantics may differ.

Coordination. Open PRs edit the same files:

Each phase keeps re-exports at the old paths, so that these PRs rebase without API breaks, and #898 splits the files that they edit after they land.

Acceptance. #897 and #898 are merged, #899 is merged or declined, and every invariant above holds at each merge.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions