diff --git a/352.patch b/352.patch new file mode 100644 index 0000000..9afa864 --- /dev/null +++ b/352.patch @@ -0,0 +1,574 @@ +From d50a978efb8e538db90dad7128cc74f19ec2bc8d Mon Sep 17 00:00:00 2001 +From: kpcyrd +Date: Sat, 11 Nov 2023 19:45:00 +0100 +Subject: [PATCH 1/2] Add version_suffix_components setting + +--- + README.md | 4 ++ + src/build.rs | 102 +++++++++++++++++++++++++++++++++++++++++- + src/install.rs | 25 ++++++----- + src/lib.rs | 15 ------- + src/pkg_config_gen.rs | 1 + + src/target.rs | 7 ++- + 6 files changed, 122 insertions(+), 32 deletions(-) + +diff --git a/README.md b/README.md +index e53231b..5a651d0 100644 +--- a/README.md ++++ b/README.md +@@ -135,6 +135,10 @@ version = "1.2.3" + install_subdir = "gstreamer-1.0" + # Used to disable versioning links when installing the dynamic library + versioning = false ++# Instead of using semver, select a fixed number of version components for your SONAME version suffix: ++# Setting this to 1 with a version of 0.0.0 allows a suffix of `.so.0` ++# Setting this to 3 always includes the full version in the SONAME (indicate any update is ABI breaking) ++#version_suffix_components = 2 + # Add `-Cpanic=abort` to the RUSTFLAGS automatically, it may be useful in case + # something might panic in the crates used by the library. + rustflags = "-Cpanic=abort" +diff --git a/src/build.rs b/src/build.rs +index 4e8279f..45c4538 100644 +--- a/src/build.rs ++++ b/src/build.rs +@@ -14,6 +14,7 @@ use cargo::util::command_prelude::{ArgMatches, ArgMatchesExt, CompileMode, Profi + use cargo::util::interning::InternedString; + use cargo::{CliResult, Config}; + ++use anyhow::Context as _; + use cargo_util::paths::{copy, create, create_dir_all, open, read, read_bytes, write}; + use semver::Version; + +@@ -406,10 +407,33 @@ pub struct LibraryCApiConfig { + pub version: Version, + pub install_subdir: Option, + pub versioning: bool, ++ pub version_suffix_components: Option, + pub import_library: bool, + pub rustflags: Vec, + } + ++impl LibraryCApiConfig { ++ pub fn sover(&self) -> anyhow::Result { ++ let major = self.version.major; ++ let minor = self.version.minor; ++ let patch = self.version.patch; ++ ++ let sover = match self.version_suffix_components { ++ None => match (major, minor, patch) { ++ (0, 0, patch) => format!("0.0.{patch}"), ++ (0, minor, _) => format!("0.{minor}"), ++ (major, _, _) => format!("{major}"), ++ }, ++ Some(1) => format!("{major}"), ++ Some(2) => format!("{major}.{minor}"), ++ Some(3) => format!("{major}.{minor}.{patch}"), ++ Some(num) => anyhow::bail!("Unexpected number of suffix components: {num}"), ++ }; ++ ++ Ok(sover) ++ } ++} ++ + #[derive(Debug, Default)] + pub struct InstallCApiConfig { + pub include: Vec, +@@ -619,6 +643,7 @@ fn load_manifest_capi_config(pkg: &Package) -> anyhow::Result { + let mut version = pkg.version().clone(); + let mut install_subdir = None; + let mut versioning = true; ++ let mut version_suffix_components = None; + let mut import_library = true; + let mut rustflags = Vec::new(); + +@@ -636,6 +661,17 @@ fn load_manifest_capi_config(pkg: &Package) -> anyhow::Result { + .get("versioning") + .and_then(|v| v.as_bool()) + .unwrap_or(true); ++ ++ if let Some(value) = library.get("version_suffix_components") { ++ let value = value.as_integer().with_context(|| { ++ format!("Value for `version_suffix_components` is not an integer: {value:?}") ++ })?; ++ let value = value ++ .try_into() ++ .with_context(|| format!("Value is too large: {value:?}"))?; ++ version_suffix_components = Some(value); ++ } ++ + import_library = library + .get("import_library") + .and_then(|v| v.as_bool()) +@@ -655,6 +691,7 @@ fn load_manifest_capi_config(pkg: &Package) -> anyhow::Result { + version, + install_subdir, + versioning, ++ version_suffix_components, + import_library, + rustflags, + }; +@@ -886,7 +923,7 @@ fn compile_with_exec( + let pkg_rustflags = &capi_config.library.rustflags; + + let mut leaf_args: Vec = rustc_target +- .shared_object_link_args(&capi_config, &install_paths.libdir, root_output) ++ .shared_object_link_args(&capi_config, &install_paths.libdir, root_output)? + .into_iter() + .flat_map(|l| vec!["-C".to_string(), format!("link-arg={l}")]) + .collect(); +@@ -1246,3 +1283,66 @@ pub fn ctest( + + ops::run_tests(ws, &ops, &test_args) + } ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use semver::Version; ++ ++ fn make_test_library_config(version: &str) -> LibraryCApiConfig { ++ LibraryCApiConfig { ++ name: "example".to_string(), ++ version: Version::parse(version).unwrap(), ++ install_subdir: None, ++ versioning: true, ++ version_suffix_components: None, ++ import_library: true, ++ rustflags: vec![], ++ } ++ } ++ ++ #[test] ++ pub fn test_semver_zero_zero_zero() { ++ let library = make_test_library_config("0.0.0"); ++ let sover = library.sover().unwrap(); ++ assert_eq!(sover, "0.0.0"); ++ } ++ ++ #[test] ++ pub fn test_semver_zero_one_zero() { ++ let library = make_test_library_config("0.1.0"); ++ let sover = library.sover().unwrap(); ++ assert_eq!(sover, "0.1"); ++ } ++ ++ #[test] ++ pub fn test_semver_one_zero_zero() { ++ let library = make_test_library_config("1.0.0"); ++ let sover = library.sover().unwrap(); ++ assert_eq!(sover, "1"); ++ } ++ ++ #[test] ++ pub fn text_one_fixed_zero_zero_zero() { ++ let mut library = make_test_library_config("0.0.0"); ++ library.version_suffix_components = Some(1); ++ let sover = library.sover().unwrap(); ++ assert_eq!(sover, "0"); ++ } ++ ++ #[test] ++ pub fn text_two_fixed_one_zero_zero() { ++ let mut library = make_test_library_config("1.0.0"); ++ library.version_suffix_components = Some(2); ++ let sover = library.sover().unwrap(); ++ assert_eq!(sover, "1.0"); ++ } ++ ++ #[test] ++ pub fn text_three_fixed_one_zero_zero() { ++ let mut library = make_test_library_config("1.0.0"); ++ library.version_suffix_components = Some(3); ++ let sover = library.sover().unwrap(); ++ assert_eq!(sover, "1.0.0"); ++ } ++} +diff --git a/src/install.rs b/src/install.rs +index d0725b9..9ad4104 100644 +--- a/src/install.rs ++++ b/src/install.rs +@@ -3,11 +3,9 @@ use std::path::{Component, Path, PathBuf}; + + use cargo::core::Workspace; + use cargo_util::paths::{copy, create_dir_all}; +-use semver::Version; + + use crate::build::*; + use crate::build_targets::BuildTargets; +-use crate::VersionExt; + + fn append_to_destdir(destdir: Option<&Path>, path: &Path) -> PathBuf { + if let Some(destdir) = destdir { +@@ -107,8 +105,13 @@ pub(crate) struct UnixLibNames { + } + + impl UnixLibNames { +- pub(crate) fn new(lib_type: LibType, lib_name: &str, lib_version: &Version) -> Option { +- let main_version = lib_version.main_version(); ++ pub(crate) fn new( ++ lib_type: LibType, ++ library: &LibraryCApiConfig, ++ ) -> anyhow::Result> { ++ let lib_name = &library.name; ++ let lib_version = &library.version; ++ let main_version = library.sover()?; + + match lib_type { + LibType::So => { +@@ -119,11 +122,11 @@ impl UnixLibNames { + ); + let lib_with_main_ver = format!("{}.{}", lib, main_version); + +- Some(Self { ++ Ok(Some(Self { + canonical: lib, + with_main_ver: lib_with_main_ver, + with_full_ver: lib_with_full_ver, +- }) ++ })) + } + LibType::Dylib => { + let lib = format!("lib{lib_name}.dylib"); +@@ -133,13 +136,13 @@ impl UnixLibNames { + "lib{}.{}.{}.{}.dylib", + lib_name, lib_version.major, lib_version.minor, lib_version.patch + ); +- Some(Self { ++ Ok(Some(Self { + canonical: lib, + with_main_ver: lib_with_main_ver, + with_full_ver: lib_with_full_ver, +- }) ++ })) + } +- LibType::Windows => None, ++ LibType::Windows => Ok(None), + } + } + +@@ -236,12 +239,10 @@ pub fn cinstall(ws: &Workspace, packages: &[CPackage]) -> anyhow::Result<()> { + if let Some(ref shared_lib) = build_targets.shared_lib { + ws.config().shell().status("Installing", "shared library")?; + +- let lib_name = &capi_config.library.name; + let lib_type = LibType::from_build_targets(build_targets); + match lib_type { + LibType::So | LibType::Dylib => { +- let lib = UnixLibNames::new(lib_type, lib_name, &capi_config.library.version) +- .unwrap(); ++ let lib = UnixLibNames::new(lib_type, &capi_config.library)?.unwrap(); + lib.install(capi_config, shared_lib, &install_path_lib)?; + } + LibType::Windows => { +diff --git a/src/lib.rs b/src/lib.rs +index 66d7b9b..c63a297 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -5,18 +5,3 @@ pub mod config; + pub mod install; + pub mod pkg_config_gen; + pub mod target; +- +-trait VersionExt { +- /// build the main version string +- fn main_version(&self) -> String; +-} +- +-impl VersionExt for semver::Version { +- fn main_version(&self) -> String { +- match (self.major, self.minor, self.patch) { +- (0, 0, patch) => format!("0.0.{patch}"), +- (0, minor, _) => format!("0.{minor}"), +- (major, _, _) => format!("{major}"), +- } +- } +-} +diff --git a/src/pkg_config_gen.rs b/src/pkg_config_gen.rs +index 91d8b4d..c825d95 100644 +--- a/src/pkg_config_gen.rs ++++ b/src/pkg_config_gen.rs +@@ -298,6 +298,7 @@ mod test { + version: Version::parse("0.1.0").unwrap(), + install_subdir: None, + versioning: true, ++ version_suffix_components: None, + import_library: true, + rustflags: Vec::default(), + }, +diff --git a/src/target.rs b/src/target.rs +index 136cf54..7ada1be 100644 +--- a/src/target.rs ++++ b/src/target.rs +@@ -3,7 +3,6 @@ use std::path::Path; + use anyhow::*; + + use crate::build::CApiConfig; +-use crate::VersionExt; + + /// Split a target string to its components + /// +@@ -57,7 +56,7 @@ impl Target { + capi_config: &CApiConfig, + libdir: &Path, + target_dir: &Path, +- ) -> Vec { ++ ) -> anyhow::Result> { + let mut lines = Vec::new(); + + let lib_name = &capi_config.library.name; +@@ -70,7 +69,7 @@ impl Target { + let os = &self.os; + let env = &self.env; + +- let sover = version.main_version(); ++ let sover = capi_config.library.sover()?; + + if os == "android" { + lines.push(format!("-Wl,-soname,lib{lib_name}.so")); +@@ -112,6 +111,6 @@ impl Target { + // See: https://github.com/emscripten-core/emscripten/blob/3.1.39/emcc.py#L92-L94 + // else if os == "emscripten" + +- lines ++ Ok(lines) + } + } + +From 3f9e452500b37204efce3091c9d0fd9366393e5b Mon Sep 17 00:00:00 2001 +From: kpcyrd +Date: Sun, 12 Nov 2023 14:47:40 +0100 +Subject: [PATCH 2/2] Refactor to validate version_suffix_components while + parsing + +--- + src/build.rs | 54 ++++++++++++++++++++++++++++---------------------- + src/install.rs | 19 ++++++++---------- + src/target.rs | 6 +++--- + 3 files changed, 41 insertions(+), 38 deletions(-) + +diff --git a/src/build.rs b/src/build.rs +index 45c4538..33b77f2 100644 +--- a/src/build.rs ++++ b/src/build.rs +@@ -401,36 +401,40 @@ pub struct PkgConfigCApiConfig { + pub strip_include_path_components: usize, + } + ++#[derive(Debug)] ++pub enum VersionSuffix { ++ Major, ++ MajorMinor, ++ MajorMinorPatch, ++} ++ + #[derive(Debug)] + pub struct LibraryCApiConfig { + pub name: String, + pub version: Version, + pub install_subdir: Option, + pub versioning: bool, +- pub version_suffix_components: Option, ++ pub version_suffix_components: Option, + pub import_library: bool, + pub rustflags: Vec, + } + + impl LibraryCApiConfig { +- pub fn sover(&self) -> anyhow::Result { ++ pub fn sover(&self) -> String { + let major = self.version.major; + let minor = self.version.minor; + let patch = self.version.patch; + +- let sover = match self.version_suffix_components { ++ match self.version_suffix_components { + None => match (major, minor, patch) { + (0, 0, patch) => format!("0.0.{patch}"), + (0, minor, _) => format!("0.{minor}"), + (major, _, _) => format!("{major}"), + }, +- Some(1) => format!("{major}"), +- Some(2) => format!("{major}.{minor}"), +- Some(3) => format!("{major}.{minor}.{patch}"), +- Some(num) => anyhow::bail!("Unexpected number of suffix components: {num}"), +- }; +- +- Ok(sover) ++ Some(VersionSuffix::Major) => format!("{major}"), ++ Some(VersionSuffix::MajorMinor) => format!("{major}.{minor}"), ++ Some(VersionSuffix::MajorMinorPatch) => format!("{major}.{minor}.{patch}"), ++ } + } + } + +@@ -666,10 +670,12 @@ fn load_manifest_capi_config(pkg: &Package) -> anyhow::Result { + let value = value.as_integer().with_context(|| { + format!("Value for `version_suffix_components` is not an integer: {value:?}") + })?; +- let value = value +- .try_into() +- .with_context(|| format!("Value is too large: {value:?}"))?; +- version_suffix_components = Some(value); ++ version_suffix_components = Some(match value { ++ 1 => VersionSuffix::Major, ++ 2 => VersionSuffix::MajorMinor, ++ 3 => VersionSuffix::MajorMinorPatch, ++ _ => anyhow::bail!("Out of range value for version suffix components: {value}"), ++ }); + } + + import_library = library +@@ -923,7 +929,7 @@ fn compile_with_exec( + let pkg_rustflags = &capi_config.library.rustflags; + + let mut leaf_args: Vec = rustc_target +- .shared_object_link_args(&capi_config, &install_paths.libdir, root_output)? ++ .shared_object_link_args(&capi_config, &install_paths.libdir, root_output) + .into_iter() + .flat_map(|l| vec!["-C".to_string(), format!("link-arg={l}")]) + .collect(); +@@ -1304,45 +1310,45 @@ mod tests { + #[test] + pub fn test_semver_zero_zero_zero() { + let library = make_test_library_config("0.0.0"); +- let sover = library.sover().unwrap(); ++ let sover = library.sover(); + assert_eq!(sover, "0.0.0"); + } + + #[test] + pub fn test_semver_zero_one_zero() { + let library = make_test_library_config("0.1.0"); +- let sover = library.sover().unwrap(); ++ let sover = library.sover(); + assert_eq!(sover, "0.1"); + } + + #[test] + pub fn test_semver_one_zero_zero() { + let library = make_test_library_config("1.0.0"); +- let sover = library.sover().unwrap(); ++ let sover = library.sover(); + assert_eq!(sover, "1"); + } + + #[test] + pub fn text_one_fixed_zero_zero_zero() { + let mut library = make_test_library_config("0.0.0"); +- library.version_suffix_components = Some(1); +- let sover = library.sover().unwrap(); ++ library.version_suffix_components = Some(VersionSuffix::Major); ++ let sover = library.sover(); + assert_eq!(sover, "0"); + } + + #[test] + pub fn text_two_fixed_one_zero_zero() { + let mut library = make_test_library_config("1.0.0"); +- library.version_suffix_components = Some(2); +- let sover = library.sover().unwrap(); ++ library.version_suffix_components = Some(VersionSuffix::MajorMinor); ++ let sover = library.sover(); + assert_eq!(sover, "1.0"); + } + + #[test] + pub fn text_three_fixed_one_zero_zero() { + let mut library = make_test_library_config("1.0.0"); +- library.version_suffix_components = Some(3); +- let sover = library.sover().unwrap(); ++ library.version_suffix_components = Some(VersionSuffix::MajorMinorPatch); ++ let sover = library.sover(); + assert_eq!(sover, "1.0.0"); + } + } +diff --git a/src/install.rs b/src/install.rs +index 9ad4104..c087e74 100644 +--- a/src/install.rs ++++ b/src/install.rs +@@ -105,13 +105,10 @@ pub(crate) struct UnixLibNames { + } + + impl UnixLibNames { +- pub(crate) fn new( +- lib_type: LibType, +- library: &LibraryCApiConfig, +- ) -> anyhow::Result> { ++ pub(crate) fn new(lib_type: LibType, library: &LibraryCApiConfig) -> Option { + let lib_name = &library.name; + let lib_version = &library.version; +- let main_version = library.sover()?; ++ let main_version = library.sover(); + + match lib_type { + LibType::So => { +@@ -122,11 +119,11 @@ impl UnixLibNames { + ); + let lib_with_main_ver = format!("{}.{}", lib, main_version); + +- Ok(Some(Self { ++ Some(Self { + canonical: lib, + with_main_ver: lib_with_main_ver, + with_full_ver: lib_with_full_ver, +- })) ++ }) + } + LibType::Dylib => { + let lib = format!("lib{lib_name}.dylib"); +@@ -136,13 +133,13 @@ impl UnixLibNames { + "lib{}.{}.{}.{}.dylib", + lib_name, lib_version.major, lib_version.minor, lib_version.patch + ); +- Ok(Some(Self { ++ Some(Self { + canonical: lib, + with_main_ver: lib_with_main_ver, + with_full_ver: lib_with_full_ver, +- })) ++ }) + } +- LibType::Windows => Ok(None), ++ LibType::Windows => None, + } + } + +@@ -242,7 +239,7 @@ pub fn cinstall(ws: &Workspace, packages: &[CPackage]) -> anyhow::Result<()> { + let lib_type = LibType::from_build_targets(build_targets); + match lib_type { + LibType::So | LibType::Dylib => { +- let lib = UnixLibNames::new(lib_type, &capi_config.library)?.unwrap(); ++ let lib = UnixLibNames::new(lib_type, &capi_config.library).unwrap(); + lib.install(capi_config, shared_lib, &install_path_lib)?; + } + LibType::Windows => { +diff --git a/src/target.rs b/src/target.rs +index 7ada1be..1cbdef4 100644 +--- a/src/target.rs ++++ b/src/target.rs +@@ -56,7 +56,7 @@ impl Target { + capi_config: &CApiConfig, + libdir: &Path, + target_dir: &Path, +- ) -> anyhow::Result> { ++ ) -> Vec { + let mut lines = Vec::new(); + + let lib_name = &capi_config.library.name; +@@ -69,7 +69,7 @@ impl Target { + let os = &self.os; + let env = &self.env; + +- let sover = capi_config.library.sover()?; ++ let sover = capi_config.library.sover(); + + if os == "android" { + lines.push(format!("-Wl,-soname,lib{lib_name}.so")); +@@ -111,6 +111,6 @@ impl Target { + // See: https://github.com/emscripten-core/emscripten/blob/3.1.39/emcc.py#L92-L94 + // else if os == "emscripten" + +- Ok(lines) ++ lines + } + } diff --git a/rust-cargo-c.spec b/rust-cargo-c.spec index eb1b503..3e55aaa 100644 --- a/rust-cargo-c.spec +++ b/rust-cargo-c.spec @@ -22,6 +22,11 @@ Patch: cargo-c-fix-metadata-auto.diff # * drop +cargo version suffix from crate version # * remove vendored OpenSSL feature Patch: cargo-c-fix-metadata.diff +# * backport upstream change: Add version_suffix_components setting +# This is required to work around breaking changes in the default +# behaviour of cargo-c v0.9.26 and newer (using "0." as the soname +# for versions < 1.0.0 instead of just "0"). +Patch: https://github.com/lu-zero/cargo-c/pull/352.patch BuildRequires: cargo-rpm-macros >= 24