melib/notmuch: show informative error messages if libloading fails

Add instructions on how to solve this, and also a config setting
`library_file_path` to set the path manually if necessary.
pull/144/head
Manos Pitsidianakis 2022-09-02 13:07:15 +03:00
parent eb5949dc9b
commit a484b397c6
3 changed files with 96 additions and 20 deletions

View File

@ -182,7 +182,11 @@ Its format is described below in
\&.
.El
.Ss notmuch only
.Ic root_mailbox
notmuch is supported by loading the dynamic library libnotmuch.
If its location is missing from your library paths, you must add it yourself.
Alternatively, you can specify its path by using a setting.
.Bl -tag -width 36n
.It Ic root_mailbox
points to the directory which contains the
.Pa .notmuch/
subdirectory.
@ -192,10 +196,15 @@ You must explicitly state the mailboxes you want in the
field and set the
.Ar query
property to each of them.
.It Ic library_file_path Ar Path
Use an arbitrary location of libnotmuch by specifying its full filesystem path.
.Pq Em optional
.El
Example:
.Bd -literal
[accounts.notmuch]
format = "notmuch"
#library_file_path = "/opt/homebrew/lib/libnotmuch.5.dylib"
\&...
[accounts.notmuch.mailboxes]
"INBOX" = { query="tag:inbox", subscribe = true }

View File

@ -108,10 +108,45 @@ impl Default for Backends {
#[cfg(feature = "notmuch_backend")]
pub const NOTMUCH_ERROR_MSG: &str =
"libnotmuch5 was not found in your system. Make sure it is installed and in the library paths.\n";
"libnotmuch5 was not found in your system. Make sure it is installed and in the library paths. For a custom file path, use `library_file_path` setting in your notmuch account.\n";
#[cfg(not(feature = "notmuch_backend"))]
pub const NOTMUCH_ERROR_MSG: &str = "this version of meli is not compiled with notmuch support. Use an appropriate version and make sure libnotmuch5 is installed and in the library paths.\n";
#[cfg(not(feature = "notmuch_backend"))]
pub const NOTMUCH_ERROR_DETAILS: &str = "";
#[cfg(all(feature = "notmuch_backend", target_os = "unix"))]
pub const NOTMUCH_ERROR_DETAILS: &str = r#"If you have installed the library manually, try setting the `LD_LIBRARY_PATH` environment variable to its `lib` directory. Otherwise, set it to the location of libnotmuch.5.so. Example:
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/path/to/notmuch/lib" meli
or, put this in your shell init script (.bashenv, .zshenv, .bashrc, .zshrc, .profile):
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/path/to/notmuch/lib"
You can also set any location by specifying the library file path with the configuration flag `library_file_path`."#;
#[cfg(all(feature = "notmuch_backend", target_os = "macos"))]
pub const NOTMUCH_ERROR_DETAILS: &str = r#"If you have installed the library via homebrew, try setting the `DYLD_LIBRARY_PATH` environment variable to its `lib` directory. Otherwise, set it to the location of libnotmuch.5.dylib. Example:
DYLD_LIBRARY_PATH="$(brew --prefix)/lib" meli
or, put this in your shell init script (.bashenv, .zshenv, .bashrc, .zshrc, .profile):
export DYLD_LIBRARY_PATH="$(brew --prefix)/lib"
Make sure to append to DYLD_LIBRARY_PATH if it's not empty, by prepending a colon to the libnotmuch5.dylib location:
export DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:$(brew --prefix)/lib"
You can also set any location by specifying the library file path with the configuration flag `library_file_path`."#;
#[cfg(all(
feature = "notmuch_backend",
not(any(target_os = "unix", target_os = "macos"))
))]
pub const NOTMUCH_ERROR_DETAILS: &str = r#"If notmuch is installed but the library isn't found, consult your system's documentation on how to make dynamic libraries discoverable."#;
impl Backends {
pub fn new() -> Self {
let mut b = Backends {
@ -156,19 +191,13 @@ impl Backends {
}
#[cfg(feature = "notmuch_backend")]
{
#[cfg(not(target_os = "macos"))]
let dlpath = "libnotmuch.so.5";
#[cfg(target_os = "macos")]
let dlpath = "libnotmuch.5.dylib";
if unsafe { libloading::Library::new(dlpath) }.is_ok() {
b.register(
"notmuch".to_string(),
Backend {
create_fn: Box::new(|| Box::new(|f, i, ev| NotmuchDb::new(f, i, ev))),
validate_conf_fn: Box::new(NotmuchDb::validate_config),
},
);
}
b.register(
"notmuch".to_string(),
Backend {
create_fn: Box::new(|| Box::new(|f, i, ev| NotmuchDb::new(f, i, ev))),
validate_conf_fn: Box::new(NotmuchDb::validate_config),
},
);
}
#[cfg(feature = "jmap_backend")]
{
@ -187,6 +216,10 @@ impl Backends {
if !self.map.contains_key(key) {
if key == "notmuch" {
eprint!("{}", NOTMUCH_ERROR_MSG);
#[cfg(feature = "notmuch_backend")]
{
eprint!("{}", NOTMUCH_ERROR_DETAILS);
}
}
panic!("{} is not a valid mail backend", key);
}
@ -206,13 +239,18 @@ impl Backends {
.get(key)
.ok_or_else(|| {
MeliError::new(format!(
"{}{} is not a valid mail backend",
"{}{} is not a valid mail backend. {}",
if key == "notmuch" {
NOTMUCH_ERROR_MSG
} else {
""
},
key
key,
if cfg!(feature = "notmuch_backend") {
NOTMUCH_ERROR_DETAILS
} else {
""
},
))
})?
.validate_conf_fn)(s)

View File

@ -310,10 +310,30 @@ impl NotmuchDb {
event_consumer: BackendEventConsumer,
) -> Result<Box<dyn MailBackend>> {
#[cfg(not(target_os = "macos"))]
let dlpath = "libnotmuch.so.5";
let mut dlpath = "libnotmuch.so.5";
#[cfg(target_os = "macos")]
let dlpath = "libnotmuch.5.dylib";
let lib = Arc::new(unsafe { libloading::Library::new(dlpath)? });
let mut dlpath = "libnotmuch.5.dylib";
let mut custom_dlpath = false;
if let Some(lib_path) = s.extra.get("library_file_path") {
dlpath = lib_path.as_str();
custom_dlpath = true;
}
let lib = Arc::new(unsafe {
match libloading::Library::new(dlpath) {
Ok(l) => l,
Err(err) => {
if custom_dlpath {
return Err(MeliError::new(format!("Notmuch `library_file_path` setting value `{}` for account {} does not exist or is a directory or not a valid library file.",dlpath, s.name()))
.set_kind(ErrorKind::Configuration)
.set_source(Some(Arc::new(err))));
} else {
return Err(MeliError::new("Could not load libnotmuch!")
.set_details(super::NOTMUCH_ERROR_DETAILS)
.set_source(Some(Arc::new(err))));
}
}
}
});
let mut path = Path::new(s.root_mailbox.as_str()).expand();
if !path.exists() {
return Err(MeliError::new(format!(
@ -423,6 +443,15 @@ impl NotmuchDb {
path.pop();
let account_name = s.name().to_string();
if let Some(lib_path) = s.extra.remove("library_file_path") {
if !Path::new(&lib_path).exists() || Path::new(&lib_path).is_dir() {
return Err(MeliError::new(format!(
"Notmuch `library_file_path` setting value `{}` for account {} does not exist or is a directory.",
&lib_path,
s.name()
)).set_kind(ErrorKind::Configuration));
}
}
for (k, f) in s.mailboxes.iter_mut() {
if f.extra.remove("query").is_none() {
return Err(MeliError::new(format!(