From 7d81cc308b2ae99b4277dae5c845111649195848 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 19 Apr 2017 18:48:01 +0200 Subject: [PATCH] Implement debuginfo path remapping. This commit adds the -Zdebug-prefix-map-from/-Zdebug-prefix-map-to commandline option pair. This feature allows to replace prefixes of any file paths that are emitted into debuginfo. --- src/grammar/verify.rs | 2 +- src/librustc/middle/cstore.rs | 15 + src/librustc/session/config.rs | 21 ++ src/librustc/session/mod.rs | 9 - src/librustc_metadata/cstore_impl.rs | 10 +- src/librustc_metadata/decoder.rs | 10 +- src/librustc_metadata/encoder.rs | 26 ++ src/librustc_metadata/schema.rs | 2 + src/librustc_trans/context.rs | 2 +- .../debuginfo/create_scope_map.rs | 25 +- src/librustc_trans/debuginfo/metadata.rs | 169 +++++++---- src/librustc_trans/debuginfo/mod.rs | 153 +++++++++- src/librustc_trans/debuginfo/namespace.rs | 2 +- src/librustc_trans/mir/mod.rs | 6 +- src/librustdoc/html/highlight.rs | 4 +- src/libsyntax/codemap.rs | 24 +- src/libsyntax/ext/expand.rs | 2 +- src/libsyntax/ext/source_util.rs | 4 +- src/libsyntax/parse/lexer/comments.rs | 2 +- src/libsyntax/parse/mod.rs | 4 +- src/libsyntax/test_snippet.rs | 2 +- src/libsyntax/util/parser_testing.rs | 2 +- src/libsyntax_pos/lib.rs | 6 - src/test/codegen/debug-prefix-map.rs | 19 ++ src/tools/compiletest/src/header.rs | 271 ++++++++++-------- src/tools/compiletest/src/runtest.rs | 17 +- 26 files changed, 558 insertions(+), 251 deletions(-) create mode 100644 src/test/codegen/debug-prefix-map.rs diff --git a/src/grammar/verify.rs b/src/grammar/verify.rs index bd28a63c5f4df..3ac043f7aa9bc 100644 --- a/src/grammar/verify.rs +++ b/src/grammar/verify.rs @@ -296,7 +296,7 @@ fn main() { syntax::errors::registry::Registry::new(&[]), Rc::new(DummyCrateStore)); let filemap = session.parse_sess.codemap() - .new_filemap("".to_string(), None, code); + .new_filemap("".to_string(), code); let mut lexer = lexer::StringReader::new(session.diagnostic(), filemap); let cm = session.codemap(); diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index cbbfeacadb408..2828016dbf9e9 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -237,6 +237,15 @@ pub trait CrateStore { fn exported_symbols(&self, cnum: CrateNum) -> Vec; fn is_no_builtins(&self, cnum: CrateNum) -> bool; + /// Gives the directory the compiler has been executed in when compiling + /// crate `cnum`. This is used by debuginfo generation. + fn compiler_working_dir(&self, cnum: CrateNum) -> String; + + /// Gives the prefix map as specified via `-Zdebug-prefix-map-*`. We keep + /// using the original prefix-mapping for a crate, not the mapping specified + /// for the current one. + fn debug_prefix_map(&self, cnum: CrateNum) -> Vec<(String, String)>; + // resolve fn retrace_path(&self, cnum: CrateNum, @@ -380,6 +389,12 @@ impl CrateStore for DummyCrateStore { { bug!("native_libraries") } fn exported_symbols(&self, cnum: CrateNum) -> Vec { bug!("exported_symbols") } fn is_no_builtins(&self, cnum: CrateNum) -> bool { bug!("is_no_builtins") } + fn compiler_working_dir(&self, cnum: CrateNum) -> String { + bug!("compiler_working_dir") + } + fn debug_prefix_map(&self, cnum: CrateNum) -> Vec<(String, String)> { + bug!("debug_prefix_map") + } // resolve fn retrace_path(&self, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index fadb1844008c0..71896ee88f497 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1012,6 +1012,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "Set the optimization fuel quota for a crate."), print_fuel: Option = (None, parse_opt_string, [TRACKED], "Make Rustc print the total optimization fuel used by a crate."), + debug_prefix_map_from: Vec = (vec![], parse_string_push, [TRACKED], + "push a debuginfo path remapping source"), + debug_prefix_map_to: Vec = (vec![], parse_string_push, [TRACKED], + "push a debuginfo path remapping target"), } pub fn default_lib_output() -> CrateType { @@ -1430,6 +1434,23 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) output_types.insert(OutputType::Exe, None); } + let debug_prefix_map_sources = debugging_opts.debug_prefix_map_from.len(); + let debug_prefix_map_targets = debugging_opts.debug_prefix_map_from.len(); + + if debug_prefix_map_targets < debug_prefix_map_sources { + for source in &debugging_opts.debug_prefix_map_from[debug_prefix_map_targets..] { + early_error(error_format, + &format!("option `-Zdebug_prefix_map_from='{}'` does not have \ + a corresponding `-Zdebug_prefix_map_to`", source)) + } + } else if debug_prefix_map_targets > debug_prefix_map_sources { + for target in &debugging_opts.debug_prefix_map_to[debug_prefix_map_sources..] { + early_error(error_format, + &format!("option `-Zdebug_prefix_map_to='{}'` does not have \ + a corresponding `-Zdebug_prefix_map_from`", target)) + } + } + let mut cg = build_codegen_options(matches, error_format); // Issue #30063: if user requests llvm-related output to one diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index adc9aabb8c77a..47f031ea07741 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -631,15 +631,6 @@ pub fn build_session_(sopts: config::Options, None => Some(filesearch::get_or_default_sysroot()) }; - // Make the path absolute, if necessary - let local_crate_source_file = local_crate_source_file.map(|path| - if path.is_absolute() { - path.clone() - } else { - env::current_dir().unwrap().join(&path) - } - ); - let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone()); let optimization_fuel_limit = Cell::new(sopts.debugging_opts.fuel.as_ref() .map(|i| i.1).unwrap_or(0)); diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 3239dfb937b5e..abe4eae4c6bc4 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -340,6 +340,14 @@ impl CrateStore for cstore::CStore { self.get_crate_data(cnum).is_no_builtins() } + fn compiler_working_dir(&self, cnum: CrateNum) -> String { + self.get_crate_data(cnum).compiler_working_dir() + } + + fn debug_prefix_map(&self, cnum: CrateNum) -> Vec<(String, String)> { + self.get_crate_data(cnum).debug_prefix_map() + } + fn retrace_path(&self, cnum: CrateNum, path: &[DisambiguatedDefPathData]) @@ -399,7 +407,7 @@ impl CrateStore for cstore::CStore { let (name, def) = data.get_macro(id.index); let source_name = format!("<{} macros>", name); - let filemap = sess.parse_sess.codemap().new_filemap(source_name, None, def.body); + let filemap = sess.parse_sess.codemap().new_filemap(source_name, def.body); let local_span = Span { lo: filemap.start_pos, hi: filemap.end_pos, ctxt: NO_EXPANSION }; let body = filemap_to_stream(&sess.parse_sess, filemap); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index fac6079529e30..3c5e3521377f0 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -646,6 +646,14 @@ impl<'a, 'tcx> CrateMetadata { self.root.lang_items.decode(self).collect() } + pub fn compiler_working_dir(&self) -> String { + self.root.compiler_working_dir.clone() + } + + pub fn debug_prefix_map(&self) -> Vec<(String, String)> { + self.root.debug_prefix_map.decode(self).collect() + } + /// Iterates over each child of the given item. pub fn each_child_of_item(&self, id: DefIndex, mut callback: F) where F: FnMut(def::Export) @@ -1120,7 +1128,6 @@ impl<'a, 'tcx> CrateMetadata { // We can't reuse an existing FileMap, so allocate a new one // containing the information we need. let syntax_pos::FileMap { name, - abs_path, start_pos, end_pos, lines, @@ -1144,7 +1151,6 @@ impl<'a, 'tcx> CrateMetadata { } let local_version = local_codemap.new_imported_filemap(name, - abs_path, source_length, lines, multibyte_chars); diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index ce9f0a73fe2b8..61385c3a42330 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1342,6 +1342,25 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_seq(exported_symbols.iter().map(|&id| tcx.hir.local_def_id(id).index)) } + fn encode_debug_prefix_map(&mut self) -> LazySeq<(String, String)> { + let tcx = self.tcx; + + let debug_prefix_map = tcx.sess + .opts + .debugging_opts + .debug_prefix_map_from + .iter() + .cloned() + .zip(tcx.sess + .opts + .debugging_opts + .debug_prefix_map_from + .iter() + .cloned()) + .collect::>(); + self.lazy_seq(debug_prefix_map.into_iter()) + } + fn encode_dylib_dependency_formats(&mut self) -> LazySeq> { match self.tcx.sess.dependency_formats.borrow().get(&config::CrateTypeDylib) { Some(arr) => { @@ -1397,6 +1416,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let exported_symbols = self.encode_exported_symbols(); let exported_symbols_bytes = self.position() - i; + i = self.position(); + let debug_prefix_map = self.encode_debug_prefix_map(); + let debug_prefix_map_bytes = self.position() - i; + // Encode and index the items. i = self.position(); let items = self.encode_info_for_items(); @@ -1435,6 +1458,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_path_table: def_path_table, impls: impls, exported_symbols: exported_symbols, + compiler_working_dir: tcx.sess.working_dir.to_string_lossy().into_owned(), + debug_prefix_map: debug_prefix_map, index: index, }); @@ -1455,6 +1480,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { println!(" codemap bytes: {}", codemap_bytes); println!(" impl bytes: {}", impl_bytes); println!(" exp. symbols bytes: {}", exported_symbols_bytes); + println!("debug prefix map bytes: {}", debug_prefix_map_bytes); println!(" def-path table bytes: {}", def_path_table_bytes); println!(" item bytes: {}", item_bytes); println!(" index bytes: {}", index_bytes); diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 2f2e0e125aea5..86ca069a873c9 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -205,6 +205,8 @@ pub struct CrateRoot { pub def_path_table: Lazy, pub impls: LazySeq, pub exported_symbols: LazySeq, + pub compiler_working_dir: String, + pub debug_prefix_map: LazySeq<(String, String)>, pub index: LazySeq, } diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index c3770470bfd05..28a3e3e4fb120 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -452,7 +452,7 @@ impl<'tcx> LocalCrateContext<'tcx> { &llmod_id[..]); let dbg_cx = if shared.tcx.sess.opts.debuginfo != NoDebugInfo { - let dctx = debuginfo::CrateDebugContext::new(llmod); + let dctx = debuginfo::CrateDebugContext::new(shared.sess(), llmod); debuginfo::metadata::compile_unit_metadata(shared, &dctx, shared.tcx.sess); Some(dctx) } else { diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs index 3d074c31c8a32..b012556f96d69 100644 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/debuginfo/create_scope_map.rs @@ -8,12 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::FunctionDebugContext; +use super::{FunctionDebugContext, FunctionDebugContextData}; use super::metadata::file_metadata; use super::utils::{DIB, span_start}; use llvm; -use llvm::debuginfo::{DIScope, DISubprogram}; +use llvm::debuginfo::DIScope; use common::CrateContext; use rustc::mir::{Mir, VisibilityScope}; @@ -53,8 +53,8 @@ pub fn create_mir_scopes(ccx: &CrateContext, mir: &Mir, debug_context: &Function }; let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); - let fn_metadata = match *debug_context { - FunctionDebugContext::RegularContext(ref data) => data.fn_metadata, + let debug_context = match *debug_context { + FunctionDebugContext::RegularContext(ref data) => data, FunctionDebugContext::DebugInfoDisabled | FunctionDebugContext::FunctionWithoutDebugInfo => { return scopes; @@ -71,7 +71,12 @@ pub fn create_mir_scopes(ccx: &CrateContext, mir: &Mir, debug_context: &Function // Instantiate all scopes. for idx in 0..mir.visibility_scopes.len() { let scope = VisibilityScope::new(idx); - make_mir_scope(ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes); + make_mir_scope(ccx, + &mir, + &has_variables, + debug_context, + scope, + &mut scopes); } scopes @@ -80,7 +85,7 @@ pub fn create_mir_scopes(ccx: &CrateContext, mir: &Mir, debug_context: &Function fn make_mir_scope(ccx: &CrateContext, mir: &Mir, has_variables: &BitVector, - fn_metadata: DISubprogram, + debug_context: &FunctionDebugContextData, scope: VisibilityScope, scopes: &mut IndexVec) { if scopes[scope].is_valid() { @@ -89,13 +94,13 @@ fn make_mir_scope(ccx: &CrateContext, let scope_data = &mir.visibility_scopes[scope]; let parent_scope = if let Some(parent) = scope_data.parent_scope { - make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes); + make_mir_scope(ccx, mir, has_variables, debug_context, parent, scopes); scopes[parent] } else { // The root is the function itself. let loc = span_start(ccx, mir.span); scopes[scope] = MirDebugScope { - scope_metadata: fn_metadata, + scope_metadata: debug_context.fn_metadata, file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_pos, }; @@ -109,14 +114,14 @@ fn make_mir_scope(ccx: &CrateContext, // However, we don't skip creating a nested scope if // our parent is the root, because we might want to // put arguments in the root and not have shadowing. - if parent_scope.scope_metadata != fn_metadata { + if parent_scope.scope_metadata != debug_context.fn_metadata { scopes[scope] = parent_scope; return; } } let loc = span_start(ccx, scope_data.span); - let file_metadata = file_metadata(ccx, &loc.file.name, &loc.file.abs_path); + let file_metadata = file_metadata(ccx, &loc.file.name, debug_context.defining_crate); let scope_metadata = unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlock( DIB(ccx), diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 1f4756a94ea33..419f4c54059e1 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -26,7 +26,7 @@ use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType, DILexicalBlock, DIFlags}; use rustc::hir::def::CtorKind; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; use rustc::ty::fold::TypeVisitor; use rustc::ty::subst::Substs; use rustc::ty::util::TypeIdHasher; @@ -39,7 +39,6 @@ use rustc::ty::{self, AdtKind, Ty}; use rustc::ty::layout::{self, LayoutTyper}; use session::config; use util::nodemap::FxHashMap; -use util::common::path2cstr; use libc::{c_uint, c_longlong}; use std::ffi::CString; @@ -48,7 +47,7 @@ use std::ptr; use syntax::ast; use syntax::symbol::{Interner, InternedString}; use syntax_pos::{self, Span}; - +use syntax_pos::symbol::Symbol; // From DWARF 5. // See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1 @@ -349,9 +348,7 @@ fn vec_slice_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, assert!(member_descriptions.len() == member_llvm_types.len()); - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path); - + let file_metadata = unknown_file_metadata(cx); let metadata = composite_type_metadata(cx, slice_llvm_type, &slice_type_name[..], @@ -659,47 +656,118 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, metadata } -pub fn file_metadata(cx: &CrateContext, path: &str, full_path: &Option) -> DIFile { - // FIXME (#9639): This needs to handle non-utf8 paths - let work_dir = cx.sess().working_dir.to_str().unwrap(); - let file_name = - full_path.as_ref().map(|p| p.as_str()).unwrap_or_else(|| { - if path.starts_with(work_dir) { - &path[work_dir.len() + 1..path.len()] - } else { - path - } - }); +pub fn file_metadata(cx: &CrateContext, + file_path: &str, + defining_crate: CrateNum) + -> DIFile { + debug!("file_metadata_raw(file_path={}, defining_crate={})", + file_path, + defining_crate); + + let debug_context = debug_context(cx); + let cache_key = (Symbol::intern(file_path), defining_crate); + + debug_context.created_files.borrow_mut().entry(cache_key).or_insert_with(|| { + let mapped_path = debug_context.map_prefix(file_path, defining_crate); + let mapped_compiler_working_dir = + debug_context.mapped_compiler_working_dir(defining_crate); + + // There is one case where we further want to manipulate the path, in + // all other cases we can pass the path as it is to LLVM. First, here's + // a list of the case where we don't need to do anything more: + // + // 1. If the compiler_working_dir or the file_path have been changed + // by debug-prefix-mapping, we do NOT want to further process them + // in any way because we assume the user has set things up they way + // they want. + // 2. If we are cross-compiling to a target that has a different path + // format than the host. The way to handle this by the user is to + // to use debug-prefix-mapping. + // 3. If the path originates from the current crate. If it is absolute, + // we are done anyway, if it is relative is will be relative to the + // compiler_working_dir specified in the CGU, so we are fine. + // 4. If the path originates from an upstream crate but is absolute. In + // this case it does not depend on the current compiler_working_dir, + // and we can just pass it on. + // + // The one case where we want to adapt the path further is when we have + // a relative path but it is relative to something else than the current + // compiler_working_dir. These paths have to be made absolute. Otherwise + // the debugger would look in the wrong place. + + if mapped_path.has_been_remapped || + mapped_compiler_working_dir.has_been_remapped || + !debug_context.target_paths_compatible_with_host || + defining_crate == LOCAL_CRATE { + // This conforms to the conditions (1), (2), or (3) + file_metadata_raw(cx, + &mapped_compiler_working_dir.path, + &mapped_path.path) + } else { + assert_eq!(file_path, mapped_path.path); + + // At this point we know that the path format is compatible with + // the one on the host, so we can safely convert the string into + // a std::Path. + let is_absolute = Path::new(file_path).is_absolute(); + let current_compiler_working_dir = + debug_context.mapped_compiler_working_dir(LOCAL_CRATE); + + if is_absolute || + current_compiler_working_dir.path == mapped_compiler_working_dir.path { + // This conforms to the conditions (4), that is, the path is + // absolute or it is relative to the right thing. + + let directory = if is_absolute { + "" + } else { + &mapped_compiler_working_dir.path + }; - file_metadata_(cx, path, file_name, &work_dir) -} + file_metadata_raw(cx, + directory, + &mapped_path.path) + } else { + // Finally, we have the case where we need to make the path + // absolute. + let full_path = Path::new(&mapped_compiler_working_dir.path) + .join(Path::new(file_path)); -pub fn unknown_file_metadata(cx: &CrateContext) -> DIFile { - // Regular filenames should not be empty, so we abuse an empty name as the - // key for the special unknown file metadata - file_metadata_(cx, "", "", "") + assert!(full_path.is_absolute()); + file_metadata_raw(cx, "", &full_path.to_string_lossy()) + } + } + }).clone() } -fn file_metadata_(cx: &CrateContext, key: &str, file_name: &str, work_dir: &str) -> DIFile { - if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(key) { - return *file_metadata; - } - - debug!("file_metadata: file_name: {}, work_dir: {}", file_name, work_dir); +fn file_metadata_raw(cx: &CrateContext, directory: &str, file_name: &str) -> DIFile { + debug!("file_metadata_raw(file_name={}, directory={})", file_name, directory); let file_name = CString::new(file_name).unwrap(); - let work_dir = CString::new(work_dir).unwrap(); + let directory = CString::new(directory).unwrap(); let file_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateFile(DIB(cx), file_name.as_ptr(), - work_dir.as_ptr()) + llvm::LLVMRustDIBuilderCreateFile(DIB(cx), + file_name.as_ptr(), + directory.as_ptr()) }; - let mut created_files = debug_context(cx).created_files.borrow_mut(); - created_files.insert(key.to_string(), file_metadata); file_metadata } +pub fn unknown_file_metadata(cx: &CrateContext) -> DIFile { + let debug_context = debug_context(cx); + // Regular filenames should not be empty, so we abuse an empty name as the + // key for the special unknown file metadata. + let cache_key = (Symbol::intern(""), LOCAL_CRATE); + + debug_context.created_files + .borrow_mut() + .entry(cache_key) + .or_insert_with(|| file_metadata_raw(cx, "", "")) + .clone() +} + fn basic_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> DIType { @@ -761,25 +829,15 @@ pub fn compile_unit_metadata(scc: &SharedCrateContext, debug_context: &CrateDebugContext, sess: &Session) -> DIDescriptor { - let work_dir = &sess.working_dir; + let current_compiler_working_dir = + debug_context.mapped_compiler_working_dir(LOCAL_CRATE); + let compile_unit_name = match sess.local_crate_source_file { None => fallback_path(scc), - Some(ref abs_path) => { - if abs_path.is_relative() { - sess.warn("debuginfo: Invalid path to crate's local root source file!"); - fallback_path(scc) - } else { - match abs_path.strip_prefix(work_dir) { - Ok(ref p) if p.is_relative() => { - if p.starts_with(Path::new("./")) { - path2cstr(p) - } else { - path2cstr(&Path::new(".").join(p)) - } - } - _ => fallback_path(scc) - } - } + Some(ref path) => { + let mapped_path = debug_context.map_prefix(&path.to_string_lossy(), + LOCAL_CRATE); + CString::new(mapped_path.path).unwrap() } }; @@ -789,7 +847,7 @@ pub fn compile_unit_metadata(scc: &SharedCrateContext, (option_env!("CFG_VERSION")).expect("CFG_VERSION")); let compile_unit_name = compile_unit_name.as_ptr(); - let work_dir = path2cstr(&work_dir); + let work_dir = CString::new(¤t_compiler_working_dir.path[..]).unwrap(); let producer = CString::new(producer).unwrap(); let flags = "\0"; let split_name = "\0"; @@ -1760,7 +1818,7 @@ pub fn create_global_var_metadata(cx: &CrateContext, let (file_metadata, line_number) = if span != syntax_pos::DUMMY_SP { let loc = span_start(cx, span); - (file_metadata(cx, &loc.file.name, &loc.file.abs_path), loc.line as c_uint) + (file_metadata(cx, &loc.file.name, LOCAL_CRATE), loc.line as c_uint) } else { (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) }; @@ -1795,9 +1853,10 @@ pub fn create_global_var_metadata(cx: &CrateContext, // Creates an "extension" of an existing DIScope into another file. pub fn extend_scope_to_file(ccx: &CrateContext, scope_metadata: DIScope, - file: &syntax_pos::FileMap) + file: &syntax_pos::FileMap, + krate: CrateNum) -> DILexicalBlock { - let file_metadata = file_metadata(ccx, &file.name, &file.abs_path); + let file_metadata = file_metadata(ccx, &file.name, krate); unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile( DIB(ccx), diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 1b7cf26853bc1..1d106f817b234 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -23,8 +23,9 @@ use self::source_loc::InternalDebugLocation::{self, UnknownLocation}; use llvm; use llvm::{ModuleRef, ContextRef, ValueRef}; use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray, DIFlags}; -use rustc::hir::def_id::DefId; +use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; use rustc::ty::subst::Substs; +use rustc_back::target::Target; use abi::Abi; use common::{self, CrateContext}; @@ -33,6 +34,7 @@ use monomorphize::Instance; use rustc::ty::{self, Ty}; use rustc::mir; use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; +use session::{self, Session}; use util::nodemap::{DefIdMap, FxHashMap, FxHashSet}; use libc::c_uint; @@ -41,8 +43,10 @@ use std::ffi::CString; use std::ptr; use syntax_pos::{self, Span, Pos}; +use syntax_pos::symbol::Symbol; use syntax::ast; use rustc::ty::layout; +use rustc_data_structures::indexed_vec::IndexVec; pub mod gdb; mod utils; @@ -67,7 +71,7 @@ const DW_TAG_arg_variable: c_uint = 0x101; pub struct CrateDebugContext<'tcx> { llcontext: ContextRef, builder: DIBuilderRef, - created_files: RefCell>, + created_files: RefCell>, created_enum_disr_types: RefCell>, type_map: RefCell>, @@ -76,14 +80,47 @@ pub struct CrateDebugContext<'tcx> { // This collection is used to assert that composite types (structs, enums, // ...) have their members only set once: composite_types_completed: RefCell>, + + debug_prefix_map: DebugPrefixMap, + mapped_compiler_working_dir: IndexVec, + target_paths_compatible_with_host: bool, } impl<'tcx> CrateDebugContext<'tcx> { - pub fn new(llmod: ModuleRef) -> CrateDebugContext<'tcx> { + pub fn new(sess: &Session, llmod: ModuleRef) -> CrateDebugContext<'tcx> { debug!("CrateDebugContext::new"); let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; // DIBuilder inherits context from the module, so we'd better use the same one let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; + + let debug_prefix_map = DebugPrefixMap::new(sess); + + // Build the debug_prefix_map + let mapped_compiler_working_dir = { + let mut extern_crates = sess.cstore.crates(); + extern_crates.sort(); + let mut mapped_compiler_working_dir = IndexVec::with_capacity(extern_crates.len() + 1); + + debug_assert_eq!(LOCAL_CRATE.as_usize(), 0); + let local = debug_prefix_map.map_prefix(&sess.working_dir.to_string_lossy(), + LOCAL_CRATE); + mapped_compiler_working_dir.push(local); + + for cnum in extern_crates { + // Make sure we are inserting things at the expected index. + debug_assert_eq!(cnum.as_usize(), mapped_compiler_working_dir.len()); + let dir = sess.cstore.compiler_working_dir(cnum); + mapped_compiler_working_dir.push(debug_prefix_map.map_prefix(&dir, cnum)); + } + + mapped_compiler_working_dir + }; + + let target = Target::search(&sess.opts.target_triple).unwrap(); + let host = Target::search(session::config::host_triple()).unwrap(); + let target_paths_compatible_with_host = target.options.is_like_windows == + host.options.is_like_windows; + CrateDebugContext { llcontext: llcontext, builder: builder, @@ -92,8 +129,21 @@ impl<'tcx> CrateDebugContext<'tcx> { type_map: RefCell::new(TypeMap::new()), namespace_map: RefCell::new(DefIdMap()), composite_types_completed: RefCell::new(FxHashSet()), + debug_prefix_map: debug_prefix_map, + mapped_compiler_working_dir: mapped_compiler_working_dir, + target_paths_compatible_with_host: target_paths_compatible_with_host, } } + + pub fn map_prefix(&self, path: &str, crate_of_origin: CrateNum) -> DebuginfoFilePath { + self.debug_prefix_map.map_prefix(path, crate_of_origin) + } + + pub fn mapped_compiler_working_dir(&self, + crate_of_origin: CrateNum) + -> &DebuginfoFilePath { + &self.mapped_compiler_working_dir[crate_of_origin] + } } pub enum FunctionDebugContext { @@ -103,7 +153,7 @@ pub enum FunctionDebugContext { } impl FunctionDebugContext { - fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData { + pub fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData { match *self { FunctionDebugContext::RegularContext(ref data) => data, FunctionDebugContext::DebugInfoDisabled => { @@ -128,6 +178,7 @@ impl FunctionDebugContext { pub struct FunctionDebugContextData { fn_metadata: DISubprogram, source_locations_enabled: Cell, + pub defining_crate: CrateNum, } pub enum VariableAccess<'a> { @@ -144,6 +195,73 @@ pub enum VariableKind { CapturedVariable, } +pub struct DebuginfoFilePath { + pub path: String, + pub has_been_remapped: bool, +} + +struct DebugPrefixMap { + mapping: IndexVec>, +} + +impl DebugPrefixMap { + fn new(sess: &Session) -> DebugPrefixMap { + let mut extern_crates = sess.cstore.crates(); + extern_crates.sort(); + + let mut debug_prefix_map = IndexVec::with_capacity(extern_crates.len() + 1); + + debug_assert_eq!(LOCAL_CRATE.as_usize(), 0); + let local = sess.opts + .debugging_opts + .debug_prefix_map_from + .iter() + .cloned() + .zip(sess.opts + .debugging_opts + .debug_prefix_map_to + .iter() + .cloned()) + .collect(); + debug_prefix_map.push(local); + + for cnum in extern_crates { + // Make sure we are inserting things at the expected index. + debug_assert_eq!(cnum.as_usize(), debug_prefix_map.len()); + debug_prefix_map.push(sess.cstore.debug_prefix_map(cnum)); + } + + DebugPrefixMap { + mapping: debug_prefix_map + } + } + + fn map_prefix(&self, path: &str, crate_of_origin: CrateNum) -> DebuginfoFilePath { + let mapping = &self.mapping[crate_of_origin]; + + // NOTE: We are iterating over the mapping entries from last to first + // because entries specified later in the command line should + // take precedence. + for &(ref from, ref to) in mapping.iter().rev() { + if path.starts_with(from) { + let mut mapped = String::with_capacity(path.len() + to.len() - from.len()); + mapped.push_str(to); + mapped.push_str(&path[from.len()..]); + + return DebuginfoFilePath { + path: mapped, + has_been_remapped: true, + }; + } + } + + DebuginfoFilePath { + path: path.to_string(), + has_been_remapped: false, + } + } +} + /// Create any deferred debug metadata nodes pub fn finalize(cx: &CrateContext) { if cx.dbg_cx().is_none() { @@ -220,8 +338,10 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, return FunctionDebugContext::FunctionWithoutDebugInfo; } + let fn_def_id = cx.tcx().closure_base_def_id(instance.def_id()); + let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path); + let file_metadata = file_metadata(cx, &loc.file.name, fn_def_id.krate); let function_type_metadata = unsafe { let fn_signature = get_function_signature(cx, sig); @@ -233,7 +353,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let mut name = def_key.disambiguated_data.data.to_string(); let name_len = name.len(); - let fn_def_id = cx.tcx().closure_base_def_id(instance.def_id()); + // Get_template_parameters() will append a `<...>` clause to the function // name if necessary. @@ -289,6 +409,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fn_debug_context = FunctionDebugContextData { fn_metadata: fn_metadata, source_locations_enabled: Cell::new(false), + defining_crate: fn_def_id.krate, }; return FunctionDebugContext::RegularContext(fn_debug_context); @@ -438,9 +559,9 @@ pub fn declare_local<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, let cx = bcx.ccx; let file = span_start(cx, span).file; - let filename = file.name.clone(); - let file_metadata = file_metadata(cx, &filename[..], &file.abs_path); - + let file_metadata = file_metadata(cx, + &file.name, + dbg_context.get_ref(span).defining_crate); let loc = span_start(cx, span); let type_metadata = type_metadata(cx, variable_type, span); @@ -496,3 +617,17 @@ pub fn declare_local<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, _ => { /* nothing to do */ } } } + +#[test] +fn test_prefix_map_order() { + + let mut map = IndexVec::new(); + map.push(vec![("abc", "qed"), ("abc/def", "xyz")]); + + let map = DebugPrefixMap { + mapping: map + }; + + assert_eq!(map.map_prefix("abc/def/lib.rs", LOCAL_CRATE).path, "xyz/lib.rs"); + assert_eq!(map.map_prefix("abc/ghi/lib.rs", LOCAL_CRATE).path, "qed/ghi/lib.rs"); +} diff --git a/src/librustc_trans/debuginfo/namespace.rs b/src/librustc_trans/debuginfo/namespace.rs index 521dd7530beea..54a129536d03d 100644 --- a/src/librustc_trans/debuginfo/namespace.rs +++ b/src/librustc_trans/debuginfo/namespace.rs @@ -72,7 +72,7 @@ pub fn item_namespace(ccx: &CrateContext, def_id: DefId) -> DIScope { let span = ccx.tcx().def_span(def_id); let (file, line) = if span != DUMMY_SP { let loc = span_start(ccx, span); - (file_metadata(ccx, &loc.file.name, &loc.file.abs_path), loc.line as c_uint) + (file_metadata(ccx, &loc.file.name, def_id.krate), loc.line as c_uint) } else { (unknown_file_metadata(ccx), UNKNOWN_LINE_NUMBER) }; diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 3d8c5085462a8..5252a354d3ebe 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -156,7 +156,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { if pos < self.scopes[scope_id].file_start_pos || pos >= self.scopes[scope_id].file_end_pos { let cm = self.ccx.sess().codemap(); - debuginfo::extend_scope_to_file(self.ccx, scope_metadata, &cm.lookup_char_pos(pos).file) + let defining_crate = self.debug_context.get_ref(DUMMY_SP).defining_crate; + debuginfo::extend_scope_to_file(self.ccx, + scope_metadata, + &cm.lookup_char_pos(pos).file, + defining_crate) } else { scope_metadata } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 0dafc4225a321..79644821742b9 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -37,7 +37,7 @@ pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str> extension: Option<&str>) -> String { debug!("highlighting: ================\n{}\n==============", src); let sess = parse::ParseSess::new(); - let fm = sess.codemap().new_filemap("".to_string(), None, src.to_string()); + let fm = sess.codemap().new_filemap("".to_string(), src.to_string()); let mut out = Vec::new(); write_header(class, id, &mut out).unwrap(); @@ -59,7 +59,7 @@ pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str> /// an enclosing `
` block.
 pub fn render_inner_with_highlighting(src: &str) -> io::Result {
     let sess = parse::ParseSess::new();
-    let fm = sess.codemap().new_filemap("".to_string(), None, src.to_string());
+    let fm = sess.codemap().new_filemap("".to_string(), src.to_string());
 
     let mut out = Vec::new();
     let mut classifier = Classifier::new(lexer::StringReader::new(&sess, fm), sess.codemap());
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 4d67390d44234..6855479a5dae6 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -128,8 +128,7 @@ impl CodeMap {
 
     pub fn load_file(&self, path: &Path) -> io::Result> {
         let src = self.file_loader.read_file(path)?;
-        let abs_path = self.file_loader.abs_path(path).map(|p| p.to_str().unwrap().to_string());
-        Ok(self.new_filemap(path.to_str().unwrap().to_string(), abs_path, src))
+        Ok(self.new_filemap(path.to_str().unwrap().to_string(), src))
     }
 
     fn next_start_pos(&self) -> usize {
@@ -144,8 +143,7 @@ impl CodeMap {
 
     /// Creates a new filemap without setting its line information. If you don't
     /// intend to set the line information yourself, you should use new_filemap_and_lines.
-    pub fn new_filemap(&self, filename: FileName, abs_path: Option,
-                       mut src: String) -> Rc {
+    pub fn new_filemap(&self, filename: FileName, mut src: String) -> Rc {
         let start_pos = self.next_start_pos();
         let mut files = self.files.borrow_mut();
 
@@ -158,7 +156,6 @@ impl CodeMap {
 
         let filemap = Rc::new(FileMap {
             name: filename,
-            abs_path: abs_path,
             src: Some(Rc::new(src)),
             start_pos: Pos::from_usize(start_pos),
             end_pos: Pos::from_usize(end_pos),
@@ -172,10 +169,8 @@ impl CodeMap {
     }
 
     /// Creates a new filemap and sets its line information.
-    pub fn new_filemap_and_lines(&self, filename: &str, abs_path: Option<&str>,
-                                 src: &str) -> Rc {
+    pub fn new_filemap_and_lines(&self, filename: &str, src: &str) -> Rc {
         let fm = self.new_filemap(filename.to_string(),
-                                  abs_path.map(|s| s.to_owned()),
                                   src.to_owned());
         let mut byte_pos: u32 = fm.start_pos.0;
         for line in src.lines() {
@@ -195,7 +190,6 @@ impl CodeMap {
     /// information for things inlined from other crates.
     pub fn new_imported_filemap(&self,
                                 filename: FileName,
-                                abs_path: Option,
                                 source_len: usize,
                                 mut file_local_lines: Vec,
                                 mut file_local_multibyte_chars: Vec)
@@ -216,7 +210,6 @@ impl CodeMap {
 
         let filemap = Rc::new(FileMap {
             name: filename,
-            abs_path: abs_path,
             src: None,
             start_pos: start_pos,
             end_pos: end_pos,
@@ -544,7 +537,6 @@ mod tests {
     fn t1 () {
         let cm = CodeMap::new();
         let fm = cm.new_filemap("blork.rs".to_string(),
-                                None,
                                 "first line.\nsecond line".to_string());
         fm.next_line(BytePos(0));
         // Test we can get lines with partial line info.
@@ -561,7 +553,6 @@ mod tests {
     fn t2 () {
         let cm = CodeMap::new();
         let fm = cm.new_filemap("blork.rs".to_string(),
-                                None,
                                 "first line.\nsecond line".to_string());
         // TESTING *REALLY* BROKEN BEHAVIOR:
         fm.next_line(BytePos(0));
@@ -572,13 +563,10 @@ mod tests {
     fn init_code_map() -> CodeMap {
         let cm = CodeMap::new();
         let fm1 = cm.new_filemap("blork.rs".to_string(),
-                                 None,
                                  "first line.\nsecond line".to_string());
         let fm2 = cm.new_filemap("empty.rs".to_string(),
-                                 None,
                                  "".to_string());
         let fm3 = cm.new_filemap("blork2.rs".to_string(),
-                                 None,
                                  "first line.\nsecond line".to_string());
 
         fm1.next_line(BytePos(0));
@@ -641,10 +629,8 @@ mod tests {
         // € is a three byte utf8 char.
         let fm1 =
             cm.new_filemap("blork.rs".to_string(),
-                           None,
                            "fir€st €€€€ line.\nsecond line".to_string());
         let fm2 = cm.new_filemap("blork2.rs".to_string(),
-                                 None,
                                  "first line€€.\n€ second line".to_string());
 
         fm1.next_line(BytePos(0));
@@ -712,7 +698,7 @@ mod tests {
         let cm = CodeMap::new();
         let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
         let selection = "     \n    ~~\n~~~\n~~~~~     \n   \n";
-        cm.new_filemap_and_lines("blork.rs", None, inputtext);
+        cm.new_filemap_and_lines("blork.rs", inputtext);
         let span = span_from_selection(inputtext, selection);
 
         // check that we are extracting the text we thought we were extracting
@@ -755,7 +741,7 @@ mod tests {
         let inputtext  = "bbbb BB\ncc CCC\n";
         let selection1 = "     ~~\n      \n";
         let selection2 = "       \n   ~~~\n";
-        cm.new_filemap_and_lines("blork.rs", None, inputtext);
+        cm.new_filemap_and_lines("blork.rs", inputtext);
         let span1 = span_from_selection(inputtext, selection1);
         let span2 = span_from_selection(inputtext, selection2);
 
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 680bd7599ace0..c2c6880ca87ba 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -773,7 +773,7 @@ fn stream_for_item(item: &Annotatable, parse_sess: &ParseSess) -> TokenStream {
 
 fn string_to_stream(text: String, parse_sess: &ParseSess) -> TokenStream {
     let filename = String::from("");
-    filemap_to_stream(parse_sess, parse_sess.codemap().new_filemap(filename, None, text))
+    filemap_to_stream(parse_sess, parse_sess.codemap().new_filemap(filename, text))
 }
 
 impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs
index 0103d6ea959dd..22a5776315a0c 100644
--- a/src/libsyntax/ext/source_util.rs
+++ b/src/libsyntax/ext/source_util.rs
@@ -142,7 +142,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenT
             // Add this input file to the code map to make it available as
             // dependency information
             let filename = format!("{}", file.display());
-            cx.codemap().new_filemap_and_lines(&filename, None, &src);
+            cx.codemap().new_filemap_and_lines(&filename, &src);
 
             base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&src)))
         }
@@ -173,7 +173,7 @@ pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::Toke
             // Add this input file to the code map to make it available as
             // dependency information, but don't enter it's contents
             let filename = format!("{}", file.display());
-            cx.codemap().new_filemap_and_lines(&filename, None, "");
+            cx.codemap().new_filemap_and_lines(&filename, "");
 
             base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Rc::new(bytes))))
         }
diff --git a/src/libsyntax/parse/lexer/comments.rs b/src/libsyntax/parse/lexer/comments.rs
index c97b8ddf91972..7a810c48ccde1 100644
--- a/src/libsyntax/parse/lexer/comments.rs
+++ b/src/libsyntax/parse/lexer/comments.rs
@@ -349,7 +349,7 @@ pub fn gather_comments_and_literals(sess: &ParseSess, path: String, srdr: &mut R
     srdr.read_to_end(&mut src).unwrap();
     let src = String::from_utf8(src).unwrap();
     let cm = CodeMap::new();
-    let filemap = cm.new_filemap(path, None, src);
+    let filemap = cm.new_filemap(path, src);
     let mut rdr = lexer::StringReader::new_raw(sess, filemap);
 
     let mut comments: Vec = Vec::new();
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index c63a6524f7459..f61e0ca23146f 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -143,13 +143,13 @@ pub fn parse_stmt_from_source_str<'a>(name: String, source: String, sess: &'a Pa
 
 pub fn parse_stream_from_source_str<'a>(name: String, source: String, sess: &'a ParseSess)
                                         -> TokenStream {
-    filemap_to_stream(sess, sess.codemap().new_filemap(name, None, source))
+    filemap_to_stream(sess, sess.codemap().new_filemap(name, source))
 }
 
 // Create a new parser from a source string
 pub fn new_parser_from_source_str<'a>(sess: &'a ParseSess, name: String, source: String)
                                       -> Parser<'a> {
-    filemap_to_parser(sess, sess.codemap().new_filemap(name, None, source))
+    filemap_to_parser(sess, sess.codemap().new_filemap(name, source))
 }
 
 /// Create a new parser, handling errors as appropriate
diff --git a/src/libsyntax/test_snippet.rs b/src/libsyntax/test_snippet.rs
index a74f59b004bb7..e2dc2166838fa 100644
--- a/src/libsyntax/test_snippet.rs
+++ b/src/libsyntax/test_snippet.rs
@@ -48,7 +48,7 @@ fn test_harness(file_text: &str, span_labels: Vec, expected_output: &
     let output = Arc::new(Mutex::new(Vec::new()));
 
     let code_map = Rc::new(CodeMap::new());
-    code_map.new_filemap_and_lines("test.rs", None, &file_text);
+    code_map.new_filemap_and_lines("test.rs", &file_text);
 
     let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
     let mut msp = MultiSpan::from_span(primary_span);
diff --git a/src/libsyntax/util/parser_testing.rs b/src/libsyntax/util/parser_testing.rs
index 51eb295b502a7..4922cdcfcc08e 100644
--- a/src/libsyntax/util/parser_testing.rs
+++ b/src/libsyntax/util/parser_testing.rs
@@ -19,7 +19,7 @@ use std::iter::Peekable;
 /// Map a string to tts, using a made-up filename:
 pub fn string_to_stream(source_str: String) -> TokenStream {
     let ps = ParseSess::new();
-    filemap_to_stream(&ps, ps.codemap().new_filemap("bogofile".to_string(), None, source_str))
+    filemap_to_stream(&ps, ps.codemap().new_filemap("bogofile".to_string(), source_str))
 }
 
 /// Map string to parser (via tts)
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index aaafcadc38a14..46b5e7e339a3b 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -374,8 +374,6 @@ pub struct FileMap {
     /// originate from files has names between angle brackets by convention,
     /// e.g. ``
     pub name: FileName,
-    /// The absolute path of the file that the source came from.
-    pub abs_path: Option,
     /// The complete source code
     pub src: Option>,
     /// The start position of this source in the CodeMap
@@ -392,7 +390,6 @@ impl Encodable for FileMap {
     fn encode(&self, s: &mut S) -> Result<(), S::Error> {
         s.emit_struct("FileMap", 6, |s| {
             s.emit_struct_field("name", 0, |s| self.name.encode(s))?;
-            s.emit_struct_field("abs_path", 1, |s| self.abs_path.encode(s))?;
             s.emit_struct_field("start_pos", 2, |s| self.start_pos.encode(s))?;
             s.emit_struct_field("end_pos", 3, |s| self.end_pos.encode(s))?;
             s.emit_struct_field("lines", 4, |s| {
@@ -453,8 +450,6 @@ impl Decodable for FileMap {
 
         d.read_struct("FileMap", 6, |d| {
             let name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?;
-            let abs_path: Option =
-                d.read_struct_field("abs_path", 1, |d| Decodable::decode(d))?;
             let start_pos: BytePos = d.read_struct_field("start_pos", 2, |d| Decodable::decode(d))?;
             let end_pos: BytePos = d.read_struct_field("end_pos", 3, |d| Decodable::decode(d))?;
             let lines: Vec = d.read_struct_field("lines", 4, |d| {
@@ -489,7 +484,6 @@ impl Decodable for FileMap {
                 d.read_struct_field("multibyte_chars", 5, |d| Decodable::decode(d))?;
             Ok(FileMap {
                 name: name,
-                abs_path: abs_path,
                 start_pos: start_pos,
                 end_pos: end_pos,
                 src: None,
diff --git a/src/test/codegen/debug-prefix-map.rs b/src/test/codegen/debug-prefix-map.rs
new file mode 100644
index 0000000000000..76f81abddbc21
--- /dev/null
+++ b/src/test/codegen/debug-prefix-map.rs
@@ -0,0 +1,19 @@
+// 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  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+
+// compile-flags: -g -Zdebug-prefix-map-from={{cwd}} -Zdebug-prefix-map-to=/the/cwd -Zdebug-prefix-map-from={{src-base}} -Zdebug-prefix-map-to=/the/src
+
+// CHECK: !DIFile(filename: "/the/src/debug-prefix-map.rs", directory: "/the/cwd")
+
+fn main() {
+    // We just check that the DIFile got remapped properly.
+}
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 522cd222c2691..0f653dfbcf07c 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -40,23 +40,24 @@ impl EarlyProps {
                     None,
                     &mut |ln| {
             props.ignore =
-                props.ignore || parse_name_directive(ln, "ignore-test") ||
-                parse_name_directive(ln, &ignore_target(config)) ||
-                parse_name_directive(ln, &ignore_architecture(config)) ||
-                parse_name_directive(ln, &ignore_stage(config)) ||
-                parse_name_directive(ln, &ignore_env(config)) ||
-                (config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) ||
+                props.ignore || config.parse_name_directive(ln, "ignore-test") ||
+                config.parse_name_directive(ln, &ignore_target(config)) ||
+                config.parse_name_directive(ln, &ignore_architecture(config)) ||
+                config.parse_name_directive(ln, &ignore_stage(config)) ||
+                config.parse_name_directive(ln, &ignore_env(config)) ||
+                (config.mode == common::Pretty &&
+                 config.parse_name_directive(ln, "ignore-pretty")) ||
                 (config.target != config.host &&
-                 parse_name_directive(ln, "ignore-cross-compile")) ||
+                 config.parse_name_directive(ln, "ignore-cross-compile")) ||
                 ignore_gdb(config, ln) ||
                 ignore_lldb(config, ln) ||
                 ignore_llvm(config, ln);
 
-            if let Some(s) = parse_aux_build(ln) {
+            if let Some(s) = config.parse_aux_build(ln) {
                 props.aux.push(s);
             }
 
-            props.should_fail = props.should_fail || parse_name_directive(ln, "should-fail");
+            props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail");
         });
 
         return props;
@@ -80,7 +81,7 @@ impl EarlyProps {
             }
 
             if !line.contains("ignore-gdb-version") &&
-               parse_name_directive(line, "ignore-gdb") {
+               config.parse_name_directive(line, "ignore-gdb") {
                 return true;
             }
 
@@ -143,7 +144,7 @@ impl EarlyProps {
                 return false;
             }
 
-            if parse_name_directive(line, "ignore-lldb") {
+            if config.parse_name_directive(line, "ignore-lldb") {
                 return true;
             }
 
@@ -260,19 +261,23 @@ impl TestProps {
         }
     }
 
-    pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>) -> Self {
+    pub fn from_aux_file(&self,
+                         testfile: &Path,
+                         cfg: Option<&str>,
+                         config: &Config)
+                         -> Self {
         let mut props = TestProps::new();
 
         // copy over select properties to the aux build:
         props.incremental_dir = self.incremental_dir.clone();
-        props.load_from(testfile, cfg);
+        props.load_from(testfile, cfg, config);
 
         props
     }
 
-    pub fn from_file(testfile: &Path) -> Self {
+    pub fn from_file(testfile: &Path, config: &Config) -> Self {
         let mut props = TestProps::new();
-        props.load_from(testfile, None);
+        props.load_from(testfile, None, config);
         props
     }
 
@@ -280,85 +285,88 @@ impl TestProps {
     /// tied to a particular revision `foo` (indicated by writing
     /// `//[foo]`), then the property is ignored unless `cfg` is
     /// `Some("foo")`.
-    pub fn load_from(&mut self, testfile: &Path, cfg: Option<&str>) {
+    pub fn load_from(&mut self,
+                     testfile: &Path,
+                     cfg: Option<&str>,
+                     config: &Config) {
         iter_header(testfile,
                     cfg,
                     &mut |ln| {
-            if let Some(ep) = parse_error_pattern(ln) {
+            if let Some(ep) = config.parse_error_pattern(ln) {
                 self.error_patterns.push(ep);
             }
 
-            if let Some(flags) = parse_compile_flags(ln) {
+            if let Some(flags) = config.parse_compile_flags(ln) {
                 self.compile_flags.extend(flags.split_whitespace()
                     .map(|s| s.to_owned()));
             }
 
-            if let Some(r) = parse_revisions(ln) {
+            if let Some(r) = config.parse_revisions(ln) {
                 self.revisions.extend(r);
             }
 
             if self.run_flags.is_none() {
-                self.run_flags = parse_run_flags(ln);
+                self.run_flags = config.parse_run_flags(ln);
             }
 
             if self.pp_exact.is_none() {
-                self.pp_exact = parse_pp_exact(ln, testfile);
+                self.pp_exact = config.parse_pp_exact(ln, testfile);
             }
 
             if !self.build_aux_docs {
-                self.build_aux_docs = parse_build_aux_docs(ln);
+                self.build_aux_docs = config.parse_build_aux_docs(ln);
             }
 
             if !self.force_host {
-                self.force_host = parse_force_host(ln);
+                self.force_host = config.parse_force_host(ln);
             }
 
             if !self.check_stdout {
-                self.check_stdout = parse_check_stdout(ln);
+                self.check_stdout = config.parse_check_stdout(ln);
             }
 
             if !self.no_prefer_dynamic {
-                self.no_prefer_dynamic = parse_no_prefer_dynamic(ln);
+                self.no_prefer_dynamic = config.parse_no_prefer_dynamic(ln);
             }
 
             if !self.pretty_expanded {
-                self.pretty_expanded = parse_pretty_expanded(ln);
+                self.pretty_expanded = config.parse_pretty_expanded(ln);
             }
 
-            if let Some(m) = parse_pretty_mode(ln) {
+            if let Some(m) = config.parse_pretty_mode(ln) {
                 self.pretty_mode = m;
             }
 
             if !self.pretty_compare_only {
-                self.pretty_compare_only = parse_pretty_compare_only(ln);
+                self.pretty_compare_only = config.parse_pretty_compare_only(ln);
             }
 
-            if let Some(ab) = parse_aux_build(ln) {
+            if let Some(ab) = config.parse_aux_build(ln) {
                 self.aux_builds.push(ab);
             }
 
-            if let Some(ee) = parse_env(ln, "exec-env") {
+            if let Some(ee) = config.parse_env(ln, "exec-env") {
                 self.exec_env.push(ee);
             }
 
-            if let Some(ee) = parse_env(ln, "rustc-env") {
+            if let Some(ee) = config.parse_env(ln, "rustc-env") {
                 self.rustc_env.push(ee);
             }
 
-            if let Some(cl) = parse_check_line(ln) {
+            if let Some(cl) = config.parse_check_line(ln) {
                 self.check_lines.push(cl);
             }
 
-            if let Some(of) = parse_forbid_output(ln) {
+            if let Some(of) = config.parse_forbid_output(ln) {
                 self.forbid_output.push(of);
             }
 
             if !self.must_compile_successfully {
-                self.must_compile_successfully = parse_must_compile_successfully(ln);
+                self.must_compile_successfully = config.parse_must_compile_successfully(ln);
             }
 
             if !self.check_test_line_numbers_match {
-                self.check_test_line_numbers_match = parse_check_test_line_numbers_match(ln);
+                self.check_test_line_numbers_match = config.parse_check_test_line_numbers_match(ln);
             }
         });
 
@@ -410,114 +418,118 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
     return;
 }
 
-fn parse_error_pattern(line: &str) -> Option {
-    parse_name_value_directive(line, "error-pattern")
-}
+impl Config {
 
-fn parse_forbid_output(line: &str) -> Option {
-    parse_name_value_directive(line, "forbid-output")
-}
+    fn parse_error_pattern(&self, line: &str) -> Option {
+        self.parse_name_value_directive(line, "error-pattern")
+    }
 
-fn parse_aux_build(line: &str) -> Option {
-    parse_name_value_directive(line, "aux-build")
-}
+    fn parse_forbid_output(&self, line: &str) -> Option {
+        self.parse_name_value_directive(line, "forbid-output")
+    }
 
-fn parse_compile_flags(line: &str) -> Option {
-    parse_name_value_directive(line, "compile-flags")
-}
+    fn parse_aux_build(&self, line: &str) -> Option {
+        self.parse_name_value_directive(line, "aux-build")
+    }
 
-fn parse_revisions(line: &str) -> Option> {
-    parse_name_value_directive(line, "revisions")
-        .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
-}
+    fn parse_compile_flags(&self, line: &str) -> Option {
+        self.parse_name_value_directive(line, "compile-flags")
+    }
 
-fn parse_run_flags(line: &str) -> Option {
-    parse_name_value_directive(line, "run-flags")
-}
+    fn parse_revisions(&self, line: &str) -> Option> {
+        self.parse_name_value_directive(line, "revisions")
+            .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
+    }
 
-fn parse_check_line(line: &str) -> Option {
-    parse_name_value_directive(line, "check")
-}
+    fn parse_run_flags(&self, line: &str) -> Option {
+        self.parse_name_value_directive(line, "run-flags")
+    }
 
-fn parse_force_host(line: &str) -> bool {
-    parse_name_directive(line, "force-host")
-}
+    fn parse_check_line(&self, line: &str) -> Option {
+        self.parse_name_value_directive(line, "check")
+    }
 
-fn parse_build_aux_docs(line: &str) -> bool {
-    parse_name_directive(line, "build-aux-docs")
-}
+    fn parse_force_host(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "force-host")
+    }
 
-fn parse_check_stdout(line: &str) -> bool {
-    parse_name_directive(line, "check-stdout")
-}
+    fn parse_build_aux_docs(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "build-aux-docs")
+    }
 
-fn parse_no_prefer_dynamic(line: &str) -> bool {
-    parse_name_directive(line, "no-prefer-dynamic")
-}
+    fn parse_check_stdout(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "check-stdout")
+    }
 
-fn parse_pretty_expanded(line: &str) -> bool {
-    parse_name_directive(line, "pretty-expanded")
-}
+    fn parse_no_prefer_dynamic(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "no-prefer-dynamic")
+    }
 
-fn parse_pretty_mode(line: &str) -> Option {
-    parse_name_value_directive(line, "pretty-mode")
-}
+    fn parse_pretty_expanded(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "pretty-expanded")
+    }
 
-fn parse_pretty_compare_only(line: &str) -> bool {
-    parse_name_directive(line, "pretty-compare-only")
-}
+    fn parse_pretty_mode(&self, line: &str) -> Option {
+        self.parse_name_value_directive(line, "pretty-mode")
+    }
 
-fn parse_must_compile_successfully(line: &str) -> bool {
-    parse_name_directive(line, "must-compile-successfully")
-}
+    fn parse_pretty_compare_only(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "pretty-compare-only")
+    }
 
-fn parse_check_test_line_numbers_match(line: &str) -> bool {
-    parse_name_directive(line, "check-test-line-numbers-match")
-}
+    fn parse_must_compile_successfully(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "must-compile-successfully")
+    }
 
-fn parse_env(line: &str, name: &str) -> Option<(String, String)> {
-    parse_name_value_directive(line, name).map(|nv| {
-        // nv is either FOO or FOO=BAR
-        let mut strs: Vec = nv.splitn(2, '=')
-            .map(str::to_owned)
-            .collect();
+    fn parse_check_test_line_numbers_match(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "check-test-line-numbers-match")
+    }
 
-        match strs.len() {
-            1 => (strs.pop().unwrap(), "".to_owned()),
-            2 => {
-                let end = strs.pop().unwrap();
-                (strs.pop().unwrap(), end)
+    fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
+        self.parse_name_value_directive(line, name).map(|nv| {
+            // nv is either FOO or FOO=BAR
+            let mut strs: Vec = nv.splitn(2, '=')
+                .map(str::to_owned)
+                .collect();
+
+            match strs.len() {
+                1 => (strs.pop().unwrap(), "".to_owned()),
+                2 => {
+                    let end = strs.pop().unwrap();
+                    (strs.pop().unwrap(), end)
+                }
+                n => panic!("Expected 1 or 2 strings, not {}", n),
             }
-            n => panic!("Expected 1 or 2 strings, not {}", n),
-        }
-    })
-}
+        })
+    }
 
-fn parse_pp_exact(line: &str, testfile: &Path) -> Option {
-    if let Some(s) = parse_name_value_directive(line, "pp-exact") {
-        Some(PathBuf::from(&s))
-    } else {
-        if parse_name_directive(line, "pp-exact") {
-            testfile.file_name().map(PathBuf::from)
+    fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option {
+        if let Some(s) = self.parse_name_value_directive(line, "pp-exact") {
+            Some(PathBuf::from(&s))
         } else {
-            None
+            if self.parse_name_directive(line, "pp-exact") {
+                testfile.file_name().map(PathBuf::from)
+            } else {
+                None
+            }
         }
     }
-}
 
-fn parse_name_directive(line: &str, directive: &str) -> bool {
-    // This 'no-' rule is a quick hack to allow pretty-expanded and no-pretty-expanded to coexist
-    line.contains(directive) && !line.contains(&("no-".to_owned() + directive))
-}
+    fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
+        // This 'no-' rule is a quick hack to allow pretty-expanded and
+        // no-pretty-expanded to coexist
+        line.contains(directive) && !line.contains(&("no-".to_owned() + directive))
+    }
 
-pub fn parse_name_value_directive(line: &str, directive: &str) -> Option {
-    let keycolon = format!("{}:", directive);
-    if let Some(colon) = line.find(&keycolon) {
-        let value = line[(colon + keycolon.len())..line.len()].to_owned();
-        debug!("{}: {}", directive, value);
-        Some(value)
-    } else {
-        None
+    pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option {
+        let keycolon = format!("{}:", directive);
+        if let Some(colon) = line.find(&keycolon) {
+            let value = line[(colon + keycolon.len())..line.len()].to_owned();
+            debug!("{}: {}", directive, value);
+            Some(expand_variables(value, self))
+        } else {
+            None
+        }
     }
 }
 
@@ -528,3 +540,24 @@ pub fn lldb_version_to_int(version_string: &str) -> isize {
     let major: isize = version_string.parse().ok().expect(&error_string);
     return major;
 }
+
+fn expand_variables(mut value: String, config: &Config) -> String {
+    const CWD: &'static str = "{{cwd}}";
+    const SRC_BASE: &'static str = "{{src-base}}";
+    const BUILD_BASE: &'static str = "{{build-base}}";
+
+    if value.contains(CWD) {
+        let cwd = env::current_dir().unwrap();
+        value = value.replace(CWD, &cwd.to_string_lossy());
+    }
+
+    if value.contains(SRC_BASE) {
+        value = value.replace(SRC_BASE, &config.src_base.to_string_lossy());
+    }
+
+    if value.contains(BUILD_BASE) {
+        value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy());
+    }
+
+    value
+}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 7fb296c19f6ed..1348c8552496b 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -16,7 +16,6 @@ use errors::{self, ErrorKind, Error};
 use filetime::FileTime;
 use json;
 use header::TestProps;
-use header;
 use procsrv;
 use test::TestPaths;
 use uidiff;
@@ -57,7 +56,7 @@ pub fn run(config: Config, testpaths: &TestPaths) {
         print!("\n\n");
     }
     debug!("running {:?}", testpaths.file.display());
-    let base_props = TestProps::from_file(&testpaths.file);
+    let base_props = TestProps::from_file(&testpaths.file, &config);
 
     let base_cx = TestCx { config: &config,
                            props: &base_props,
@@ -70,7 +69,7 @@ pub fn run(config: Config, testpaths: &TestPaths) {
     } else {
         for revision in &base_props.revisions {
             let mut revision_props = base_props.clone();
-            revision_props.load_from(&testpaths.file, Some(&revision));
+            revision_props.load_from(&testpaths.file, Some(&revision), &config);
             let rev_cx = TestCx {
                 config: &config,
                 props: &revision_props,
@@ -867,13 +866,13 @@ actual:\n\
                     }
 
                     for &(ref command_directive, ref check_directive) in &directives {
-                        header::parse_name_value_directive(
+                        self.config.parse_name_value_directive(
                             &line,
                             &command_directive).map(|cmd| {
                                 commands.push(cmd)
                             });
 
-                        header::parse_name_value_directive(
+                        self.config.parse_name_value_directive(
                             &line,
                             &check_directive).map(|cmd| {
                                 check_lines.push(cmd)
@@ -1158,7 +1157,9 @@ actual:\n\
         if self.props.build_aux_docs {
             for rel_ab in &self.props.aux_builds {
                 let aux_testpaths = self.compute_aux_test_paths(rel_ab);
-                let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision);
+                let aux_props = self.props.from_aux_file(&aux_testpaths.file,
+                                                         self.revision,
+                                                         self.config);
                 let aux_cx = TestCx {
                     config: self.config,
                     props: &aux_props,
@@ -1279,7 +1280,9 @@ actual:\n\
 
         for rel_ab in &self.props.aux_builds {
             let aux_testpaths = self.compute_aux_test_paths(rel_ab);
-            let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision);
+            let aux_props = self.props.from_aux_file(&aux_testpaths.file,
+                                                     self.revision,
+                                                     self.config);
             let mut crate_type = if aux_props.no_prefer_dynamic {
                 Vec::new()
             } else {