Skip to content

Commit 7d98d96

Browse files
committed
strip prefix of temporary file names when it exceeds filesystem name length limit
When doing lto, rustc generates filenames that are concatenating many information. In the case of this testcase, it is concatenating crate name and rust file name, plus some hash, and the extension. In some other cases it will concatenate even more information reducing the maximum effective crate name to about 110 chars on linux filesystems where filename max length is 255 This commit is ensuring that the temporary file names are limited in size, while still reasonabily ensuring the unicity (with hashing of the stripped part)
1 parent dc0bae1 commit 7d98d96

File tree

4 files changed

+62
-2
lines changed

4 files changed

+62
-2
lines changed

compiler/rustc_session/src/config.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ use std::{cmp, fmt, fs, iter};
1616

1717
use externs::{ExternOpt, split_extern_opt};
1818
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
19-
use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
19+
use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey};
2020
use rustc_errors::emitter::HumanReadableErrorType;
2121
use rustc_errors::{ColorConfig, DiagArgValue, DiagCtxtFlags, IntoDiagArg};
2222
use rustc_feature::UnstableFeatures;
23+
use rustc_hashes::Hash64;
2324
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
2425
use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION};
2526
use rustc_span::source_map::FilePathMapping;
@@ -1198,7 +1199,25 @@ pub struct OutputFilenames {
11981199
pub const RLINK_EXT: &str = "rlink";
11991200
pub const RUST_CGU_EXT: &str = "rcgu";
12001201
pub const DWARF_OBJECT_EXT: &str = "dwo";
1202+
pub const MAX_FILENAME_LENGTH: usize = 143; // ecryptfs limits filenames to 143 bytes see #49914
12011203

1204+
/// Ensure the filename is not too long, as some filesystems have a limit.
1205+
/// If the filename is too long, hash part of it and append the hash to the filename.
1206+
/// This is a workaround for long crate names generating overly long filenames.
1207+
fn maybe_strip_file_name(mut path: PathBuf) -> PathBuf {
1208+
if path.file_name().map_or(0, |name| name.len()) > MAX_FILENAME_LENGTH {
1209+
let filename = path.file_name().unwrap().to_string_lossy();
1210+
let hash_len = 64 / 4; // Hash64 is 64 bits encoded in hex
1211+
let stripped_len = filename.len() - MAX_FILENAME_LENGTH + hash_len;
1212+
1213+
let mut hasher = StableHasher::new();
1214+
filename[..stripped_len].hash(&mut hasher);
1215+
let hash = hasher.finish::<Hash64>();
1216+
1217+
path.set_file_name(format!("{:x}-{}", hash, &filename[stripped_len..]));
1218+
}
1219+
path
1220+
}
12021221
impl OutputFilenames {
12031222
pub fn new(
12041223
out_directory: PathBuf,
@@ -1291,7 +1310,7 @@ impl OutputFilenames {
12911310
}
12921311

12931312
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
1294-
self.with_directory_and_extension(temps_directory, &extension)
1313+
maybe_strip_file_name(self.with_directory_and_extension(temps_directory, &extension))
12951314
}
12961315

12971316
pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
generated*.rs
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// This file has very long lines, but there is no way to avoid it as we are testing
2+
// long crate names. so:
3+
// ignore-tidy-linelength
4+
5+
extern crate generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name;
6+
7+
fn main() {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// This file has very long lines, but there is no way to avoid it as we are testing
2+
// long crate names. so:
3+
// ignore-tidy-linelength
4+
5+
// A variant of the smoke test to check that link time optimization
6+
// (LTO) is accepted by the compiler, and that
7+
// passing its various flags still results in successful compilation, even for very long crate names.
8+
// See https://github.com/rust-lang/rust/issues/49914
9+
10+
//@ ignore-cross-compile
11+
12+
use std::fs;
13+
14+
use run_make_support::rustc;
15+
16+
// This test make sure we don't get such following error:
17+
// error: could not write output to generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.9384edb61bfd127c-cgu.0.rcgu.o: File name too long
18+
// as reported in issue #49914
19+
fn main() {
20+
let lto_flags = ["-Clto", "-Clto=yes", "-Clto=off", "-Clto=thin", "-Clto=fat"];
21+
let aux_file = "generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.rs";
22+
// The auxiliary file is used to test long crate names.
23+
// The file name is intentionally long to test the handling of long filenames.
24+
// but we don't commit it to avoid issues with windows paths, which have a limitation for the full path length.
25+
// Linux only has a limit for the length of the file name.
26+
// and we want to make sure we can compile files with long names.
27+
fs::write(aux_file, "#![crate_type = \"rlib\"]\n").expect("Failed to write lib.rs");
28+
29+
for flag in lto_flags {
30+
rustc().input(aux_file).arg(flag).run();
31+
rustc().input("main.rs").arg(flag).run();
32+
}
33+
}

0 commit comments

Comments
 (0)