feat: add support for MAKEFLAGS in build flags and update TOML parsing

This commit is contained in:
2026-03-01 14:08:29 -06:00
parent 3c2bcc0ce2
commit 7841b06b5a
3 changed files with 144 additions and 0 deletions
+20
View File
@@ -78,6 +78,13 @@ pub fn standard_build_env(
if !flags.prefix.is_empty() {
set_env_var(&mut env_vars, "PREFIX", flags.prefix.clone());
}
if !flags.makeflags.trim().is_empty() {
set_env_var(
&mut env_vars,
"MAKEFLAGS",
flags.makeflags.trim().to_string(),
);
}
set_env_var(&mut env_vars, "DEPOT_ROOTFS", flags.rootfs.clone());
set_env_var(
@@ -417,6 +424,19 @@ mod tests {
);
}
#[test]
fn test_standard_build_env_exports_makeflags() {
let mut spec = mk_spec(Vec::new(), Vec::new());
spec.build.flags.makeflags = "-j12 --output-sync=target".to_string();
let env = standard_build_env(&spec, None, true, true);
assert!(
env.iter()
.any(|(k, v)| k == "MAKEFLAGS" && v == "-j12 --output-sync=target"),
"expected MAKEFLAGS to be exported from build flags"
);
}
#[test]
fn test_standard_build_env_exports_passthrough_env() {
let mut spec = mk_spec(Vec::new(), Vec::new());
+8
View File
@@ -972,6 +972,12 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
Value::String(spec.build.flags.carch.clone()),
);
}
if !spec.build.flags.makeflags.is_empty() {
flags_tbl.insert(
"makeflags".into(),
Value::String(spec.build.flags.makeflags.clone()),
);
}
if !spec.build.flags.make_vars.is_empty() {
flags_tbl.insert(
"make_vars".into(),
@@ -1475,6 +1481,7 @@ mod tests {
flags.no_delete_static = true;
flags.no_compress_man = true;
flags.skip_tests = true;
flags.makeflags = "-j12 --output-sync=target".into();
flags.make_vars = vec!["V=1".into()];
flags.make_dirs = vec!["lib".into(), "libelf".into()];
flags.make_test_vars = vec!["TESTS=unit".into()];
@@ -1533,6 +1540,7 @@ mod tests {
assert!(toml.contains("no_delete_static = true"));
assert!(toml.contains("no_compress_man = true"));
assert!(toml.contains("skip_tests = true"));
assert!(toml.contains("makeflags = \"-j12 --output-sync=target\""));
assert!(toml.contains("make_vars = ["));
assert!(toml.contains("make_dirs = ["));
assert!(toml.contains("make_test_vars = ["));
+116
View File
@@ -346,6 +346,19 @@ impl PackageSpec {
self.build.flags.carch = s.to_string();
}
}
"makeflags" | "make_flags" | "make-flags" => {
if let Some(s) = v.as_str() {
self.build.flags.makeflags = s.to_string();
} else if let Some(arr) = v.as_array() {
self.build.flags.makeflags = arr
.iter()
.filter_map(|x| x.as_str())
.map(str::trim)
.filter(|x| !x.is_empty())
.collect::<Vec<_>>()
.join(" ");
}
}
"make_vars" | "make-vars" | "make_build_vars" | "make-build-vars" => {
if let Some(arr) = v.as_array() {
self.build.flags.make_vars = arr
@@ -860,6 +873,22 @@ impl PackageSpec {
self.build.flags.carch = s.to_string();
}
}
"makeflags" | "make_flags" | "make-flags" | "MAKEFLAGS" => {
for v in values {
if let Some(arr) = v.as_array() {
let joined = arr
.iter()
.filter_map(|x| x.as_str())
.map(str::trim)
.filter(|x| !x.is_empty())
.collect::<Vec<_>>()
.join(" ");
append_whitespace_separated(&mut self.build.flags.makeflags, &joined);
} else if let Some(s) = v.as_str() {
append_whitespace_separated(&mut self.build.flags.makeflags, s);
}
}
}
"make_vars" | "make-vars" | "make_build_vars" | "make-build-vars" => {
for v in values {
if let Some(arr) = v.as_array() {
@@ -1652,6 +1681,7 @@ cc = "my-cc"
cflags = ["-O2"]
cxxflags = ["-O2", "-pipe"]
passthrough_env = ["RUSTFLAGS"]
makeflags = "-j8"
make_vars = ["V=1"]
make_dirs = ["lib"]
make_test_dirs = ["tests"]
@@ -1709,6 +1739,10 @@ post_configure = ["echo configured"]
"build.flags.make_test_vars".to_string(),
vec![toml::Value::String("TESTS=smoke".to_string())],
);
config.appends.insert(
"build.flags.makeflags".to_string(),
vec![toml::Value::String("--output-sync=target".to_string())],
);
config.appends.insert(
"build.flags.make_dirs".to_string(),
vec![toml::Value::String("libelf".to_string())],
@@ -1782,6 +1816,7 @@ post_configure = ["echo configured"]
.passthrough_env
.contains(&"CARGO_HOME".to_string())
);
assert_eq!(spec.build.flags.makeflags, "-j8 --output-sync=target");
assert!(spec.build.flags.make_vars.contains(&"V=1".to_string()));
assert!(spec.build.flags.make_dirs.contains(&"lib".to_string()));
assert!(spec.build.flags.make_dirs.contains(&"libelf".to_string()));
@@ -2378,6 +2413,39 @@ make_install_dirs = ["lib", "apps"]
);
}
#[test]
fn parse_makeflags_from_spec() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("pkg.toml");
std::fs::write(
&path,
r#"
[package]
name = "foo"
version = "1.0"
description = "d"
homepage = "h"
license = "MIT"
[source]
url = "https://example.com/foo.tar.gz"
sha256 = "skip"
extract_dir = "foo"
[build]
type = "autotools"
[build.flags]
MAKEFLAGS = ["-j12", "--output-sync=target"]
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert_eq!(spec.build.flags.makeflags, "-j12 --output-sync=target");
}
#[test]
fn test_chost_cbuild_overrides() {
let mut spec = mk_spec("foo", "1.0");
@@ -2857,6 +2925,15 @@ pub struct BuildFlags {
/// CPU architecture short name (CARCH equivalent), e.g. "x86_64", "aarch64"
#[serde(default = "default_carch")]
pub carch: String,
/// MAKEFLAGS environment variable passed to build commands.
#[serde(
default,
alias = "make-flags",
alias = "make_flags",
alias = "MAKEFLAGS",
deserialize_with = "deserialize_string_or_array_joined"
)]
pub makeflags: String,
/// Variable overrides passed directly to `make` (compile step), e.g. ["V=1", "CC=clang"].
#[serde(
default,
@@ -3022,6 +3099,7 @@ impl Default for BuildFlags {
chost: String::new(),
cbuild: String::new(),
carch: default_carch(),
makeflags: String::new(),
make_vars: Vec::new(),
make_exec: String::new(),
make_target: String::new(),
@@ -3089,6 +3167,31 @@ where
}
}
fn deserialize_string_or_array_joined<'de, D>(
deserializer: D,
) -> std::result::Result<String, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum StringOrArray {
String(String),
Array(Vec<String>),
}
match Option::<StringOrArray>::deserialize(deserializer)? {
Some(StringOrArray::String(s)) => Ok(s),
Some(StringOrArray::Array(a)) => Ok(a
.iter()
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join(" ")),
None => Ok(String::new()),
}
}
fn deserialize_boolish<'de, D>(deserializer: D) -> std::result::Result<bool, D::Error>
where
D: Deserializer<'de>,
@@ -3127,6 +3230,19 @@ fn toml_value_as_boolish(value: &toml::Value) -> Option<bool> {
})
}
fn append_whitespace_separated(dst: &mut String, value: &str) {
let trimmed = value.trim();
if trimmed.is_empty() {
return;
}
if dst.is_empty() {
dst.push_str(trimmed);
} else {
dst.push(' ');
dst.push_str(trimmed);
}
}
fn default_cc() -> String {
// Prefer clang if available (supports -print-resource-dir and other useful flags)
if std::process::Command::new("which")