feat(build): archive feature, progress UI, and workspace path fixes

This commit is contained in:
2026-07-23 13:56:36 +08:00
parent a9dd8c1631
commit 588e4dd13f
27 changed files with 597 additions and 276 deletions

View File

@@ -77,8 +77,15 @@ fn build_run_clean_flags_normal_project() {
assert!(out.status.success(), "dcr build --release should succeed");
let out = run_dcr_env(&["run"], &dir, &envs);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.contains("Running"), "dcr run should start");
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
combined.contains("run") || combined.contains("Running"),
"dcr run should start"
);
let out = run_dcr_env(&["clean", "--release"], &dir, &envs);
assert!(out.status.success(), "dcr clean --release should succeed");
@@ -170,3 +177,92 @@ fn build_with_out_dir() {
"artifact should NOT be at default path when out_dir is set"
);
}
#[test]
fn flat_bin_nasm_build() {
let nasm_ok = std::process::Command::new("nasm")
.arg("-v")
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if !nasm_ok {
eprintln!("nasm not found; skipping flat-bin test");
return;
}
let dir = unique_sandbox_dir("flat_bin");
let out = run_dcr(&["init"], &dir);
assert!(out.status.success(), "dcr init should succeed");
std::fs::write(
dir.join("dcr.toml"),
r#"[package]
name = "flatdemo"
version = "0.1.0"
[build]
language = "asm"
compiler = "nasm"
kind = "flat-bin"
extension = "bin"
roots = ["src"]
"#,
)
.expect("write dcr.toml");
std::fs::create_dir_all(dir.join("src")).expect("src");
std::fs::write(
dir.join("src/boot.asm"),
"bits 16\norg 0x7c00\ncli\nhlt\ntimes 510-($-$$) db 0\ndw 0xaa55\n",
)
.expect("write boot.asm");
let out = run_dcr(&["build"], &dir);
if !out.status.success() {
eprintln!("stdout: {}", String::from_utf8_lossy(&out.stdout));
eprintln!("stderr: {}", String::from_utf8_lossy(&out.stderr));
}
assert!(out.status.success(), "flat-bin nasm build should succeed");
let candidates = [
dir.join("target/debug/boot.bin"),
dir.join("target").join(format!(
"{}-unknown-linux-gnu/debug/boot.bin",
std::env::consts::ARCH
)),
];
assert!(
candidates.iter().any(|p| p.is_file()),
"expected boot.bin under target/…/debug, candidates: {candidates:?}"
);
}
#[test]
fn package_build_target_used_without_cli_flag() {
let Some(compiler) = available_compiler() else {
eprintln!("no compiler found; skipping package target test");
return;
};
let dir = unique_sandbox_dir("pkg_target");
let out = run_dcr(&["init"], &dir);
assert!(out.status.success());
let toml_path = dir.join("dcr.toml");
let toml = std::fs::read_to_string(&toml_path).unwrap();
let project_name = parse_project_name(&toml);
let updated = toml.replace("[build]", "[build]\ntarget = \"x86_64-unknown-linux-gnu\"");
std::fs::write(&toml_path, updated).unwrap();
let envs = [("DCR_COMPILER", compiler)];
let out = run_dcr_env(&["build"], &dir, &envs);
assert!(out.status.success(), "build should use package target");
let artifact = dir
.join("target/x86_64-unknown-linux-gnu/debug")
.join(&project_name);
assert!(
artifact.is_file(),
"artifact should be under package build.target path"
);
}

View File

@@ -92,9 +92,17 @@ fn no_args_shows_help() {
fn unknown_command_fails() {
let dir = unique_sandbox_dir("unknown_cmd");
let out = run_dcr(&["foobar"], &dir);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("error") || stdout.contains("Unknown"),
!out.status.success(),
"unknown command should exit non-zero"
);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
combined.contains("error") || combined.contains("Unknown"),
"unknown command should print error"
);
}
@@ -104,9 +112,13 @@ fn build_without_toml_fails() {
let dir = unique_sandbox_dir("build_no_toml");
let out = run_dcr(&["build"], &dir);
assert!(!out.status.success(), "build without dcr.toml should fail");
let stdout = String::from_utf8_lossy(&out.stdout);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
stdout.contains("dcr.toml") || stdout.contains("not found"),
combined.contains("dcr.toml") || combined.contains("not found"),
"should mention missing dcr.toml"
);
}
@@ -132,9 +144,13 @@ fn new_existing_dir_fails() {
std::fs::create_dir_all(dir.join("hello")).expect("failed to create dir");
let out = run_dcr(&["new", "hello"], &dir);
assert!(!out.status.success(), "dcr new on existing dir should fail");
let stdout = String::from_utf8_lossy(&out.stdout);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
stdout.contains("already exists") || stdout.contains("error"),
combined.contains("already exists") || combined.contains("error"),
"should report dir already exists"
);
}
@@ -149,9 +165,13 @@ fn init_nonempty_dir_fails() {
!out.status.success(),
"dcr init in non-empty dir should fail"
);
let stdout = String::from_utf8_lossy(&out.stdout);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
stdout.contains("not empty") || stdout.contains("error"),
combined.contains("not empty") || combined.contains("error"),
"should report dir not empty"
);
}
@@ -186,9 +206,13 @@ fn run_library_project_fails() {
let envs = [("DCR_COMPILER", compiler)];
let out = run_dcr_env(&["run"], &dir, &envs);
assert!(!out.status.success(), "dcr run on staticlib should fail");
let stdout = String::from_utf8_lossy(&out.stdout);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
stdout.contains("library") || stdout.contains("Cannot run"),
combined.contains("library") || combined.contains("Cannot run"),
"should report cannot run library"
);
}
@@ -226,9 +250,13 @@ fn new_no_name_fails() {
let dir = unique_sandbox_dir("new_noname");
let out = run_dcr(&["new"], &dir);
assert!(!out.status.success(), "dcr new without name should fail");
let stdout = String::from_utf8_lossy(&out.stdout);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
stdout.contains("not specified") || stdout.contains("error"),
combined.contains("not specified") || combined.contains("error"),
"should report name not specified"
);
}

View File

@@ -45,6 +45,16 @@ fn workspace_build_and_clean_all() {
out.status.success(),
"workspace clean --all --release should succeed"
);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
let spam = combined.matches("Directory target not found").count();
assert!(
spam <= 1,
"clean --all should not spam missing member target/: {combined}"
);
let target_dir = "target/x86_64-unknown-linux-gnu";
assert!(
@@ -55,4 +65,8 @@ fn workspace_build_and_clean_all() {
root.join(target_dir).join("debug").exists(),
"root target/x86_64-unknown-linux-gnu/debug should remain"
);
let out = run_dcr_env(&["clean", "--all"], &root, &envs);
assert!(out.status.success(), "workspace clean --all should succeed");
assert!(!root.join("target").exists(), "root target/ should be gone");
}