cargo.build: a persistent build cache shared by all worlds
- The cargo target folder is <cache>/cargo-target, next to Wasmtime's cache, which moves to <cache>/wasmtime: Wasmtime's cleanup worker deletes unknown entries in its folder, also Cargo's artifacts. - Builds use the SDK at <cache>/sdk/<content hash>/, so cargo reuses the compiled SDK in every world. /plugins/cargo/sdk/ stays for agents. - Tests run in the release profile and share compiled crates with the release build. - The cargo and cli tests use a persistent cache in CARGO_TARGET_TMPDIR: warm, the cargo suite takes 11 s (was 235 s) and the cli suite 6 s (was 149 s). - nix flake check runs ignored stress tests. AGENTS.md: rules for fast tests.
This commit is contained in:
+34
-8
@@ -3,6 +3,7 @@ use sausage::{Host, world::World};
|
||||
use serde_json::{Value, json};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::sync::OnceCell;
|
||||
const CACHE: &str = concat!(env!("CARGO_TARGET_TMPDIR"), "/cache");
|
||||
struct TestWorld(PathBuf);
|
||||
impl TestWorld {
|
||||
/// Creates a fresh native build world.
|
||||
@@ -45,10 +46,12 @@ fn fixture(name: &str) -> PathBuf {
|
||||
}
|
||||
/// Builds through the real registered native plugin.
|
||||
async fn build(world: &TestWorld, name: &str) -> Result<Value, Value> {
|
||||
let host = Host::start(&world.0).await.unwrap();
|
||||
let host = Host::start_with_cache(&world.0, Path::new(CACHE))
|
||||
.await
|
||||
.unwrap();
|
||||
let runner = world.0.join("state/runner with spaces");
|
||||
std::os::unix::fs::symlink(env!("CARGO_BIN_EXE_sausage"), &runner).unwrap();
|
||||
sausage::cargo::Cargo::with_runner(World::open(&world.0).unwrap(), runner)
|
||||
sausage::cargo::Cargo::with_runner(World::open(&world.0).unwrap(), runner, Path::new(CACHE))
|
||||
.unwrap()
|
||||
.register(&host);
|
||||
let result = host.call("cargo.build", json!({"plugin":name})).await;
|
||||
@@ -93,6 +96,7 @@ async fn valid() -> &'static Valid {
|
||||
.args([
|
||||
"test",
|
||||
"--no-run",
|
||||
"--release",
|
||||
"--message-format=json",
|
||||
"--bin",
|
||||
"cargo-valid",
|
||||
@@ -100,7 +104,7 @@ async fn valid() -> &'static Valid {
|
||||
"wasm32-wasip2",
|
||||
"--target-dir",
|
||||
])
|
||||
.arg(world.0.join("state/cargo/target"))
|
||||
.arg(Path::new(CACHE).join("cargo-target"))
|
||||
.current_dir(world.0.join("state/cargo/work"))
|
||||
.stdin(std::process::Stdio::null())
|
||||
.output()
|
||||
@@ -221,7 +225,16 @@ async fn manifest_and_nested_sources() {
|
||||
let manifest = std::fs::read_to_string(root.join("Cargo.toml")).unwrap();
|
||||
assert!(manifest.contains("workspace.package"));
|
||||
assert!(manifest.contains("optional = true"));
|
||||
assert!(manifest.contains("/plugins/cargo/sdk"));
|
||||
let document: toml_edit::DocumentMut = manifest.parse().unwrap();
|
||||
let sdk = Path::new(document["target"]["cfg(target_arch = \"wasm32\")"]["dependencies"]["sausage_plugin"]["path"].as_str().unwrap());
|
||||
assert_eq!(sdk.parent().unwrap(), Path::new(CACHE).join("sdk"));
|
||||
let hash = sdk.file_name().unwrap().to_str().unwrap();
|
||||
assert_eq!(hash.len(), 64);
|
||||
assert!(hash.bytes().all(|byte| byte.is_ascii_hexdigit()));
|
||||
assert_eq!(
|
||||
std::fs::read(sdk.join("src/lib.rs")).unwrap(),
|
||||
std::fs::read(valid.world.0.join("plugins/cargo/sdk/src/lib.rs")).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(root.join("src/target/data")).unwrap(),
|
||||
"nested target"
|
||||
@@ -235,7 +248,9 @@ async fn manifest_and_nested_sources() {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn native_discovery_and_error_paths() {
|
||||
let world = TestWorld::new();
|
||||
let host = Host::start(&world.0).await.unwrap();
|
||||
let host = Host::start_with_cache(&world.0, Path::new(CACHE))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
host.call("cargo.rpc.discover", json!({})).await.unwrap()["methods"][0]["name"],
|
||||
"build"
|
||||
@@ -264,7 +279,7 @@ async fn release_only_failure() {
|
||||
"[package]\nname='release-only'\nversion='0.1.0'\nedition='2024'\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(root.join("main.rs"), "#[cfg(not(debug_assertions))] compile_error!(\"release-only error\"); fn main() {} #[test] fn passes() {}\n").unwrap();
|
||||
std::fs::write(root.join("main.rs"), "#[cfg(not(test))] compile_error!(\"release-only error\"); fn main() {} #[test] fn passes() {}\n").unwrap();
|
||||
let error = build(&world, "release").await.unwrap_err();
|
||||
assert!(
|
||||
error["data"]["output"]
|
||||
@@ -296,6 +311,7 @@ async fn cancelled_preparation_is_serialized() {
|
||||
sausage::cargo::Cargo::with_runner(
|
||||
World::open(&world.0).unwrap(),
|
||||
PathBuf::from(env!("CARGO_BIN_EXE_sausage")),
|
||||
Path::new(CACHE),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
@@ -358,6 +374,12 @@ async fn valid_build() {
|
||||
assert_eq!(valid.result["tests"]["passed"], 12);
|
||||
assert_eq!(valid.result["tests"]["failed"], 0);
|
||||
assert!(!valid.world.0.join("plugins/valid/plugin.wasm").exists());
|
||||
assert!(!valid.world.0.join("state/cargo/target").exists());
|
||||
assert!(
|
||||
Path::new(CACHE)
|
||||
.join("cargo-target/wasm32-wasip2/release")
|
||||
.is_dir()
|
||||
);
|
||||
}
|
||||
/// PROTOCOL 11: tests and compilation share one source copy without target metadata.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -373,7 +395,7 @@ async fn source_copy() {
|
||||
assert!(
|
||||
std::fs::read_to_string(valid.world.0.join("state/cargo/work/Cargo.toml"))
|
||||
.unwrap()
|
||||
.contains("/plugins/cargo/sdk")
|
||||
.contains(Path::new(CACHE).join("sdk").to_str().unwrap())
|
||||
);
|
||||
}
|
||||
/// PROTOCOL 11: compiler failures return compiler diagnostics.
|
||||
@@ -514,6 +536,7 @@ async fn unchanged_sdk() {
|
||||
sausage::cargo::Cargo::with_runner(
|
||||
World::open(&world.0).unwrap(),
|
||||
PathBuf::from(env!("CARGO_BIN_EXE_sausage")),
|
||||
Path::new(CACHE),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
@@ -534,6 +557,7 @@ async fn standalone_builder() {
|
||||
let builder = sausage::cargo::Cargo::with_runner(
|
||||
World::open(&world.0).unwrap(),
|
||||
PathBuf::from(env!("CARGO_BIN_EXE_sausage")),
|
||||
Path::new(CACHE),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -555,7 +579,9 @@ async fn kill_process_group() {
|
||||
);
|
||||
let world = TestWorld::new();
|
||||
world.source("hang", &fixture("cargo-hang"));
|
||||
let host = Host::start(&world.0).await.unwrap();
|
||||
let host = Host::start_with_cache(&world.0, Path::new(CACHE))
|
||||
.await
|
||||
.unwrap();
|
||||
let building = host.clone();
|
||||
let task =
|
||||
tokio::spawn(async move { building.call("cargo.build", json!({"plugin":"hang"})).await });
|
||||
|
||||
Reference in New Issue
Block a user