Refactor and enhance source management and installation processes

- Improved error handling and context messages in `checkout` function in `git.rs`.
- Updated license field in package specification to use a vector in `hooks.rs`.
- Enhanced `copy_manual_sources` function in `mod.rs` to support both local file and remote URL sources, including checksum verification.
- Added new utility functions for path normalization and skipping installation of specific files in `staging/mod.rs`.
- Implemented logic to handle existing files during installation, allowing for `.depotnew` suffix for kept files.
- Added tests for manual source copying, installation behavior, and path validation.
- Introduced a new LICENSE file with MIT License terms.
This commit is contained in:
2026-02-21 13:25:14 -06:00
parent af0571c33b
commit c9bed308e2
27 changed files with 3868 additions and 939 deletions
+784 -129
View File
File diff suppressed because it is too large Load Diff
+49 -2
View File
@@ -8,6 +8,19 @@ use std::path::{Path, PathBuf};
use tar::Builder;
use zstd::stream::write::Encoder;
fn license_value(licenses: &[String]) -> toml::Value {
if licenses.len() == 1 {
toml::Value::String(licenses[0].clone())
} else {
toml::Value::Array(
licenses
.iter()
.map(|license| toml::Value::String(license.clone()))
.collect(),
)
}
}
pub struct Packager {
pub spec: PackageSpec,
pub destdir: PathBuf,
@@ -119,7 +132,7 @@ impl Packager {
);
map.insert(
"license".to_string(),
toml::Value::String(self.spec.package.license.clone()),
license_value(&self.spec.package.license),
);
// Add provides
@@ -159,6 +172,17 @@ impl Packager {
.collect(),
),
);
build_deps.insert(
"test".to_string(),
toml::Value::Array(
self.spec
.dependencies
.test
.iter()
.map(|s| toml::Value::String(s.clone()))
.collect(),
),
);
map.insert("dependencies".to_string(), toml::Value::Table(build_deps));
let toml_str = toml::to_string(&toml::Value::Table(map))
@@ -229,7 +253,7 @@ mod tests {
revision: 1,
description: "d".into(),
homepage: "h".into(),
license: "MIT".into(),
license: vec!["MIT".into()],
},
packages: Vec::new(),
alternatives: Alternatives::default(),
@@ -301,5 +325,28 @@ mod tests {
let deps = val.get("dependencies").unwrap();
assert!(deps.get("build").unwrap().as_array().unwrap().is_empty());
assert!(deps.get("runtime").unwrap().as_array().unwrap().is_empty());
assert!(deps.get("test").unwrap().as_array().unwrap().is_empty());
}
#[test]
fn test_generate_metadata_toml_with_multiple_licenses() {
let tmp = tempfile::tempdir().unwrap();
let dest = tmp.path();
let mut packager = mk_packager(dest.to_path_buf());
packager.spec.package.license = vec!["MIT".into(), "Apache-2.0".into()];
packager.generate_metadata_toml().unwrap();
let meta_path = dest.join(".metadata.toml");
let content = fs::read_to_string(meta_path).unwrap();
let val: toml::Value = toml::from_str(&content).unwrap();
let arr = val
.get("license")
.and_then(|v| v.as_array())
.expect("license should be an array");
assert_eq!(arr.len(), 2);
assert_eq!(arr[0].as_str(), Some("MIT"));
assert_eq!(arr[1].as_str(), Some("Apache-2.0"));
}
}
+765 -10
View File
@@ -3,6 +3,8 @@
use anyhow::{Context, Result};
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use std::fmt;
use std::fs;
use std::path::Path;
@@ -52,10 +54,43 @@ impl PackageSpec {
if spec.source.is_empty() && spec.manual_sources.is_empty() {
anyhow::bail!("Package must have at least one source or manual_sources entry");
}
spec.validate_manual_sources()?;
Ok(spec)
}
fn validate_manual_sources(&self) -> Result<()> {
for (idx, manual) in self.manual_sources.iter().enumerate() {
let has_file = manual
.file
.as_ref()
.map(|s| !s.trim().is_empty())
.unwrap_or(false);
let has_url = manual
.url
.as_ref()
.map(|s| !s.trim().is_empty())
.unwrap_or(false);
match (has_file, has_url) {
(true, false) | (false, true) => {}
(false, false) => {
anyhow::bail!(
"manual_sources[{}] must specify exactly one of 'file' or 'url'",
idx
);
}
(true, true) => {
anyhow::bail!(
"manual_sources[{}] cannot specify both 'file' and 'url'",
idx
);
}
}
}
Ok(())
}
/// Expand variables like $name and $version in a string
pub fn expand_vars(&self, input: &str) -> String {
let specdir = self.spec_dir.to_string_lossy();
@@ -127,6 +162,17 @@ impl PackageSpec {
self.build.flags.ldflags = vec![s.to_string()];
}
}
"keep" => {
if let Some(arr) = v.as_array() {
self.build.flags.keep = arr
.iter()
.filter_map(|x| x.as_str())
.map(String::from)
.collect();
} else if let Some(s) = v.as_str() {
self.build.flags.keep = vec![s.to_string()];
}
}
"cc" => {
if let Some(s) = v.as_str() {
self.build.flags.cc = s.to_string();
@@ -162,6 +208,65 @@ impl PackageSpec {
self.build.flags.carch = s.to_string();
}
}
"make_vars" | "make-vars" | "make_build_vars" | "make-build-vars" => {
if let Some(arr) = v.as_array() {
self.build.flags.make_vars = arr
.iter()
.filter_map(|x| x.as_str())
.map(String::from)
.collect();
} else if let Some(s) = v.as_str() {
self.build.flags.make_vars =
s.split_whitespace().map(String::from).collect();
}
}
"make_test_vars" | "make-test-vars" => {
if let Some(arr) = v.as_array() {
self.build.flags.make_test_vars = arr
.iter()
.filter_map(|x| x.as_str())
.map(String::from)
.collect();
} else if let Some(s) = v.as_str() {
self.build.flags.make_test_vars =
s.split_whitespace().map(String::from).collect();
}
}
"make_install_vars" | "make-install-vars" => {
if let Some(arr) = v.as_array() {
self.build.flags.make_install_vars = arr
.iter()
.filter_map(|x| x.as_str())
.map(String::from)
.collect();
} else if let Some(s) = v.as_str() {
self.build.flags.make_install_vars =
s.split_whitespace().map(String::from).collect();
}
}
"passthrough_env" | "passthrough-env" | "pass_env" | "pass-env" | "export_env"
| "export-env" => {
if let Some(arr) = v.as_array() {
self.build.flags.passthrough_env = arr
.iter()
.filter_map(|x| x.as_str())
.map(String::from)
.collect();
} else if let Some(s) = v.as_str() {
self.build.flags.passthrough_env =
s.split_whitespace().map(String::from).collect();
}
}
"no_flags" | "no-flags" => {
if let Some(b) = v.as_bool() {
self.build.flags.no_flags = b;
}
}
"skip_tests" | "skip-tests" => {
if let Some(b) = v.as_bool() {
self.build.flags.skip_tests = b;
}
}
// Add more fields as needed
_ => {}
}
@@ -194,6 +299,18 @@ impl PackageSpec {
}
}
}
"keep" => {
for v in values {
if let Some(arr) = v.as_array() {
self.build
.flags
.keep
.extend(arr.iter().filter_map(|x| x.as_str()).map(String::from));
} else if let Some(s) = v.as_str() {
self.build.flags.keep.push(s.to_string());
}
}
}
"configure" => {
for v in values {
if let Some(arr) = v.as_array() {
@@ -260,6 +377,77 @@ impl PackageSpec {
self.build.flags.carch = s.to_string();
}
}
"make_vars" | "make-vars" | "make_build_vars" | "make-build-vars" => {
for v in values {
if let Some(arr) = v.as_array() {
self.build
.flags
.make_vars
.extend(arr.iter().filter_map(|x| x.as_str()).map(String::from));
} else if let Some(s) = v.as_str() {
self.build
.flags
.make_vars
.extend(s.split_whitespace().map(String::from));
}
}
}
"make_test_vars" | "make-test-vars" => {
for v in values {
if let Some(arr) = v.as_array() {
self.build
.flags
.make_test_vars
.extend(arr.iter().filter_map(|x| x.as_str()).map(String::from));
} else if let Some(s) = v.as_str() {
self.build
.flags
.make_test_vars
.extend(s.split_whitespace().map(String::from));
}
}
}
"make_install_vars" | "make-install-vars" => {
for v in values {
if let Some(arr) = v.as_array() {
self.build
.flags
.make_install_vars
.extend(arr.iter().filter_map(|x| x.as_str()).map(String::from));
} else if let Some(s) = v.as_str() {
self.build
.flags
.make_install_vars
.extend(s.split_whitespace().map(String::from));
}
}
}
"passthrough_env" | "passthrough-env" | "pass_env" | "pass-env" | "export_env"
| "export-env" => {
for v in values {
if let Some(arr) = v.as_array() {
self.build
.flags
.passthrough_env
.extend(arr.iter().filter_map(|x| x.as_str()).map(String::from));
} else if let Some(s) = v.as_str() {
self.build
.flags
.passthrough_env
.extend(s.split_whitespace().map(String::from));
}
}
}
"no_flags" | "no-flags" => {
if let Some(b) = values.last().and_then(|v| v.as_bool()) {
self.build.flags.no_flags = b;
}
}
"skip_tests" | "skip-tests" => {
if let Some(b) = values.last().and_then(|v| v.as_bool()) {
self.build.flags.skip_tests = b;
}
}
_ => {}
}
}
@@ -344,6 +532,39 @@ type = "custom"
assert_eq!(spec.sources()[1].extract_dir, "bar");
}
#[test]
fn parse_multiple_licenses() {
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", "Apache-2.0"]
[source]
url = "https://example.com/foo.tar.gz"
sha256 = "skip"
extract_dir = "foo"
[build]
type = "custom"
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert_eq!(
spec.package.license,
vec!["MIT".to_string(), "Apache-2.0".to_string()]
);
}
#[test]
fn parse_rejects_empty_sources() {
let tmp = tempfile::tempdir().unwrap();
@@ -371,6 +592,101 @@ type = "custom"
assert!(PackageSpec::from_file(&path).is_err());
}
#[test]
fn parse_manual_source_with_url() {
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"
[[manual_sources]]
url = "https://example.com/manual.patch"
sha256 = "skip"
dest = "patches/manual.patch"
[build]
type = "custom"
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert_eq!(spec.manual_sources.len(), 1);
assert_eq!(
spec.manual_sources[0].url.as_deref(),
Some("https://example.com/manual.patch")
);
assert_eq!(spec.manual_sources[0].file, None);
}
#[test]
fn parse_manual_source_rejects_missing_file_and_url() {
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"
[[manual_sources]]
sha256 = "skip"
[build]
type = "custom"
"#,
)
.unwrap();
let err = PackageSpec::from_file(&path).expect_err("spec should be rejected");
assert!(err.to_string().contains("exactly one of 'file' or 'url'"));
}
#[test]
fn parse_manual_source_rejects_file_and_url_together() {
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"
[[manual_sources]]
file = "manual.patch"
url = "https://example.com/manual.patch"
[build]
type = "custom"
"#,
)
.unwrap();
let err = PackageSpec::from_file(&path).expect_err("spec should be rejected");
assert!(
err.to_string()
.contains("cannot specify both 'file' and 'url'")
);
}
#[test]
fn test_apply_config() {
let mut spec = mk_spec("foo", "1.0");
@@ -382,6 +698,11 @@ type = "custom"
[flags]
cc = "my-cc"
cflags = ["-O2"]
passthrough_env = ["RUSTFLAGS"]
make_vars = ["V=1"]
no_flags = true
skip_tests = true
keep = ["etc/locale.gen"]
"#,
)
.unwrap();
@@ -396,6 +717,24 @@ cflags = ["-O2"]
toml::Value::String("opt-level=3".to_string()),
])],
);
config.appends.insert(
"build.flags.keep".to_string(),
vec![toml::Value::Array(vec![toml::Value::String(
"etc/locale.gen".to_string(),
)])],
);
config.appends.insert(
"build.flags.passthrough_env".to_string(),
vec![toml::Value::String("CARGO_HOME".to_string())],
);
config.appends.insert(
"build.flags.make_test_vars".to_string(),
vec![toml::Value::String("TESTS=smoke".to_string())],
);
config.appends.insert(
"build.flags.make_install_vars".to_string(),
vec![toml::Value::String("DESTDIR=/tmp/pkg".to_string())],
);
spec.apply_config(&config);
@@ -409,6 +748,326 @@ cflags = ["-O2"]
.rustflags
.contains(&"opt-level=3".to_string())
);
assert!(spec.build.flags.no_flags);
assert!(
spec.build
.flags
.keep
.contains(&"etc/locale.gen".to_string())
);
assert!(
spec.build
.flags
.passthrough_env
.contains(&"RUSTFLAGS".to_string())
);
assert!(
spec.build
.flags
.passthrough_env
.contains(&"CARGO_HOME".to_string())
);
assert!(spec.build.flags.make_vars.contains(&"V=1".to_string()));
assert!(spec.build.flags.skip_tests);
assert!(
spec.build
.flags
.make_test_vars
.contains(&"TESTS=smoke".to_string())
);
assert!(
spec.build
.flags
.make_install_vars
.contains(&"DESTDIR=/tmp/pkg".to_string())
);
}
#[test]
fn parse_no_flags_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 = "custom"
[build.flags]
no_flags = true
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert!(spec.build.flags.no_flags);
}
#[test]
fn parse_no_flags_alias_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 = "custom"
[build.flags]
"no-flags" = true
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert!(spec.build.flags.no_flags);
}
#[test]
fn parse_skip_tests_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]
skip_tests = true
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert!(spec.build.flags.skip_tests);
}
#[test]
fn parse_skip_tests_alias_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]
"skip-tests" = true
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert!(spec.build.flags.skip_tests);
}
#[test]
fn parse_keep_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 = "custom"
[build.flags]
keep = ["etc/locale.gen", "etc/resolv.conf"]
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert_eq!(
spec.build.flags.keep,
vec!["etc/locale.gen".to_string(), "etc/resolv.conf".to_string()]
);
}
#[test]
fn parse_passthrough_env_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 = "custom"
[build.flags]
passthrough_env = ["RUSTFLAGS", "CARGO_HOME"]
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert_eq!(
spec.build.flags.passthrough_env,
vec!["RUSTFLAGS".to_string(), "CARGO_HOME".to_string()]
);
}
#[test]
fn parse_test_dependencies_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"
[dependencies]
build = ["make"]
test = ["python", "bats"]
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert_eq!(
spec.dependencies.test,
vec!["python".to_string(), "bats".to_string()]
);
}
#[test]
fn parse_make_var_overrides_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]
make_vars = ["V=1", "CC=clang"]
make_test_vars = ["TESTS=unit"]
make_install_vars = ["STRIPPROG=true"]
"#,
)
.unwrap();
let spec = PackageSpec::from_file(&path).unwrap();
assert_eq!(
spec.build.flags.make_vars,
vec!["V=1".to_string(), "CC=clang".to_string()]
);
assert_eq!(
spec.build.flags.make_test_vars,
vec!["TESTS=unit".to_string()]
);
assert_eq!(
spec.build.flags.make_install_vars,
vec!["STRIPPROG=true".to_string()]
);
}
#[test]
@@ -444,9 +1103,12 @@ cbuild = "x86_64-pc-linux-gnu"
// Override via config
let mut config = crate::config::Config::for_rootfs(Path::new("/tmp/nonexistent"));
config.build_overrides = toml::from_str(r#"[flags]
config.build_overrides = toml::from_str(
r#"[flags]
carch = "armv7"
"#).unwrap();
"#,
)
.unwrap();
spec.apply_config(&config);
assert_eq!(spec.build.flags.carch, "armv7");
}
@@ -509,7 +1171,7 @@ type = "custom"
revision: 1,
description: "d".into(),
homepage: "h".into(),
license: "MIT".into(),
license: vec!["MIT".into()],
},
packages: Vec::new(),
alternatives: Alternatives::default(),
@@ -540,7 +1202,7 @@ impl fmt::Display for PackageSpec {
)?;
writeln!(f, "Description: {}", self.package.description)?;
writeln!(f, "Homepage: {}", self.package.homepage)?;
writeln!(f, "License: {}", self.package.license)?;
writeln!(f, "License: {}", self.package.license.join(", "))?;
writeln!(f, "Sources: {}", self.source.len())?;
writeln!(f, "Build Type: {:?}", self.build.build_type)?;
if !self.alternatives.provides.is_empty() {
@@ -560,13 +1222,45 @@ pub struct PackageInfo {
pub revision: u32,
pub description: String,
pub homepage: String,
pub license: String,
#[serde(
deserialize_with = "deserialize_licenses",
serialize_with = "serialize_licenses"
)]
pub license: Vec<String>,
}
fn default_revision() -> u32 {
1
}
fn deserialize_licenses<'de, D>(deserializer: D) -> std::result::Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum StringOrArray {
String(String),
Array(Vec<String>),
}
match StringOrArray::deserialize(deserializer)? {
StringOrArray::String(s) => Ok(vec![s]),
StringOrArray::Array(v) => Ok(v),
}
}
fn serialize_licenses<S>(licenses: &[String], serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
if licenses.len() == 1 {
serializer.serialize_str(&licenses[0])
} else {
licenses.serialize(serializer)
}
}
impl PackageSpec {
/// Generate the standard package filename: <name>-<version>-<revision>-<arch>.depot.pkg.tar.zst
pub fn package_filename(&self, arch: &str) -> String {
@@ -613,15 +1307,20 @@ pub struct Source {
pub post_extract: Vec<String>,
}
/// Manual (local) source file to copy before fetching remote sources.
/// Manual source copied before standard source fetching.
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct ManualSource {
/// Filename in the spec directory
pub file: String,
/// SHA256 checksum (optional, use "skip" to bypass verification)
/// Filename in the spec directory (local manual source mode).
#[serde(default)]
pub file: Option<String>,
/// Remote URL to fetch (remote manual source mode).
#[serde(default)]
pub url: Option<String>,
/// Checksum (optional, use "skip" to bypass verification).
#[serde(default)]
pub sha256: Option<String>,
/// Destination path relative to build work directory (default: same as file)
/// Destination path relative to build work directory.
/// Defaults to `file` for local mode or a derived filename for URL mode.
#[serde(default)]
pub dest: Option<String>,
}
@@ -675,6 +1374,15 @@ pub struct BuildFlags {
pub cflags: Vec<String>,
#[serde(default, deserialize_with = "deserialize_string_or_array")]
pub ldflags: Vec<String>,
/// Keep existing files and install package-provided replacement as `<path>.depotnew`.
#[serde(default, deserialize_with = "deserialize_string_or_array")]
pub keep: Vec<String>,
/// Disable exporting CFLAGS/CXXFLAGS/LDFLAGS for this package build.
#[serde(default, alias = "no-flags")]
pub no_flags: bool,
/// Skip automatic build-system test execution (e.g. Autotools `make check`/`make test`).
#[serde(default, alias = "skip-tests")]
pub skip_tests: bool,
#[serde(default)]
pub configure: Vec<String>,
/// C compiler
@@ -721,6 +1429,41 @@ pub struct BuildFlags {
/// CPU architecture short name (CARCH equivalent), e.g. "x86_64", "aarch64"
#[serde(default = "default_carch")]
pub carch: String,
/// Variable overrides passed directly to `make` (compile step), e.g. ["V=1", "CC=clang"].
#[serde(
default,
alias = "make-vars",
alias = "make_build_vars",
alias = "make-build-vars",
deserialize_with = "deserialize_string_or_array"
)]
pub make_vars: Vec<String>,
/// Variable overrides passed directly to `make check` / `make test`.
#[serde(
default,
alias = "make-test-vars",
deserialize_with = "deserialize_string_or_array"
)]
pub make_test_vars: Vec<String>,
/// Variable overrides passed directly to `make install`.
#[serde(
default,
alias = "make-install-vars",
deserialize_with = "deserialize_string_or_array"
)]
pub make_install_vars: Vec<String>,
/// Additional host environment variable names to export unchanged to build commands.
/// Example: ["RUSTFLAGS", "CARGO_HOME"].
#[serde(
default,
alias = "passthrough-env",
alias = "pass_env",
alias = "pass-env",
alias = "export_env",
alias = "export-env",
deserialize_with = "deserialize_string_or_array"
)]
pub passthrough_env: Vec<String>,
// Rust-specific fields
/// Rust build profile: "debug" or "release" (default: release)
@@ -756,6 +1499,9 @@ impl Default for BuildFlags {
BuildFlags {
cflags: Vec::new(),
ldflags: Vec::new(),
keep: Vec::new(),
no_flags: false,
skip_tests: false,
configure: Vec::new(),
cc: default_cc(),
cxx: default_cxx(),
@@ -770,6 +1516,10 @@ impl Default for BuildFlags {
chost: String::new(),
cbuild: String::new(),
carch: default_carch(),
make_vars: Vec::new(),
make_test_vars: Vec::new(),
make_install_vars: Vec::new(),
passthrough_env: Vec::new(),
profile: default_profile(),
target: String::new(),
rustflags: Vec::new(),
@@ -852,8 +1602,13 @@ fn default_cxx() -> String {
/// Package dependencies
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct Dependencies {
/// Dependencies required for building packages.
#[serde(default)]
pub build: Vec<String>,
/// Dependencies required at runtime.
#[serde(default)]
pub runtime: Vec<String>,
/// Dependencies required to run package test suites.
#[serde(default)]
pub test: Vec<String>,
}