Skip to content

Commit 2971d49

Browse files
committed
Auto merge of #41508 - michaelwoerister:generic-path-remapping, r=alexcrichton
Implement a file-path remapping feature in support of debuginfo and reproducible builds This PR adds the `-Zremap-path-prefix-from`/`-Zremap-path-prefix-to` commandline option pair and is a more general implementation of #41419. As opposed to the previous attempt, this implementation should enable reproducible builds regardless of the working directory of the compiler. This implementation of the feature is more general in the sense that the re-mapping will affect *all* paths the compiler emits, including the ones in error messages. r? @alexcrichton
2 parents ace517d + 8ea050d commit 2971d49

File tree

37 files changed

+547
-313
lines changed

37 files changed

+547
-313
lines changed

src/doc/unstable-book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- [Compiler flags](compiler-flags.md)
44
- [linker_flavor](compiler-flags/linker-flavor.md)
5+
- [remap_path_prefix](compiler-flags/remap-path-prefix.md)
56
- [Language features](language-features.md)
67
- [abi_msp430_interrupt](language-features/abi-msp430-interrupt.md)
78
- [abi_ptx](language-features/abi-ptx.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# `remap-path-prefix`
2+
3+
The tracking issue for this feature is: [#41555](https://github.com/rust-lang/rust/issues/41555)
4+
5+
------------------------
6+
7+
The `-Z remap-path-prefix-from`, `-Z remap-path-prefix-to` commandline option
8+
pair allows to replace prefixes of any file paths the compiler emits in various
9+
places. This is useful for bringing debuginfo paths into a well-known form and
10+
for achieving reproducible builds independent of the directory the compiler was
11+
executed in. All paths emitted by the compiler are affected, including those in
12+
error messages.
13+
14+
In order to map all paths starting with `/home/foo/my-project/src` to
15+
`/sources/my-project`, one would invoke the compiler as follows:
16+
17+
```text
18+
rustc -Zremap-path-prefix-from="/home/foo/my-project/src" -Zremap-path-prefix-to="/sources/my-project"
19+
```
20+
21+
Debuginfo for code from the file `/home/foo/my-project/src/foo/mod.rs`,
22+
for example, would then point debuggers to `/sources/my-project/foo/mod.rs`
23+
instead of the original file.
24+
25+
The options can be specified multiple times when multiple prefixes should be
26+
mapped:
27+
28+
```text
29+
rustc -Zremap-path-prefix-from="/home/foo/my-project/src" \
30+
-Zremap-path-prefix-to="/sources/my-project" \
31+
-Zremap-path-prefix-from="/home/foo/my-project/build-dir" \
32+
-Zremap-path-prefix-to="/stable-build-dir"
33+
```
34+
35+
When the options are given multiple times, the nth `-from` will be matched up
36+
with the nth `-to` and they can appear anywhere on the commandline. Mappings
37+
specified later on the line will take precedence over earlier ones.

src/grammar/verify.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ fn main() {
296296
syntax::errors::registry::Registry::new(&[]),
297297
Rc::new(DummyCrateStore));
298298
let filemap = session.parse_sess.codemap()
299-
.new_filemap("<n/a>".to_string(), None, code);
299+
.new_filemap("<n/a>".to_string(), code);
300300
let mut lexer = lexer::StringReader::new(session.diagnostic(), filemap);
301301
let cm = session.codemap();
302302

src/librustc/session/config.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use lint;
2525
use middle::cstore;
2626

2727
use syntax::ast::{self, IntTy, UintTy};
28+
use syntax::codemap::FilePathMapping;
2829
use syntax::parse::token;
2930
use syntax::parse;
3031
use syntax::symbol::Symbol;
@@ -492,6 +493,14 @@ impl Options {
492493
self.incremental.is_none() ||
493494
self.cg.codegen_units == 1
494495
}
496+
497+
pub fn file_path_mapping(&self) -> FilePathMapping {
498+
FilePathMapping::new(
499+
self.debugging_opts.remap_path_prefix_from.iter().zip(
500+
self.debugging_opts.remap_path_prefix_to.iter()
501+
).map(|(src, dst)| (src.clone(), dst.clone())).collect()
502+
)
503+
}
495504
}
496505

497506
// The type of entry function, so
@@ -1012,6 +1021,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
10121021
"Set the optimization fuel quota for a crate."),
10131022
print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
10141023
"Make Rustc print the total optimization fuel used by a crate."),
1024+
remap_path_prefix_from: Vec<String> = (vec![], parse_string_push, [TRACKED],
1025+
"add a source pattern to the file path remapping config"),
1026+
remap_path_prefix_to: Vec<String> = (vec![], parse_string_push, [TRACKED],
1027+
"add a mapping target to the file path remapping config"),
10151028
}
10161029

10171030
pub fn default_lib_output() -> CrateType {
@@ -1319,7 +1332,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
13191332
// Convert strings provided as --cfg [cfgspec] into a crate_cfg
13201333
pub fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
13211334
cfgspecs.into_iter().map(|s| {
1322-
let sess = parse::ParseSess::new();
1335+
let sess = parse::ParseSess::new(FilePathMapping::empty());
13231336
let mut parser =
13241337
parse::new_parser_from_source_str(&sess, "cfgspec".to_string(), s.to_string());
13251338

@@ -1430,6 +1443,23 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
14301443
output_types.insert(OutputType::Exe, None);
14311444
}
14321445

1446+
let remap_path_prefix_sources = debugging_opts.remap_path_prefix_from.len();
1447+
let remap_path_prefix_targets = debugging_opts.remap_path_prefix_from.len();
1448+
1449+
if remap_path_prefix_targets < remap_path_prefix_sources {
1450+
for source in &debugging_opts.remap_path_prefix_from[remap_path_prefix_targets..] {
1451+
early_error(error_format,
1452+
&format!("option `-Zremap-path-prefix-from='{}'` does not have \
1453+
a corresponding `-Zremap-path-prefix-to`", source))
1454+
}
1455+
} else if remap_path_prefix_targets > remap_path_prefix_sources {
1456+
for target in &debugging_opts.remap_path_prefix_to[remap_path_prefix_sources..] {
1457+
early_error(error_format,
1458+
&format!("option `-Zremap-path-prefix-to='{}'` does not have \
1459+
a corresponding `-Zremap-path-prefix-from`", target))
1460+
}
1461+
}
1462+
14331463
let mut cg = build_codegen_options(matches, error_format);
14341464

14351465
// Issue #30063: if user requests llvm-related output to one

src/librustc/session/mod.rs

+17-12
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ pub struct Session {
7474
// The name of the root source file of the crate, in the local file system.
7575
// The path is always expected to be absolute. `None` means that there is no
7676
// source file.
77-
pub local_crate_source_file: Option<PathBuf>,
78-
pub working_dir: PathBuf,
77+
pub local_crate_source_file: Option<String>,
78+
// The directory the compiler has been executed in plus a flag indicating
79+
// if the value stored here has been affected by path remapping.
80+
pub working_dir: (String, bool),
7981
pub lint_store: RefCell<lint::LintStore>,
8082
pub lints: RefCell<lint::LintTable>,
8183
/// Set of (LintId, span, message) tuples tracking lint (sub)diagnostics
@@ -553,12 +555,14 @@ pub fn build_session(sopts: config::Options,
553555
registry: errors::registry::Registry,
554556
cstore: Rc<CrateStore>)
555557
-> Session {
558+
let file_path_mapping = sopts.file_path_mapping();
559+
556560
build_session_with_codemap(sopts,
557561
dep_graph,
558562
local_crate_source_file,
559563
registry,
560564
cstore,
561-
Rc::new(codemap::CodeMap::new()),
565+
Rc::new(codemap::CodeMap::new(file_path_mapping)),
562566
None)
563567
}
564568

@@ -622,7 +626,7 @@ pub fn build_session_(sopts: config::Options,
622626
Ok(t) => t,
623627
Err(e) => {
624628
panic!(span_diagnostic.fatal(&format!("Error loading host specification: {}", e)));
625-
}
629+
}
626630
};
627631
let target_cfg = config::build_target_config(&sopts, &span_diagnostic);
628632
let p_s = parse::ParseSess::with_span_handler(span_diagnostic, codemap);
@@ -631,21 +635,22 @@ pub fn build_session_(sopts: config::Options,
631635
None => Some(filesearch::get_or_default_sysroot())
632636
};
633637

638+
let file_path_mapping = sopts.file_path_mapping();
639+
634640
// Make the path absolute, if necessary
635-
let local_crate_source_file = local_crate_source_file.map(|path|
636-
if path.is_absolute() {
637-
path.clone()
638-
} else {
639-
env::current_dir().unwrap().join(&path)
640-
}
641-
);
641+
let local_crate_source_file = local_crate_source_file.map(|path| {
642+
file_path_mapping.map_prefix(path.to_string_lossy().into_owned()).0
643+
});
642644

643645
let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone());
644646
let optimization_fuel_limit = Cell::new(sopts.debugging_opts.fuel.as_ref()
645647
.map(|i| i.1).unwrap_or(0));
646648
let print_fuel_crate = sopts.debugging_opts.print_fuel.clone();
647649
let print_fuel = Cell::new(0);
648650

651+
let working_dir = env::current_dir().unwrap().to_string_lossy().into_owned();
652+
let working_dir = file_path_mapping.map_prefix(working_dir);
653+
649654
let sess = Session {
650655
dep_graph: dep_graph.clone(),
651656
target: target_cfg,
@@ -660,7 +665,7 @@ pub fn build_session_(sopts: config::Options,
660665
derive_registrar_fn: Cell::new(None),
661666
default_sysroot: default_sysroot,
662667
local_crate_source_file: local_crate_source_file,
663-
working_dir: env::current_dir().unwrap(),
668+
working_dir: working_dir,
664669
lint_store: RefCell::new(lint::LintStore::new()),
665670
lints: RefCell::new(lint::LintTable::new()),
666671
one_time_diagnostics: RefCell::new(FxHashSet()),

src/librustc_driver/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ pub fn run_compiler<'a>(args: &[String],
206206
let cstore = Rc::new(CStore::new(&dep_graph));
207207

208208
let loader = file_loader.unwrap_or(box RealFileLoader);
209-
let codemap = Rc::new(CodeMap::with_file_loader(loader));
209+
let codemap = Rc::new(CodeMap::with_file_loader(loader, sopts.file_path_mapping()));
210210
let mut sess = session::build_session_with_codemap(
211211
sopts, &dep_graph, input_file_path, descriptions, cstore.clone(), codemap, emitter_dest,
212212
);

src/librustc_driver/test.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use rustc::session::{self, config};
3131
use std::rc::Rc;
3232
use syntax::ast;
3333
use syntax::abi::Abi;
34-
use syntax::codemap::CodeMap;
34+
use syntax::codemap::{CodeMap, FilePathMapping};
3535
use errors;
3636
use errors::emitter::Emitter;
3737
use errors::{Level, DiagnosticBuilder};
@@ -108,7 +108,7 @@ fn test_env<F>(source_string: &str,
108108
&dep_graph,
109109
None,
110110
diagnostic_handler,
111-
Rc::new(CodeMap::new()),
111+
Rc::new(CodeMap::new(FilePathMapping::empty())),
112112
cstore.clone());
113113
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
114114
let input = config::Input::Str {

src/librustc_metadata/cstore_impl.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ impl CrateStore for cstore::CStore {
397397
let (name, def) = data.get_macro(id.index);
398398
let source_name = format!("<{} macros>", name);
399399

400-
let filemap = sess.parse_sess.codemap().new_filemap(source_name, None, def.body);
400+
let filemap = sess.parse_sess.codemap().new_filemap(source_name, def.body);
401401
let local_span = Span { lo: filemap.start_pos, hi: filemap.end_pos, ctxt: NO_EXPANSION };
402402
let body = filemap_to_stream(&sess.parse_sess, filemap);
403403

src/librustc_metadata/decoder.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1138,7 +1138,7 @@ impl<'a, 'tcx> CrateMetadata {
11381138
// We can't reuse an existing FileMap, so allocate a new one
11391139
// containing the information we need.
11401140
let syntax_pos::FileMap { name,
1141-
abs_path,
1141+
name_was_remapped,
11421142
start_pos,
11431143
end_pos,
11441144
lines,
@@ -1162,7 +1162,7 @@ impl<'a, 'tcx> CrateMetadata {
11621162
}
11631163

11641164
let local_version = local_codemap.new_imported_filemap(name,
1165-
abs_path,
1165+
name_was_remapped,
11661166
source_length,
11671167
lines,
11681168
multibyte_chars);

src/librustc_metadata/encoder.rs

+30-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use std::hash::Hash;
3030
use std::intrinsics;
3131
use std::io::prelude::*;
3232
use std::io::Cursor;
33+
use std::path::Path;
3334
use std::rc::Rc;
3435
use std::u32;
3536
use syntax::ast::{self, CRATE_NODE_ID};
@@ -1270,13 +1271,40 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
12701271
fn encode_codemap(&mut self) -> LazySeq<syntax_pos::FileMap> {
12711272
let codemap = self.tcx.sess.codemap();
12721273
let all_filemaps = codemap.files.borrow();
1273-
self.lazy_seq_ref(all_filemaps.iter()
1274+
let adapted = all_filemaps.iter()
12741275
.filter(|filemap| {
12751276
// No need to re-export imported filemaps, as any downstream
12761277
// crate will import them from their original source.
12771278
!filemap.is_imported()
12781279
})
1279-
.map(|filemap| &**filemap))
1280+
.map(|filemap| {
1281+
// When exporting FileMaps, we expand all paths to absolute
1282+
// paths because any relative paths are potentially relative to
1283+
// a wrong directory.
1284+
// However, if a path has been modified via
1285+
// `-Zremap-path-prefix` we assume the user has already set
1286+
// things up the way they want and don't touch the path values
1287+
// anymore.
1288+
let name = Path::new(&filemap.name);
1289+
let (ref working_dir, working_dir_was_remapped) = self.tcx.sess.working_dir;
1290+
if filemap.name_was_remapped ||
1291+
(name.is_relative() && working_dir_was_remapped) {
1292+
// This path of this FileMap has been modified by
1293+
// path-remapping, so we use it verbatim (and avoid cloning
1294+
// the whole map in the process).
1295+
filemap.clone()
1296+
} else {
1297+
let mut adapted = (**filemap).clone();
1298+
let abs_path = Path::new(working_dir).join(name)
1299+
.to_string_lossy()
1300+
.into_owned();
1301+
adapted.name = abs_path;
1302+
Rc::new(adapted)
1303+
}
1304+
})
1305+
.collect::<Vec<_>>();
1306+
1307+
self.lazy_seq_ref(adapted.iter().map(|fm| &**fm))
12801308
}
12811309

12821310
fn encode_def_path_table(&mut self) -> Lazy<DefPathTable> {

src/librustc_save_analysis/dump_visitor.rs

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use rustc::ty::{self, TyCtxt, AssociatedItemContainer};
3737
use std::collections::HashSet;
3838
use std::collections::hash_map::DefaultHasher;
3939
use std::hash::*;
40+
use std::path::Path;
4041

4142
use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID};
4243
use syntax::parse::token;
@@ -128,6 +129,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
128129
pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) {
129130
let source_file = self.tcx.sess.local_crate_source_file.as_ref();
130131
let crate_root = source_file.map(|source_file| {
132+
let source_file = Path::new(source_file);
131133
match source_file.file_name() {
132134
Some(_) => source_file.parent().unwrap().display().to_string(),
133135
None => source_file.display().to_string(),

src/librustc_trans/debuginfo/create_scope_map.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use super::FunctionDebugContext;
11+
use super::{FunctionDebugContext, FunctionDebugContextData};
1212
use super::metadata::file_metadata;
1313
use super::utils::{DIB, span_start};
1414

1515
use llvm;
16-
use llvm::debuginfo::{DIScope, DISubprogram};
16+
use llvm::debuginfo::DIScope;
1717
use common::CrateContext;
1818
use rustc::mir::{Mir, VisibilityScope};
1919

@@ -53,8 +53,8 @@ pub fn create_mir_scopes(ccx: &CrateContext, mir: &Mir, debug_context: &Function
5353
};
5454
let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes);
5555

56-
let fn_metadata = match *debug_context {
57-
FunctionDebugContext::RegularContext(ref data) => data.fn_metadata,
56+
let debug_context = match *debug_context {
57+
FunctionDebugContext::RegularContext(ref data) => data,
5858
FunctionDebugContext::DebugInfoDisabled |
5959
FunctionDebugContext::FunctionWithoutDebugInfo => {
6060
return scopes;
@@ -71,7 +71,7 @@ pub fn create_mir_scopes(ccx: &CrateContext, mir: &Mir, debug_context: &Function
7171
// Instantiate all scopes.
7272
for idx in 0..mir.visibility_scopes.len() {
7373
let scope = VisibilityScope::new(idx);
74-
make_mir_scope(ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes);
74+
make_mir_scope(ccx, &mir, &has_variables, debug_context, scope, &mut scopes);
7575
}
7676

7777
scopes
@@ -80,7 +80,7 @@ pub fn create_mir_scopes(ccx: &CrateContext, mir: &Mir, debug_context: &Function
8080
fn make_mir_scope(ccx: &CrateContext,
8181
mir: &Mir,
8282
has_variables: &BitVector,
83-
fn_metadata: DISubprogram,
83+
debug_context: &FunctionDebugContextData,
8484
scope: VisibilityScope,
8585
scopes: &mut IndexVec<VisibilityScope, MirDebugScope>) {
8686
if scopes[scope].is_valid() {
@@ -89,13 +89,13 @@ fn make_mir_scope(ccx: &CrateContext,
8989

9090
let scope_data = &mir.visibility_scopes[scope];
9191
let parent_scope = if let Some(parent) = scope_data.parent_scope {
92-
make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes);
92+
make_mir_scope(ccx, mir, has_variables, debug_context, parent, scopes);
9393
scopes[parent]
9494
} else {
9595
// The root is the function itself.
9696
let loc = span_start(ccx, mir.span);
9797
scopes[scope] = MirDebugScope {
98-
scope_metadata: fn_metadata,
98+
scope_metadata: debug_context.fn_metadata,
9999
file_start_pos: loc.file.start_pos,
100100
file_end_pos: loc.file.end_pos,
101101
};
@@ -109,14 +109,17 @@ fn make_mir_scope(ccx: &CrateContext,
109109
// However, we don't skip creating a nested scope if
110110
// our parent is the root, because we might want to
111111
// put arguments in the root and not have shadowing.
112-
if parent_scope.scope_metadata != fn_metadata {
112+
if parent_scope.scope_metadata != debug_context.fn_metadata {
113113
scopes[scope] = parent_scope;
114114
return;
115115
}
116116
}
117117

118118
let loc = span_start(ccx, scope_data.span);
119-
let file_metadata = file_metadata(ccx, &loc.file.name, &loc.file.abs_path);
119+
let file_metadata = file_metadata(ccx,
120+
&loc.file.name,
121+
debug_context.defining_crate);
122+
120123
let scope_metadata = unsafe {
121124
llvm::LLVMRustDIBuilderCreateLexicalBlock(
122125
DIB(ccx),

0 commit comments

Comments
 (0)