diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 88f3b547605..b33e6b66117 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -11,7 +11,7 @@ use std::fmt; use std::fs; use std::io; -use std::path::Path; +use std::path::{Path, PathBuf}; use tracing::{instrument, trace}; #[cfg(parallel_compiler)] @@ -45,7 +45,7 @@ pub enum TranslationBundleError { /// Failed to add `FluentResource` to `FluentBundle`. AddResource(FluentError), /// `$sysroot/share/locale/$locale` does not exist. - MissingLocale(io::Error), + MissingLocale, /// Cannot read directory entries of `$sysroot/share/locale/$locale`. ReadLocalesDir(io::Error), /// Cannot read directory entry of `$sysroot/share/locale/$locale`. @@ -62,9 +62,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "could not parse ftl file: {}", e) } TranslationBundleError::AddResource(e) => write!(f, "failed to add resource: {}", e), - TranslationBundleError::MissingLocale(e) => { - write!(f, "missing locale directory: {}", e) - } + TranslationBundleError::MissingLocale => write!(f, "missing locale directory"), TranslationBundleError::ReadLocalesDir(e) => { write!(f, "could not read locales dir: {}", e) } @@ -84,7 +82,7 @@ fn source(&self) -> Option<&(dyn Error + 'static)> { TranslationBundleError::ReadFtl(e) => Some(e), TranslationBundleError::ParseFtl(e) => Some(e), TranslationBundleError::AddResource(e) => Some(e), - TranslationBundleError::MissingLocale(e) => Some(e), + TranslationBundleError::MissingLocale => None, TranslationBundleError::ReadLocalesDir(e) => Some(e), TranslationBundleError::ReadLocalesDirEntry(e) => Some(e), TranslationBundleError::LocaleIsNotDir => None, @@ -113,7 +111,8 @@ fn from(mut errs: Vec) -> Self { /// (overriding any conflicting messages). #[instrument(level = "trace")] pub fn fluent_bundle( - sysroot: &Path, + mut user_provided_sysroot: Option, + mut sysroot_candidates: Vec, requested_locale: Option, additional_ftl_path: Option<&Path>, with_directionality_markers: bool, @@ -140,33 +139,43 @@ pub fn fluent_bundle( // If the user requests the default locale then don't try to load anything. if !requested_fallback_locale && let Some(requested_locale) = requested_locale { - let mut sysroot = sysroot.to_path_buf(); - sysroot.push("share"); - sysroot.push("locale"); - sysroot.push(requested_locale.to_string()); - trace!(?sysroot); + let mut found_resources = false; + for sysroot in user_provided_sysroot.iter_mut().chain(sysroot_candidates.iter_mut()) { + sysroot.push("share"); + sysroot.push("locale"); + sysroot.push(requested_locale.to_string()); + trace!(?sysroot); - let _ = sysroot.try_exists().map_err(TranslationBundleError::MissingLocale)?; - - if !sysroot.is_dir() { - return Err(TranslationBundleError::LocaleIsNotDir); - } - - for entry in sysroot.read_dir().map_err(TranslationBundleError::ReadLocalesDir)? { - let entry = entry.map_err(TranslationBundleError::ReadLocalesDirEntry)?; - let path = entry.path(); - trace!(?path); - if path.extension().and_then(|s| s.to_str()) != Some("ftl") { + if !sysroot.exists() { trace!("skipping"); continue; } - let resource_str = - fs::read_to_string(path).map_err(TranslationBundleError::ReadFtl)?; - let resource = - FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; - trace!(?resource); - bundle.add_resource(resource).map_err(TranslationBundleError::from)?; + if !sysroot.is_dir() { + return Err(TranslationBundleError::LocaleIsNotDir); + } + + for entry in sysroot.read_dir().map_err(TranslationBundleError::ReadLocalesDir)? { + let entry = entry.map_err(TranslationBundleError::ReadLocalesDirEntry)?; + let path = entry.path(); + trace!(?path); + if path.extension().and_then(|s| s.to_str()) != Some("ftl") { + trace!("skipping"); + continue; + } + + let resource_str = + fs::read_to_string(path).map_err(TranslationBundleError::ReadFtl)?; + let resource = + FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; + trace!(?resource); + bundle.add_resource(resource).map_err(TranslationBundleError::from)?; + found_resources = true; + } + } + + if !found_resources { + return Err(TranslationBundleError::MissingLocale); } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index a3570320767..fe75ee8b37b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -44,6 +44,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { let sess = build_session( sessopts, None, + None, registry, DiagnosticOutput::Default, Default::default(), diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 592cf60e6c3..3fa8017dc93 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -83,9 +83,23 @@ pub fn create_session( // target_override is documented to be called before init(), so this is okay let target_override = codegen_backend.target_override(&sopts); + let bundle = match rustc_errors::fluent_bundle( + sopts.maybe_sysroot.clone(), + sysroot_candidates(), + sopts.debugging_opts.translate_lang.clone(), + sopts.debugging_opts.translate_additional_ftl.as_deref(), + sopts.debugging_opts.translate_directionality_markers, + ) { + Ok(bundle) => bundle, + Err(e) => { + early_error(sopts.error_format, &format!("failed to load fluent bundle: {e}")); + } + }; + let mut sess = session::build_session( sopts, input_path, + bundle, descriptions, diagnostic_output, lint_caps, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9881046ddfa..d70f89760a1 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -20,8 +20,8 @@ use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{ - fallback_fluent_bundle, fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, - EmissionGuarantee, ErrorGuaranteed, FluentBundle, MultiSpan, + fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, EmissionGuarantee, + ErrorGuaranteed, FluentBundle, MultiSpan, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -1162,6 +1162,7 @@ pub enum DiagnosticOutput { pub fn build_session( sopts: config::Options, local_crate_source_file: Option, + bundle: Option>, registry: rustc_errors::registry::Registry, diagnostics_output: DiagnosticOutput, driver_lint_caps: FxHashMap, @@ -1214,16 +1215,17 @@ pub fn build_session( hash_kind, )); - let bundle = fluent_bundle( - &sysroot, - sopts.debugging_opts.translate_lang.clone(), - sopts.debugging_opts.translate_additional_ftl.as_deref(), - sopts.debugging_opts.translate_directionality_markers, - ) - .expect("failed to load fluent bundle"); let fallback_bundle = - fallback_fluent_bundle(sopts.debugging_opts.translate_directionality_markers) - .expect("failed to load fallback fluent bundle"); + match fallback_fluent_bundle(sopts.debugging_opts.translate_directionality_markers) { + Ok(bundle) => bundle, + Err(e) => { + early_error( + sopts.error_format, + &format!("failed to load fallback fluent bundle: {e}"), + ); + } + }; + let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle, write_dest); diff --git a/src/test/run-make/translation/Makefile b/src/test/run-make/translation/Makefile index 22a3bf57ecf..bfff75e7acb 100644 --- a/src/test/run-make/translation/Makefile +++ b/src/test/run-make/translation/Makefile @@ -15,7 +15,9 @@ normal: basic-translation.rs custom: basic-translation.rs basic-translation.ftl $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/basic-translation.ftl 2>&1 | grep "this is a test message" -# Make a local copy of the sysroot and add the custom locale to it. +# Check that a locale can be loaded from the sysroot given a language +# identifier by making a local copy of the sysroot and adding the custom locale +# to it. sysroot: basic-translation.rs basic-translation.ftl mkdir $(FAKEROOT) ln -s $(SYSROOT)/* $(FAKEROOT) @@ -31,3 +33,27 @@ sysroot: basic-translation.rs basic-translation.ftl mkdir -p $(FAKEROOT)/share/locale/zh-CN/ ln -s $(CURDIR)/basic-translation.ftl $(FAKEROOT)/share/locale/zh-CN/basic-translation.ftl $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | grep "this is a test message" + +# Check that the compiler errors out when the sysroot requested cannot be +# found. This test might start failing if there actually exists a Klingon +# translation of rustc's error messages. +sysroot-missing: + $(RUSTC) $< -Ztranslate-lang=tlh 2>&1 || grep "missing locale directory" + +# Check that the compiler errors out when the sysroot requested cannot be +# found. This test might start failing if there actually exists a Klingon +# translation of rustc's error messages. +sysroot-invalid: basic-translation.rs basic-translation.ftl + mkdir $(FAKEROOT) + ln -s $(SYSROOT)/* $(FAKEROOT) + rm -f $(FAKEROOT)/lib + mkdir $(FAKEROOT)/lib + ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib + rm -f $(FAKEROOT)/lib/rustlib + mkdir $(FAKEROOT)/lib/rustlib + ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib + rm -f $(FAKEROOT)/lib/rustlib/src + mkdir $(FAKEROOT)/lib/rustlib/src + ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src + touch $(FAKEROOT)/share/locale/zh-CN/ + $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 || grep "`\$sysroot/share/locales/\$locale` is not a directory"