diff --git a/Cargo.lock b/Cargo.lock index e921edba..925ef9bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,6 +309,7 @@ dependencies = [ name = "marker_adapter" version = "0.1.0" dependencies = [ + "cfg-if", "libloading", "marker_api", ] diff --git a/marker_adapter/Cargo.toml b/marker_adapter/Cargo.toml index a7308f1f..4274dda2 100644 --- a/marker_adapter/Cargo.toml +++ b/marker_adapter/Cargo.toml @@ -10,3 +10,4 @@ license = "MIT OR Apache-2.0" marker_api = { path = "../marker_api" } libloading = "0.7.3" +cfg-if = "1.0.0" diff --git a/marker_adapter/src/context.rs b/marker_adapter/src/context.rs index 5db201b9..dd0f7704 100644 --- a/marker_adapter/src/context.rs +++ b/marker_adapter/src/context.rs @@ -14,7 +14,7 @@ use marker_api::{ /// change and calling functions on them would require a stable ABI which Rust /// doesn't provide. /// -/// In this case, the `DriverContextWrapper` will be passed as a `*const ()` +/// In this case, the [`DriverContextWrapper`] will be passed as a `*const ()` /// pointer to [`DriverCallbacks`] which will do nothing with this data other /// than giving it back to functions declared in this module. Since the `&dyn` /// object is created, only used here and everything is compiled during the same diff --git a/marker_adapter/src/loader.rs b/marker_adapter/src/loader.rs index 3b37ccc5..d0a284b2 100644 --- a/marker_adapter/src/loader.rs +++ b/marker_adapter/src/loader.rs @@ -1,9 +1,46 @@ +use cfg_if::cfg_if; use libloading::Library; use marker_api::context::AstContext; use marker_api::lint::Lint; use marker_api::LintPass; +use std::ffi::{OsStr, OsString}; + +/// Splits [`OsStr`] by an ascii character +fn split_os_str(s: &OsStr, c: u8) -> Vec { + cfg_if! { + if #[cfg(unix)] { + unix_split_os_str(s, c) + } else if #[cfg(windows)] { + windows_split_os_str(s, c) + } else { + unimplemented!("`split_os_str` currently works only on unix and windows") + } + } +} + +#[cfg(unix)] +#[doc(hidden)] +fn unix_split_os_str(s: &OsStr, c: u8) -> Vec { + use std::os::unix::ffi::OsStrExt; + + s.as_bytes() + .split(|byte| *byte == c) + .map(|bytes| OsStr::from_bytes(bytes).into()) + .collect() +} + +#[cfg(windows)] +#[doc(hidden)] +fn windows_split_os_str(s: &OsStr, c: u8) -> Vec { + use std::os::windows::ffi::{OsStrExt, OsStringExt}; + + let bytes: Vec = s.encode_wide().collect(); + + bytes.split(|v| *v == u16::from(c)).map(OsString::from_wide).collect() +} + /// This struct loads external lint crates into memory and provides a safe API /// to call the respective methods on all of them. #[derive(Default)] @@ -15,17 +52,16 @@ impl<'ast> LintCrateRegistry<'ast> { /// # Errors /// This can return errors if the library couldn't be found or if the /// required symbols weren't provided. - fn load_external_lib(&mut self, lib_path: &str) -> Result<(), LoadingError> { + fn load_external_lib(lib_path: &OsStr) -> Result, LoadingError> { let lib: &'static Library = Box::leak(Box::new( unsafe { Library::new(lib_path) }.map_err(|_| LoadingError::FileNotFound)?, )); let pass = LoadedLintCrate::try_from_lib(lib)?; - self.passes.push(pass); // FIXME: Create issue for lifetimes and fix droping and pointer decl stuff - Ok(()) + Ok(pass) } /// # Panics @@ -34,12 +70,21 @@ impl<'ast> LintCrateRegistry<'ast> { pub fn new_from_env() -> Self { let mut new_self = Self::default(); - if let Ok(lint_crates_lst) = std::env::var("MARKER_LINT_CRATES") { - for lint_crate in lint_crates_lst.split(';') { - if let Err(err) = new_self.load_external_lib(lint_crate) { - panic!("Unable to load `{lint_crate}`, reason: {err:?}"); - } + let Some((_, lint_crates_lst)) = std::env::vars_os().find(|(name, _val)| name == "MARKER_LINT_CRATES") else { + panic!("Adapter tried to find `MARKER_LINT_CRATES` env variable, but it was not present"); + }; + + for lib in split_os_str(&lint_crates_lst, b';') { + if lib.is_empty() { + continue; } + + let lib = match Self::load_external_lib(&lib) { + Ok(v) => v, + Err(err) => panic!("Unable to load `{}`, reason: {err:?}", lib.to_string_lossy()), + }; + + new_self.passes.push(lib); } new_self @@ -113,7 +158,7 @@ macro_rules! gen_LoadedLintCrate { } let set_ast_context = unsafe { - lib.get:: unsafe extern "C" fn(&'ast AstContext<'ast>) -> ()>(b"set_ast_context\0") + lib.get:: unsafe extern "C" fn(&'ast AstContext<'ast>)>(b"set_ast_context\0") .map_err(|_| LoadingError::MissingLintDeclaration)? }; @@ -144,7 +189,7 @@ macro_rules! gen_LoadedLintCrate { // safe wrapper to external functions $( - fn $fn_name<'ast>(& $($mut_)* self $(, $arg_name: $arg_ty)*) -> $ret_ty { + fn $fn_name<'ast>(&self $(, $arg_name: $arg_ty)*) -> $ret_ty { unsafe { (self.$fn_name)($($arg_name,)*) }