deps: resolve :native against DEB_BUILD_ARCH in cross builds

CheckOpts had no build-arch concept: the build-side facts and :native
qualifiers resolved against the host arch, so in a cross build
(-a armhf on amd64) 'Build-Depends: foo:native' looked for an armhf
package where dpkg-checkbuilddeps looks for an amd64 one. CheckOpts
gains build_arch (DEB_BUILD_ARCH), used for :native and the dpkg status
attribution; bracketed arch restrictions keep evaluating against the
host arch.
This commit is contained in:
2026-09-17 18:17:08 +02:00
parent eaf1b40369
commit 12828f8498
2 changed files with 84 additions and 5 deletions
+7 -1
View File
@@ -354,6 +354,10 @@ pub fn run_source_build(
.get("DEB_HOST_ARCH")
.cloned()
.unwrap_or_else(|| crate::debian::arch::native().unwrap_or_default()),
build_arch: arch_vars
.get("DEB_BUILD_ARCH")
.cloned()
.unwrap_or_else(|| crate::debian::arch::native().unwrap_or_default()),
build_profiles: profiles.clone(),
..Default::default()
};
@@ -1599,8 +1603,10 @@ mod differential_tests {
}
i += 1;
}
let host_arch = crate::debian::arch::native().unwrap_or_else(|_| "amd64".into());
let opts = crate::debian::deps::CheckOpts {
host_arch: crate::debian::arch::native().unwrap_or_else(|_| "amd64".into()),
host_arch: host_arch.clone(),
build_arch: host_arch,
build_profiles: profiles,
ignore_arch,
ignore_indep,
+77 -4
View File
@@ -785,8 +785,15 @@ impl Facts {
/// Options for [`check_build_depends`].
#[derive(Debug, Clone)]
pub struct CheckOpts {
/// Host architecture (defaults to the native architecture).
/// Host architecture, i.e. `DEB_HOST_ARCH`: the architecture the
/// packages are built FOR (defaults to the native architecture).
/// Bracketed `foo [arch]` restrictions evaluate against it.
pub host_arch: String,
/// Build architecture, i.e. `DEB_BUILD_ARCH`: the architecture the
/// build runs ON (defaults to the native architecture). `:native`
/// dependency qualifiers and the dpkg status attribution of the
/// build-side facts resolve against it.
pub build_arch: String,
/// Active build profiles.
pub build_profiles: Vec<String>,
/// Ignore `Build-Depends-Arch`/`Build-Conflicts-Arch` (`-A`).
@@ -804,6 +811,7 @@ impl Default for CheckOpts {
fn default() -> Self {
CheckOpts {
host_arch: arch::native().unwrap_or_default(),
build_arch: arch::native().unwrap_or_default(),
build_profiles: Vec::new(),
ignore_arch: false,
ignore_indep: false,
@@ -904,14 +912,14 @@ pub fn check_build_depends(control: &ControlInfo, opts: &CheckOpts) -> Result<Un
let bc_value = bc_parts.join(", ");
let status_path = opts.admindir.join("status");
let facts = Facts::load_status(&status_path, &opts.host_arch, &opts.host_arch)?;
let facts = Facts::load_status(&status_path, &opts.host_arch, &opts.build_arch)?;
let mut report = UnmetReport::default();
if !bd_value.trim().is_empty() {
let parse_opts = ParseOpts {
host_arch: opts.host_arch.clone(),
build_arch: opts.host_arch.clone(),
build_arch: opts.build_arch.clone(),
build_profiles: opts.build_profiles.clone(),
reduce_restrictions: true,
union: false,
@@ -933,7 +941,7 @@ pub fn check_build_depends(control: &ControlInfo, opts: &CheckOpts) -> Result<Un
if !bc_value.trim().is_empty() {
let parse_opts = ParseOpts {
host_arch: opts.host_arch.clone(),
build_arch: opts.host_arch.clone(),
build_arch: opts.build_arch.clone(),
build_profiles: opts.build_profiles.clone(),
reduce_restrictions: true,
union: true,
@@ -1363,4 +1371,69 @@ Architecture: amd64
let report = check_build_depends(&control_conflict, &opts).unwrap();
assert_eq!(report.message(), "unmet build conflicts: mypackage");
}
#[test]
fn check_build_depends_cross_native_qualifier() {
let dir = tempfile::tempdir().unwrap();
let admindir = dir.path();
// Cross build: armhf packages built ON an amd64 machine, so
// DEB_HOST_ARCH=armhf but DEB_BUILD_ARCH=amd64.
let opts = CheckOpts {
host_arch: "armhf".to_string(),
build_arch: "amd64".to_string(),
admindir: admindir.to_path_buf(),
..Default::default()
};
let control_for = |bd: &str| {
ControlInfo::parse_content(&format!(
"Source: t\nMaintainer: a <a@b.c>\nBuild-Depends: {bd}\n\nPackage: t\nArchitecture: any\nDescription: x\n y\n"
))
.unwrap()
};
// foo is only installed for the build architecture, like a native
// toolchain package pulled in on the build machine.
std::fs::write(
admindir.join("status"),
"\
Package: foo
Status: install ok installed
Version: 1.0
Architecture: amd64
",
)
.unwrap();
// `:native` resolves against the BUILD architecture: satisfied even
// though the host architecture is armhf.
let report = check_build_depends(&control_for("foo:native"), &opts).unwrap();
assert!(report.is_ok());
// Bracketed architecture restrictions keep evaluating against the
// HOST architecture: the amd64 instance cannot satisfy `foo [armhf]`.
// (The applied restriction reduces away, per dpkg's reduce_arch.)
let report = check_build_depends(&control_for("foo [armhf]"), &opts).unwrap();
assert_eq!(report.message(), "unmet build dependencies: foo");
// Now foo is only installed for the host architecture.
std::fs::write(
admindir.join("status"),
"\
Package: foo
Status: install ok installed
Version: 1.0
Architecture: armhf
",
)
.unwrap();
// `:native` no longer matches: no amd64 instance is installed.
let report = check_build_depends(&control_for("foo:native"), &opts).unwrap();
assert_eq!(report.message(), "unmet build dependencies: foo:native");
// The host-arch restriction matches the armhf instance.
let report = check_build_depends(&control_for("foo [armhf]"), &opts).unwrap();
assert!(report.is_ok());
}
}