Remove unused locale path code

The locale path was used to tell GNU gettext where to look for MO files
at runtime. Since we now embed the message catalog data into the
executable, we no longer need a locale path.

Part of #11726
This commit is contained in:
Daniel Rainer
2025-09-16 19:34:41 +02:00
committed by Johannes Altmanninger
parent ad323d03b6
commit bee1e122f9
5 changed files with 111 additions and 194 deletions

View File

@@ -35,7 +35,7 @@
PROFILING_ACTIVE, PROGRAM_NAME,
},
env::{
config_paths::{init_locale_dir, ConfigPaths},
config_paths::ConfigPaths,
environment::{env_init, EnvStack, Environment},
EnvMode, Statuses,
},
@@ -413,13 +413,6 @@ fn throwing_main() -> i32 {
}
}
let mut args: Vec<WString> = env::args_os()
.map(|osstr| str2wcstring(osstr.as_bytes()))
.collect();
let config_path_detection = init_locale_dir(&args[0]);
// Initialize gettext translation.
#[cfg(feature = "localize-messages")]
fish::wutil::gettext::initialize_gettext();
// Enable debug categories set in FISH_DEBUG.
@@ -429,6 +422,9 @@ fn throwing_main() -> i32 {
activate_flog_categories_by_pattern(&s);
}
let mut args: Vec<WString> = env::args_os()
.map(|osstr| str2wcstring(osstr.as_bytes()))
.collect();
let mut opts = FishCmdOpts::default();
let mut my_optind = match fish_parse_opt(&mut args, &mut opts) {
ControlFlow::Continue(optind) => optind,
@@ -464,8 +460,6 @@ fn throwing_main() -> i32 {
};
}
config_path_detection.log_config_paths();
// No-exec is prohibited when in interactive mode.
if opts.is_interactive_session && opts.no_exec {
FLOG!(
@@ -497,13 +491,13 @@ fn throwing_main() -> i32 {
// If we're not executing, there's no need to find the config.
let config_paths = if !opts.no_exec {
let paths = config_path_detection.paths;
let config_paths = ConfigPaths::new(&args[0]);
env_init(
Some(&paths),
Some(&config_paths),
/* do uvars */ !opts.no_config,
/* default paths */ opts.no_config,
);
Some(paths)
Some(config_paths)
} else {
None
};

View File

@@ -11,7 +11,6 @@
use std::io::{Read, Write};
use std::os::unix::ffi::OsStrExt;
use crate::env::config_paths::init_locale_dir;
use crate::panic::panic_handler;
use libc::LC_ALL;
@@ -904,10 +903,7 @@ fn throwing_main() -> i32 {
let s = CString::new("").unwrap();
unsafe { libc::setlocale(LC_ALL, s.as_ptr()) };
}
let args: Vec<WString> = std::env::args_os()
.map(|osstr| str2wcstring(osstr.as_bytes()))
.collect();
init_locale_dir(&args[0]);
crate::wutil::gettext::initialize_gettext();
env_init(None, true, false);
// Only set these here so you can't set them via the builtin.
@@ -917,6 +913,9 @@ fn throwing_main() -> i32 {
}
}
let args: Vec<WString> = std::env::args_os()
.map(|osstr| str2wcstring(osstr.as_bytes()))
.collect();
do_indent(&mut streams, args).builtin_status_code()
}

View File

@@ -17,7 +17,7 @@
use crate::{
builtins::shared::BUILTIN_ERR_UNKNOWN,
common::{shell_modes, str2wcstring, PROGRAM_NAME},
env::{config_paths::init_locale_dir, env_init, EnvStack, Environment},
env::{env_init, EnvStack, Environment},
future_feature_flags,
input_common::{
match_key_event_to_key, CharEvent, InputEventQueue, InputEventQueuer, KeyEvent,
@@ -278,10 +278,7 @@ fn throwing_main() -> i32 {
set_interactive_session(true);
topic_monitor_init();
threads::init();
let args: Vec<WString> = std::env::args_os()
.map(|osstr| str2wcstring(osstr.as_bytes()))
.collect();
init_locale_dir(&args[0]);
crate::wutil::gettext::initialize_gettext();
env_init(None, true, false);
reader_init(false);
if let Some(features_var) = EnvStack::globals().get(L!("fish_features")) {
@@ -298,6 +295,9 @@ fn throwing_main() -> i32 {
let mut continuous_mode = false;
let mut verbose = false;
let args: Vec<WString> = std::env::args_os()
.map(|osstr| str2wcstring(osstr.as_bytes()))
.collect();
if let ControlFlow::Break(s) =
parse_flags(&mut streams, args, &mut continuous_mode, &mut verbose)
{

View File

@@ -1,5 +1,4 @@
use crate::common::wcs2string;
use crate::global_safety::AtomicRef;
use crate::wchar::prelude::*;
use crate::{common::get_executable_path, FLOG, FLOGF};
#[cfg(not(feature = "embed-data"))]
@@ -17,181 +16,18 @@ pub struct ConfigPaths {
pub data: PathBuf, // e.g., /usr/local/share
#[cfg(not(feature = "embed-data"))]
pub doc: PathBuf, // e.g., /usr/local/share/doc/fish
#[cfg(not(feature = "embed-data"))]
pub locale: PathBuf, // e.g., /usr/local/share/locale
}
const SYSCONF_DIR: &str = env!("SYSCONFDIR");
#[cfg(not(feature = "embed-data"))]
const DOC_DIR: &str = env!("DOCDIR");
#[cfg(not(feature = "embed-data"))]
enum ConfigPathKind {
WorkspaceRoot,
RelocatableTree,
StaticPathsDueToInvalidExecPath,
StaticPathsDueToWeirdLayout,
}
pub struct ConfigPathDetection {
#[cfg(not(feature = "embed-data"))]
kind: ConfigPathKind,
pub paths: ConfigPaths,
exec_path: PathBuf,
}
impl ConfigPaths {
#[cfg(not(feature = "embed-data"))]
fn static_paths() -> Self {
Self {
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
bin: Some(PathBuf::from(env!("BINDIR"))),
data: PathBuf::from(env!("DATADIR")).join("fish"),
doc: DOC_DIR.into(),
locale: PathBuf::from(env!("LOCALEDIR")),
}
}
}
const EMPTY_LOCALE_DIR: &[u8] = &[];
pub static LOCALE_DIR: AtomicRef<[u8]> = AtomicRef::new(&EMPTY_LOCALE_DIR);
/// NOTE: This is called during early startup, do not log anything.
pub fn init_locale_dir(argv0: &wstr) -> ConfigPathDetection {
#[allow(clippy::let_and_return)] // for old clippy
let detection = ConfigPathDetection::new(argv0);
#[cfg(not(feature = "embed-data"))]
{
use std::os::unix::ffi::OsStrExt;
let bytes = detection.paths.locale.as_os_str().as_bytes();
assert!(!bytes.is_empty());
let bytes: Box<[u8]> = Box::from(bytes);
let bytes: Box<&'static [u8]> = Box::new(Box::leak(bytes));
LOCALE_DIR.store(Box::leak(bytes));
}
detection
}
impl ConfigPathDetection {
fn new(argv0: &wstr) -> Self {
pub fn new(argv0: &wstr) -> Self {
let argv0 = PathBuf::from(OsString::from_vec(wcs2string(argv0)));
let exec_path = get_executable_path(argv0);
Self::from_exec_path(exec_path)
}
#[cfg(feature = "embed-data")]
fn from_exec_path(exec_path: PathBuf) -> Self {
Self {
paths: ConfigPaths {
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
bin: exec_path.parent().map(|x| x.to_path_buf()),
},
exec_path,
}
}
#[cfg(not(feature = "embed-data"))]
fn from_exec_path(unresolved_exec_path: PathBuf) -> Self {
use ConfigPathKind::*;
let invalid_exec_path = |exec_path| Self {
kind: StaticPathsDueToInvalidExecPath,
paths: ConfigPaths::static_paths(),
exec_path,
};
let Ok(exec_path) = unresolved_exec_path.canonicalize() else {
return invalid_exec_path(unresolved_exec_path);
};
let Some(exec_path_parent) = exec_path.parent() else {
return invalid_exec_path(exec_path);
};
let workspace_root = workspace_root();
// The next check is that we are in a relocatable directory tree
if exec_path_parent.ends_with("bin") {
let base_path = exec_path_parent.parent().unwrap();
let data = base_path.join("share/fish");
let sysconf = base_path.join("etc/fish");
if base_path != workspace_root // Install to repo root is not supported.
&& data.exists() && sysconf.exists()
{
let doc = base_path.join("share/doc/fish");
return Self {
kind: RelocatableTree,
paths: ConfigPaths {
sysconf,
bin: Some(exec_path_parent.to_owned()),
data,
// The docs dir may not exist; in that case fall back to the compiled in path.
doc: if doc.exists() {
doc
} else {
PathBuf::from(DOC_DIR)
},
locale: base_path.join("share/locale"),
},
exec_path,
};
}
}
// If we're in Cargo's target directory or in CMake's build directory, use the source files.
if exec_path.starts_with(env!("FISH_BUILD_DIR")) {
return Self {
kind: WorkspaceRoot,
paths: ConfigPaths {
sysconf: workspace_root.join("etc"),
bin: Some(exec_path_parent.to_owned()),
data: workspace_root.join("share"),
doc: workspace_root.join("user_doc/html"),
locale: workspace_root.join("share/locale"),
},
exec_path,
};
}
Self {
kind: StaticPathsDueToWeirdLayout,
paths: ConfigPaths::static_paths(),
exec_path,
}
}
pub fn log_config_paths(&self) {
FLOG!(
config,
format!("executable path: {}", self.exec_path.display())
);
#[cfg(feature = "embed-data")]
FLOG!(config, "embed-data feature is active, ignoring data paths");
#[cfg(not(feature = "embed-data"))]
use ConfigPathKind::*;
#[cfg(not(feature = "embed-data"))]
match &self.kind {
WorkspaceRoot => {
FLOG!(
config,
format!(
"Running out of build directory, using paths relative to $CARGO_MANIFEST_DIR ({})",
workspace_root().display()
),
);
}
RelocatableTree => {
FLOG!(config, "Running from relocatable tree");
}
StaticPathsDueToInvalidExecPath => {
FLOG!(config, "Invalid executable path, using compiled-in paths");
}
StaticPathsDueToWeirdLayout => {
FLOG!(
config,
"Unexpected directory layout, using compiled-in paths"
);
}
}
let paths = &self.paths;
FLOG!(config, format!("executable path: {}", exec_path.display()));
let paths = Self::from_exec_path(exec_path);
FLOGF!(
config,
"paths.sysconf: %ls",
@@ -210,11 +46,96 @@ pub fn log_config_paths(&self) {
FLOGF!(config, "paths.data: %ls", paths.data.display().to_string());
#[cfg(not(feature = "embed-data"))]
FLOGF!(config, "paths.doc: %ls", paths.doc.display().to_string());
#[cfg(not(feature = "embed-data"))]
FLOGF!(
paths
}
#[cfg(not(feature = "embed-data"))]
fn static_paths() -> Self {
Self {
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
bin: Some(PathBuf::from(env!("BINDIR"))),
data: PathBuf::from(env!("DATADIR")).join("fish"),
doc: DOC_DIR.into(),
}
}
#[cfg(feature = "embed-data")]
fn from_exec_path(exec_path: PathBuf) -> Self {
FLOG!(config, "embed-data feature is active, ignoring data paths");
Self {
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
bin: exec_path.parent().map(|x| x.to_path_buf()),
}
}
#[cfg(not(feature = "embed-data"))]
fn from_exec_path(unresolved_exec_path: PathBuf) -> Self {
use std::path::Path;
let invalid_exec_path = |exec_path: &Path| {
FLOG!(
config,
format!(
"Invalid executable path '{}', using compiled-in paths",
exec_path.display()
)
);
Self::static_paths()
};
let Ok(exec_path) = unresolved_exec_path.canonicalize() else {
return invalid_exec_path(&unresolved_exec_path);
};
let Some(exec_path_parent) = exec_path.parent() else {
return invalid_exec_path(&exec_path);
};
let workspace_root = workspace_root();
// The next check is that we are in a relocatable directory tree
if exec_path_parent.ends_with("bin") {
let base_path = exec_path_parent.parent().unwrap();
let data = base_path.join("share/fish");
let sysconf = base_path.join("etc/fish");
if base_path != workspace_root // Install to repo root is not supported.
&& data.exists() && sysconf.exists()
{
FLOG!(config, "Running from relocatable tree");
let doc = base_path.join("share/doc/fish");
return Self {
sysconf,
bin: Some(exec_path_parent.to_owned()),
data,
// The docs dir may not exist; in that case fall back to the compiled in path.
doc: if doc.exists() {
doc
} else {
PathBuf::from(DOC_DIR)
},
};
}
}
// If we're in Cargo's target directory or in CMake's build directory, use the source files.
if exec_path.starts_with(env!("FISH_BUILD_DIR")) {
FLOG!(
config,
format!(
"Running out of build directory, using paths relative to $CARGO_MANIFEST_DIR ({})",
workspace_root.display()
),
);
return Self {
sysconf: workspace_root.join("etc"),
bin: Some(exec_path_parent.to_owned()),
data: workspace_root.join("share"),
doc: workspace_root.join("user_doc/html"),
};
}
FLOG!(
config,
"paths.locale: %ls",
paths.locale.display().to_string()
"Unexpected directory layout, using compiled-in paths"
);
Self::static_paths()
}
}

View File

@@ -160,6 +160,9 @@ pub fn update_locale_from_env(vars: &EnvStack) {
gettext_impl::update_locale_from_env(vars);
}
#[cfg(not(feature = "localize-messages"))]
pub fn initialize_gettext() {}
/// This function only exists to provide a way for initializing gettext before an [`EnvStack`] is
/// available. Without this, early error messages cannot be localized.
#[cfg(feature = "localize-messages")]