Skip to content

Commit

Permalink
Rework plugins_loading/search_dirs config option
Browse files Browse the repository at this point in the history
  • Loading branch information
fuzzypixelz committed Jul 29, 2024
1 parent e38fc16 commit 5b10bfe
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 55 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 3 additions & 19 deletions commons/zenoh-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use zenoh_protocol::{
transport::{BatchSize, TransportSn},
};
use zenoh_result::{bail, zerror, ZResult};
use zenoh_util::LibLoader;
use zenoh_util::{LibLoader, LibSearchDirs};

pub mod mode_dependent;
pub use mode_dependent::*;
Expand Down Expand Up @@ -547,7 +547,7 @@ validated_struct::validator! {
pub plugins_loading: #[derive(Default)]
PluginsLoading {
pub enabled: bool,
pub search_dirs: Option<Vec<String>>, // TODO (low-prio): Switch this String to a PathBuf? (applies to other paths in the config as well)
pub search_dirs: LibSearchDirs,
},
#[validated(recursive_accessors)]
/// The configuration for plugins.
Expand All @@ -573,19 +573,6 @@ fn set_false() -> bool {
false
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PluginSearchDirs(Vec<String>);
impl Default for PluginSearchDirs {
fn default() -> Self {
Self(
(*zenoh_util::LIB_DEFAULT_SEARCH_PATHS)
.split(':')
.map(|c| c.to_string())
.collect(),
)
}
}

#[test]
fn config_deser() {
let config = Config::from_deserializer(
Expand Down Expand Up @@ -763,10 +750,7 @@ impl Config {

pub fn libloader(&self) -> LibLoader {
if self.plugins_loading.enabled {
match self.plugins_loading.search_dirs() {
Some(dirs) => LibLoader::new(dirs, true),
None => LibLoader::default(),
}
LibLoader::new(self.plugins_loading.search_dirs().clone())
} else {
LibLoader::empty()
}
Expand Down
2 changes: 2 additions & 0 deletions commons/zenoh-util/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ shellexpand = { workspace = true }
zenoh-core = { workspace = true }
zenoh-result = { workspace = true, features = ["default"] }
const_format = { workspace = true }
serde = { workspace = true, features = ["default"] }
serde_json = { workspace = true }

[target.'cfg(windows)'.dependencies]
winapi = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions commons/zenoh-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ use lazy_static::lazy_static;

pub mod ffi;
mod lib_loader;
pub mod lib_search_dirs;
pub mod net;
pub mod time_range;

pub use lib_loader::*;
pub mod timer;
pub use timer::*;
pub mod log;
pub use lib_search_dirs::*;
pub use log::*;

/// The "ZENOH_HOME" environment variable name
Expand Down
52 changes: 18 additions & 34 deletions commons/zenoh-util/src/lib_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,22 @@ use tracing::{debug, warn};
use zenoh_core::{zconfigurable, zerror};
use zenoh_result::{bail, ZResult};

use crate::LibSearchDirs;

zconfigurable! {
/// The libraries prefix for the current platform (usually: `"lib"`)
pub static ref LIB_PREFIX: String = DLL_PREFIX.to_string();
/// The libraries suffix for the current platform (`".dll"` or `".so"` or `".dylib"`...)
pub static ref LIB_SUFFIX: String = DLL_SUFFIX.to_string();
/// The default list of paths where to search for libraries to load
pub static ref LIB_DEFAULT_SEARCH_PATHS: String = ".:~/.zenoh/lib:/opt/homebrew/lib:/usr/local/lib:/usr/lib".to_string();
pub static ref LIB_DEFAULT_SEARCH_PATHS: String = r#"[
{ "kind": "current_exe_parent" },
".",
"~/.zenoh/lib",
"/opt/homebrew/lib",
"/usr/local/lib",
"/usr/lib"
]"#.to_string();
}

/// LibLoader allows search for libraries and to load them.
Expand All @@ -44,40 +53,16 @@ impl LibLoader {
LibLoader { search_paths: None }
}

/// Returns the list of search paths used by `LibLoader::default()`
pub fn default_search_paths() -> &'static str {
&LIB_DEFAULT_SEARCH_PATHS
}

/// Creates a new [LibLoader] with a set of paths where the libraries will be searched for.
/// If `exe_parent_dir`is true, the parent directory of the current executable is also added
/// to the set of paths for search.
pub fn new<S>(search_dirs: &[S], exe_parent_dir: bool) -> LibLoader
where
S: AsRef<str>,
{
let mut search_paths: Vec<PathBuf> = vec![];
for s in search_dirs {
match shellexpand::full(s) {
Ok(cow_str) => match PathBuf::from(&*cow_str).canonicalize() {
Ok(path) => search_paths.push(path),
Err(err) => debug!("Cannot search for libraries in {}: {}", cow_str, err),
},
Err(err) => warn!("Cannot search for libraries in '{}': {} ", s.as_ref(), err),
}
}
Self::_new(search_paths, exe_parent_dir)
}
fn _new(mut search_paths: Vec<PathBuf>, exe_parent_dir: bool) -> Self {
if exe_parent_dir {
match std::env::current_exe() {
Ok(path) => match path.parent() {
Some(p) => if p.is_dir() {
search_paths.push(p.canonicalize().unwrap())
},
None => warn!("Can't search for plugins in executable parent directory: no parent directory for {}.", path.to_string_lossy()),
},
Err(e) => warn!("Can't search for plugins in executable parent directory: {}.", e),
pub fn new(dirs: LibSearchDirs) -> LibLoader {
let mut search_paths = Vec::new();

for path in dirs.into_iter() {
match path {
Ok(path) => search_paths.push(path),
Err(err) => tracing::error!("{err}"),
}
}

Expand Down Expand Up @@ -237,7 +222,6 @@ impl LibLoader {

impl Default for LibLoader {
fn default() -> Self {
let paths: Vec<&str> = (*LIB_DEFAULT_SEARCH_PATHS).split(':').collect();
LibLoader::new(&paths, true)
LibLoader::new(LibSearchDirs::default())
}
}
205 changes: 205 additions & 0 deletions commons/zenoh-util/src/lib_search_dirs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
use std::{env, error::Error, fmt::Display, path::PathBuf, str::FromStr};

use crate::lib_loader::LIB_DEFAULT_SEARCH_PATHS;

use serde::{
de::{value::MapAccessDeserializer, Visitor},
Deserialize, Serialize,
};

#[derive(Clone, Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
#[serde(default)]
pub struct LibSearchDirs(Vec<LibSearchDir>);

impl LibSearchDirs {
pub fn from_paths<T: AsRef<str>>(paths: &[T]) -> Self {
Self(
paths
.iter()
.map(|s| LibSearchDir::Path(s.as_ref().to_string()))
.collect(),
)
}
}

#[derive(Debug)]
pub struct InvalidLibSearchDir {
found: LibSearchDir,
source: String,
}

impl Display for InvalidLibSearchDir {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"invalid library search directory `{:?}`: {}",
self.found, self.source
)
}
}

impl Error for InvalidLibSearchDir {}

pub struct IntoIter {
iter: std::vec::IntoIter<LibSearchDir>,
}

impl Iterator for IntoIter {
type Item = Result<PathBuf, InvalidLibSearchDir>;

fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(LibSearchDir::into_path)
}
}

impl IntoIterator for LibSearchDirs {
type Item = Result<PathBuf, InvalidLibSearchDir>;

type IntoIter = IntoIter;

fn into_iter(self) -> Self::IntoIter {
IntoIter {
iter: self.0.into_iter(),
}
}
}

impl Default for LibSearchDirs {
fn default() -> Self {
let de = &mut serde_json::Deserializer::from_str(&LIB_DEFAULT_SEARCH_PATHS);
LibSearchDirs::deserialize(de)
.expect("`zenoh_util::lib_loader::LIB_DEFAULT_SEARCH_PATHS` should be deserializable")
}
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum LibSearchDir {
Path(String),
Spec(LibSearchSpec),
}

impl LibSearchDir {
fn into_path(self) -> Result<PathBuf, InvalidLibSearchDir> {
match self {
LibSearchDir::Path(path) => LibSearchSpec {
kind: LibSearchSpecKind::Path,
value: Some(path),
}
.into_path(),
LibSearchDir::Spec(spec) => spec.into_path(),
}
}
}

#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct LibSearchSpec {
kind: LibSearchSpecKind,
value: Option<String>,
}

impl LibSearchSpec {
fn into_path(self) -> Result<PathBuf, InvalidLibSearchDir> {
fn error_from_source<T: Error>(spec: &LibSearchSpec, err: T) -> InvalidLibSearchDir {
InvalidLibSearchDir {
found: LibSearchDir::Spec(spec.clone()),
source: err.to_string(),
}
}

fn error_from_str(spec: &LibSearchSpec, err: &str) -> InvalidLibSearchDir {
InvalidLibSearchDir {
found: LibSearchDir::Spec(spec.clone()),
source: err.to_string(),
}
}

match self.kind {
LibSearchSpecKind::Path => {
let Some(value) = &self.value else {
return Err(error_from_str(
&self,
"`path` specs should have a `value` field",
));
};

let expanded =
shellexpand::full(value).map_err(|err| error_from_source(&self, err))?;

let path =
PathBuf::from_str(&expanded).map_err(|err| error_from_source(&self, err))?;

Ok(path)
}
LibSearchSpecKind::CurrentExeParent => {
let current_exe =
env::current_exe().map_err(|err| error_from_source(&self, err))?;

let Some(current_exe_parent) = current_exe.parent() else {
return Err(error_from_str(
&self,
"current executable's path has no parent directory",
));
};

let canonicalized = current_exe_parent
.canonicalize()
.map_err(|err| error_from_source(&self, err))?;

Ok(canonicalized)
}
}
}
}

#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum LibSearchSpecKind {
Path,
CurrentExeParent,
}

impl<'de> Deserialize<'de> for LibSearchDir {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_any(LibSearchSpecOrPathVisitor)
}
}

impl Serialize for LibSearchDir {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
LibSearchDir::Path(path) => serializer.serialize_str(path),
LibSearchDir::Spec(spec) => spec.serialize(serializer),
}
}
}

struct LibSearchSpecOrPathVisitor;

impl<'de> Visitor<'de> for LibSearchSpecOrPathVisitor {
type Value = LibSearchDir;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("str or map with field `kind` and optionally field `value`")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(LibSearchDir::Path(v.to_string()))
}

fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
LibSearchSpec::deserialize(MapAccessDeserializer::new(map)).map(LibSearchDir::Spec)
}
}
1 change: 1 addition & 0 deletions plugins/zenoh-plugin-storage-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ zenoh = { workspace = true, features = [
] }
zenoh-plugin-trait = { workspace = true }
zenoh_backend_traits = { workspace = true }
zenoh-util = { workspace = true }

[build-dependencies]
rustc_version = { workspace = true }
Expand Down
Loading

0 comments on commit 5b10bfe

Please sign in to comment.