- `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

@@ -355,9 +355,15 @@ fn ensure_target_dirs(items: &[String], profile: &str, target_dir: Option<String
if let Some(dir) = &target_dir {
let _ = fs::create_dir_all(dir);
} else {
let default_dir = if cfg!(target_os = "linux") {
let arch = std::env::consts::ARCH;
format!("{arch}-unknown-linux-gnu/{profile}")
} else {
profile.to_string()
};
let target_items = check_dir(Some("target")).unwrap_or_default();
if !target_items.contains(&profile.to_string()) {
let _ = fs::create_dir(format!("./target/{profile}"));
if !target_items.contains(&default_dir) {
let _ = fs::create_dir_all(format!("./target/{default_dir}"));
}
}
}
@@ -1317,8 +1323,10 @@ fn write_build_fingerprint(ctx: &BuildContext, fingerprint: &str) -> Result<(),
}
fn build_cache_path(profile: &str, target_dir: Option<&str>) -> std::path::PathBuf {
let _ = target_dir;
Path::new("./target").join(profile).join(".dcr-build.hash")
match target_dir {
Some(dir) => Path::new(dir).join(".dcr-build.hash"),
None => Path::new("./target").join(profile).join(".dcr-build.hash"),
}
}
fn build_output_path(ctx: &BuildContext) -> String {
@@ -1332,12 +1340,17 @@ fn build_output_path(ctx: &BuildContext) -> String {
};
if ctx.kind == "staticlib" {
// Для библиотек добавляем lib префикс если нужно, но упростим
return crate::platform::lib_path(ctx.profile, &final_name, ctx.target_dir);
}
if ctx.kind == "sharedlib" {
return crate::platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir);
}
if ctx.kind == "efi" {
return crate::platform::efi_path(ctx.profile, &final_name, ctx.target_dir);
}
if ctx.kind == "elf" {
return crate::platform::elf_path(ctx.profile, &final_name, ctx.target_dir);
}
crate::platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
}

View File

@@ -91,17 +91,27 @@ fn parse_clean_flags(args: &[String]) -> Result<CleanFlags, String> {
fn clean_from_root(root: &Path, flags: &CleanFlags) -> Result<(), String> {
let config = Config::open("./dcr.toml").map_err(|err| err.to_string())?;
let target = flags.target.clone().or_else(|| {
Some(if cfg!(target_os = "linux") {
"x86_64-unknown-linux-gnu".to_string()
} else if cfg!(target_os = "macos") {
"x86_64-apple-darwin".to_string()
} else if cfg!(target_os = "windows") {
"x86_64-pc-windows-msvc".to_string()
} else {
"unknown".to_string()
let target = flags
.target
.clone()
.or_else(|| {
config
.get("build.target")
.and_then(|v| v.as_str())
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
})
});
.or_else(|| {
Some(if cfg!(target_os = "linux") {
"x86_64-unknown-linux-gnu".to_string()
} else if cfg!(target_os = "macos") {
"x86_64-apple-darwin".to_string()
} else if cfg!(target_os = "windows") {
"x86_64-pc-windows-msvc".to_string()
} else {
"unknown".to_string()
})
});
if flags.all
&& let Some(workspace) = parse_workspace(

View File

@@ -138,8 +138,8 @@ fn download_asset(client: &Client, url: &str) -> Result<Vec<u8>, String> {
fn asset_candidates(target: &str) -> Vec<String> {
let mut names = vec![format!("dcr-{target}")];
if target == "x86_64-pc-windows-msvc" {
names.push("dcr-x86_64-pc-windows-msvc.exe".to_string());
if target.contains("-windows-") || target.ends_with("-windows") {
names.push(format!("dcr-{target}.exe"));
}
names

View File

@@ -69,15 +69,6 @@ pub fn run(args: &[String]) -> i32 {
.get("package.name")
.and_then(|v| v.as_str())
.unwrap_or("");
let build_kind = config
.get("build.kind")
.and_then(|v| v.as_str())
.unwrap_or("");
let target_dir = config
.get("build.target")
.and_then(|v| v.as_str())
.map(|s| s.trim())
.filter(|s| !s.is_empty());
let mut flags = match parse_build_run_flags(args) {
Ok(v) => v,
@@ -98,6 +89,12 @@ pub fn run(args: &[String]) -> i32 {
flags.target = Some(default_target.to_string());
}
let build_kind = config
.get(&format!("build.{}.kind", flags.profile))
.and_then(|v| v.as_str())
.or_else(|| config.get("build.kind").and_then(|v| v.as_str()))
.unwrap_or("");
let normalized_target_dir = flags
.target
.as_ref()
@@ -110,7 +107,9 @@ pub fn run(args: &[String]) -> i32 {
let run_cmd = get_run_cmd(&config, &flags.profile, flags.target.as_deref(), version);
let kind = build_kind.trim();
if run_cmd.is_none() && (kind == "staticlib" || kind == "sharedlib") {
if run_cmd.is_none()
&& (kind == "staticlib" || kind == "sharedlib" || kind == "efi" || kind == "elf")
{
error("Cannot run library build");
return 1;
}
@@ -128,13 +127,21 @@ pub fn run(args: &[String]) -> i32 {
}
println!("\n {} {}", colored("Running", BOLD_GREEN), bin_path);
println!("--------------------------------");
return run_binary(project_name, &flags.profile, target_dir);
return run_binary(
project_name,
&flags.profile,
normalized_target_dir.as_deref(),
);
}
let fallback_code = if let Some(cmd) = run_cmd {
run_shell(&cmd)
} else {
run_binary(project_name, &flags.profile, target_dir)
run_binary(
project_name,
&flags.profile,
normalized_target_dir.as_deref(),
)
};
if fallback_code != 1 {
return fallback_code;

View File

@@ -16,10 +16,11 @@ const BOLD_BLUE: &str = "\x1b[1m\x1b[94m";
pub fn test(args: &[String]) -> i32 {
let mut init_header = false;
let mut profile = "debug";
for arg in args {
if arg == "--help" {
println!("USAGE:");
println!(" dcr test [--init]");
println!(" dcr test [--init] [--debug | --release]");
println!();
println!("ALIASES:");
println!(" dcr tests");
@@ -29,12 +30,18 @@ pub fn test(args: &[String]) -> i32 {
println!();
println!("OPTIONS:");
println!(" --init Create tests/dcr_test.h in current project");
println!(" --debug Build and run tests with debug profile (default)");
println!(" --release Build and run tests with release profile");
return 0;
}
if arg == "--init" {
init_header = true;
continue;
}
if arg == "--debug" || arg == "--release" {
profile = arg.trim_start_matches("--");
continue;
}
error("Unknown argument");
return 1;
}
@@ -68,7 +75,7 @@ pub fn test(args: &[String]) -> i32 {
}
}
match with_dir(&root, run_testsuite) {
match with_dir(&root, || run_testsuite(profile)) {
Ok(code) => code,
Err(msg) => {
error(&msg);
@@ -77,13 +84,12 @@ pub fn test(args: &[String]) -> i32 {
}
}
fn run_testsuite() -> Result<i32, String> {
if build::build(&[String::from("--release")]) != 0 {
fn run_testsuite(profile: &str) -> Result<i32, String> {
if build::build(&[format!("--{profile}")]) != 0 {
return Ok(1);
}
let config = Config::open("./dcr.toml").map_err(|_| "Failed to read dcr.toml".to_string())?;
let profile = "release";
let name = get_config_str(&config, "package.name");
let language = get_language_with_profile(&config, profile)?;
let compiler_cfg = get_string_with_profile(&config, "compiler", profile);