Skip to content

Commit ca7d839

Browse files
committed
Auto merge of #47203 - varkor:output-filename-conflicts-with-directory, r=estebank
Warn when rustc output conflicts with existing directories When the compiled executable would conflict with a directory, display a rustc error instead of a verbose and potentially-confusing linker error. This is a usability improvement, and doesn’t actually change behaviour with regards to compilation success. This addresses the concern in #35887. Fixes #13098.
2 parents b224fc8 + e92bdb9 commit ca7d839

File tree

7 files changed

+111
-43
lines changed

7 files changed

+111
-43
lines changed

src/librustc/session/config.rs

-19
Original file line numberDiff line numberDiff line change
@@ -548,25 +548,6 @@ impl OutputFilenames {
548548
pub fn filestem(&self) -> String {
549549
format!("{}{}", self.out_filestem, self.extra)
550550
}
551-
552-
pub fn contains_path(&self, input_path: &PathBuf) -> bool {
553-
let input_path = input_path.canonicalize().ok();
554-
if input_path.is_none() {
555-
return false
556-
}
557-
match self.single_output_file {
558-
Some(ref output_path) => output_path.canonicalize().ok() == input_path,
559-
None => {
560-
for k in self.outputs.keys() {
561-
let output_path = self.path(k.to_owned());
562-
if output_path.canonicalize().ok() == input_path {
563-
return true;
564-
}
565-
}
566-
false
567-
}
568-
}
569-
}
570551
}
571552

572553
pub fn host_triple() -> &'static str {

src/librustc_driver/driver.rs

+77-22
Original file line numberDiff line numberDiff line change
@@ -121,23 +121,8 @@ pub fn compile_input(trans: Box<TransCrate>,
121121
};
122122

123123
let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess);
124-
125-
// Ensure the source file isn't accidentally overwritten during compilation.
126-
match *input_path {
127-
Some(ref input_path) => {
128-
if outputs.contains_path(input_path) && sess.opts.will_create_output_file() {
129-
sess.err(&format!(
130-
"the input file \"{}\" would be overwritten by the generated executable",
131-
input_path.display()));
132-
return Err(CompileIncomplete::Stopped);
133-
}
134-
},
135-
None => {}
136-
}
137-
138124
let crate_name =
139125
::rustc_trans_utils::link::find_crate_name(Some(sess), &krate.attrs, input);
140-
141126
let ExpansionResult { expanded_crate, defs, analysis, resolutions, mut hir_forest } = {
142127
phase_2_configure_and_expand(
143128
sess,
@@ -157,7 +142,29 @@ pub fn compile_input(trans: Box<TransCrate>,
157142
)?
158143
};
159144

160-
write_out_deps(sess, &outputs, &crate_name);
145+
let output_paths = generated_output_paths(sess, &outputs, output.is_some(), &crate_name);
146+
147+
// Ensure the source file isn't accidentally overwritten during compilation.
148+
if let Some(ref input_path) = *input_path {
149+
if sess.opts.will_create_output_file() {
150+
if output_contains_path(&output_paths, input_path) {
151+
sess.err(&format!(
152+
"the input file \"{}\" would be overwritten by the generated \
153+
executable",
154+
input_path.display()));
155+
return Err(CompileIncomplete::Stopped);
156+
}
157+
if let Some(dir_path) = output_conflicts_with_dir(&output_paths) {
158+
sess.err(&format!(
159+
"the generated executable for the input file \"{}\" conflicts with the \
160+
existing directory \"{}\"",
161+
input_path.display(), dir_path.display()));
162+
return Err(CompileIncomplete::Stopped);
163+
}
164+
}
165+
}
166+
167+
write_out_deps(sess, &outputs, &output_paths);
161168
if sess.opts.output_types.contains_key(&OutputType::DepInfo) &&
162169
sess.opts.output_types.keys().count() == 1 {
163170
return Ok(())
@@ -1101,16 +1108,22 @@ fn escape_dep_filename(filename: &FileName) -> String {
11011108
filename.to_string().replace(" ", "\\ ")
11021109
}
11031110

1104-
fn write_out_deps(sess: &Session, outputs: &OutputFilenames, crate_name: &str) {
1111+
// Returns all the paths that correspond to generated files.
1112+
fn generated_output_paths(sess: &Session,
1113+
outputs: &OutputFilenames,
1114+
exact_name: bool,
1115+
crate_name: &str) -> Vec<PathBuf> {
11051116
let mut out_filenames = Vec::new();
11061117
for output_type in sess.opts.output_types.keys() {
11071118
let file = outputs.path(*output_type);
11081119
match *output_type {
1109-
OutputType::Exe => {
1110-
for output in sess.crate_types.borrow().iter() {
1120+
// If the filename has been overridden using `-o`, it will not be modified
1121+
// by appending `.rlib`, `.exe`, etc., so we can skip this transformation.
1122+
OutputType::Exe if !exact_name => {
1123+
for crate_type in sess.crate_types.borrow().iter() {
11111124
let p = ::rustc_trans_utils::link::filename_for_input(
11121125
sess,
1113-
*output,
1126+
*crate_type,
11141127
crate_name,
11151128
outputs
11161129
);
@@ -1125,7 +1138,46 @@ fn write_out_deps(sess: &Session, outputs: &OutputFilenames, crate_name: &str) {
11251138
}
11261139
}
11271140
}
1141+
out_filenames
1142+
}
1143+
1144+
// Runs `f` on every output file path and returns the first non-None result, or None if `f`
1145+
// returns None for every file path.
1146+
fn check_output<F, T>(output_paths: &Vec<PathBuf>, f: F) -> Option<T>
1147+
where F: Fn(&PathBuf) -> Option<T> {
1148+
for output_path in output_paths {
1149+
if let Some(result) = f(output_path) {
1150+
return Some(result);
1151+
}
1152+
}
1153+
None
1154+
}
11281155

1156+
pub fn output_contains_path(output_paths: &Vec<PathBuf>, input_path: &PathBuf) -> bool {
1157+
let input_path = input_path.canonicalize().ok();
1158+
if input_path.is_none() {
1159+
return false
1160+
}
1161+
let check = |output_path: &PathBuf| {
1162+
if output_path.canonicalize().ok() == input_path {
1163+
Some(())
1164+
} else { None }
1165+
};
1166+
check_output(output_paths, check).is_some()
1167+
}
1168+
1169+
pub fn output_conflicts_with_dir(output_paths: &Vec<PathBuf>) -> Option<PathBuf> {
1170+
let check = |output_path: &PathBuf| {
1171+
if output_path.is_dir() {
1172+
Some(output_path.clone())
1173+
} else { None }
1174+
};
1175+
check_output(output_paths, check)
1176+
}
1177+
1178+
fn write_out_deps(sess: &Session,
1179+
outputs: &OutputFilenames,
1180+
out_filenames: &Vec<PathBuf>) {
11291181
// Write out dependency rules to the dep-info file if requested
11301182
if !sess.opts.output_types.contains_key(&OutputType::DepInfo) {
11311183
return;
@@ -1144,7 +1196,7 @@ fn write_out_deps(sess: &Session, outputs: &OutputFilenames, crate_name: &str) {
11441196
.map(|fmap| escape_dep_filename(&fmap.name))
11451197
.collect();
11461198
let mut file = fs::File::create(&deps_filename)?;
1147-
for path in &out_filenames {
1199+
for path in out_filenames {
11481200
write!(file, "{}: {}\n\n", path.display(), files.join(" "))?;
11491201
}
11501202

@@ -1327,7 +1379,10 @@ pub fn build_output_filenames(input: &Input,
13271379
Some(out_file.clone())
13281380
};
13291381
if *odir != None {
1330-
sess.warn("ignoring --out-dir flag due to -o flag.");
1382+
sess.warn("ignoring --out-dir flag due to -o flag");
1383+
}
1384+
if !sess.opts.cg.extra_filename.is_empty() {
1385+
sess.warn("ignoring -C extra-filename flag due to -o flag");
13311386
}
13321387

13331388
let cur_dir = Path::new("");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-include ../tools.mk
2+
3+
all:
4+
cp foo.rs $(TMPDIR)/foo.rs
5+
mkdir $(TMPDIR)/foo
6+
$(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo 2>&1 \
7+
| $(CGREP) -e "the generated executable for the input file \".*foo\.rs\" conflicts with the existing directory \".*foo\""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {}

src/test/run-make/output-filename-overwrites-input/Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
all:
44
cp foo.rs $(TMPDIR)/foo
5-
$(RUSTC) $(TMPDIR)/foo 2>&1 \
5+
$(RUSTC) $(TMPDIR)/foo -o $(TMPDIR)/foo 2>&1 \
66
| $(CGREP) -e "the input file \".*foo\" would be overwritten by the generated executable"
7+
cp bar.rs $(TMPDIR)/bar.rlib
8+
$(RUSTC) $(TMPDIR)/bar.rlib -o $(TMPDIR)/bar.rlib 2>&1 \
9+
| $(CGREP) -e "the input file \".*bar.rlib\" would be overwritten by the generated executable"
710
$(RUSTC) foo.rs 2>&1 && $(RUSTC) -Z ls $(TMPDIR)/foo 2>&1
811
cp foo.rs $(TMPDIR)/foo.rs
912
$(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo.rs 2>&1 \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type = "lib"]

src/test/run-make/output-filename-overwrites-input/foo.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//

0 commit comments

Comments
 (0)