deb: scope the arch-indep build-dep pass to the host arch in cross builds
Without --host-architecture, the second build-dep pass re-resolves the whole Build-Depends field for the native architecture: apt swaps host-arch -dev packages for native ones (e.g. libcurl4-gnutls-dev, whose arch-differing curl-config makes dpkg refuse the co-install) and breaks the cross build environment. Per dpkg-checkbuilddeps, both Build-Depends and Build-Depends-Indep resolve for the host architecture in cross mode, so pass --host-architecture to the second pass as well. Skip the pass entirely when the source declares no Build-Depends-Indep. Add an end-to-end regression test building a package that declares libdb-dev in both fields and links a host-arch binary against it: the test only passes if the arch-indep pass did not swap the arm64 -dev packages for native ones.
This commit is contained in:
+29
-16
@@ -276,26 +276,39 @@ pub async fn build(
|
|||||||
return Err("Could not install build-dependencies for the build".into());
|
return Err("Could not install build-dependencies for the build".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install arch-independant build dependencies
|
// Install arch-independant build dependencies, only if the source declares
|
||||||
log::debug!("Installing arch-independant build dependencies...");
|
// any: without --arch-only this pass resolves the whole Build-Depends field
|
||||||
let status = cap(
|
// too, which is redundant after the first pass and breaks cross builds.
|
||||||
ctx.command("apt-get")
|
let has_indep_deps = match ctx.read_file(&package_dir.join("debian/control")) {
|
||||||
.current_dir(package_dir_str)
|
Ok(control) => control
|
||||||
|
.lines()
|
||||||
|
.any(|l| l.to_ascii_lowercase().starts_with("build-depends-indep:")),
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!("cannot read debian/control for indep build-deps: {}", e);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if has_indep_deps {
|
||||||
|
log::debug!("Installing arch-independant build dependencies...");
|
||||||
|
let mut cmd = ctx.command("apt-get");
|
||||||
|
cmd.current_dir(package_dir_str)
|
||||||
.envs(env.clone())
|
.envs(env.clone())
|
||||||
.arg("-y")
|
.arg("-y")
|
||||||
.arg("build-dep")
|
.arg("build-dep");
|
||||||
.arg("./"),
|
if cross {
|
||||||
&sink,
|
cmd.arg(format!("--host-architecture={arch}"));
|
||||||
)
|
}
|
||||||
.status()?;
|
cmd.arg("./");
|
||||||
|
let status = cap(&mut cmd, &sink).status()?;
|
||||||
|
|
||||||
// If build-dep fails, we try to explain the failure using dose-debcheck
|
// If build-dep fails, we try to explain the failure using dose-debcheck
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
if let Some(u) = &ui {
|
if let Some(u) = &ui {
|
||||||
u.suspend();
|
u.suspend();
|
||||||
|
}
|
||||||
|
dose3_explain_dependencies(package, version, arch, build_root, cross, ctx.clone())?;
|
||||||
|
return Err("Could not install build-dependencies for the build".into());
|
||||||
}
|
}
|
||||||
dose3_explain_dependencies(package, version, arch, build_root, cross, ctx.clone())?;
|
|
||||||
return Err("Could not install build-dependencies for the build".into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the build step
|
// Run the build step
|
||||||
|
|||||||
+119
@@ -492,4 +492,123 @@ mod tests {
|
|||||||
async fn test_deb_gcc_debian_end_to_end() {
|
async fn test_deb_gcc_debian_end_to_end() {
|
||||||
test_build_end_to_end("gcc-15", "sid", None, None, false).await;
|
test_build_end_to_end("gcc-15", "sid", None, None, false).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a synthetic source package that discriminates which architecture
|
||||||
|
/// is used to resolve Build-Depends-Indep during cross builds:
|
||||||
|
///
|
||||||
|
/// - 'libdb-dev' is an arch:any package that is not Multi-Arch: same, so an
|
||||||
|
/// amd64 copy can only be installed by replacing the arm64 one
|
||||||
|
/// - the arch-specific binary links against libdb for the host
|
||||||
|
/// architecture, so the build only succeeds if the arm64 libdb-dev was
|
||||||
|
/// left in place by the arch-independant build-dep pass
|
||||||
|
fn create_indep_cross_test_source(parent: &Path) -> PathBuf {
|
||||||
|
let pkg_dir = parent.join("pkh-crosstest");
|
||||||
|
std::fs::create_dir_all(pkg_dir.join("debian/source")).unwrap();
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
pkg_dir.join("debian/changelog"),
|
||||||
|
"pkh-crosstest (1.0) noble; urgency=medium\n\n \
|
||||||
|
* Synthetic package exercising Build-Depends-Indep in cross builds.\n\n \
|
||||||
|
-- pkh tests <pkh@example.com> Tue, 15 Sep 2026 08:00:00 +0000\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
pkg_dir.join("debian/control"),
|
||||||
|
"Source: pkh-crosstest\n\
|
||||||
|
Section: devel\n\
|
||||||
|
Priority: optional\n\
|
||||||
|
Maintainer: pkh tests <pkh@example.com>\n\
|
||||||
|
Standards-Version: 4.7.4\n\
|
||||||
|
Build-Depends: debhelper-compat (= 13), libdb-dev\n\
|
||||||
|
Build-Depends-Indep: libdb-dev\n\
|
||||||
|
Architecture: any all\n\
|
||||||
|
\n\
|
||||||
|
Package: pkh-crosstest\n\
|
||||||
|
Architecture: any\n\
|
||||||
|
Depends: ${misc:Depends}, ${shlibs:Depends}\n\
|
||||||
|
Description: Cross-build regression package for build-dep resolution\n \
|
||||||
|
Builds a host-architecture binary against libdb to detect a cross\n \
|
||||||
|
build environment damaged by a wrongly-scoped build-dep pass.\n\
|
||||||
|
\n\
|
||||||
|
Package: pkh-crosstest-data\n\
|
||||||
|
Architecture: all\n\
|
||||||
|
Description: Cross-build regression package data (arch-indep)\n \
|
||||||
|
Arch-indep binary so the indep build path is exercised.\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
pkg_dir.join("debian/rules"),
|
||||||
|
"#!/usr/bin/make -f\n\
|
||||||
|
%:\n\
|
||||||
|
\tdh $@\n\
|
||||||
|
\n\
|
||||||
|
override_dh_auto_build:\n\
|
||||||
|
\tprintf '#include <db.h>\\nint main(void){DB *d; return db_create(&d, NULL, 0);}\\n' > main.c\n\
|
||||||
|
\t$(DEB_HOST_GNU_TYPE)-gcc main.c -ldb -o pkh-crosstest\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
let rules = pkg_dir.join("debian/rules");
|
||||||
|
let mut perms = std::fs::metadata(&rules).unwrap().permissions();
|
||||||
|
perms.set_mode(0o755);
|
||||||
|
std::fs::set_permissions(&rules, perms).unwrap();
|
||||||
|
|
||||||
|
std::fs::write(pkg_dir.join("debian/source/format"), "3.0 (native)\n").unwrap();
|
||||||
|
|
||||||
|
pkg_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This ensures the arch-independant build-dep pass of a cross build
|
||||||
|
/// resolves dependencies for the host architecture, like dpkg-checkbuilddeps
|
||||||
|
/// does, instead of re-resolving the whole Build-Depends field for the
|
||||||
|
/// native architecture, which swaps host-arch -dev packages for native ones
|
||||||
|
/// and breaks the cross build environment.
|
||||||
|
#[tokio::test]
|
||||||
|
#[test_log::test]
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
async fn test_deb_cross_indep_host_arch_end_to_end() {
|
||||||
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
|
let pkg_dir = create_indep_cross_test_source(temp_dir.path());
|
||||||
|
|
||||||
|
let ctx = Arc::new(Context::new(crate::context::ContextConfig::Local));
|
||||||
|
|
||||||
|
crate::deb::build_binary_package(
|
||||||
|
Some("arm64"),
|
||||||
|
Some("noble"),
|
||||||
|
None,
|
||||||
|
Some(&pkg_dir),
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(ctx),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Cannot cross-build package declaring Build-Depends-Indep");
|
||||||
|
|
||||||
|
// Both binary packages must have been produced, including the
|
||||||
|
// arch-independant one
|
||||||
|
let deb_files: Vec<String> = std::fs::read_dir(temp_dir.path())
|
||||||
|
.unwrap()
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.map(|e| e.file_name().to_string_lossy().to_string())
|
||||||
|
.collect();
|
||||||
|
assert!(
|
||||||
|
deb_files
|
||||||
|
.iter()
|
||||||
|
.any(|f| f.starts_with("pkh-crosstest_1.0_arm64.deb")),
|
||||||
|
"arch-specific .deb not produced, got: {deb_files:?}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
deb_files
|
||||||
|
.iter()
|
||||||
|
.any(|f| f.starts_with("pkh-crosstest-data_1.0_all.deb")),
|
||||||
|
"arch-independant .deb not produced, got: {deb_files:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user