Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ pub fn main() {
let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();

mf.write_all(
"type UtilityMap<T> = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
"type UtilityMap<T> = phf::OrderedMap<\n\
&'static str,\n\
(fn(T) -> i32, fn() -> Command, &'static allocation::AllocErrorConfig),\n\
>;\n\
\n\
#[allow(clippy::too_many_lines)]
#[allow(clippy::unreadable_literal)]
Expand All @@ -77,27 +80,27 @@ pub fn main() {

let mut phf_map = phf_codegen::OrderedMap::<&str>::new();
let mut entries = Vec::new();
let map_value = |krate: &str| {
format!("({krate}::uumain, {krate}::uu_app, &{krate}::UU_ALLOC_ERROR_CONFIG)")
};

for krate in &crates {
let map_value = format!("({krate}::uumain, {krate}::uu_app)");
match krate.as_ref() {
// 'test' is named uu_test to avoid collision with rust core crate 'test'.
// It can also be invoked by name '[' for the '[ expr ] syntax'.
"uu_test" => {
entries.push(("test", map_value.clone()));
entries.push(("[", map_value.clone()));
entries.push(("test", map_value(krate)));
entries.push(("[", map_value(krate)));
}
k if k.starts_with(OVERRIDE_PREFIX) => {
entries.push((&k[OVERRIDE_PREFIX.len()..], map_value.clone()));
entries.push((&k[OVERRIDE_PREFIX.len()..], map_value(krate)));
}
"false" | "true" => {
entries.push((
krate.as_str(),
format!("(r#{krate}::uumain, r#{krate}::uu_app)"),
));
let raw_krate = format!("r#{krate}");
entries.push((krate.as_str(), map_value(&raw_krate)));
}
_ => {
entries.push((krate.as_str(), map_value.clone()));
entries.push((krate.as_str(), map_value(krate)));
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions docs/src/l10n.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,44 @@ The string parameter determines the lookup path for Fluent files. **English alwa

---

## 🧯 Allocation-failure diagnostics

Standalone utilities and the `coreutils` multi-call binary install a
process-wide allocator. If an allocation fails, the allocator writes a
diagnostic without allocating more memory and exits with status `1` by default:

```
tsort: memory exhausted
```

The message is looked up in the following order:

1. `<utility>-memory-exhausted` in the current Fluent bundle
2. `common-memory-exhausted` in the current bundle or its English fallback
3. The built-in `memory exhausted` text when localization is unavailable

For example, a utility can provide its own localized message with:

```
tsort-memory-exhausted = memory exhausted while sorting
```

The allocation-failure status can be changed for utilities using the
`uucore::main` attribute:

```
#[uucore::main(alloc_error_exit_code = 2)]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// ...
}
```

`alloc_error_exit_code` accepts values from `1` through `255`. It only controls
the status used when the global allocator handles an allocation failure;
ordinary utility errors keep their existing status.

---

## 🌐 Locale Detection

Locale selection is automatic and performed via:
Expand Down
12 changes: 10 additions & 2 deletions src/bin/coreutils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ use itertools::Itertools as _;
use std::cmp;
use std::ffi::OsString;
use std::io::{self, Write};
use uucore::{Args, error::strip_errno};
use uucore::{Args, allocation, error::strip_errno};

#[global_allocator]
static UU_ALLOCATOR: allocation::UuAllocator =
allocation::UuAllocator::new(&allocation::COREUTILS_ALLOC_ERROR_CONFIG);

const VERSION: &str = env!("CARGO_PKG_VERSION");

Expand Down Expand Up @@ -113,13 +117,17 @@ fn main() {
_ => {}
}

if let Some(&(uumain, _)) = utils.get(util) {
if let Some((&alloc_error_program, &(uumain, _, alloc_error_config))) =
utils.get_entry(util)
{
allocation::activate(alloc_error_program, alloc_error_config);
// TODO: plug the deactivation of the translation
// and load the English strings directly at compilation time in the
// binary to avoid the load of the flt
// Could be something like:
// #[cfg(not(feature = "only_english"))]
validation::setup_localization_or_exit(util);
allocation::localize_message();
exit(uumain(vec![util_os].into_iter().chain(args)));
}
// GNU coreutils --help string shows help for coreutils
Expand Down
8 changes: 4 additions & 4 deletions src/bin/uudoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ use textwrap::{fill, indent, termwidth};
use zip::ZipArchive;

use coreutils::validation;
use uucore::Args;
use uucore::locale::get_message;
use uucore::{Args, allocation};

include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));

Expand Down Expand Up @@ -112,7 +112,7 @@ fn usage<T: Args>(utils: &UtilityMap<T>) {
/// Generates the coreutils app for the utility map
fn gen_coreutils_app<T: Args>(util_map: &UtilityMap<T>) -> Command {
let mut command = Command::new("coreutils");
for (name, (_, sub_app)) in util_map {
for (name, (_, sub_app, _)) in util_map {
// Recreate a small subcommand with only the relevant info
// (name & short description)
let about = sub_app()
Expand Down Expand Up @@ -321,7 +321,7 @@ fn main() -> io::Result<()> {
};

let mut utils = utils.entries().collect::<Vec<_>>();
utils.sort();
utils.sort_by_key(|(name, _)| *name);

println!("Writing util per platform table");
{
Expand Down Expand Up @@ -361,7 +361,7 @@ fn main() -> io::Result<()> {
}

println!("Writing to utils");
for (&name, (_, command)) in utils {
for (&name, (_, command, _)) in utils {
let (utils_name, usage_name, command) = match name {
"[" => {
continue;
Expand Down
9 changes: 8 additions & 1 deletion src/common/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,14 @@ pub fn exit(code: i32) -> ! {
/// Gets all available utilities including "coreutils"
#[allow(clippy::type_complexity)]
pub fn get_all_utilities<T: Args>(
util_map: &phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> clap::Command)>,
util_map: &phf::OrderedMap<
&'static str,
(
fn(T) -> i32,
fn() -> clap::Command,
&'static uucore::allocation::AllocErrorConfig,
),
>,
) -> Vec<&'static str> {
std::iter::once("coreutils")
.chain(util_map.keys().copied())
Expand Down
5 changes: 5 additions & 0 deletions src/uu/false/src/false.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ use std::io::{self, Write as _};
use uucore::error::strip_errno;
use uucore::{crate_version, show_error, translate};

#[doc(hidden)]
/// Allocation-failure policy for this utility.
pub const UU_ALLOC_ERROR_CONFIG: uucore::allocation::AllocErrorConfig =
uucore::allocation::AllocErrorConfig::default_for(env!("CARGO_PKG_NAME"));

// uucore::main does not support no-result
pub fn uumain(mut args: impl uucore::Args) -> i32 {
// skip binary name
Expand Down
5 changes: 5 additions & 0 deletions src/uu/true/src/true.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ use std::io::{self, Write as _};
use uucore::error::strip_errno;
use uucore::{crate_version, show_error, translate};

#[doc(hidden)]
/// Allocation-failure policy for this utility.
pub const UU_ALLOC_ERROR_CONFIG: uucore::allocation::AllocErrorConfig =
uucore::allocation::AllocErrorConfig::default_for(env!("CARGO_PKG_NAME"));

// uucore::main does not support no-result
pub fn uumain(mut args: impl uucore::Args) -> i32 {
// skip binary name
Expand Down
3 changes: 3 additions & 0 deletions src/uucore/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ common-help = help
common-version = version
common-read-error = read error
common-write-error = write error
# Fatal allocation failure. Utilities may define `<util>-memory-exhausted` in
# their own Fluent resource to override this diagnostic for that utility.
common-memory-exhausted = memory exhausted

# Common clap error messages
clap-error-unexpected-argument = { $error_word }: unexpected argument '{ $arg }' found
Expand Down
1 change: 1 addition & 0 deletions src/uucore/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ common-help = aide
common-version = version
common-write-error = erreur d'écriture
common-read-error = erreur de lecture
common-memory-exhausted = mémoire épuisée
# Messages d'erreur clap communs
clap-error-unexpected-argument = { $error_word } : argument inattendu '{ $arg }' trouvé
clap-error-unexpected-argument-simple = argument inattendu
Expand Down
8 changes: 8 additions & 0 deletions src/uucore/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod mods; // core cross-platform modules
pub use uucore_procs::*;

// * cross-platform modules
pub use crate::mods::allocation;
pub use crate::mods::clap_localization;
pub use crate::mods::display;
pub use crate::mods::error;
Expand Down Expand Up @@ -206,10 +207,16 @@ pub fn get_canonical_util_name(util_name: &str) -> &str {
#[macro_export]
macro_rules! bin_inner {
($util:ident, $post:expr) => {
#[global_allocator]
static UU_ALLOCATOR: $crate::allocation::UuAllocator =
$crate::allocation::UuAllocator::new(&$util::UU_ALLOC_ERROR_CONFIG);

pub fn main() {
use std::io::Write;
use uucore::locale;

$crate::allocation::activate(stringify!($util), &$util::UU_ALLOC_ERROR_CONFIG);

// Preserve inherited SIGPIPE settings (e.g., from env --default-signal=PIPE)
uucore::panic::preserve_inherited_sigpipe();

Expand All @@ -226,6 +233,7 @@ macro_rules! bin_inner {
}
std::process::exit(99)
});
$crate::allocation::localize_message();

// execute utility code
let code = $util::uumain(uucore::args_os());
Expand Down
1 change: 1 addition & 0 deletions src/uucore/src/lib/mods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// file that was distributed with this source code.
// mods ~ cross-platforms modules (core/bundler file)

pub mod allocation;
pub mod clap_localization;
pub mod display;
pub mod error;
Expand Down
Loading
Loading