about summary refs log tree commit diff
path: root/src/test
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-09-07 04:07:09 +0000
committerbors <bors@rust-lang.org>2017-09-07 04:07:09 +0000
commita6a9d4c5fd214cb8110482dee2017607e23ccc7b (patch)
tree65fdacc215a88f9812d4b12b355bfea5695ad25c /src/test
parent05e3c96d36b5281e969b5e79b92d671c7872bf83 (diff)
parented938f08a949cb487f7513d4f22f92355a40d2f3 (diff)
downloadrust-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/Makefile5
-rw-r--r--src/test/run-make/long-linker-command-lines/foo.rs88
-rw-r--r--src/test/run-make/tools.mk1
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)