mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-31 20:31:19 -03:00
Extract config path module. NFC
This is the "code movement" part of bf65b9e3 ("Change `gettext` paths to be
relocatable (#11195)").
While at it, fix some warnings.
This commit is contained in:
164
src/bin/fish.rs
164
src/bin/fish.rs
@@ -21,6 +21,8 @@
|
||||
#![allow(unstable_name_collisions)]
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
#[cfg(feature = "installable")]
|
||||
use fish::common::get_executable_path;
|
||||
#[allow(unused_imports)]
|
||||
use fish::future::IsSomeAnd;
|
||||
use fish::{
|
||||
@@ -29,12 +31,12 @@
|
||||
BUILTIN_ERR_MISSING, BUILTIN_ERR_UNKNOWN, STATUS_CMD_OK, STATUS_CMD_UNKNOWN,
|
||||
},
|
||||
common::{
|
||||
escape, get_executable_path, save_term_foreground_process_group, scoped_push_replacer,
|
||||
str2wcstring, wcs2osstring, wcs2string, PACKAGE_NAME, PROFILING_ACTIVE, PROGRAM_NAME,
|
||||
escape, save_term_foreground_process_group, scoped_push_replacer, str2wcstring, wcs2string,
|
||||
PACKAGE_NAME, PROFILING_ACTIVE, PROGRAM_NAME,
|
||||
},
|
||||
env::{
|
||||
environment::{env_init, EnvStack, Environment},
|
||||
ConfigPaths, EnvMode, Statuses,
|
||||
ConfigPaths, EnvMode, Statuses, CONFIG_PATHS,
|
||||
},
|
||||
eprintf,
|
||||
event::{self, Event},
|
||||
@@ -65,22 +67,16 @@
|
||||
use std::fs::File;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::{env, ops::ControlFlow};
|
||||
|
||||
const DOC_DIR: &str = env!("DOCDIR");
|
||||
const DATA_DIR: &str = env!("DATADIR");
|
||||
const DATA_DIR_SUBDIR: &str = env!("DATADIR_SUBDIR");
|
||||
const SYSCONF_DIR: &str = env!("SYSCONFDIR");
|
||||
const BIN_DIR: &str = env!("BINDIR");
|
||||
|
||||
#[cfg(feature = "installable")]
|
||||
// Disable for clippy because otherwise it would require sphinx
|
||||
#[cfg(not(clippy))]
|
||||
fn install(confirm: bool, dir: PathBuf) -> bool {
|
||||
fn install(confirm: bool, dir: &PathBuf) -> bool {
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
@@ -192,7 +188,8 @@ fn install(confirm: bool, dir: PathBuf) -> bool {
|
||||
}
|
||||
|
||||
#[cfg(any(clippy, not(feature = "installable")))]
|
||||
fn install(_confirm: bool, _dir: PathBuf) -> bool {
|
||||
#[allow(dead_code)]
|
||||
fn install(_confirm: bool, _dir: &PathBuf) -> bool {
|
||||
eprintln!("Fish was built without support for self-installation");
|
||||
return false;
|
||||
}
|
||||
@@ -264,128 +261,6 @@ fn print_rusage_self() {
|
||||
eprintln!(" signals: {signals}");
|
||||
}
|
||||
|
||||
fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
// PORTING: why is this not just an associated method on ConfigPaths?
|
||||
|
||||
let mut paths = ConfigPaths::default();
|
||||
let mut done = false;
|
||||
let exec_path = get_executable_path(argv0.as_ref());
|
||||
if let Ok(exec_path) = exec_path.canonicalize() {
|
||||
FLOG!(
|
||||
config,
|
||||
format!("exec_path: {:?}, argv[0]: {:?}", exec_path, argv0.as_ref())
|
||||
);
|
||||
// TODO: we should determine program_name from argv0 somewhere in this file
|
||||
|
||||
// Detect if we're running right out of the CMAKE build directory
|
||||
if exec_path.starts_with(env!("CARGO_MANIFEST_DIR")) {
|
||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
FLOG!(
|
||||
config,
|
||||
"Running out of target directory, using paths relative to CARGO_MANIFEST_DIR:\n",
|
||||
manifest_dir.display()
|
||||
);
|
||||
done = true;
|
||||
paths = ConfigPaths {
|
||||
data: manifest_dir.join("share"),
|
||||
sysconf: manifest_dir.join("etc"),
|
||||
doc: manifest_dir.join("user_doc/html"),
|
||||
bin: Some(exec_path.parent().unwrap().to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// The next check is that we are in a relocatable directory tree
|
||||
if exec_path.ends_with("bin/fish") {
|
||||
let base_path = exec_path.parent().unwrap().parent().unwrap();
|
||||
paths = ConfigPaths {
|
||||
// One obvious path is ~/.local (with fish in ~/.local/bin/).
|
||||
// If we picked ~/.local/share/fish as our data path,
|
||||
// we would install there and erase history.
|
||||
// So let's isolate us a bit more.
|
||||
#[cfg(feature = "installable")]
|
||||
data: base_path.join("share/fish/install"),
|
||||
#[cfg(not(feature = "installable"))]
|
||||
data: base_path.join("share/fish"),
|
||||
sysconf: base_path.join("etc/fish"),
|
||||
doc: base_path.join("share/doc/fish"),
|
||||
bin: Some(base_path.join("bin")),
|
||||
}
|
||||
} else if exec_path.ends_with("fish") {
|
||||
FLOG!(
|
||||
config,
|
||||
"'fish' not in a 'bin/', trying paths relative to source tree"
|
||||
);
|
||||
let base_path = exec_path.parent().unwrap();
|
||||
paths = ConfigPaths {
|
||||
#[cfg(feature = "installable")]
|
||||
data: base_path.join("share/install"),
|
||||
#[cfg(not(feature = "installable"))]
|
||||
data: base_path.join("share"),
|
||||
sysconf: base_path.join("etc"),
|
||||
doc: base_path.join("user_doc/html"),
|
||||
bin: Some(base_path.to_path_buf()),
|
||||
}
|
||||
}
|
||||
|
||||
if paths.data.exists() && paths.sysconf.exists() {
|
||||
// The docs dir may not exist; in that case fall back to the compiled in path.
|
||||
if !paths.doc.exists() {
|
||||
paths.doc = PathBuf::from(DOC_DIR);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// Fall back to what got compiled in.
|
||||
let data = if cfg!(feature = "installable") {
|
||||
let Some(home) = fish::env::get_home() else {
|
||||
FLOG!(
|
||||
error,
|
||||
"Cannot find home directory and will refuse to read configuration.\n",
|
||||
"Consider installing into a directory tree with `fish --install=PATH`."
|
||||
);
|
||||
return paths;
|
||||
};
|
||||
|
||||
PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
} else {
|
||||
Path::new(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
};
|
||||
let bin = if cfg!(feature = "installable") {
|
||||
exec_path.parent().map(|x| x.to_path_buf())
|
||||
} else {
|
||||
Some(PathBuf::from(BIN_DIR))
|
||||
};
|
||||
|
||||
FLOG!(config, "Using compiled in paths:");
|
||||
paths = ConfigPaths {
|
||||
data,
|
||||
sysconf: Path::new(SYSCONF_DIR).join("fish"),
|
||||
doc: DOC_DIR.into(),
|
||||
bin,
|
||||
}
|
||||
}
|
||||
|
||||
FLOGF!(
|
||||
config,
|
||||
"determine_config_directory_paths() results:\npaths.data: %ls\npaths.sysconf: \
|
||||
%ls\npaths.doc: %ls\npaths.bin: %ls",
|
||||
paths.data.display().to_string(),
|
||||
paths.sysconf.display().to_string(),
|
||||
paths.doc.display().to_string(),
|
||||
paths
|
||||
.bin
|
||||
.clone()
|
||||
.map(|x| x.display().to_string())
|
||||
.unwrap_or("|not found|".to_string()),
|
||||
);
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
// Source the file config.fish in the given directory.
|
||||
// Returns true if successful, false if not.
|
||||
fn source_config_in_directory(parser: &Parser, dir: &wstr) -> bool {
|
||||
@@ -418,6 +293,7 @@ fn source_config_in_directory(parser: &Parser, dir: &wstr) -> bool {
|
||||
|
||||
#[cfg(feature = "installable")]
|
||||
fn check_version_file(paths: &ConfigPaths, datapath: &wstr) -> Option<bool> {
|
||||
use crate::common::wcs2osstring;
|
||||
// (false-positive, is_none_or is a backport, this builds with 1.70)
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
if paths
|
||||
@@ -463,7 +339,7 @@ fn read_init(parser: &Parser, paths: &ConfigPaths) {
|
||||
);
|
||||
}
|
||||
|
||||
install(true, PathBuf::from(wcs2osstring(&datapath)));
|
||||
install(true, &PathBuf::from(wcs2osstring(&datapath)));
|
||||
// We try to go on if installation failed (or was rejected) here
|
||||
// If the assets are missing, we will trigger a later error,
|
||||
// if they are outdated, things will probably (tm) work somewhat.
|
||||
@@ -592,8 +468,9 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
||||
// path/share/fish/ is the data directory
|
||||
// path/etc/fish is sysconf????
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
let dir = PathBuf::from(wcs2osstring(path));
|
||||
if install(true, dir.join("share/fish/install")) {
|
||||
if install(true, &dir.join("share/fish/install")) {
|
||||
for sub in &["share/fish/install", "etc/fish", "bin"] {
|
||||
let p = dir.join(sub);
|
||||
let Ok(_) = fs::create_dir_all(p.clone()) else {
|
||||
@@ -626,14 +503,12 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let paths = Some(determine_config_directory_paths(OsString::from_vec(
|
||||
wcs2string(&args[0]),
|
||||
)));
|
||||
let paths = Some(&*CONFIG_PATHS);
|
||||
let Some(paths) = paths else {
|
||||
FLOG!(error, "Cannot find config paths");
|
||||
std::process::exit(1);
|
||||
};
|
||||
install(true, paths.data);
|
||||
install(true, &paths.data);
|
||||
}
|
||||
}
|
||||
'l' => opts.is_login = true,
|
||||
@@ -817,13 +692,12 @@ fn throwing_main() -> i32 {
|
||||
save_term_foreground_process_group();
|
||||
}
|
||||
|
||||
let mut paths: Option<&ConfigPaths> = None;
|
||||
// If we're not executing, there's no need to find the config.
|
||||
let paths: Option<ConfigPaths> = if !opts.no_exec {
|
||||
let paths = Some(determine_config_directory_paths(OsString::from_vec(
|
||||
wcs2string(&args[0]),
|
||||
)));
|
||||
if !opts.no_exec {
|
||||
paths = Some(&*CONFIG_PATHS);
|
||||
env_init(
|
||||
paths.as_ref(),
|
||||
paths,
|
||||
/* do uvars */ !opts.no_config,
|
||||
/* default paths */ opts.no_config,
|
||||
);
|
||||
|
||||
147
src/env/config_paths.rs
vendored
Normal file
147
src/env/config_paths.rs
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
use super::ConfigPaths;
|
||||
use crate::env;
|
||||
use crate::{common::get_executable_path, FLOG, FLOGF};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const DOC_DIR: &str = env!("DOCDIR");
|
||||
const DATA_DIR: &str = env!("DATADIR");
|
||||
const DATA_DIR_SUBDIR: &str = env!("DATADIR_SUBDIR");
|
||||
const SYSCONF_DIR: &str = env!("SYSCONFDIR");
|
||||
const BIN_DIR: &str = env!("BINDIR");
|
||||
|
||||
pub static CONFIG_PATHS: Lazy<ConfigPaths> = Lazy::new(|| {
|
||||
// Read the current executable and follow all symlinks to it.
|
||||
// OpenBSD has issues with `std::env::current_exe`, see gh-9086 and
|
||||
// https://github.com/rust-lang/rust/issues/60560
|
||||
let argv0 = PathBuf::from(std::env::args().next().unwrap());
|
||||
let argv0 = if argv0.exists() {
|
||||
argv0
|
||||
} else {
|
||||
std::env::current_exe().unwrap_or(argv0)
|
||||
};
|
||||
let argv0 = argv0.canonicalize().unwrap_or(argv0);
|
||||
determine_config_directory_paths(argv0)
|
||||
});
|
||||
|
||||
fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
// PORTING: why is this not just an associated method on ConfigPaths?
|
||||
|
||||
let mut paths = ConfigPaths::default();
|
||||
let mut done = false;
|
||||
let exec_path = get_executable_path(argv0.as_ref());
|
||||
if let Ok(exec_path) = exec_path.canonicalize() {
|
||||
FLOG!(
|
||||
config,
|
||||
format!("exec_path: {:?}, argv[0]: {:?}", exec_path, argv0.as_ref())
|
||||
);
|
||||
// TODO: we should determine program_name from argv0 somewhere in this file
|
||||
|
||||
// Detect if we're running right out of the CMAKE build directory
|
||||
if exec_path.starts_with(env!("CARGO_MANIFEST_DIR")) {
|
||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
FLOG!(
|
||||
config,
|
||||
"Running out of target directory, using paths relative to CARGO_MANIFEST_DIR:\n",
|
||||
manifest_dir.display()
|
||||
);
|
||||
done = true;
|
||||
paths = ConfigPaths {
|
||||
data: manifest_dir.join("share"),
|
||||
sysconf: manifest_dir.join("etc"),
|
||||
doc: manifest_dir.join("user_doc/html"),
|
||||
bin: Some(exec_path.parent().unwrap().to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// The next check is that we are in a relocatable directory tree
|
||||
if exec_path.ends_with("bin/fish") {
|
||||
let base_path = exec_path.parent().unwrap().parent().unwrap();
|
||||
paths = ConfigPaths {
|
||||
// One obvious path is ~/.local (with fish in ~/.local/bin/).
|
||||
// If we picked ~/.local/share/fish as our data path,
|
||||
// we would install there and erase history.
|
||||
// So let's isolate us a bit more.
|
||||
#[cfg(feature = "installable")]
|
||||
data: base_path.join("share/fish/install"),
|
||||
#[cfg(not(feature = "installable"))]
|
||||
data: base_path.join("share/fish"),
|
||||
sysconf: base_path.join("etc/fish"),
|
||||
doc: base_path.join("share/doc/fish"),
|
||||
bin: Some(base_path.join("bin")),
|
||||
}
|
||||
} else if exec_path.ends_with("fish") {
|
||||
FLOG!(
|
||||
config,
|
||||
"'fish' not in a 'bin/', trying paths relative to source tree"
|
||||
);
|
||||
let base_path = exec_path.parent().unwrap();
|
||||
paths = ConfigPaths {
|
||||
#[cfg(feature = "installable")]
|
||||
data: base_path.join("share/install"),
|
||||
#[cfg(not(feature = "installable"))]
|
||||
data: base_path.join("share"),
|
||||
sysconf: base_path.join("etc"),
|
||||
doc: base_path.join("user_doc/html"),
|
||||
bin: Some(base_path.to_path_buf()),
|
||||
}
|
||||
}
|
||||
|
||||
if paths.data.exists() && paths.sysconf.exists() {
|
||||
// The docs dir may not exist; in that case fall back to the compiled in path.
|
||||
if !paths.doc.exists() {
|
||||
paths.doc = PathBuf::from(DOC_DIR);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// Fall back to what got compiled in.
|
||||
let data = if cfg!(feature = "installable") {
|
||||
let Some(home) = env::get_home() else {
|
||||
FLOG!(
|
||||
error,
|
||||
"Cannot find home directory and will refuse to read configuration.\n",
|
||||
"Consider installing into a directory tree with `fish --install=PATH`."
|
||||
);
|
||||
return paths;
|
||||
};
|
||||
|
||||
PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
} else {
|
||||
Path::new(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
};
|
||||
let bin = if cfg!(feature = "installable") {
|
||||
exec_path.parent().map(|x| x.to_path_buf())
|
||||
} else {
|
||||
Some(PathBuf::from(BIN_DIR))
|
||||
};
|
||||
|
||||
FLOG!(config, "Using compiled in paths:");
|
||||
paths = ConfigPaths {
|
||||
data,
|
||||
sysconf: Path::new(SYSCONF_DIR).join("fish"),
|
||||
doc: DOC_DIR.into(),
|
||||
bin,
|
||||
}
|
||||
}
|
||||
|
||||
FLOGF!(
|
||||
config,
|
||||
"determine_config_directory_paths() results:\npaths.data: %ls\npaths.sysconf: \
|
||||
%ls\npaths.doc: %ls\npaths.bin: %ls",
|
||||
paths.data.display().to_string(),
|
||||
paths.sysconf.display().to_string(),
|
||||
paths.doc.display().to_string(),
|
||||
paths
|
||||
.bin
|
||||
.clone()
|
||||
.map(|x| x.display().to_string())
|
||||
.unwrap_or("|not found|".to_string()),
|
||||
);
|
||||
|
||||
paths
|
||||
}
|
||||
2
src/env/mod.rs
vendored
2
src/env/mod.rs
vendored
@@ -1,6 +1,8 @@
|
||||
mod config_paths;
|
||||
pub mod environment;
|
||||
mod environment_impl;
|
||||
pub mod var;
|
||||
pub use config_paths::CONFIG_PATHS;
|
||||
|
||||
use crate::common::ToCString;
|
||||
pub use environment::*;
|
||||
|
||||
Reference in New Issue
Block a user