feat: implement ABI-breaking provider derivation for runtime dependencies
This commit is contained in:
@@ -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<package::PackageSpec> {
|
||||
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)?;
|
||||
|
||||
@@ -1124,6 +1124,54 @@ pub(crate) fn get_dependency_version(db_path: &Path, name: &str) -> Result<Optio
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
/// Return the concrete installed ABI-breaking package satisfying a dependency name.
|
||||
///
|
||||
/// Dependency aliases are matched through the package name, stable `real_name`,
|
||||
/// `provides`, and `replaces` metadata. The returned value is always the
|
||||
/// concrete installed `packages.name` so packages built against renamed streams
|
||||
/// can remember the ABI-carrying package they actually used.
|
||||
pub(crate) fn get_abi_breaking_provider_for_dependency(
|
||||
db_path: &Path,
|
||||
name: &str,
|
||||
) -> Result<Option<String>> {
|
||||
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<Option<String>> {
|
||||
if !db_path.exists() {
|
||||
|
||||
+45
@@ -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<Vec<String>> {
|
||||
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()]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user