Support Cargo scaffolding and human output, and find boolean expressions
This commit is contained in:
@@ -7,7 +7,11 @@ The response contains newline-delimited JSON events.
|
||||
The final `exit` event contains a code and component locations.
|
||||
Each location contains an executable path and working directory.
|
||||
|
||||
The CLI forwards the bytes immediately.
|
||||
The CLI forwards human output immediately.
|
||||
The builder captures compiler JSON for artifact discovery without forwarding it to stdout.
|
||||
Compiler diagnostics remain on stderr. `cargo run` reserves stdout for the program.
|
||||
`cargo new` and `cargo init` use native Cargo, including `--lib`, `--name`, and `--edition`.
|
||||
They need no existing manifest. Subsequent builds use the world's default WASI target.
|
||||
For `test` and `run`, it executes the artifacts through the process interface.
|
||||
Cargo selects run binaries through a capture runner, so `--bin` and `default-run` retain their normal behavior.
|
||||
The test runner uses each package's root directory, even when the caller runs Cargo from a subdirectory.
|
||||
|
||||
+21
-9
@@ -66,7 +66,7 @@ impl Builder {
|
||||
ensure!(
|
||||
[
|
||||
"build", "check", "clippy", "test", "run", "fmt", "fetch", "add", "remove",
|
||||
"update", "tree", "metadata", "clean"
|
||||
"update", "tree", "metadata", "clean", "new", "init"
|
||||
]
|
||||
.contains(&action),
|
||||
"unsupported Cargo command"
|
||||
@@ -96,7 +96,14 @@ impl Builder {
|
||||
.checked_add(Duration::from_secs(seconds))
|
||||
.context("timeout too large")?;
|
||||
let world_cwd = sausage_vfs::resolve("/", &request.cwd);
|
||||
let workspace = files::workspace(self.root.as_ref(), &world_cwd).await?;
|
||||
let scaffold = matches!(action, "new" | "init");
|
||||
let workspace = if scaffold {
|
||||
files::workspace(self.root.as_ref(), &world_cwd)
|
||||
.await
|
||||
.unwrap_or_else(|_| world_cwd.clone())
|
||||
} else {
|
||||
files::workspace(self.root.as_ref(), &world_cwd).await?
|
||||
};
|
||||
let world_target = sausage_vfs::resolve(&workspace, "target");
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut hash = std::collections::hash_map::DefaultHasher::new();
|
||||
@@ -140,10 +147,10 @@ impl Builder {
|
||||
fetch.arg("fetch");
|
||||
}
|
||||
fetch.current_dir(&cwd).env("CARGO_HOME", ®istry);
|
||||
let mut code = if matches!(action, "clean" | "fmt" | "remove") {
|
||||
let mut code = if scaffold || matches!(action, "clean" | "fmt" | "remove") {
|
||||
0
|
||||
} else {
|
||||
run(fetch, tx, deadline).await?.0
|
||||
run(fetch, tx, deadline, false).await?.0
|
||||
};
|
||||
if code == 0 && action == "add" {
|
||||
let mut fetch = Command::new("cargo");
|
||||
@@ -151,7 +158,7 @@ impl Builder {
|
||||
.arg("fetch")
|
||||
.current_dir(&cwd)
|
||||
.env("CARGO_HOME", ®istry);
|
||||
code = run(fetch, tx, deadline).await?.0;
|
||||
code = run(fetch, tx, deadline, false).await?.0;
|
||||
}
|
||||
let mut executables = Vec::new();
|
||||
if code == 0 {
|
||||
@@ -191,7 +198,7 @@ impl Builder {
|
||||
)?;
|
||||
let mut startup = Command::new("nu");
|
||||
startup.arg(tools.join("build.nu"));
|
||||
let (startup_code, _) = run(startup, tx, deadline).await?;
|
||||
let (startup_code, _) = run(startup, tx, deadline, false).await?;
|
||||
if startup_code != 0 {
|
||||
files::copy_back(self.root.as_ref(), &source, &workspace, Some(&before)).await?;
|
||||
self.evict(20 * 1024 * 1024 * 1024)?;
|
||||
@@ -303,7 +310,7 @@ impl Builder {
|
||||
]);
|
||||
}
|
||||
cmd.args(&args[split..]);
|
||||
let (status, stdout) = run(cmd, tx, deadline).await?;
|
||||
let (status, stdout) = run(cmd, tx, deadline, compiles).await?;
|
||||
code = status;
|
||||
if action == "clean" && code == 0 {
|
||||
files::clean_artifacts(self.root.as_ref(), &target, &world_target).await?;
|
||||
@@ -439,6 +446,7 @@ async fn pump(
|
||||
mut reader: impl tokio::io::AsyncRead + Unpin,
|
||||
tx: &mpsc::Sender<Event>,
|
||||
stderr: bool,
|
||||
silent_stdout: bool,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut all = Vec::new();
|
||||
let mut bytes = vec![0; 8192];
|
||||
@@ -450,6 +458,9 @@ async fn pump(
|
||||
if !stderr {
|
||||
all.extend_from_slice(&bytes[..n]);
|
||||
}
|
||||
if !stderr && silent_stdout {
|
||||
continue;
|
||||
}
|
||||
tx.send(if stderr {
|
||||
Event::Stderr {
|
||||
bytes: bytes[..n].to_vec(),
|
||||
@@ -466,6 +477,7 @@ async fn run(
|
||||
mut cmd: Command,
|
||||
tx: &mpsc::Sender<Event>,
|
||||
deadline: Instant,
|
||||
silent_stdout: bool,
|
||||
) -> Result<(i32, Vec<u8>)> {
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
@@ -479,8 +491,8 @@ async fn run(
|
||||
let collect = async {
|
||||
let (status, out, _) = tokio::try_join!(
|
||||
async { Ok::<_, anyhow::Error>(child.wait().await?) },
|
||||
pump(out, tx, false),
|
||||
pump(err, tx, true)
|
||||
pump(out, tx, false, silent_stdout),
|
||||
pump(err, tx, true, false)
|
||||
)?;
|
||||
Ok::<_, anyhow::Error>((status.code().unwrap_or(1), out))
|
||||
};
|
||||
|
||||
@@ -191,10 +191,9 @@ async fn timeout_kills_before_output_can_drain() -> Result<()> {
|
||||
let output = tx.clone();
|
||||
let mut cmd = Command::new("sh");
|
||||
cmd.args(["-c", "echo $$; exec sleep 1000"]);
|
||||
let task =
|
||||
tokio::spawn(
|
||||
async move { run(cmd, &output, Instant::now() + Duration::from_secs(1)).await },
|
||||
);
|
||||
let task = tokio::spawn(async move {
|
||||
run(cmd, &output, Instant::now() + Duration::from_secs(1), false).await
|
||||
});
|
||||
let Some(Event::Stdout { bytes }) =
|
||||
tokio::time::timeout(Duration::from_secs(2), rx.recv()).await?
|
||||
else {
|
||||
@@ -219,8 +218,9 @@ async fn closed_receiver_cancels_command() -> Result<()> {
|
||||
let (tx, mut rx) = mpsc::channel(1);
|
||||
let mut cmd = Command::new("sh");
|
||||
cmd.args(["-c", "printf ready; exec sleep 1000"]);
|
||||
let task =
|
||||
tokio::spawn(async move { run(cmd, &tx, Instant::now() + Duration::from_secs(60)).await });
|
||||
let task = tokio::spawn(async move {
|
||||
run(cmd, &tx, Instant::now() + Duration::from_secs(60), false).await
|
||||
});
|
||||
// Output must arrive before the command exits, not after collection completes.
|
||||
let event = tokio::time::timeout(Duration::from_secs(2), rx.recv())
|
||||
.await?
|
||||
|
||||
Reference in New Issue
Block a user