mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 13:38:30 +00:00
feat: host-target config resolution and cargo-style run args
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -154,7 +154,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dcr"
|
name = "dcr"
|
||||||
version = "0.8.2"
|
version = "0.8.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"fatfs",
|
"fatfs",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dcr"
|
name = "dcr"
|
||||||
version = "0.8.2"
|
version = "0.8.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ dcr-run \- Build and run a DCR project
|
|||||||
[\fB\-\-force\fR]
|
[\fB\-\-force\fR]
|
||||||
[\fB\-\-clean\fR]
|
[\fB\-\-clean\fR]
|
||||||
[\fB\-\-verbose\fR]
|
[\fB\-\-verbose\fR]
|
||||||
|
[\fB\-\-\fR \fI<args>...\fR]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
Builds and runs the project. Only available for projects with
|
Builds and runs the project. Only available for projects with
|
||||||
\fBkind = "bin"\fR.
|
\fBkind = "bin"\fR.
|
||||||
|
Arguments after a bare
|
||||||
|
.B \-\-
|
||||||
|
are forwarded to the built binary (cargo-style).
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.B \-\-debug
|
.B \-\-debug
|
||||||
@@ -30,6 +34,14 @@ Clean before building.
|
|||||||
.TP
|
.TP
|
||||||
.B \-\-verbose
|
.B \-\-verbose
|
||||||
Print detailed build output.
|
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
|
.SH SEE ALSO
|
||||||
.BR dcr (1),
|
.BR dcr (1),
|
||||||
.BR dcr-build (1)
|
.BR dcr-build (1)
|
||||||
|
|||||||
@@ -25,16 +25,25 @@ pub struct BuildRunFlags {
|
|||||||
pub force: bool,
|
pub force: bool,
|
||||||
pub clean: bool,
|
pub clean: bool,
|
||||||
pub verbose: 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> {
|
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 profile = PROFILE.to_string();
|
||||||
let mut target = None;
|
let mut target = None;
|
||||||
let mut workspace = None;
|
let mut workspace = None;
|
||||||
let mut force = false;
|
let mut force = false;
|
||||||
let mut clean = false;
|
let mut clean = false;
|
||||||
let mut verbose = false;
|
let mut verbose = false;
|
||||||
let mut iter = args.iter();
|
let mut iter = dcr_args.iter();
|
||||||
|
|
||||||
while let Some(arg) = iter.next() {
|
while let Some(arg) = iter.next() {
|
||||||
if !arg.starts_with("--") {
|
if !arg.starts_with("--") {
|
||||||
@@ -91,5 +100,38 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
|
|||||||
force,
|
force,
|
||||||
clean,
|
clean,
|
||||||
verbose,
|
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") {
|
if args.first().is_some_and(|a| a == "--help") {
|
||||||
printc("USAGE:", BOLD_GREEN);
|
printc("USAGE:", BOLD_GREEN);
|
||||||
printc(
|
printc(
|
||||||
" dcr run [--debug | --release] [--target <triple>] [--force] [--clean] [--verbose]",
|
" dcr run [--debug | --release] [--target <triple>] [--force] [--clean] [--verbose] [-- <args>...]",
|
||||||
BOLD_CYAN,
|
BOLD_CYAN,
|
||||||
);
|
);
|
||||||
println!();
|
println!();
|
||||||
printc("DESCRIPTION:", BOLD_GREEN);
|
printc("DESCRIPTION:", BOLD_GREEN);
|
||||||
println!(" Builds and runs the project. Only available for kind = \"bin\".");
|
println!(" Builds and runs the project. Only available for kind = \"bin\".");
|
||||||
|
println!(" Arguments after `--` are passed to the built binary (cargo-style).");
|
||||||
println!();
|
println!();
|
||||||
printc("OPTIONS:", BOLD_GREEN);
|
printc("OPTIONS:", BOLD_GREEN);
|
||||||
println!(" --debug Run with debug profile (default)");
|
println!(" --debug Run with debug profile (default)");
|
||||||
@@ -73,6 +74,7 @@ pub fn run(args: &[String]) -> i32 {
|
|||||||
println!(" --force Force a full rebuild");
|
println!(" --force Force a full rebuild");
|
||||||
println!(" --clean Clean before building");
|
println!(" --clean Clean before building");
|
||||||
println!(" --verbose Print detailed build output");
|
println!(" --verbose Print detailed build output");
|
||||||
|
println!(" -- <args>... Arguments forwarded to the binary");
|
||||||
return 0;
|
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(), "") {
|
if let Some(cmd) = get_run_cmd(&config, &flags.profile, flags.target.as_deref(), "") {
|
||||||
let build_status = build(&args_for_build(&flags));
|
let build_status = build(&args_for_build(&flags));
|
||||||
if build_status == 0 {
|
if build_status == 0 {
|
||||||
|
let display = display_run_cmd(&cmd, &flags.bin_args);
|
||||||
println!(
|
println!(
|
||||||
" {} {}",
|
" {} {}",
|
||||||
colored(&format!("{:<9}", "run"), BOLD_GREEN),
|
colored(&format!("{:<9}", "run"), BOLD_GREEN),
|
||||||
cmd
|
display
|
||||||
);
|
);
|
||||||
return run_shell(&cmd);
|
return run_shell_with_args(&cmd, &flags.bin_args);
|
||||||
}
|
}
|
||||||
return build_status;
|
return build_status;
|
||||||
}
|
}
|
||||||
@@ -250,32 +253,36 @@ fn run_project(
|
|||||||
);
|
);
|
||||||
if build_status == 0 {
|
if build_status == 0 {
|
||||||
if let Some(cmd) = run_cmd {
|
if let Some(cmd) = run_cmd {
|
||||||
|
let display = display_run_cmd(&cmd, &flags.bin_args);
|
||||||
println!(
|
println!(
|
||||||
" {} {}",
|
" {} {}",
|
||||||
colored(&format!("{:<9}", "run"), BOLD_GREEN),
|
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!(
|
println!(
|
||||||
" {} {}",
|
" {} {}",
|
||||||
colored(&format!("{:<9}", "run"), BOLD_GREEN),
|
colored(&format!("{:<9}", "run"), BOLD_GREEN),
|
||||||
bin_path
|
display
|
||||||
);
|
);
|
||||||
return Ok(run_binary(
|
return Ok(run_binary(
|
||||||
project_name,
|
project_name,
|
||||||
&flags.profile,
|
&flags.profile,
|
||||||
normalized_target_dir.as_deref(),
|
normalized_target_dir.as_deref(),
|
||||||
|
&flags.bin_args,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fallback_code = if let Some(cmd) = run_cmd {
|
let fallback_code = if let Some(cmd) = run_cmd {
|
||||||
run_shell(&cmd)
|
run_shell_with_args(&cmd, &flags.bin_args)
|
||||||
} else {
|
} else {
|
||||||
run_binary(
|
run_binary(
|
||||||
project_name,
|
project_name,
|
||||||
&flags.profile,
|
&flags.profile,
|
||||||
normalized_target_dir.as_deref(),
|
normalized_target_dir.as_deref(),
|
||||||
|
&flags.bin_args,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if fallback_code != 1 {
|
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 {
|
fn substitute_run_vars(cmd: &str, profile: &str, version: &str) -> String {
|
||||||
let info = parse_version_info(version);
|
let info = parse_version_info(version);
|
||||||
substitute_vars(cmd, &info, profile, "")
|
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::deps::{register, resolve_deps};
|
||||||
use crate::core::workspace::parse_workspace;
|
use crate::core::workspace::parse_workspace;
|
||||||
use crate::utils::build::{
|
use crate::utils::build::{
|
||||||
get_bool_with_profile, get_config_opt, get_config_str, get_language_with_profile,
|
default_target_triple, get_bool_with_profile, get_config_opt, get_config_str,
|
||||||
is_bare_metal_target, normalize_kind, normalize_platform, normalize_target,
|
get_language_with_profile, is_bare_metal_target, normalize_kind, normalize_platform,
|
||||||
normalize_target_os, parse_version_info, prepend_clang_target_flag, primary_language,
|
normalize_target, normalize_target_os, parse_version_info, prepend_clang_target_flag,
|
||||||
resolve_compiler, resolve_pkg_config_flags, resolve_tool, substitute_vars,
|
primary_language, resolve_compiler, resolve_pkg_config_flags, resolve_tool, substitute_vars,
|
||||||
};
|
};
|
||||||
use crate::utils::fs::check_dir;
|
use crate::utils::fs::check_dir;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -187,15 +187,27 @@ fn build_all(
|
|||||||
let project_version = get_config_str(&config, "package.version");
|
let project_version = get_config_str(&config, "package.version");
|
||||||
|
|
||||||
let targets_to_build: Vec<Option<String>> = if let Some(t) = target {
|
let targets_to_build: Vec<Option<String>> = if let Some(t) = target {
|
||||||
vec![Some(normalize_target_os(t).to_string())]
|
let normalized = normalize_target_os(t);
|
||||||
|
if normalized.is_empty() {
|
||||||
|
vec![Some(default_target_triple())]
|
||||||
|
} else {
|
||||||
|
vec![Some(normalized.to_string())]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let config_targets = get_targets(&config, profile)?;
|
let config_targets = get_targets(&config, profile)?;
|
||||||
if config_targets.is_empty() {
|
if config_targets.is_empty() {
|
||||||
vec![None]
|
vec![Some(default_target_triple())]
|
||||||
} else {
|
} else {
|
||||||
config_targets
|
config_targets
|
||||||
.into_iter()
|
.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())
|
||||||
|
} else {
|
||||||
|
Some(normalized.to_string())
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -344,12 +356,32 @@ fn build_project_at(
|
|||||||
}
|
}
|
||||||
let project_name = get_config_str(&config, "package.name");
|
let project_name = get_config_str(&config, "package.name");
|
||||||
let project_version = get_config_str(&config, "package.version");
|
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_config = get_build_string_with_profile(&config, "target", profile);
|
||||||
let build_target = target.or(if build_target_config.is_empty() {
|
let build_target_owned = target
|
||||||
|
.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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
if build_target_config.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(build_target_config.as_str())
|
let normalized = normalize_target_os(&build_target_config);
|
||||||
});
|
if normalized.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(normalized.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(host_target);
|
||||||
|
let build_target = Some(build_target_owned.as_str());
|
||||||
let build_language = get_language_with_profile(&config, profile)?;
|
let build_language = get_language_with_profile(&config, profile)?;
|
||||||
let build_qt = get_bool_with_profile(&config, "qt", profile, false);
|
let build_qt = get_bool_with_profile(&config, "qt", profile, false);
|
||||||
let lang_key = primary_language(&build_language);
|
let lang_key = primary_language(&build_language);
|
||||||
@@ -530,8 +562,7 @@ fn build_project_at(
|
|||||||
root.join(&rel).to_string_lossy().to_string()
|
root.join(&rel).to_string_lossy().to_string()
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let base_dir =
|
let base_dir = if let Some(rel) = normalize_target(&build_target_owned, profile) {
|
||||||
if let Some(rel) = normalize_target(build_target.unwrap_or(""), profile) {
|
|
||||||
PathBuf::from(rel)
|
PathBuf::from(rel)
|
||||||
} else {
|
} else {
|
||||||
let default_dir = if cfg!(target_os = "linux") {
|
let default_dir = if cfg!(target_os = "linux") {
|
||||||
|
|||||||
@@ -19,10 +19,15 @@ use crate::platform;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Command;
|
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);
|
let bin_path = platform::bin_path(profile, project_name, target_dir);
|
||||||
if Path::new(&bin_path).exists() {
|
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 {
|
match status {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
return s.code().unwrap_or(0);
|
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 {
|
pub fn normalize_target_os(target: &str) -> &str {
|
||||||
match target {
|
match target {
|
||||||
|
"" => "",
|
||||||
"linux" => "x86_64-unknown-linux-gnu",
|
"linux" => "x86_64-unknown-linux-gnu",
|
||||||
"macos" => "x86_64-apple-darwin",
|
"macos" => "x86_64-apple-darwin",
|
||||||
"windows" => "x86_64-pc-windows-msvc",
|
"windows" => "x86_64-pc-windows-msvc",
|
||||||
@@ -515,6 +516,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalize_target_short_names() {
|
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("linux"), "x86_64-unknown-linux-gnu");
|
||||||
assert_eq!(normalize_target_os("macos"), "x86_64-apple-darwin");
|
assert_eq!(normalize_target_os("macos"), "x86_64-apple-darwin");
|
||||||
assert_eq!(normalize_target_os("windows"), "x86_64-pc-windows-msvc");
|
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"
|
"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);
|
let out = run_dcr_env(&["clean", "--release"], &dir, &envs);
|
||||||
assert!(out.status.success(), "dcr clean --release should succeed");
|
assert!(out.status.success(), "dcr clean --release should succeed");
|
||||||
let release_dir = host_profile_dir(&dir, "release");
|
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",
|
"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