mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 09:48:31 +00:00
Compare commits
3 Commits
v0.8.2
...
794d571d58
| Author | SHA1 | Date | |
|---|---|---|---|
| 794d571d58 | |||
| d2df85a064 | |||
| 5e8037da97 |
81
CHANGELOG.md
81
CHANGELOG.md
@@ -1,5 +1,51 @@
|
||||
# Changelog
|
||||
|
||||
## [0.8.3] - 2026-08-03 "Конфигурация host target, аргументы run и нативные пути артефактов / Host Target Config, Run Args & Native Artifact Paths"
|
||||
|
||||
### RU
|
||||
|
||||
**Добавлено:**
|
||||
|
||||
- **`dcr run -- <args>...`** — cargo-style: всё после bare `--` передаётся собранному бинарнику
|
||||
(`dcr run --release -- --test_help`). Работает и для прямого запуска, и для `[run].cmd`.
|
||||
- **Интеграционные / unit-тесты** — host target sections применяют `ldflags` без `--target`; проброс аргументов после `--`; парсер `split_double_dash`.
|
||||
|
||||
**Изменено:**
|
||||
|
||||
- **Native-сборка резолвит host triple** — без `--target` / без `build.target` конфиг-секции вида
|
||||
`[build.x86_64-unknown-linux-gnu]` (и аналоги host) снова применяются (`cflags` / `ldflags` / toolchain).
|
||||
Раньше target был «пустым», секции молча игнорировались, а артефакты уже лежали под host triple — линковка без `-L`/`-l`.
|
||||
- **Пустой target** — `normalize_target_os("")` больше не warn'ит `Unknown target ''`.
|
||||
- **`dcr run --help` / man `dcr-run`** — документирован разделитель `--` и forwarding args.
|
||||
|
||||
**Исправлено:**
|
||||
|
||||
- **Нативные пути артефактов на Windows и macOS** — без явного `--target` / `build.target` артефакты снова сохраняются в `target/<profile>`, поэтому `dcr run` и flat-bin находят результат сборки.
|
||||
- Target-specific `ldflags` на native Linux/macOS/Windows (host triple) не доходили до линкера.
|
||||
- Ложный warning на пустой target string.
|
||||
|
||||
### EN
|
||||
|
||||
**Added:**
|
||||
|
||||
- **`dcr run -- <args>...`** — cargo-style: arguments after a bare `--` are forwarded to the built binary
|
||||
(`dcr run --release -- --test_help`). Works for direct binary runs and `[run].cmd`.
|
||||
- **Tests** — host target sections apply `ldflags` without CLI `--target`; run-arg forwarding; `split_double_dash` unit tests.
|
||||
|
||||
**Changed:**
|
||||
|
||||
- **Native builds resolve the host triple** — without `--target` / without package `build.target`, host sections such as
|
||||
`[build.x86_64-unknown-linux-gnu]` (and host equivalents) apply again (`cflags` / `ldflags` / toolchain).
|
||||
Previously the target was empty, those sections were silently ignored while artifacts already used the host triple path — link failed without `-L`/`-l` (e.g. sandbox micro-lang + `libsct-elf`).
|
||||
- **Empty target** — `normalize_target_os("")` no longer warns `Unknown target ''`.
|
||||
- **`dcr run --help` / man `dcr-run`** — document `--` and argument forwarding.
|
||||
|
||||
**Fixed:**
|
||||
|
||||
- **Native artifact paths on Windows and macOS** — without an explicit `--target` / `build.target`, artifacts again use `target/<profile>`, so `dcr run` and flat-bin find the build output.
|
||||
- Target-specific `ldflags` on native host builds not reaching the linker.
|
||||
- Spurious warning for an empty target string.
|
||||
|
||||
## [0.8.2] - 2026-07-22 "CLI Progress, archive Feature, Workspace Path Fixes / CLI Progress, archive Feature, Workspace Path Fixes"
|
||||
|
||||
### RU
|
||||
@@ -215,25 +261,31 @@
|
||||
### RU
|
||||
|
||||
**Добавлено:**
|
||||
|
||||
- Учтён `target_env` в `default_target_triple()` — Linux (gnu/musl) и Windows (msvc/gnu) теперь используют правильное окружение, а не хардкод.
|
||||
|
||||
**Исправлено:**
|
||||
|
||||
- **Сборка на macOS Apple Silicon падала с `_main` undefined** — `--target=` теперь передаётся и в ldflags, не только в cflags. `default_target_triple()` на macOS использует `std::env::consts::ARCH` вместо хардкода `x86_64`.
|
||||
- **Бейджи GitHub Stars и GPL-3.0 не рендерились в README** — блок бейджей переведён на чистый HTML.
|
||||
|
||||
**Изменено:**
|
||||
|
||||
- Вычисление таргета по умолчанию вынесено в общую функцию `default_target_triple()` — устранено дублирование в build, run, clean.
|
||||
|
||||
### EN
|
||||
|
||||
**Added:**
|
||||
|
||||
- `default_target_triple()` now respects `target_env` — Linux (gnu/musl) and Windows (msvc/gnu) use the correct environment instead of hardcoded values.
|
||||
|
||||
**Fixed:**
|
||||
|
||||
- **macOS Apple Silicon builds failed with `_main` undefined** — `--target=` is now passed to ldflags, not just cflags. `default_target_triple()` uses `std::env::consts::ARCH` on macOS instead of hardcoded `x86_64`.
|
||||
- **GitHub Stars and GPL-3.0 badges not rendering in README** — badge block converted to pure HTML.
|
||||
|
||||
**Changed:**
|
||||
|
||||
- Default target resolution extracted into shared `default_target_triple()` — removed duplication across build, run, clean.
|
||||
|
||||
## [0.7.1] - 2026-06-02 "Мультиархитектурное расширение / The Multi-Arch Expansion"
|
||||
@@ -241,6 +293,7 @@
|
||||
### RU
|
||||
|
||||
**Добавлено:**
|
||||
|
||||
- Вывод `--help` для всех команд с цветным форматированием.
|
||||
- man-страницы (12 штук в `man/man1/`) и их установка во всех пакетах.
|
||||
- Валидация имен проектов в `dcr init` и `dcr new` (только ASCII буквы, цифры, `_` и `-`).
|
||||
@@ -251,6 +304,7 @@
|
||||
- Пакеты AUR (`dcr-dev`, `dcr-dev-bin`) и сборка Snap в CI.
|
||||
|
||||
**Изменено:**
|
||||
|
||||
- Функция `validate_package_name` стала публичной.
|
||||
- Команды `gen`, `tree`, `fmt`, `setup` больше не игнорируют аргументы и используют цветной вывод.
|
||||
- Инструкция в README изменена с `| bash` на `| sh` для POSIX.
|
||||
@@ -268,6 +322,7 @@
|
||||
### EN
|
||||
|
||||
**Added:**
|
||||
|
||||
- `--help` for all commands with styled headers and usage lines.
|
||||
- Man pages (12 troff man pages in `man/man1/`) and their installation across all packaging formats.
|
||||
- Project name validation in `dcr init` and `dcr new` (ASCII letters, digits, `_`, `-` only).
|
||||
@@ -278,6 +333,7 @@
|
||||
- AUR packages (`dcr-dev`, `dcr-dev-bin`) and Snap build in CI.
|
||||
|
||||
**Changed:**
|
||||
|
||||
- `validate_package_name` made public and callable from CLI commands directly.
|
||||
- `gen.rs`, `tree.rs`, `fmt.rs`, `setup.rs` no longer ignore arguments and use styled output.
|
||||
- README install instructions changed from `| bash` to `| sh` for POSIX compatibility.
|
||||
@@ -454,6 +510,7 @@ Added:
|
||||
```
|
||||
|
||||
Produces `KERNEL.EFI` (works for `bin`, `staticlib`, and `sharedlib`).
|
||||
|
||||
- Automatic injection of `--target=<build.target>` into compiler flags when `build.target` is set in `dcr.toml`. This greatly simplifies clang-based cross-compilation (especially for bare-metal targets like `aarch64-none-elf`).
|
||||
- Bare-metal targets (containing `none`, `-elf`, `eabi`, `baremetal`) no longer receive DCR's internal default flags (`-g`, `-Wall`, `-Wextra`, `-fno-omit-frame-pointer`, `-DDCR_DEBUG`, etc.). Prevents unwanted sections (`.comment`, debug info, etc.) that break custom linker scripts when `inherit = true`.
|
||||
- `build.ldscript` in `dcr.toml` — linker script path passed as `-T <path>` to the linker. Essential for bare-metal/embedded/freestanding targets.
|
||||
@@ -488,28 +545,28 @@ Fixed:
|
||||
|
||||
- Path dependencies now work correctly: `dcr add` stores them as `{ path =
|
||||
"./..." }` tables, `is_registry_dep` properly distinguishes registry strings
|
||||
from path/git strings, and `deps/mod.rs` resolves both table-form and
|
||||
legacy string-form path dependencies
|
||||
from path/git strings, and `deps/mod.rs` resolves both table-form and
|
||||
legacy string-form path dependencies
|
||||
- Registry dependency paths no longer hardcoded to `project_root/dcr-index`;
|
||||
now use `package_root_from_registry_info()` which resolves relative to the
|
||||
registry cache root (`~/.dcr/`)
|
||||
now use `package_root_from_registry_info()` which resolves relative to the
|
||||
registry cache root (`~/.dcr/`)
|
||||
- Registry dependencies are now actually built: `build_project_at()` is
|
||||
called when `include_dir` or `lib_dir` is missing
|
||||
called when `include_dir` or `lib_dir` is missing
|
||||
|
||||
Added:
|
||||
|
||||
- `SIGINT`/`Ctrl+C` handler via `ctrlc` crate — `dcr build` now checks
|
||||
`BUILD_INTERRUPTED` flag at key points and aborts cleanly
|
||||
`BUILD_INTERRUPTED` flag at key points and aborts cleanly
|
||||
- `utils/build.rs` — extracted shared utilities: `parse_version_info`,
|
||||
`normalize_target_os`, `resolve_compiler`, `primary_language`,
|
||||
`resolve_pkg_config_flags` and config helpers. Eliminates code duplication
|
||||
between `cli/build.rs`, `cli/run.rs`, `cli/clean.rs`, `cli/gen.rs`
|
||||
`normalize_target_os`, `resolve_compiler`, `primary_language`,
|
||||
`resolve_pkg_config_flags` and config helpers. Eliminates code duplication
|
||||
between `cli/build.rs`, `cli/run.rs`, `cli/clean.rs`, `cli/gen.rs`
|
||||
- `utils/fs.rs::with_dir` — extracted common directory-scoped execution
|
||||
- `run_command_sync_output` in `builder/common.rs` with global `OUTPUT_MUTEX`
|
||||
— synchronized compiler output across all backends (unix_cc, gas, nasm, msvc)
|
||||
— synchronized compiler output across all backends (unix_cc, gas, nasm, msvc)
|
||||
- New helper functions in `deps/register.rs`: `get_registry_cache_root`,
|
||||
`package_root_from_registry_info`, `registry_include_dir`, `registry_lib_dir`,
|
||||
`path_from_string_dep`
|
||||
`package_root_from_registry_info`, `registry_include_dir`, `registry_lib_dir`,
|
||||
`path_from_string_dep`
|
||||
- Unit tests for `register.rs` (`is_registry_dep`, `package_root_from_registry_info`),
|
||||
`deps/mod.rs` (`path_dep_path`, `push_default_lib_dirs`),
|
||||
`utils/build.rs` (`normalize_target_os`, `parse_version_info`)
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -154,7 +154,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dcr"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
dependencies = [
|
||||
"ctrlc",
|
||||
"fatfs",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "dcr"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
||||
|
||||
@@ -4,6 +4,28 @@ sidebar_label: Changelog
|
||||
|
||||
# Changelog
|
||||
|
||||
## 0.8.3 (2026-08-03)
|
||||
|
||||
### Added
|
||||
|
||||
- **`dcr run -- <args>...`** — cargo-style argument forwarding after a bare `--`
|
||||
(`dcr run --release -- --test_help`). Applies to direct binary runs and `[run].cmd`.
|
||||
- **Tests** — host target section `ldflags` without CLI `--target`; run-arg forwarding; `split_double_dash` unit tests.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Native builds resolve the host triple** — without `--target` / package `build.target`, host sections such as
|
||||
`[build.x86_64-unknown-linux-gnu]` apply again (`cflags` / `ldflags` / toolchain).
|
||||
Previously the target was empty and those sections were ignored while artifacts already used the host triple path.
|
||||
- **Empty target** — no more `Unknown target ''` warning.
|
||||
- **`dcr run --help` / man `dcr-run`** — document `--` forwarding.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Native artifact paths on Windows and macOS** — without an explicit `--target` / `build.target`, artifacts use `target/<profile>`, so `dcr run` and flat-bin find the build output.
|
||||
- Target-specific `ldflags` missing on native host builds (e.g. micro-lang + `libsct-elf`).
|
||||
- Spurious empty-target warning.
|
||||
|
||||
## 0.8.2 (2026-07-22)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -8,9 +8,13 @@ dcr-run \- Build and run a DCR project
|
||||
[\fB\-\-force\fR]
|
||||
[\fB\-\-clean\fR]
|
||||
[\fB\-\-verbose\fR]
|
||||
[\fB\-\-\fR \fI<args>...\fR]
|
||||
.SH DESCRIPTION
|
||||
Builds and runs the project. Only available for projects with
|
||||
\fBkind = "bin"\fR.
|
||||
Arguments after a bare
|
||||
.B \-\-
|
||||
are forwarded to the built binary (cargo-style).
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-\-debug
|
||||
@@ -30,6 +34,14 @@ Clean before building.
|
||||
.TP
|
||||
.B \-\-verbose
|
||||
Print detailed build output.
|
||||
.TP
|
||||
.B \-\- \fI<args>...\fR
|
||||
Arguments passed to the built binary.
|
||||
.SH EXAMPLES
|
||||
.nf
|
||||
dcr run --release -- --test_help
|
||||
dcr run -- --foo bar
|
||||
.fi
|
||||
.SH SEE ALSO
|
||||
.BR dcr (1),
|
||||
.BR dcr-build (1)
|
||||
|
||||
@@ -25,16 +25,25 @@ pub struct BuildRunFlags {
|
||||
pub force: bool,
|
||||
pub clean: bool,
|
||||
pub verbose: bool,
|
||||
pub bin_args: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn split_double_dash(args: &[String]) -> (&[String], &[String]) {
|
||||
match args.iter().position(|a| a == "--") {
|
||||
Some(i) => (&args[..i], &args[i + 1..]),
|
||||
None => (args, &[]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
|
||||
let (dcr_args, bin_args_slice) = split_double_dash(args);
|
||||
let mut profile = PROFILE.to_string();
|
||||
let mut target = None;
|
||||
let mut workspace = None;
|
||||
let mut force = false;
|
||||
let mut clean = false;
|
||||
let mut verbose = false;
|
||||
let mut iter = args.iter();
|
||||
let mut iter = dcr_args.iter();
|
||||
|
||||
while let Some(arg) = iter.next() {
|
||||
if !arg.starts_with("--") {
|
||||
@@ -91,5 +100,38 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
|
||||
force,
|
||||
clean,
|
||||
verbose,
|
||||
bin_args: bin_args_slice.to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn s(args: &[&str]) -> Vec<String> {
|
||||
args.iter().map(|a| a.to_string()).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_double_dash_empty_tail() {
|
||||
let args = s(&["--release", "--force"]);
|
||||
let (left, right) = split_double_dash(&args);
|
||||
assert_eq!(left, &s(&["--release", "--force"]));
|
||||
assert!(right.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_double_dash_with_bin_args() {
|
||||
let args = s(&["--release", "--", "--test_help", "x"]);
|
||||
let (left, right) = split_double_dash(&args);
|
||||
assert_eq!(left, &s(&["--release"]));
|
||||
assert_eq!(right, &s(&["--test_help", "x"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_run_flags_forwards_after_double_dash() {
|
||||
let flags = parse_build_run_flags(&s(&["--release", "--", "--test_help"])).unwrap();
|
||||
assert_eq!(flags.profile, "release");
|
||||
assert_eq!(flags.bin_args, s(&["--test_help"]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,12 +59,13 @@ pub fn run(args: &[String]) -> i32 {
|
||||
if args.first().is_some_and(|a| a == "--help") {
|
||||
printc("USAGE:", BOLD_GREEN);
|
||||
printc(
|
||||
" dcr run [--debug | --release] [--target <triple>] [--force] [--clean] [--verbose]",
|
||||
" dcr run [--debug | --release] [--target <triple>] [--force] [--clean] [--verbose] [-- <args>...]",
|
||||
BOLD_CYAN,
|
||||
);
|
||||
println!();
|
||||
printc("DESCRIPTION:", BOLD_GREEN);
|
||||
println!(" Builds and runs the project. Only available for kind = \"bin\".");
|
||||
println!(" Arguments after `--` are passed to the built binary (cargo-style).");
|
||||
println!();
|
||||
printc("OPTIONS:", BOLD_GREEN);
|
||||
println!(" --debug Run with debug profile (default)");
|
||||
@@ -73,6 +74,7 @@ pub fn run(args: &[String]) -> i32 {
|
||||
println!(" --force Force a full rebuild");
|
||||
println!(" --clean Clean before building");
|
||||
println!(" --verbose Print detailed build output");
|
||||
println!(" -- <args>... Arguments forwarded to the binary");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -112,12 +114,13 @@ pub fn run(args: &[String]) -> i32 {
|
||||
if let Some(cmd) = get_run_cmd(&config, &flags.profile, flags.target.as_deref(), "") {
|
||||
let build_status = build(&args_for_build(&flags));
|
||||
if build_status == 0 {
|
||||
let display = display_run_cmd(&cmd, &flags.bin_args);
|
||||
println!(
|
||||
" {} {}",
|
||||
colored(&format!("{:<9}", "run"), BOLD_GREEN),
|
||||
cmd
|
||||
display
|
||||
);
|
||||
return run_shell(&cmd);
|
||||
return run_shell_with_args(&cmd, &flags.bin_args);
|
||||
}
|
||||
return build_status;
|
||||
}
|
||||
@@ -250,32 +253,36 @@ fn run_project(
|
||||
);
|
||||
if build_status == 0 {
|
||||
if let Some(cmd) = run_cmd {
|
||||
let display = display_run_cmd(&cmd, &flags.bin_args);
|
||||
println!(
|
||||
" {} {}",
|
||||
colored(&format!("{:<9}", "run"), BOLD_GREEN),
|
||||
cmd
|
||||
display
|
||||
);
|
||||
return Ok(run_shell(&cmd));
|
||||
return Ok(run_shell_with_args(&cmd, &flags.bin_args));
|
||||
}
|
||||
let display = display_bin_run(&bin_path, &flags.bin_args);
|
||||
println!(
|
||||
" {} {}",
|
||||
colored(&format!("{:<9}", "run"), BOLD_GREEN),
|
||||
bin_path
|
||||
display
|
||||
);
|
||||
return Ok(run_binary(
|
||||
project_name,
|
||||
&flags.profile,
|
||||
normalized_target_dir.as_deref(),
|
||||
&flags.bin_args,
|
||||
));
|
||||
}
|
||||
|
||||
let fallback_code = if let Some(cmd) = run_cmd {
|
||||
run_shell(&cmd)
|
||||
run_shell_with_args(&cmd, &flags.bin_args)
|
||||
} else {
|
||||
run_binary(
|
||||
project_name,
|
||||
&flags.profile,
|
||||
normalized_target_dir.as_deref(),
|
||||
&flags.bin_args,
|
||||
)
|
||||
};
|
||||
if fallback_code != 1 {
|
||||
@@ -345,6 +352,62 @@ fn run_shell(cmd: &str) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_shell_with_args(cmd: &str, bin_args: &[String]) -> i32 {
|
||||
if bin_args.is_empty() {
|
||||
return run_shell(cmd);
|
||||
}
|
||||
let mut full = cmd.to_string();
|
||||
for arg in bin_args {
|
||||
full.push(' ');
|
||||
full.push_str(&shell_escape(arg));
|
||||
}
|
||||
run_shell(&full)
|
||||
}
|
||||
|
||||
fn display_run_cmd(cmd: &str, bin_args: &[String]) -> String {
|
||||
if bin_args.is_empty() {
|
||||
return cmd.to_string();
|
||||
}
|
||||
let mut display = cmd.to_string();
|
||||
for arg in bin_args {
|
||||
display.push(' ');
|
||||
display.push_str(arg);
|
||||
}
|
||||
display
|
||||
}
|
||||
|
||||
fn display_bin_run(bin_path: &str, bin_args: &[String]) -> String {
|
||||
if bin_args.is_empty() {
|
||||
return bin_path.to_string();
|
||||
}
|
||||
let mut display = bin_path.to_string();
|
||||
for arg in bin_args {
|
||||
display.push(' ');
|
||||
display.push_str(arg);
|
||||
}
|
||||
display
|
||||
}
|
||||
|
||||
fn shell_escape(arg: &str) -> String {
|
||||
if cfg!(target_os = "windows") {
|
||||
// cmd.exe: wrap in double quotes and escape embedded quotes.
|
||||
let escaped = arg.replace('"', "\\\"");
|
||||
format!("\"{escaped}\"")
|
||||
} else {
|
||||
// POSIX sh: single-quote and escape embedded single quotes as '\''.
|
||||
let mut out = String::from("'");
|
||||
for ch in arg.chars() {
|
||||
if ch == '\'' {
|
||||
out.push_str("'\\''");
|
||||
} else {
|
||||
out.push(ch);
|
||||
}
|
||||
}
|
||||
out.push('\'');
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
fn substitute_run_vars(cmd: &str, profile: &str, version: &str) -> String {
|
||||
let info = parse_version_info(version);
|
||||
substitute_vars(cmd, &info, profile, "")
|
||||
|
||||
@@ -36,10 +36,10 @@ use crate::core::build_config::Config;
|
||||
use crate::core::deps::{register, resolve_deps};
|
||||
use crate::core::workspace::parse_workspace;
|
||||
use crate::utils::build::{
|
||||
get_bool_with_profile, get_config_opt, get_config_str, get_language_with_profile,
|
||||
is_bare_metal_target, normalize_kind, normalize_platform, normalize_target,
|
||||
normalize_target_os, parse_version_info, prepend_clang_target_flag, primary_language,
|
||||
resolve_compiler, resolve_pkg_config_flags, resolve_tool, substitute_vars,
|
||||
default_target_triple, get_bool_with_profile, get_config_opt, get_config_str,
|
||||
get_language_with_profile, is_bare_metal_target, normalize_kind, normalize_platform,
|
||||
normalize_target, normalize_target_os, parse_version_info, prepend_clang_target_flag,
|
||||
primary_language, resolve_compiler, resolve_pkg_config_flags, resolve_tool, substitute_vars,
|
||||
};
|
||||
use crate::utils::fs::check_dir;
|
||||
use std::fs;
|
||||
@@ -159,6 +159,7 @@ fn build_all(
|
||||
&member.path,
|
||||
profile,
|
||||
target,
|
||||
target.is_some_and(|t| !t.trim().is_empty()),
|
||||
&[],
|
||||
force,
|
||||
verbose,
|
||||
@@ -186,22 +187,34 @@ fn build_all(
|
||||
let project_name = get_config_str(&config, "package.name");
|
||||
let project_version = get_config_str(&config, "package.version");
|
||||
|
||||
let targets_to_build: Vec<Option<String>> = if let Some(t) = target {
|
||||
vec![Some(normalize_target_os(t).to_string())]
|
||||
let targets_to_build: Vec<(Option<String>, bool)> = if let Some(t) = target {
|
||||
let normalized = normalize_target_os(t);
|
||||
if normalized.is_empty() {
|
||||
vec![(Some(default_target_triple()), true)]
|
||||
} else {
|
||||
vec![(Some(normalized.to_string()), true)]
|
||||
}
|
||||
} else {
|
||||
let config_targets = get_targets(&config, profile)?;
|
||||
if config_targets.is_empty() {
|
||||
vec![None]
|
||||
vec![(Some(default_target_triple()), false)]
|
||||
} else {
|
||||
config_targets
|
||||
.into_iter()
|
||||
.map(|t| Some(normalize_target_os(&t).to_string()))
|
||||
.map(|t| {
|
||||
let normalized = normalize_target_os(&t);
|
||||
if normalized.is_empty() {
|
||||
(Some(default_target_triple()), true)
|
||||
} else {
|
||||
(Some(normalized.to_string()), true)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
let start_time = Instant::now();
|
||||
for (i, build_target) in targets_to_build.iter().enumerate() {
|
||||
for (i, (build_target, explicit_target)) in targets_to_build.iter().enumerate() {
|
||||
if cancel.load(Ordering::SeqCst) {
|
||||
return Err("Build interrupted".to_string());
|
||||
}
|
||||
@@ -226,6 +239,7 @@ fn build_all(
|
||||
&member.path,
|
||||
profile,
|
||||
build_target.as_deref(),
|
||||
*explicit_target,
|
||||
&[],
|
||||
force,
|
||||
verbose,
|
||||
@@ -239,6 +253,7 @@ fn build_all(
|
||||
&ws,
|
||||
profile,
|
||||
build_target.as_deref(),
|
||||
*explicit_target,
|
||||
force,
|
||||
verbose,
|
||||
Some(root),
|
||||
@@ -251,6 +266,7 @@ fn build_all(
|
||||
root,
|
||||
profile,
|
||||
build_target.as_deref(),
|
||||
*explicit_target,
|
||||
&excludes,
|
||||
force,
|
||||
verbose,
|
||||
@@ -264,6 +280,7 @@ fn build_all(
|
||||
root,
|
||||
profile,
|
||||
build_target.as_deref(),
|
||||
*explicit_target,
|
||||
&[],
|
||||
force,
|
||||
verbose,
|
||||
@@ -283,6 +300,7 @@ fn build_workspace(
|
||||
workspace: &crate::core::workspace::Workspace,
|
||||
profile: &str,
|
||||
target: Option<&str>,
|
||||
explicit_target: bool,
|
||||
force: bool,
|
||||
verbose: bool,
|
||||
workspace_root: Option<&Path>,
|
||||
@@ -294,6 +312,7 @@ fn build_workspace(
|
||||
&member.path,
|
||||
profile,
|
||||
target,
|
||||
explicit_target,
|
||||
&[],
|
||||
force,
|
||||
verbose,
|
||||
@@ -310,6 +329,7 @@ fn build_project_at(
|
||||
project_root: &Path,
|
||||
profile: &str,
|
||||
target: Option<&str>,
|
||||
explicit_target: bool,
|
||||
exclude_dirs: &[std::path::PathBuf],
|
||||
force: bool,
|
||||
verbose: bool,
|
||||
@@ -344,12 +364,43 @@ fn build_project_at(
|
||||
}
|
||||
let project_name = get_config_str(&config, "package.name");
|
||||
let project_version = get_config_str(&config, "package.version");
|
||||
let host_target = default_target_triple();
|
||||
let build_target_config = get_build_string_with_profile(&config, "target", profile);
|
||||
let build_target = target.or(if build_target_config.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(build_target_config.as_str())
|
||||
});
|
||||
let has_explicit_target = explicit_target || !build_target_config.is_empty();
|
||||
let explicit_build_target = explicit_target
|
||||
.then_some(target)
|
||||
.flatten()
|
||||
.filter(|t| !t.trim().is_empty())
|
||||
.map(|t| {
|
||||
let normalized = normalize_target_os(t);
|
||||
if normalized.is_empty() {
|
||||
host_target.clone()
|
||||
} else {
|
||||
normalized.to_string()
|
||||
}
|
||||
});
|
||||
let configured_build_target = || {
|
||||
if build_target_config.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let normalized = normalize_target_os(&build_target_config);
|
||||
if normalized.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(normalized.to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
let implicit_build_target = || {
|
||||
target
|
||||
.filter(|t| !t.trim().is_empty())
|
||||
.map(|t| normalize_target_os(t).to_string())
|
||||
};
|
||||
let build_target_owned = explicit_build_target
|
||||
.or_else(configured_build_target)
|
||||
.or_else(implicit_build_target)
|
||||
.unwrap_or(host_target);
|
||||
let build_target = Some(build_target_owned.as_str());
|
||||
let build_language = get_language_with_profile(&config, profile)?;
|
||||
let build_qt = get_bool_with_profile(&config, "qt", profile, false);
|
||||
let lang_key = primary_language(&build_language);
|
||||
@@ -530,27 +581,29 @@ fn build_project_at(
|
||||
root.join(&rel).to_string_lossy().to_string()
|
||||
}
|
||||
None => {
|
||||
let base_dir =
|
||||
if let Some(rel) = normalize_target(build_target.unwrap_or(""), profile) {
|
||||
PathBuf::from(rel)
|
||||
let base_dir = if has_explicit_target {
|
||||
PathBuf::from(
|
||||
normalize_target(&build_target_owned, profile)
|
||||
.expect("explicit build target must be non-empty"),
|
||||
)
|
||||
} else {
|
||||
let default_dir = if cfg!(target_os = "linux") {
|
||||
let arch = std::env::consts::ARCH;
|
||||
format!("{arch}-unknown-linux-gnu/{profile}")
|
||||
} else if cfg!(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
)) {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let os = std::env::consts::OS;
|
||||
format!("{arch}-unknown-{os}/{profile}")
|
||||
} else {
|
||||
let default_dir = if cfg!(target_os = "linux") {
|
||||
let arch = std::env::consts::ARCH;
|
||||
format!("{arch}-unknown-linux-gnu/{profile}")
|
||||
} else if cfg!(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
)) {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let os = std::env::consts::OS;
|
||||
format!("{arch}-unknown-{os}/{profile}")
|
||||
} else {
|
||||
profile.to_string()
|
||||
};
|
||||
Path::new("target").join(&default_dir)
|
||||
profile.to_string()
|
||||
};
|
||||
Path::new("target").join(&default_dir)
|
||||
};
|
||||
project_root.join(base_dir).to_string_lossy().to_string()
|
||||
}
|
||||
}
|
||||
@@ -694,6 +747,7 @@ fn build_project_at(
|
||||
&dep_root,
|
||||
profile,
|
||||
build_target,
|
||||
has_explicit_target,
|
||||
&[],
|
||||
force,
|
||||
verbose,
|
||||
|
||||
@@ -19,10 +19,15 @@ use crate::platform;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn run_binary(project_name: &str, profile: &str, target_dir: Option<&str>) -> i32 {
|
||||
pub fn run_binary(
|
||||
project_name: &str,
|
||||
profile: &str,
|
||||
target_dir: Option<&str>,
|
||||
bin_args: &[String],
|
||||
) -> i32 {
|
||||
let bin_path = platform::bin_path(profile, project_name, target_dir);
|
||||
if Path::new(&bin_path).exists() {
|
||||
let status = Command::new(&bin_path).status();
|
||||
let status = Command::new(&bin_path).args(bin_args).status();
|
||||
match status {
|
||||
Ok(s) => {
|
||||
return s.code().unwrap_or(0);
|
||||
|
||||
@@ -105,6 +105,7 @@ pub fn normalize_platform(platform: &str) -> Option<&str> {
|
||||
|
||||
pub fn normalize_target_os(target: &str) -> &str {
|
||||
match target {
|
||||
"" => "",
|
||||
"linux" => "x86_64-unknown-linux-gnu",
|
||||
"macos" => "x86_64-apple-darwin",
|
||||
"windows" => "x86_64-pc-windows-msvc",
|
||||
@@ -515,6 +516,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn normalize_target_short_names() {
|
||||
assert_eq!(normalize_target_os(""), "");
|
||||
assert_eq!(normalize_target_os("linux"), "x86_64-unknown-linux-gnu");
|
||||
assert_eq!(normalize_target_os("macos"), "x86_64-apple-darwin");
|
||||
assert_eq!(normalize_target_os("windows"), "x86_64-pc-windows-msvc");
|
||||
|
||||
@@ -87,6 +87,33 @@ fn build_run_clean_flags_normal_project() {
|
||||
"dcr run should start"
|
||||
);
|
||||
|
||||
std::fs::write(
|
||||
dir.join("src/main.c"),
|
||||
r#"#include <stdio.h>
|
||||
int main(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
printf("ARG:%s\n", argv[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.expect("rewrite main.c");
|
||||
let out = run_dcr_env(&["run", "--force", "--", "--test_help", "foo"], &dir, &envs);
|
||||
let combined = format!(
|
||||
"{}{}",
|
||||
String::from_utf8_lossy(&out.stdout),
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"dcr run -- <args> should succeed:\n{combined}"
|
||||
);
|
||||
assert!(
|
||||
combined.contains("ARG:--test_help") && combined.contains("ARG:foo"),
|
||||
"binary should receive args after `--`:\n{combined}"
|
||||
);
|
||||
|
||||
let out = run_dcr_env(&["clean", "--release"], &dir, &envs);
|
||||
assert!(out.status.success(), "dcr clean --release should succeed");
|
||||
let release_dir = host_profile_dir(&dir, "release");
|
||||
@@ -286,3 +313,57 @@ fn package_build_target_used_without_cli_flag() {
|
||||
"artifact should be under package build.target path",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_target_section_ldflags_apply_without_cli_target() {
|
||||
let Some(compiler) = available_compiler() else {
|
||||
eprintln!("no compiler found; skipping host target ldflags test");
|
||||
return;
|
||||
};
|
||||
|
||||
let dir = unique_sandbox_dir("host_ldflags");
|
||||
let out = run_dcr(&["init"], &dir);
|
||||
assert!(out.status.success(), "dcr init should succeed");
|
||||
|
||||
let host = if cfg!(target_os = "linux") {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let env = if cfg!(target_env = "musl") {
|
||||
"musl"
|
||||
} else {
|
||||
"gnu"
|
||||
};
|
||||
format!("{arch}-unknown-linux-{env}")
|
||||
} else if cfg!(target_os = "macos") {
|
||||
format!("{}-apple-darwin", std::env::consts::ARCH)
|
||||
} else if cfg!(target_os = "windows") {
|
||||
let env = if cfg!(target_env = "gnu") {
|
||||
"gnu"
|
||||
} else {
|
||||
"msvc"
|
||||
};
|
||||
format!("{}-pc-windows-{env}", std::env::consts::ARCH)
|
||||
} else {
|
||||
eprintln!("unsupported host for host_target_section test; skipping");
|
||||
return;
|
||||
};
|
||||
|
||||
let toml_path = dir.join("dcr.toml");
|
||||
let toml = std::fs::read_to_string(&toml_path).expect("read dcr.toml");
|
||||
let updated = format!(
|
||||
"{toml}\n[build.{host}]\nldflags = [\"-Wl,-rpath,/nonexistent-dcr-host-ldflags\"]\n"
|
||||
);
|
||||
std::fs::write(&toml_path, updated).expect("write dcr.toml");
|
||||
|
||||
let envs = [("DCR_COMPILER", compiler), ("DCR_DEBUG", "1")];
|
||||
let out = run_dcr_env(&["build", "--force"], &dir, &envs);
|
||||
let combined = format!(
|
||||
"{}{}",
|
||||
String::from_utf8_lossy(&out.stdout),
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
assert!(
|
||||
combined.contains("-Wl,-rpath,/nonexistent-dcr-host-ldflags")
|
||||
|| combined.contains("rpath,/nonexistent-dcr-host-ldflags"),
|
||||
"host target section ldflags must be applied on native build:\n{combined}"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user