diff --git a/src/linux/link.rs b/src/linux/link.rs index 434c0e3f3..58c7f7bce 100644 --- a/src/linux/link.rs +++ b/src/linux/link.rs @@ -1,6 +1,6 @@ //! Relink shared objects to use an relative path prefix -use globset::GlobMatcher; +use globset::GlobSet; use goblin::elf::{Dyn, Elf}; use goblin::elf64::header::ELFMAG; use goblin::strtab::Strtab; @@ -92,7 +92,7 @@ impl SharedObject { prefix: &Path, encoded_prefix: &Path, custom_rpaths: &[String], - rpath_allowlist: &[GlobMatcher], + rpath_allowlist: Option<&GlobSet>, ) -> Result<(), RelinkError> { if !self.has_dynamic { tracing::debug!("{} is not dynamically linked", self.path.display()); @@ -134,7 +134,10 @@ impl SharedObject { "$ORIGIN/{}", relative_path.to_string_lossy() ))); - } else if rpath_allowlist.iter().any(|glob| glob.is_match(rpath)) { + } else if rpath_allowlist + .map(|glob| glob.is_match(rpath)) + .unwrap_or(false) + { tracing::info!("rpath ({:?}) for {:?} found in allowlist", rpath, self.path); final_rpath.push(rpath.clone()); } else { @@ -339,6 +342,7 @@ fn relink(elf_path: &Path, new_rpath: &[PathBuf]) -> Result<(), RelinkError> { #[cfg(test)] mod test { use super::*; + use globset::{Glob, GlobSetBuilder}; use std::{fs, path::Path}; use tempfile::tempdir_in; @@ -350,27 +354,30 @@ mod test { // prefix: "test-data/binary_files" // new rpath: $ORIGIN/../lib #[test] - #[cfg(target_os = "linux")] fn relink_patchelf() -> Result<(), RelinkError> { - use globset::Glob; + if which::which("patchelf").is_err() { + tracing::warn!("patchelf not found, skipping test"); + return Ok(()); + } + // copy binary to a temporary directory let prefix = Path::new(env!("CARGO_MANIFEST_DIR")).join("test-data/binary_files"); let tmp_dir = tempdir_in(&prefix)?.into_path(); let binary_path = tmp_dir.join("zlink"); fs::copy(prefix.join("zlink"), &binary_path)?; + let globset = GlobSetBuilder::new() + .add(Glob::new("/usr/lib/custom**").unwrap()) + .build() + .unwrap(); + // default rpaths of the test binary are: // - /rattler-build_zlink/host_env_placehold/lib // - /rattler-build_zlink/build_env/lib // so we are expecting it to keep the host prefix and discard the build prefix let encoded_prefix = Path::new("/rattler-build_zlink/host_env_placehold"); let object = SharedObject::new(&binary_path)?; - object.relink( - &prefix, - encoded_prefix, - &[], - &[Glob::new("/usr/lib/custom**").unwrap().compile_matcher()], - )?; + object.relink(&prefix, encoded_prefix, &[], Some(&globset))?; let object = SharedObject::new(&binary_path)?; assert_eq!( vec!["$ORIGIN/../lib", "/usr/lib/custom_lib"], @@ -394,8 +401,12 @@ mod test { // prefix: "test-data/binary_files" // new rpath: $ORIGIN/../lib #[test] - #[cfg(target_os = "linux")] fn relink_add_rpath() -> Result<(), RelinkError> { + if which::which("patchelf").is_err() { + tracing::warn!("patchelf not found, skipping test"); + return Ok(()); + } + // copy binary to a temporary directory let prefix = Path::new(env!("CARGO_MANIFEST_DIR")).join("test-data/binary_files"); let tmp_dir = tempdir_in(&prefix)?.into_path(); @@ -404,7 +415,7 @@ mod test { let encoded_prefix = Path::new("/rattler-build_zlink/host_env_placehold"); let object = SharedObject::new(&binary_path)?; - object.relink(&prefix, encoded_prefix, &[String::from("lib/")], &[])?; + object.relink(&prefix, encoded_prefix, &[String::from("lib/")], None)?; let object = SharedObject::new(&binary_path)?; assert_eq!( vec!["$ORIGIN/../lib"], diff --git a/src/packaging.rs b/src/packaging.rs index 36f0b158c..f6f7ba33e 100644 --- a/src/packaging.rs +++ b/src/packaging.rs @@ -899,27 +899,25 @@ pub fn package_conda( let dynamic_linking = output.recipe.build().dynamic_linking(); let relocation_config = dynamic_linking - .clone() .and_then(|v| v.binary_relocation()) .unwrap_or_default(); + if output.build_configuration.target_platform != Platform::NoArch && !relocation_config.no_relocation() { - let rpath_allowlist = match dynamic_linking { - Some(v) => v.rpath_allowlist()?, - None => Vec::new(), - }; + let rpath_allowlist = dynamic_linking.and_then(|dl| dl.rpath_allowlist()); let mut binaries = tmp_files.clone(); - if let Some(paths) = relocation_config.relocate_paths()? { - binaries.retain(|v| paths.iter().any(|glob| glob.is_match(v))); + if let Some(globs) = relocation_config.relocate_paths() { + binaries.retain(|v| globs.is_match(v)); } + post::relink( &binaries, tmp_dir_path, prefix, &output.build_configuration.target_platform, - &dynamic_linking.clone().unwrap_or_default().rpaths(), - &rpath_allowlist, + &dynamic_linking.map(|dl| dl.rpaths()).unwrap_or_default(), + rpath_allowlist, )?; } diff --git a/src/post.rs b/src/post.rs index e617e2069..4a206012e 100644 --- a/src/post.rs +++ b/src/post.rs @@ -12,7 +12,7 @@ use std::{ path::{Path, PathBuf}, }; -use globset::GlobMatcher; +use globset::GlobSet; use rattler_conda_types::{PackageName, Platform}; use crate::{linux::link::SharedObject, macos::link::Dylib, packaging::PackagingError}; @@ -53,7 +53,7 @@ pub fn relink( encoded_prefix: &Path, target_platform: &Platform, rpaths: &[String], - rpath_allowlist: &[GlobMatcher], + rpath_allowlist: Option<&GlobSet>, ) -> Result<(), RelinkError> { for p in paths { let metadata = fs::symlink_metadata(p)?; diff --git a/src/recipe/parser/build.rs b/src/recipe/parser/build.rs index 4da30a16c..dfbf3fd77 100644 --- a/src/recipe/parser/build.rs +++ b/src/recipe/parser/build.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use globset::{Glob, GlobMatcher, GlobSet}; +use globset::GlobSet; use rattler_conda_types::{package::EntryPoint, NoArchType}; use serde::{Deserialize, Serialize}; @@ -89,8 +89,8 @@ impl Build { } /// Settings for shared libraries and executables - pub const fn dynamic_linking(&self) -> &Option { - &self.dynamic_linking + pub const fn dynamic_linking(&self) -> Option<&DynamicLinking> { + self.dynamic_linking.as_ref() } /// Check if the build should be skipped. @@ -171,8 +171,8 @@ pub struct DynamicLinking { #[serde(default, skip_serializing_if = "Vec::is_empty")] pub(super) rpaths: Vec, /// Allow runpath / rpath to point to these locations outside of the environment. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub(super) rpath_allowlist: Vec, + #[serde(default, skip_serializing_if = "GlobVec::is_empty")] + pub(super) rpath_allowlist: GlobVec, /// Whether to relocate binaries or not. #[serde(default, skip_serializing_if = "Option::is_none")] pub(super) binary_relocation: Option, @@ -189,13 +189,8 @@ impl DynamicLinking { } /// Get the rpath allow list. - pub fn rpath_allowlist(&self) -> Result, globset::Error> { - let mut matchers = Vec::new(); - for glob in self.rpath_allowlist.iter() { - let glob = Glob::new(glob)?.compile_matcher(); - matchers.push(glob); - } - Ok(matchers) + pub fn rpath_allowlist(&self) -> Option<&GlobSet> { + self.rpath_allowlist.globset() } // Get the binary relocation settings. @@ -248,7 +243,7 @@ pub enum BinaryRelocation { /// Relocate all binaries. All(bool), /// Relocate specific paths. - SpecificPaths(Vec), + SpecificPaths(GlobVec), } impl Default for BinaryRelocation { @@ -259,17 +254,10 @@ impl Default for BinaryRelocation { impl BinaryRelocation { /// Return the paths to relocate. - pub fn relocate_paths(&self) -> Result>, globset::Error> { + pub fn relocate_paths(&self) -> Option<&GlobSet> { match self { - BinaryRelocation::All(_) => Ok(None), - BinaryRelocation::SpecificPaths(paths) => { - let mut matchers = Vec::new(); - for glob in paths { - let glob = Glob::new(glob)?.compile_matcher(); - matchers.push(glob); - } - Ok(Some(matchers)) - } + BinaryRelocation::All(_) => None, + BinaryRelocation::SpecificPaths(paths) => paths.globset(), } } @@ -296,11 +284,8 @@ impl TryConvertNode for RenderedNode { impl TryConvertNode for RenderedSequenceNode { fn try_convert(&self, name: &str) -> Result> { - let mut paths = Vec::with_capacity(self.len()); - for item in self.iter() { - paths.push(item.try_convert(name)?) - } - Ok(BinaryRelocation::SpecificPaths(paths)) + let globvec: GlobVec = self.try_convert(name)?; + Ok(BinaryRelocation::SpecificPaths(globvec)) } } diff --git a/src/recipe/parser/glob_vec.rs b/src/recipe/parser/glob_vec.rs index c482ca768..893d27551 100644 --- a/src/recipe/parser/glob_vec.rs +++ b/src/recipe/parser/glob_vec.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Debug, Formatter}; + use globset::{Glob, GlobSet}; use serde::ser::SerializeSeq; @@ -7,9 +9,17 @@ use crate::_partialerror; use crate::recipe::custom_yaml::{HasSpan, RenderedNode, RenderedSequenceNode, TryConvertNode}; use crate::recipe::error::{ErrorKind, PartialParsingError}; -#[derive(Default, Debug, Clone)] +#[derive(Default, Clone)] pub struct GlobVec(Vec, Option); +impl PartialEq for GlobVec { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for GlobVec {} + impl Serialize for GlobVec { fn serialize(&self, serializer: S) -> Result { let mut seq = serializer.serialize_seq(Some(self.0.len()))?; @@ -20,6 +30,14 @@ impl Serialize for GlobVec { } } +impl Debug for GlobVec { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_list() + .entries(self.0.iter().map(|glob| glob.glob())) + .finish() + } +} + impl<'de> Deserialize<'de> for GlobVec { fn deserialize(deserializer: D) -> Result where diff --git a/src/recipe/snapshots/rattler_build__recipe__parser__tests__recipe_windows.snap b/src/recipe/snapshots/rattler_build__recipe__parser__tests__recipe_windows.snap index 0d03ca08f..145adadd9 100644 --- a/src/recipe/snapshots/rattler_build__recipe__parser__tests__recipe_windows.snap +++ b/src/recipe/snapshots/rattler_build__recipe__parser__tests__recipe_windows.snap @@ -104,14 +104,8 @@ Recipe { ), }, ), - always_copy_files: GlobVec( - [], - None, - ), - always_include_files: GlobVec( - [], - None, - ), + always_copy_files: [], + always_include_files: [], }, requirements: Requirements { build: [ diff --git a/src/recipe/snapshots/rattler_build__recipe__parser__tests__unix_recipe.snap b/src/recipe/snapshots/rattler_build__recipe__parser__tests__unix_recipe.snap index 62923eae7..cb078c39d 100644 --- a/src/recipe/snapshots/rattler_build__recipe__parser__tests__unix_recipe.snap +++ b/src/recipe/snapshots/rattler_build__recipe__parser__tests__unix_recipe.snap @@ -104,14 +104,8 @@ Recipe { ), }, ), - always_copy_files: GlobVec( - [], - None, - ), - always_include_files: GlobVec( - [], - None, - ), + always_copy_files: [], + always_include_files: [], }, requirements: Requirements { build: [