fix: split target into target (triple) and out_dir (output directory)

build.target now holds the target triple (e.g. x86_64-unknown-linux-gnu)
build.out_dir is a custom output directory that overrides the default
target/<triple>/<profile> path.
This commit is contained in:
2026-05-30 14:18:54 +08:00
parent 0587965283
commit 865d8c52ff
7 changed files with 352 additions and 40 deletions

View File

@@ -211,7 +211,7 @@ fn get_string_with_profile_and_target(
}
}
fn get_build_string_with_profile(config: &Config, field: &str, profile: &str) -> String {
pub fn get_build_string_with_profile(config: &Config, field: &str, profile: &str) -> String {
get_string_with_profile_and_target(config, field, profile, None)
}
@@ -712,26 +712,31 @@ fn build_project_at(
let resolved_linker = resolve_tool("DCR_LD", tc_ld.as_deref());
let resolved_archiver = resolve_tool("DCR_AR", tc_ar.as_deref());
let target_dir_binding = match workspace_root {
Some(root) => {
let target_str = build_target
.map(normalize_target_os)
.filter(|t| !t.is_empty());
let rel: PathBuf = match target_str {
Some(ref t) => Path::new("target").join(t).join(profile),
None => {
let default_dir = if cfg!(target_os = "linux") {
let arch = std::env::consts::ARCH;
format!("{arch}-unknown-linux-gnu/{profile}")
} else {
profile.to_string()
};
Path::new("target").join(&default_dir)
}
};
root.join(&rel).to_string_lossy().to_string()
let out_dir_config = get_build_string_with_profile(&config, "out_dir", profile);
let target_dir_binding = if !out_dir_config.is_empty() {
out_dir_config
} else {
match workspace_root {
Some(root) => {
let target_str = build_target
.map(normalize_target_os)
.filter(|t| !t.is_empty());
let rel: PathBuf = match target_str {
Some(ref t) => Path::new("target").join(t).join(profile),
None => {
let default_dir = if cfg!(target_os = "linux") {
let arch = std::env::consts::ARCH;
format!("{arch}-unknown-linux-gnu/{profile}")
} else {
profile.to_string()
};
Path::new("target").join(&default_dir)
}
};
root.join(&rel).to_string_lossy().to_string()
}
None => normalize_target(build_target.unwrap_or(""), profile).unwrap_or_default(),
}
None => normalize_target(build_target.unwrap_or(""), profile).unwrap_or_default(),
};
let target_dir = if target_dir_binding.is_empty() {
None

View File

@@ -83,7 +83,6 @@ pub fn run(args: &[String]) -> i32 {
return 1;
}
};
let flags = match parse_build_run_flags(args) {
Ok(v) => v,
Err(_) => return 1,
@@ -188,14 +187,20 @@ fn run_project(
.or_else(|| config.get("build.kind").and_then(|v| v.as_str()))
.unwrap_or("");
let normalized_target_dir = match workspace_root {
Some(wr) => target
.as_ref()
.and_then(|t| crate::cli::build::normalize_target(t, &flags.profile))
.map(|rel| wr.join(&rel).to_string_lossy().to_string()),
None => target
.as_ref()
.and_then(|t| crate::cli::build::normalize_target(t, &flags.profile)),
let out_dir =
crate::cli::build::get_build_string_with_profile(&config, "out_dir", &flags.profile);
let normalized_target_dir = if !out_dir.is_empty() {
Some(out_dir)
} else {
match workspace_root {
Some(wr) => target
.as_ref()
.and_then(|t| crate::cli::build::normalize_target(t, &flags.profile))
.map(|rel| wr.join(&rel).to_string_lossy().to_string()),
None => target
.as_ref()
.and_then(|t| crate::cli::build::normalize_target(t, &flags.profile)),
}
};
let version = config

View File

@@ -119,6 +119,8 @@ pub struct BuildConfig {
#[serde(default)]
pub target: Option<String>,
#[serde(default)]
pub out_dir: Option<String>,
#[serde(default)]
pub platform: Option<String>,
#[serde(default)]
pub cflags: Vec<String>,
@@ -396,6 +398,9 @@ impl Config {
if let Some(target) = &build.target {
validate_non_empty_string(target, "build.target")?;
}
if let Some(out_dir) = &build.out_dir {
validate_non_empty_string(out_dir, "build.out_dir")?;
}
for profile in ["release", "debug"] {
if let Some(section) = self
.data
@@ -539,7 +544,9 @@ impl Config {
if let Some(lang) = table.get("language") {
self.validate_language(lang)?;
}
for key in ["standard", "compiler", "kind", "target", "platform"] {
for key in [
"standard", "compiler", "kind", "target", "out_dir", "platform",
] {
if let Some(value) = table.get(key) {
let s = value.as_str().unwrap_or("");
if s.trim().is_empty() {
@@ -784,6 +791,11 @@ fn format_toml(value: &Value) -> Result<String, ConfigError> {
{
out.push_str(&format!("target = \"{target}\"\n"));
}
if let Some(out_dir) = build.get("out_dir").and_then(|v| v.as_str())
&& !out_dir.trim().is_empty()
{
out.push_str(&format!("out_dir = \"{out_dir}\"\n"));
}
if let Some(cflags) = build.get("cflags") {
out.push_str(&format!("cflags = {}\n", format_string_array(cflags)));
}