diff --git a/src/commands/build_cmd.rs b/src/commands/build_cmd.rs index 2db5e65..16a342f 100644 --- a/src/commands/build_cmd.rs +++ b/src/commands/build_cmd.rs @@ -22,6 +22,15 @@ pub(crate) fn build_env_rootfs(rootfs: &Path) -> String { .into_owned() } +fn derive_built_against_for_package( + mut pkg_spec: package::PackageSpec, + db_path: &Path, +) -> Result { + pkg_spec.package.built_against = + deps::derive_built_against_for_runtime_deps(&pkg_spec, db_path)?; + Ok(pkg_spec) +} + pub(super) fn run_build(args: BuildArgs, cli_test_deps: bool) -> Result<()> { let BuildArgs { rootfs_args, @@ -366,13 +375,14 @@ pub(super) fn run_build(args: BuildArgs, cli_test_deps: bool) -> Result<()> { let arch = cross_prefix.as_deref().unwrap_or(std::env::consts::ARCH); let mut created_files = Vec::new(); - let staged_outputs = if !lib32_only { + let mut staged_outputs = if !lib32_only { staged_output_specs(&pkg_spec, &destdir)? } else { Vec::new() }; if !lib32_only { - for (spec_for_out, out_destdir) in &staged_outputs { + for (spec_for_out, out_destdir) in &mut staged_outputs { + *spec_for_out = derive_built_against_for_package(spec_for_out.clone(), &db_path)?; let packager = package::Packager::new( spec_for_out.clone(), out_destdir.clone(), @@ -395,6 +405,7 @@ pub(super) fn run_build(args: BuildArgs, cli_test_deps: bool) -> Result<()> { !no_flags, lib32_only, )? { + let lib32_spec = derive_built_against_for_package(lib32_spec, &db_path)?; let packager = package::Packager::new(lib32_spec.clone(), lib32_destdir.clone(), config.clone()); let pkg_file = packager.create_package(Path::new("."), arch)?; diff --git a/src/db/mod.rs b/src/db/mod.rs index 97a1207..cc6d8e6 100755 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1124,6 +1124,54 @@ pub(crate) fn get_dependency_version(db_path: &Path, name: &str) -> Result Result> { + if !db_path.exists() { + return Ok(None); + } + + let conn = Connection::open(db_path)?; + init_db(&conn)?; + let provider = conn + .query_row( + "SELECT p.name + FROM packages p + WHERE p.abi_breaking = 1 + AND ( + p.name = ?1 + OR p.real_name = ?1 + OR EXISTS ( + SELECT 1 FROM provides pr + WHERE pr.package_id = p.id AND pr.provides_name = ?1 + ) + OR EXISTS ( + SELECT 1 FROM replaces r + WHERE r.package_id = p.id AND r.replaces_name = ?1 + ) + ) + ORDER BY + CASE + WHEN p.name = ?1 THEN 0 + WHEN p.real_name = ?1 THEN 1 + ELSE 2 + END, + p.name + LIMIT 1", + params![name], + |row| row.get(0), + ) + .optional()?; + Ok(provider) +} + /// Find the installed package that owns a filesystem path from the local DB. pub fn owns_path(db_path: &Path, path: &Path) -> Result> { if !db_path.exists() { diff --git a/src/deps.rs b/src/deps.rs index 11f725f..2417217 100755 --- a/src/deps.rs +++ b/src/deps.rs @@ -230,6 +230,27 @@ pub(crate) fn declared_test_deps(spec: &PackageSpec, outputs: RequestedOutputs) deps } +/// Derive concrete ABI-breaking provider packages for an output's runtime deps. +/// +/// This is intentionally computed from the installed database after dependency +/// installation, not from static package spec metadata. For a dependency such +/// as `libressl`, this records the currently installed concrete ABI package +/// name, for example `libressl43`. +pub(crate) fn derive_built_against_for_runtime_deps( + spec: &PackageSpec, + db_path: &Path, +) -> Result> { + let mut built_against = Vec::new(); + for dep in &spec.dependencies.runtime { + if let Some(provider) = + db::get_abi_breaking_provider_for_dependency(db_path, dep_name(dep))? + { + push_unique(&mut built_against, provider); + } + } + Ok(built_against) +} + /// Check if all build dependencies are satisfied for the selected outputs. pub(crate) fn check_build_deps_for_outputs( spec: &PackageSpec, @@ -692,4 +713,28 @@ mod tests { assert!(is_dep_satisfied_in_db("libressl", &db_path).unwrap()); assert!(is_dep_satisfied_in_db("libressl>=4.3.0", &db_path).unwrap()); } + + #[test] + fn test_derive_built_against_uses_installed_abi_breaking_provider() { + let tmp = tempfile::tempdir().unwrap(); + let db_path = tmp.path().join("packages.db"); + let destdir = tmp.path().join("dest"); + std::fs::create_dir_all(destdir.join("usr/lib")).unwrap(); + std::fs::write(destdir.join("usr/lib/libssl.so"), "ssl").unwrap(); + + let mut provider = test_spec_with_build(BuildType::Custom, None, &[]); + provider.package.name = "libressl43".into(); + provider.package.real_name = Some("libressl".into()); + provider.package.version = "4.3.2".into(); + provider.package.abi_breaking = true; + + crate::db::register_package(&db_path, &provider, &destdir).unwrap(); + + let mut consumer = test_spec_with_build(BuildType::Custom, None, &[]); + consumer.package.name = "libssh2".into(); + consumer.dependencies.runtime = vec!["zlib".into(), "libressl>=4.3.0".into()]; + + let built_against = derive_built_against_for_runtime_deps(&consumer, &db_path).unwrap(); + assert_eq!(built_against, vec!["libressl43".to_string()]); + } }