- `build.kind = "efi"` — UEFI PE32+ executable support. Links with `-shared -nostdlib -Wl,-dll -Wl,--subsystem,10`, output via `efi_path()` with `.efi` extension, rejected by `dcr run`.
- `build.kind = "elf"` — bare-metal ELF executable support. Uses `elf_path()` output path, rejected by `dcr run`, no extra linker flags.

Fixed:

- `dcr run` with `--target` or `build.target` now correctly finds the binary in the arch-specific target directory.
- `dcr run` checks profile-specific `build.{profile}.kind` before rejecting library builds (previously only checked `build.kind`).
- `dcr clean` now reads `build.target` from `dcr.toml` when no `--target` flag is given.
- Linux: target directory is now consistent — `target/<arch>-unknown-linux-gnu/<profile>/` (no stale `target/<profile>/`).
- `dcr.lock` is now populated with all resolved dependencies (registry, path, git) instead of being always empty.
- Git dependencies are now properly recognized and recorded in `dcr.lock`.
- `is_registry_dep` correctly identifies version-based registry dependency tables (`{ version = "..." }`) and rejects tables with unknown keys.
- `dcr test` accepts `--debug`/`--release` flags (defaults to `debug` profile instead of always `release`).
- Build fingerprint cache (`build_cache_path`) now includes `target_dir` — different targets no longer share a single cache entry.
- `object_path` no longer hardcodes the `src/` prefix — works correctly with custom `build.roots` (e.g. `roots = ["lib"]`).
- `dcr update` now generates `.exe` candidates for all Windows targets, not just `x86_64-pc-windows-msvc`.
- Compiler existence is verified before starting the build (clear error if compiler is not found in PATH).
- Consolidated duplicate `OUTPUT_MUTEX` definitions — MSVC backend now uses the shared mutex from `common.rs`.
- Registry cache root directory is created if missing (prevents cryptic errors when `~/.dcr/` does not exist).
This commit is contained in:
2026-05-26 16:38:23 +08:00
parent adfa24f8f4
commit 7969209eae
26 changed files with 368 additions and 85 deletions

View File

@@ -5,6 +5,10 @@ use std::sync::{Mutex, OnceLock};
static OUTPUT_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
pub fn get_output_lock() -> &'static Mutex<()> {
OUTPUT_MUTEX.get_or_init(|| Mutex::new(()))
}
pub fn collect_sources(
roots: &[PathBuf],
extensions: &[&str],
@@ -230,10 +234,16 @@ pub fn normalize_source_path(path: &Path) -> String {
pub fn object_path(obj_dir: &Path, source: &str, obj_ext: &str) -> String {
let src_path = Path::new(source);
let rel = src_path
.strip_prefix("./src")
.or_else(|_| src_path.strip_prefix("src"))
.unwrap_or(src_path);
let stripped = src_path.strip_prefix("./").unwrap_or(src_path);
let rel = stripped
.components()
.skip(1)
.collect::<std::path::PathBuf>();
let rel = if rel.components().next().is_some() {
rel
} else {
stripped.to_path_buf()
};
let mut out = obj_dir.join(rel);
out.set_extension(obj_ext.trim_start_matches('.'));
out.to_string_lossy().to_string()

View File

@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
};
let start_time = Instant::now();
let sources = collect_sources(ctx)?;
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
let obj_dir = match ctx.target_dir {
Some(dir) => Path::new(dir).join("obj"),
None => Path::new("./target").join(ctx.profile).join("obj"),
};
let objects = build_objects(assembler, &sources, &obj_dir, ctx, "o")?;
if ctx.kind == "staticlib" {
@@ -80,6 +83,8 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
let out_path = if ctx.kind == "sharedlib" {
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
} else if ctx.kind == "elf" {
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
} else {
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
};

View File

@@ -33,6 +33,12 @@ pub struct BuildContext<'a> {
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
let compiler = ctx.compiler.to_lowercase();
if !check_compiler_exists(ctx.compiler) {
return Err(format!(
"Compiler not found: {}. Make sure it is installed and available in PATH.",
ctx.compiler
));
}
if compiler.contains("clang-cl") {
return msvc::build(ctx);
}
@@ -48,6 +54,14 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
unix_cc::build(ctx)
}
fn check_compiler_exists(compiler: &str) -> bool {
let name = if compiler.is_empty() { "cc" } else { compiler };
std::process::Command::new(name)
.arg("--version")
.output()
.is_ok()
}
pub fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
let compiler = ctx.compiler.to_lowercase();
if compiler.contains("clang-cl") || compiler == "cl" || compiler.contains("msvc") {

View File

@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
}
let start_time = Instant::now();
let sources = collect_sources(ctx)?;
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
let obj_dir = match ctx.target_dir {
Some(dir) => Path::new(dir).join("obj"),
None => Path::new("./target").join(ctx.profile).join("obj"),
};
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "obj")?;
if ctx.kind == "staticlib" {
@@ -92,6 +95,8 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
}
let out_path = if ctx.kind == "sharedlib" {
platform::shared_lib_path(ctx.profile, ctx.project_name, ctx.target_dir)
} else if ctx.kind == "elf" {
platform::elf_path(ctx.profile, ctx.project_name, ctx.target_dir)
} else {
platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
};
@@ -185,8 +190,6 @@ fn default_flags(profile: &str) -> &'static [&'static str] {
}
}
static OUTPUT_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn build_objects(
compiler: &str,
sources: &[String],
@@ -282,7 +285,7 @@ fn build_object(
}
}
let _lock = OUTPUT_MUTEX.lock().unwrap();
let _lock = common::get_output_lock().lock().unwrap();
if !output.status.success() {
eprint!("{}", clean_stdout);

View File

@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
};
let start_time = Instant::now();
let sources = collect_sources(ctx)?;
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
let obj_dir = match ctx.target_dir {
Some(dir) => Path::new(dir).join("obj"),
None => Path::new("./target").join(ctx.profile).join("obj"),
};
let objects = build_objects(assembler, &sources, &obj_dir, ctx, "o")?;
if ctx.kind == "staticlib" {
@@ -80,6 +83,8 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
let out_path = if ctx.kind == "sharedlib" {
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
} else if ctx.kind == "elf" {
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
} else {
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
};

View File

@@ -20,7 +20,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
ctx.exclude_dirs,
ctx.include_paths,
)?;
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
let obj_dir = match ctx.target_dir {
Some(dir) => Path::new(dir).join("obj"),
None => Path::new("./target").join(ctx.profile).join("obj"),
};
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "o")?;
if ctx.kind == "staticlib" {
@@ -51,6 +54,12 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
cmd.arg("-shared");
}
}
if ctx.kind == "efi" {
cmd.arg("-shared");
cmd.arg("-nostdlib");
cmd.arg("-Wl,-dll");
cmd.arg("-Wl,--subsystem,10");
}
for obj in &objects {
cmd.arg(obj);
}
@@ -73,6 +82,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
let out_path = if ctx.kind == "sharedlib" {
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
} else if ctx.kind == "efi" {
platform::efi_path(ctx.profile, &final_name, ctx.target_dir)
} else if ctx.kind == "elf" {
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
} else {
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
};