diff options
| author | bors <bors@rust-lang.org> | 2017-09-07 04:07:09 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-09-07 04:07:09 +0000 |
| commit | a6a9d4c5fd214cb8110482dee2017607e23ccc7b (patch) | |
| tree | 65fdacc215a88f9812d4b12b355bfea5695ad25c /src/test | |
| parent | 05e3c96d36b5281e969b5e79b92d671c7872bf83 (diff) | |
| parent | ed938f08a949cb487f7513d4f22f92355a40d2f3 (diff) | |
| download | rust-a6a9d4c5fd214cb8110482dee2017607e23ccc7b.tar.gz rust-a6a9d4c5fd214cb8110482dee2017607e23ccc7b.zip | |
Auto merge of #44094 - alexcrichton:long-linkers, r=michaelwoerister
rustc: Attempt to handle super long linker invocations This commit adds logic to the compiler to attempt to handle super long linker invocations by falling back to the `@`-file syntax if the invoked command is too large. Each OS has a limit on how many arguments and how large the arguments can be when spawning a new process, and linkers tend to be one of those programs that can hit the limit! The logic implemented here is to unconditionally attempt to spawn a linker and then if it fails to spawn with an error from the OS that indicates the command line is too big we attempt a fallback. The fallback is roughly the same for all linkers where an argument pointing to a file, prepended with `@`, is passed. This file then contains all the various arguments that we want to pass to the linker. Closes #41190
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/run-make/long-linker-command-lines/Makefile | 5 | ||||
| -rw-r--r-- | src/test/run-make/long-linker-command-lines/foo.rs | 88 | ||||
| -rw-r--r-- | src/test/run-make/tools.mk | 1 |
3 files changed, 94 insertions, 0 deletions
diff --git a/src/test/run-make/long-linker-command-lines/Makefile b/src/test/run-make/long-linker-command-lines/Makefile new file mode 100644 index 00000000000..309a27fe503 --- /dev/null +++ b/src/test/run-make/long-linker-command-lines/Makefile @@ -0,0 +1,5 @@ +-include ../tools.mk + +all: + $(RUSTC) foo.rs -g + RUSTC="$(RUSTC_ORIGINAL)" $(call RUN,foo) diff --git a/src/test/run-make/long-linker-command-lines/foo.rs b/src/test/run-make/long-linker-command-lines/foo.rs new file mode 100644 index 00000000000..e6fd6b65366 --- /dev/null +++ b/src/test/run-make/long-linker-command-lines/foo.rs @@ -0,0 +1,88 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This is a test which attempts to blow out the system limit with how many +// arguments can be passed to a process. This'll successively call rustc with +// larger and larger argument lists in an attempt to find one that's way too +// big for the system at hand. This file itself is then used as a "linker" to +// detect when the process creation succeeds. +// +// Eventually we should see an argument that looks like `@` as we switch from +// passing literal arguments to passing everything in the file. + +use std::env; +use std::fs::{self, File}; +use std::io::{BufWriter, Write, Read}; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let tmpdir = PathBuf::from(env::var_os("TMPDIR").unwrap()); + let ok = tmpdir.join("ok"); + if env::var("YOU_ARE_A_LINKER").is_ok() { + if let Some(file) = env::args().find(|a| a.contains("@")) { + fs::copy(&file[1..], &ok).unwrap(); + } + return + } + + let rustc = env::var_os("RUSTC").unwrap_or("rustc".into()); + let me_as_linker = format!("linker={}", env::current_exe().unwrap().display()); + for i in (1..).map(|i| i * 100) { + println!("attempt: {}", i); + let file = tmpdir.join("bar.rs"); + let mut f = BufWriter::new(File::create(&file).unwrap()); + let mut lib_name = String::new(); + for _ in 0..i { + lib_name.push_str("foo"); + } + for j in 0..i { + writeln!(f, "#[link(name = \"{}{}\")]", lib_name, j).unwrap(); + } + writeln!(f, "extern {{}}\nfn main() {{}}").unwrap(); + f.into_inner().unwrap(); + + drop(fs::remove_file(&ok)); + let output = Command::new(&rustc) + .arg(&file) + .arg("-C").arg(&me_as_linker) + .arg("--out-dir").arg(&tmpdir) + .env("YOU_ARE_A_LINKER", "1") + .output() + .unwrap(); + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + panic!("status: {}\nstdout:\n{}\nstderr:\n{}", + output.status, + String::from_utf8_lossy(&output.stdout), + stderr.lines().map(|l| { + if l.len() > 200 { + format!("{}...\n", &l[..200]) + } else { + format!("{}\n", l) + } + }).collect::<String>()); + } + + if !ok.exists() { + continue + } + + let mut contents = String::new(); + File::open(&ok).unwrap().read_to_string(&mut contents).unwrap(); + + for j in 0..i { + assert!(contents.contains(&format!("{}{}", lib_name, j))); + } + + break + } +} diff --git a/src/test/run-make/tools.mk b/src/test/run-make/tools.mk index d13ba11e96a..27f235d54d4 100644 --- a/src/test/run-make/tools.mk +++ b/src/test/run-make/tools.mk @@ -5,6 +5,7 @@ HOST_RPATH_ENV = \ TARGET_RPATH_ENV = \ $(LD_LIB_PATH_ENVVAR)="$(TMPDIR):$(TARGET_RPATH_DIR):$($(LD_LIB_PATH_ENVVAR))" +RUSTC_ORIGINAL := $(RUSTC) BARE_RUSTC := $(HOST_RPATH_ENV) '$(RUSTC)' RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS) #CC := $(CC) -L $(TMPDIR) |
