]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #35340 - michaelwoerister:incr-comp-cli-args, r=nikomatsakis
authorbors <bors@rust-lang.org>
Mon, 15 Aug 2016 15:35:18 +0000 (08:35 -0700)
committerGitHub <noreply@github.com>
Mon, 15 Aug 2016 15:35:18 +0000 (08:35 -0700)
Take commandline arguments into account for incr. comp.

Implements the conservative strategy described in https://github.com/rust-lang/rust/issues/33727.

From now one, every time a new commandline option is added, one has to specify if it influences the incremental compilation cache. I've tried to implement this as automatic as possible: One just has to added either the `[TRACKED]` or the `[UNTRACKED]` marker next to the field. The `Options`, `CodegenOptions`, and `DebuggingOptions` definitions in `session::config` show plenty of examples.

The PR removes some cruft from `session::config::Options`, mostly unnecessary copies of flags also present in `DebuggingOptions` or `CodeGenOptions` in the same struct.

One notable removal is the `cfg` field that contained the values passed via `--cfg` commandline arguments. I chose to remove it because (1) its content is only a subset of what later is stored in `hir::Crate::config` and it's pretty likely that reading the cfgs from `Options` would not be what you wanted, and (2) we could not incorporate it into the dep-tracking hash of the `Options` struct because of how the test framework works, leaving us with a piece of untracked but vital data.

It is now recommended (just as before) to access the crate config via the `krate()` method in the HIR map.

Because the `cfg` field is not present in the `Options` struct any more, some methods in the `CompilerCalls` trait now take the crate config as an explicit parameter -- which might constitute a breaking change for plugin authors.

26 files changed:
src/librustc/lint/mod.rs
src/librustc/middle/cstore.rs
src/librustc/session/config.rs
src/librustc/session/mod.rs
src/librustc/session/search_paths.rs
src/librustc_driver/driver.rs
src/librustc_driver/lib.rs
src/librustc_incremental/persist/directory.rs
src/librustc_incremental/persist/load.rs
src/librustc_incremental/persist/save.rs
src/librustc_metadata/creader.rs
src/librustc_metadata/loader.rs
src/librustc_metadata/macro_import.rs
src/librustc_plugin/load.rs
src/librustc_trans/back/link.rs
src/librustc_trans/back/write.rs
src/librustc_trans/base.rs
src/librustdoc/core.rs
src/librustdoc/lib.rs
src/librustdoc/markdown.rs
src/librustdoc/test.rs
src/libsyntax/feature_gate.rs
src/test/incremental/commandline-args.rs [new file with mode: 0644]
src/test/run-make/issue-19371/foo.rs
src/test/run-pass-fulldeps/compiler-calls.rs
src/tools/compiletest/src/runtest.rs

index 121033549c0d5b0554c208a6551dca5d6e2bf766..f34b14224f7797f0ad798cd4e045e294fdb5104c 100644 (file)
@@ -269,7 +269,7 @@ pub fn as_str(&self) -> String {
 }
 
 /// Setting for how to handle a lint.
-#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)]
+#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
 pub enum Level {
     Allow, Warn, Deny, Forbid
 }
index f1bb3a37e3c273c9ebbfbf39850cff2cd8a3b1f6..dec6f360847bfb2ac6a4928389f6523b266b9a6d 100644 (file)
@@ -73,7 +73,7 @@ pub enum LinkagePreference {
 }
 
 enum_from_u32! {
-    #[derive(Copy, Clone, PartialEq)]
+    #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
     pub enum NativeLibraryKind {
         NativeStatic,    // native static library (.a archive)
         NativeFramework, // OSX-specific
index 0fb4d0f8fea5e63f171c5ddbc642505775b894f8..e988ddcd97b1518e34adde4771ca071903db99d6 100644 (file)
 use errors::{ColorConfig, FatalError, Handler};
 
 use getopts;
-use std::collections::HashMap;
+use std::collections::{BTreeMap, BTreeSet};
+use std::collections::btree_map::Iter as BTreeMapIter;
+use std::collections::btree_map::Keys as BTreeMapKeysIter;
+use std::collections::btree_map::Values as BTreeMapValuesIter;
+
 use std::env;
 use std::fmt;
+use std::hash::{Hasher, SipHasher};
+use std::iter::FromIterator;
 use std::path::PathBuf;
 
 pub struct Config {
@@ -44,7 +50,7 @@ pub struct Config {
     pub uint_type: UintTy,
 }
 
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, Copy, PartialEq, Hash)]
 pub enum OptLevel {
     No, // -O0
     Less, // -O1
@@ -54,14 +60,15 @@ pub enum OptLevel {
     SizeMin, // -Oz
 }
 
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, Copy, PartialEq, Hash)]
 pub enum DebugInfoLevel {
     NoDebugInfo,
     LimitedDebugInfo,
     FullDebugInfo,
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord,
+         RustcEncodable, RustcDecodable)]
 pub enum OutputType {
     Bitcode,
     Assembly,
@@ -118,57 +125,173 @@ pub fn extension(&self) -> &'static str {
     }
 }
 
-#[derive(Clone)]
-pub struct Options {
-    // The crate config requested for the session, which may be combined
-    // with additional crate configurations during the compile process
-    pub crate_types: Vec<CrateType>,
-
-    pub optimize: OptLevel,
-    pub debug_assertions: bool,
-    pub debuginfo: DebugInfoLevel,
-    pub lint_opts: Vec<(String, lint::Level)>,
-    pub lint_cap: Option<lint::Level>,
-    pub describe_lints: bool,
-    pub output_types: HashMap<OutputType, Option<PathBuf>>,
-    // This was mutable for rustpkg, which updates search paths based on the
-    // parsed code. It remains mutable in case its replacements wants to use
-    // this.
-    pub search_paths: SearchPaths,
-    pub libs: Vec<(String, cstore::NativeLibraryKind)>,
-    pub maybe_sysroot: Option<PathBuf>,
-    pub target_triple: String,
-    // User-specified cfg meta items. The compiler itself will add additional
-    // items to the crate config, and during parsing the entire crate config
-    // will be added to the crate AST node.  This should not be used for
-    // anything except building the full crate config prior to parsing.
-    pub cfg: ast::CrateConfig,
-    pub test: bool,
-    pub parse_only: bool,
-    pub no_trans: bool,
-    pub error_format: ErrorOutputType,
-    pub treat_err_as_bug: bool,
-    pub continue_parse_after_error: bool,
-    pub mir_opt_level: usize,
-
-    /// if Some, enable incremental compilation, using the given
-    /// directory to store intermediate results
-    pub incremental: Option<PathBuf>,
-
-    pub no_analysis: bool,
-    pub debugging_opts: DebuggingOptions,
-    pub prints: Vec<PrintRequest>,
-    pub cg: CodegenOptions,
-    pub externs: HashMap<String, Vec<String>>,
-    pub crate_name: Option<String>,
-    /// An optional name to use as the crate for std during std injection,
-    /// written `extern crate std = "name"`. Default to "std". Used by
-    /// out-of-tree drivers.
-    pub alt_std_name: Option<String>,
-    /// Indicates how the compiler should treat unstable features
-    pub unstable_features: UnstableFeatures
+// Use tree-based collections to cheaply get a deterministic Hash implementation.
+// DO NOT switch BTreeMap out for an unsorted container type! That would break
+// dependency tracking for commandline arguments.
+#[derive(Clone, Hash)]
+pub struct OutputTypes(BTreeMap<OutputType, Option<PathBuf>>);
+
+impl OutputTypes {
+    pub fn new(entries: &[(OutputType, Option<PathBuf>)]) -> OutputTypes {
+        OutputTypes(BTreeMap::from_iter(entries.iter()
+                                               .map(|&(k, ref v)| (k, v.clone()))))
+    }
+
+    pub fn get(&self, key: &OutputType) -> Option<&Option<PathBuf>> {
+        self.0.get(key)
+    }
+
+    pub fn contains_key(&self, key: &OutputType) -> bool {
+        self.0.contains_key(key)
+    }
+
+    pub fn keys<'a>(&'a self) -> BTreeMapKeysIter<'a, OutputType, Option<PathBuf>> {
+        self.0.keys()
+    }
+
+    pub fn values<'a>(&'a self) -> BTreeMapValuesIter<'a, OutputType, Option<PathBuf>> {
+        self.0.values()
+    }
 }
 
+
+// Use tree-based collections to cheaply get a deterministic Hash implementation.
+// DO NOT switch BTreeMap or BTreeSet out for an unsorted container type! That
+// would break dependency tracking for commandline arguments.
+#[derive(Clone, Hash)]
+pub struct Externs(BTreeMap<String, BTreeSet<String>>);
+
+impl Externs {
+    pub fn new(data: BTreeMap<String, BTreeSet<String>>) -> Externs {
+        Externs(data)
+    }
+
+    pub fn get(&self, key: &str) -> Option<&BTreeSet<String>> {
+        self.0.get(key)
+    }
+
+    pub fn iter<'a>(&'a self) -> BTreeMapIter<'a, String, BTreeSet<String>> {
+        self.0.iter()
+    }
+}
+
+macro_rules! hash_option {
+    ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [UNTRACKED]) => ({});
+    ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [TRACKED]) => ({
+        if $sub_hashes.insert(stringify!($opt_name),
+                              $opt_expr as &dep_tracking::DepTrackingHash).is_some() {
+            bug!("Duplicate key in CLI DepTrackingHash: {}", stringify!($opt_name))
+        }
+    });
+    ($opt_name:ident,
+     $opt_expr:expr,
+     $sub_hashes:expr,
+     [UNTRACKED_WITH_WARNING $warn_val:expr, $warn_text:expr, $error_format:expr]) => ({
+        if *$opt_expr == $warn_val {
+            early_warn($error_format, $warn_text)
+        }
+    });
+}
+
+macro_rules! top_level_options {
+    (pub struct Options { $(
+        $opt:ident : $t:ty [$dep_tracking_marker:ident $($warn_val:expr, $warn_text:expr)*],
+    )* } ) => (
+        #[derive(Clone)]
+        pub struct Options {
+            $(pub $opt: $t),*
+        }
+
+        impl Options {
+            pub fn dep_tracking_hash(&self) -> u64 {
+                let mut sub_hashes = BTreeMap::new();
+                $({
+                    hash_option!($opt,
+                                 &self.$opt,
+                                 &mut sub_hashes,
+                                 [$dep_tracking_marker $($warn_val,
+                                                         $warn_text,
+                                                         self.error_format)*]);
+                })*
+                let mut hasher =  SipHasher::new();
+                dep_tracking::stable_hash(sub_hashes,
+                                          &mut hasher,
+                                          self.error_format);
+                hasher.finish()
+            }
+        }
+    );
+}
+
+// The top-level commandline options struct
+//
+// For each option, one has to specify how it behaves with regard to the
+// dependency tracking system of incremental compilation. This is done via the
+// square-bracketed directive after the field type. The options are:
+//
+// [TRACKED]
+// A change in the given field will cause the compiler to completely clear the
+// incremental compilation cache before proceeding.
+//
+// [UNTRACKED]
+// Incremental compilation is not influenced by this option.
+//
+// [UNTRACKED_WITH_WARNING(val, warning)]
+// The option is incompatible with incremental compilation in some way. If it
+// has the value `val`, the string `warning` is emitted as a warning.
+//
+// If you add a new option to this struct or one of the sub-structs like
+// CodegenOptions, think about how it influences incremental compilation. If in
+// doubt, specify [TRACKED], which is always "correct" but might lead to
+// unnecessary re-compilation.
+top_level_options!(
+    pub struct Options {
+        // The crate config requested for the session, which may be combined
+        // with additional crate configurations during the compile process
+        crate_types: Vec<CrateType> [TRACKED],
+        optimize: OptLevel [TRACKED],
+        // Include the debug_assertions flag into dependency tracking, since it
+        // can influence whether overflow checks are done or not.
+        debug_assertions: bool [TRACKED],
+        debuginfo: DebugInfoLevel [TRACKED],
+        lint_opts: Vec<(String, lint::Level)> [TRACKED],
+        lint_cap: Option<lint::Level> [TRACKED],
+        describe_lints: bool [UNTRACKED],
+        output_types: OutputTypes [TRACKED],
+        // FIXME(mw): We track this for now but it actually doesn't make too
+        //            much sense: The search path can stay the same while the
+        //            things discovered there might have changed on disk.
+        search_paths: SearchPaths [TRACKED],
+        libs: Vec<(String, cstore::NativeLibraryKind)> [TRACKED],
+        maybe_sysroot: Option<PathBuf> [TRACKED],
+
+        target_triple: String [TRACKED],
+
+        test: bool [TRACKED],
+        error_format: ErrorOutputType [UNTRACKED],
+        mir_opt_level: usize [TRACKED],
+
+        // if Some, enable incremental compilation, using the given
+        // directory to store intermediate results
+        incremental: Option<PathBuf> [UNTRACKED],
+
+        debugging_opts: DebuggingOptions [TRACKED],
+        prints: Vec<PrintRequest> [UNTRACKED],
+        cg: CodegenOptions [TRACKED],
+        // FIXME(mw): We track this for now but it actually doesn't make too
+        //            much sense: The value of this option can stay the same
+        //            while the files they refer to might have changed on disk.
+        externs: Externs [TRACKED],
+        crate_name: Option<String> [TRACKED],
+        // An optional name to use as the crate for std during std injection,
+        // written `extern crate std = "name"`. Default to "std". Used by
+        // out-of-tree drivers.
+        alt_std_name: Option<String> [TRACKED],
+        // Indicates how the compiler should treat unstable features
+        unstable_features: UnstableFeatures [TRACKED],
+    }
+);
+
 #[derive(Clone, PartialEq, Eq)]
 pub enum PrintRequest {
     FileNames,
@@ -209,7 +332,7 @@ pub struct OutputFilenames {
     pub out_filestem: String,
     pub single_output_file: Option<PathBuf>,
     pub extra: String,
-    pub outputs: HashMap<OutputType, Option<PathBuf>>,
+    pub outputs: OutputTypes,
 }
 
 /// Codegen unit names generated by the numbered naming scheme will contain this
@@ -301,24 +424,18 @@ pub fn basic_options() -> Options {
         lint_opts: Vec::new(),
         lint_cap: None,
         describe_lints: false,
-        output_types: HashMap::new(),
+        output_types: OutputTypes(BTreeMap::new()),
         search_paths: SearchPaths::new(),
         maybe_sysroot: None,
         target_triple: host_triple().to_string(),
-        cfg: Vec::new(),
         test: false,
-        parse_only: false,
-        no_trans: false,
-        treat_err_as_bug: false,
-        continue_parse_after_error: false,
         mir_opt_level: 1,
         incremental: None,
-        no_analysis: false,
         debugging_opts: basic_debugging_options(),
         prints: Vec::new(),
         cg: basic_codegen_options(),
         error_format: ErrorOutputType::default(),
-        externs: HashMap::new(),
+        externs: Externs(BTreeMap::new()),
         crate_name: None,
         alt_std_name: None,
         libs: Vec::new(),
@@ -361,7 +478,7 @@ pub enum CrateType {
     CrateTypeCdylib,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Hash)]
 pub enum Passes {
     SomePasses(Vec<String>),
     AllPasses,
@@ -376,7 +493,7 @@ pub fn is_empty(&self) -> bool {
     }
 }
 
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Hash)]
 pub enum PanicStrategy {
     Unwind,
     Abort,
@@ -405,7 +522,12 @@ macro_rules! options {
     ($struct_name:ident, $setter_name:ident, $defaultfn:ident,
      $buildfn:ident, $prefix:expr, $outputname:expr,
      $stat:ident, $mod_desc:ident, $mod_set:ident,
-     $($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) =>
+     $($opt:ident : $t:ty = (
+        $init:expr,
+        $parse:ident,
+        [$dep_tracking_marker:ident $(($dep_warn_val:expr, $dep_warn_text:expr))*],
+        $desc:expr)
+     ),* ,) =>
 (
     #[derive(Clone)]
     pub struct $struct_name { $(pub $opt: $t),* }
@@ -457,6 +579,22 @@ pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $s
         return op;
     }
 
+    impl<'a> dep_tracking::DepTrackingHash for $struct_name {
+
+        fn hash(&self, hasher: &mut SipHasher, error_format: ErrorOutputType) {
+            let mut sub_hashes = BTreeMap::new();
+            $({
+                hash_option!($opt,
+                             &self.$opt,
+                             &mut sub_hashes,
+                             [$dep_tracking_marker $($dep_warn_val,
+                                                     $dep_warn_text,
+                                                     error_format)*]);
+            })*
+            dep_tracking::stable_hash(sub_hashes, hasher, error_format);
+        }
+    }
+
     pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool;
     pub const $stat: &'static [(&'static str, $setter_name,
                                      Option<&'static str>, &'static str)] =
@@ -622,168 +760,177 @@ fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool {
 options! {CodegenOptions, CodegenSetter, basic_codegen_options,
          build_codegen_options, "C", "codegen",
          CG_OPTIONS, cg_type_desc, cgsetters,
-    ar: Option<String> = (None, parse_opt_string,
+    ar: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "tool to assemble archives with"),
-    linker: Option<String> = (None, parse_opt_string,
+    linker: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "system linker to link outputs with"),
-    link_args: Option<Vec<String>> = (None, parse_opt_list,
+    link_args: Option<Vec<String>> = (None, parse_opt_list, [UNTRACKED],
         "extra arguments to pass to the linker (space separated)"),
-    link_dead_code: bool = (false, parse_bool,
+    link_dead_code: bool = (false, parse_bool, [UNTRACKED],
         "don't let linker strip dead code (turning it on can be used for code coverage)"),
-    lto: bool = (false, parse_bool,
+    lto: bool = (false, parse_bool, [TRACKED],
         "perform LLVM link-time optimizations"),
-    target_cpu: Option<String> = (None, parse_opt_string,
+    target_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
         "select target processor (rustc --print target-cpus for details)"),
-    target_feature: String = ("".to_string(), parse_string,
+    target_feature: String = ("".to_string(), parse_string, [TRACKED],
         "target specific attributes (rustc --print target-features for details)"),
-    passes: Vec<String> = (Vec::new(), parse_list,
+    passes: Vec<String> = (Vec::new(), parse_list, [TRACKED],
         "a list of extra LLVM passes to run (space separated)"),
-    llvm_args: Vec<String> = (Vec::new(), parse_list,
+    llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED],
         "a list of arguments to pass to llvm (space separated)"),
-    save_temps: bool = (false, parse_bool,
+    save_temps: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true,
+        "`-C save-temps` might not produce all requested temporary products \
+         when incremental compilation is enabled.")],
         "save all temporary output files during compilation"),
-    rpath: bool = (false, parse_bool,
+    rpath: bool = (false, parse_bool, [UNTRACKED],
         "set rpath values in libs/exes"),
-    no_prepopulate_passes: bool = (false, parse_bool,
+    no_prepopulate_passes: bool = (false, parse_bool, [TRACKED],
         "don't pre-populate the pass manager with a list of passes"),
-    no_vectorize_loops: bool = (false, parse_bool,
+    no_vectorize_loops: bool = (false, parse_bool, [TRACKED],
         "don't run the loop vectorization optimization passes"),
-    no_vectorize_slp: bool = (false, parse_bool,
+    no_vectorize_slp: bool = (false, parse_bool, [TRACKED],
         "don't run LLVM's SLP vectorization pass"),
-    soft_float: bool = (false, parse_bool,
+    soft_float: bool = (false, parse_bool, [TRACKED],
         "generate software floating point library calls"),
-    prefer_dynamic: bool = (false, parse_bool,
+    prefer_dynamic: bool = (false, parse_bool, [TRACKED],
         "prefer dynamic linking to static linking"),
-    no_integrated_as: bool = (false, parse_bool,
+    no_integrated_as: bool = (false, parse_bool, [TRACKED],
         "use an external assembler rather than LLVM's integrated one"),
-    no_redzone: Option<bool> = (None, parse_opt_bool,
+    no_redzone: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "disable the use of the redzone"),
-    relocation_model: Option<String> = (None, parse_opt_string,
+    relocation_model: Option<String> = (None, parse_opt_string, [TRACKED],
          "choose the relocation model to use (rustc --print relocation-models for details)"),
-    code_model: Option<String> = (None, parse_opt_string,
+    code_model: Option<String> = (None, parse_opt_string, [TRACKED],
          "choose the code model to use (rustc --print code-models for details)"),
-    metadata: Vec<String> = (Vec::new(), parse_list,
+    metadata: Vec<String> = (Vec::new(), parse_list, [TRACKED],
          "metadata to mangle symbol names with"),
-    extra_filename: String = ("".to_string(), parse_string,
+    extra_filename: String = ("".to_string(), parse_string, [UNTRACKED],
          "extra data to put in each output filename"),
-    codegen_units: usize = (1, parse_uint,
+    codegen_units: usize = (1, parse_uint, [UNTRACKED],
         "divide crate into N units to optimize in parallel"),
-    remark: Passes = (SomePasses(Vec::new()), parse_passes,
+    remark: Passes = (SomePasses(Vec::new()), parse_passes, [UNTRACKED],
         "print remarks for these optimization passes (space separated, or \"all\")"),
-    no_stack_check: bool = (false, parse_bool,
+    no_stack_check: bool = (false, parse_bool, [UNTRACKED],
         "disable checks for stack exhaustion (a memory-safety hazard!)"),
-    debuginfo: Option<usize> = (None, parse_opt_uint,
+    debuginfo: Option<usize> = (None, parse_opt_uint, [TRACKED],
         "debug info emission level, 0 = no debug info, 1 = line tables only, \
          2 = full debug info with variable and type information"),
-    opt_level: Option<String> = (None, parse_opt_string,
+    opt_level: Option<String> = (None, parse_opt_string, [TRACKED],
         "optimize with possible levels 0-3, s, or z"),
-    debug_assertions: Option<bool> = (None, parse_opt_bool,
+    debug_assertions: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "explicitly enable the cfg(debug_assertions) directive"),
-    inline_threshold: Option<usize> = (None, parse_opt_uint,
+    inline_threshold: Option<usize> = (None, parse_opt_uint, [TRACKED],
         "set the inlining threshold for"),
     panic: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy,
-        "panic strategy to compile crate with"),
+        [TRACKED], "panic strategy to compile crate with"),
 }
 
 options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
          build_debugging_options, "Z", "debugging",
          DB_OPTIONS, db_type_desc, dbsetters,
-    verbose: bool = (false, parse_bool,
+    verbose: bool = (false, parse_bool, [UNTRACKED],
         "in general, enable more debug printouts"),
-    time_passes: bool = (false, parse_bool,
+    time_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each rustc pass"),
     count_llvm_insns: bool = (false, parse_bool,
+        [UNTRACKED_WITH_WARNING(true,
+        "The output generated by `-Z count_llvm_insns` might not be reliable \
+         when used with incremental compilation")],
         "count where LLVM instrs originate"),
-    time_llvm_passes: bool = (false, parse_bool,
+    time_llvm_passes: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true,
+        "The output of `-Z time-llvm-passes` will only reflect timings of \
+         re-translated modules when used with incremental compilation" )],
         "measure time of each LLVM pass"),
-    input_stats: bool = (false, parse_bool,
+    input_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather statistics about the input"),
-    trans_stats: bool = (false, parse_bool,
+    trans_stats: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true,
+        "The output of `-Z trans-stats` might not be accurate when incremental \
+         compilation is enabled")],
         "gather trans statistics"),
-    asm_comments: bool = (false, parse_bool,
+    asm_comments: bool = (false, parse_bool, [TRACKED],
         "generate comments into the assembly (may change behavior)"),
-    no_verify: bool = (false, parse_bool,
+    no_verify: bool = (false, parse_bool, [TRACKED],
         "skip LLVM verification"),
-    borrowck_stats: bool = (false, parse_bool,
+    borrowck_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather borrowck statistics"),
-    no_landing_pads: bool = (false, parse_bool,
+    no_landing_pads: bool = (false, parse_bool, [TRACKED],
         "omit landing pads for unwinding"),
-    debug_llvm: bool = (false, parse_bool,
+    debug_llvm: bool = (false, parse_bool, [UNTRACKED],
         "enable debug output from LLVM"),
-    meta_stats: bool = (false, parse_bool,
+    meta_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather metadata statistics"),
-    print_link_args: bool = (false, parse_bool,
+    print_link_args: bool = (false, parse_bool, [UNTRACKED],
         "print the arguments passed to the linker"),
-    print_llvm_passes: bool = (false, parse_bool,
+    print_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
         "prints the llvm optimization passes being run"),
-    ast_json: bool = (false, parse_bool,
+    ast_json: bool = (false, parse_bool, [UNTRACKED],
         "print the AST as JSON and halt"),
-    ast_json_noexpand: bool = (false, parse_bool,
+    ast_json_noexpand: bool = (false, parse_bool, [UNTRACKED],
         "print the pre-expansion AST as JSON and halt"),
-    ls: bool = (false, parse_bool,
+    ls: bool = (false, parse_bool, [UNTRACKED],
         "list the symbols defined by a library crate"),
-    save_analysis: bool = (false, parse_bool,
+    save_analysis: bool = (false, parse_bool, [UNTRACKED],
         "write syntax and type analysis (in JSON format) information in addition to normal output"),
-    save_analysis_csv: bool = (false, parse_bool,
+    save_analysis_csv: bool = (false, parse_bool, [UNTRACKED],
         "write syntax and type analysis (in CSV format) information in addition to normal output"),
-    print_move_fragments: bool = (false, parse_bool,
+    print_move_fragments: bool = (false, parse_bool, [UNTRACKED],
         "print out move-fragment data for every fn"),
-    flowgraph_print_loans: bool = (false, parse_bool,
+    flowgraph_print_loans: bool = (false, parse_bool, [UNTRACKED],
         "include loan analysis data in --unpretty flowgraph output"),
-    flowgraph_print_moves: bool = (false, parse_bool,
+    flowgraph_print_moves: bool = (false, parse_bool, [UNTRACKED],
         "include move analysis data in --unpretty flowgraph output"),
-    flowgraph_print_assigns: bool = (false, parse_bool,
+    flowgraph_print_assigns: bool = (false, parse_bool, [UNTRACKED],
         "include assignment analysis data in --unpretty flowgraph output"),
-    flowgraph_print_all: bool = (false, parse_bool,
+    flowgraph_print_all: bool = (false, parse_bool, [UNTRACKED],
         "include all dataflow analysis data in --unpretty flowgraph output"),
-    print_region_graph: bool = (false, parse_bool,
+    print_region_graph: bool = (false, parse_bool, [UNTRACKED],
          "prints region inference graph. \
           Use with RUST_REGION_GRAPH=help for more info"),
-    parse_only: bool = (false, parse_bool,
+    parse_only: bool = (false, parse_bool, [UNTRACKED],
           "parse only; do not compile, assemble, or link"),
-    no_trans: bool = (false, parse_bool,
+    no_trans: bool = (false, parse_bool, [TRACKED],
           "run all passes except translation; no output"),
-    treat_err_as_bug: bool = (false, parse_bool,
+    treat_err_as_bug: bool = (false, parse_bool, [TRACKED],
           "treat all errors that occur as bugs"),
-    continue_parse_after_error: bool = (false, parse_bool,
+    continue_parse_after_error: bool = (false, parse_bool, [TRACKED],
           "attempt to recover from parse errors (experimental)"),
-    incremental: Option<String> = (None, parse_opt_string,
+    incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
           "enable incremental compilation (experimental)"),
-    incremental_info: bool = (false, parse_bool,
+    incremental_info: bool = (false, parse_bool, [UNTRACKED],
         "print high-level information about incremental reuse (or the lack thereof)"),
-    dump_dep_graph: bool = (false, parse_bool,
+    dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
           "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"),
-    query_dep_graph: bool = (false, parse_bool,
+    query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
           "enable queries of the dependency graph for regression testing"),
-    no_analysis: bool = (false, parse_bool,
+    no_analysis: bool = (false, parse_bool, [UNTRACKED],
           "parse and expand the source, but run no analysis"),
-    extra_plugins: Vec<String> = (Vec::new(), parse_list,
+    extra_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED],
         "load extra plugins"),
-    unstable_options: bool = (false, parse_bool,
+    unstable_options: bool = (false, parse_bool, [UNTRACKED],
           "adds unstable command line options to rustc interface"),
-    force_overflow_checks: Option<bool> = (None, parse_opt_bool,
+    force_overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
           "force overflow checks on or off"),
-    force_dropflag_checks: Option<bool> = (None, parse_opt_bool,
+    force_dropflag_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
           "force drop flag checks on or off"),
-    trace_macros: bool = (false, parse_bool,
+    trace_macros: bool = (false, parse_bool, [UNTRACKED],
           "for every macro invocation, print its name and arguments"),
-    enable_nonzeroing_move_hints: bool = (false, parse_bool,
+    enable_nonzeroing_move_hints: bool = (false, parse_bool, [TRACKED],
           "force nonzeroing move optimization on"),
-    keep_hygiene_data: bool = (false, parse_bool,
+    keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
           "don't clear the hygiene data after analysis"),
-    keep_ast: bool = (false, parse_bool,
+    keep_ast: bool = (false, parse_bool, [UNTRACKED],
           "keep the AST after lowering it to HIR"),
-    show_span: Option<String> = (None, parse_opt_string,
+    show_span: Option<String> = (None, parse_opt_string, [TRACKED],
           "show spans for compiler debugging (expr|pat|ty)"),
-    print_trans_items: Option<String> = (None, parse_opt_string,
+    print_trans_items: Option<String> = (None, parse_opt_string, [UNTRACKED],
           "print the result of the translation item collection pass"),
-    mir_opt_level: Option<usize> = (None, parse_opt_uint,
+    mir_opt_level: Option<usize> = (None, parse_opt_uint, [TRACKED],
           "set the MIR optimization level (0-3)"),
-    dump_mir: Option<String> = (None, parse_opt_string,
+    dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED],
           "dump MIR state at various points in translation"),
-    dump_mir_dir: Option<String> = (None, parse_opt_string,
+    dump_mir_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
           "the directory the MIR is dumped into"),
-    orbit: bool = (true, parse_all_bool,
+    orbit: bool = (true, parse_all_bool, [UNTRACKED],
           "get MIR where it belongs - everywhere; most importantly, in orbit"),
 }
 
@@ -849,11 +996,12 @@ pub fn append_configuration(cfg: &mut ast::CrateConfig,
     }
 }
 
-pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
+pub fn build_configuration(sess: &Session,
+                           mut user_cfg: ast::CrateConfig)
+                           -> ast::CrateConfig {
     // Combine the configuration requested by the session (command line) with
     // some default and generated configuration items
     let default_cfg = default_configuration(sess);
-    let mut user_cfg = sess.opts.cfg.clone();
     // If the user wants a test runner, then add the test cfg
     if sess.opts.test {
         append_configuration(&mut user_cfg, InternedString::new("test"))
@@ -1115,7 +1263,8 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
     }).collect::<ast::CrateConfig>()
 }
 
-pub fn build_session_options(matches: &getopts::Matches) -> Options {
+pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
+                                              -> (Options, ast::CrateConfig) {
     let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) {
         Some("auto")   => ColorConfig::Auto,
         Some("always") => ColorConfig::Always,
@@ -1185,14 +1334,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         debugging_opts.orbit = true;
     }
 
-    let parse_only = debugging_opts.parse_only;
-    let no_trans = debugging_opts.no_trans;
-    let treat_err_as_bug = debugging_opts.treat_err_as_bug;
-    let continue_parse_after_error = debugging_opts.continue_parse_after_error;
     let mir_opt_level = debugging_opts.mir_opt_level.unwrap_or(1);
-    let no_analysis = debugging_opts.no_analysis;
 
-    let mut output_types = HashMap::new();
+    let mut output_types = BTreeMap::new();
     if !debugging_opts.parse_only {
         for list in matches.opt_strs("emit") {
             for output_type in list.split(',') {
@@ -1360,7 +1504,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
                                 --debuginfo");
     }
 
-    let mut externs = HashMap::new();
+    let mut externs = BTreeMap::new();
     for arg in &matches.opt_strs("extern") {
         let mut parts = arg.splitn(2, '=');
         let name = match parts.next() {
@@ -1372,44 +1516,41 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             None => early_error(error_format, "--extern value must be of the format `foo=bar`"),
         };
 
-        externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string());
+        externs.entry(name.to_string())
+               .or_insert_with(BTreeSet::new)
+               .insert(location.to_string());
     }
 
     let crate_name = matches.opt_str("crate-name");
 
     let incremental = debugging_opts.incremental.as_ref().map(|m| PathBuf::from(m));
 
-    Options {
+    (Options {
         crate_types: crate_types,
         optimize: opt_level,
         debuginfo: debuginfo,
         lint_opts: lint_opts,
         lint_cap: lint_cap,
         describe_lints: describe_lints,
-        output_types: output_types,
+        output_types: OutputTypes(output_types),
         search_paths: search_paths,
         maybe_sysroot: sysroot_opt,
         target_triple: target,
-        cfg: cfg,
         test: test,
-        parse_only: parse_only,
-        no_trans: no_trans,
-        treat_err_as_bug: treat_err_as_bug,
-        continue_parse_after_error: continue_parse_after_error,
         mir_opt_level: mir_opt_level,
         incremental: incremental,
-        no_analysis: no_analysis,
         debugging_opts: debugging_opts,
         prints: prints,
         cg: cg,
         error_format: error_format,
-        externs: externs,
+        externs: Externs(externs),
         crate_name: crate_name,
         alt_std_name: None,
         libs: libs,
         unstable_features: get_unstable_features_setting(),
         debug_assertions: debug_assertions,
-    }
+    },
+    cfg)
 }
 
 pub fn get_unstable_features_setting() -> UnstableFeatures {
@@ -1530,15 +1671,139 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+/// Commandline arguments passed to the compiler have to be incorporated with
+/// the dependency tracking system for incremental compilation. This module
+/// provides some utilities to make this more convenient.
+///
+/// The values of all commandline arguments that are relevant for dependency
+/// tracking are hashed into a single value that determines whether the
+/// incremental compilation cache can be re-used or not. This hashing is done
+/// via the DepTrackingHash trait defined below, since the standard Hash
+/// implementation might not be suitable (e.g. arguments are stored in a Vec,
+/// the hash of which is order dependent, but we might not want the order of
+/// arguments to make a difference for the hash).
+///
+/// However, since the value provided by Hash::hash often *is* suitable,
+/// especially for primitive types, there is the
+/// impl_dep_tracking_hash_via_hash!() macro that allows to simply reuse the
+/// Hash implementation for DepTrackingHash. It's important though that
+/// we have an opt-in scheme here, so one is hopefully forced to think about
+/// how the hash should be calculated when adding a new commandline argument.
+mod dep_tracking {
+    use lint;
+    use middle::cstore;
+    use session::search_paths::{PathKind, SearchPaths};
+    use std::collections::BTreeMap;
+    use std::hash::{Hash, SipHasher};
+    use std::path::PathBuf;
+    use super::{Passes, PanicStrategy, CrateType, OptLevel, DebugInfoLevel,
+                OutputTypes, Externs, ErrorOutputType};
+    use syntax::feature_gate::UnstableFeatures;
+
+    pub trait DepTrackingHash {
+        fn hash(&self, &mut SipHasher, ErrorOutputType);
+    }
+
+    macro_rules! impl_dep_tracking_hash_via_hash {
+        ($t:ty) => (
+            impl DepTrackingHash for $t {
+                fn hash(&self, hasher: &mut SipHasher, _: ErrorOutputType) {
+                    Hash::hash(self, hasher);
+                }
+            }
+        )
+    }
+
+    macro_rules! impl_dep_tracking_hash_for_sortable_vec_of {
+        ($t:ty) => (
+            impl DepTrackingHash for Vec<$t> {
+                fn hash(&self, hasher: &mut SipHasher, error_format: ErrorOutputType) {
+                    let mut elems: Vec<&$t> = self.iter().collect();
+                    elems.sort();
+                    Hash::hash(&elems.len(), hasher);
+                    for (index, elem) in elems.iter().enumerate() {
+                        Hash::hash(&index, hasher);
+                        DepTrackingHash::hash(*elem, hasher, error_format);
+                    }
+                }
+            }
+        );
+    }
+
+    impl_dep_tracking_hash_via_hash!(bool);
+    impl_dep_tracking_hash_via_hash!(usize);
+    impl_dep_tracking_hash_via_hash!(String);
+    impl_dep_tracking_hash_via_hash!(lint::Level);
+    impl_dep_tracking_hash_via_hash!(Option<bool>);
+    impl_dep_tracking_hash_via_hash!(Option<usize>);
+    impl_dep_tracking_hash_via_hash!(Option<String>);
+    impl_dep_tracking_hash_via_hash!(Option<lint::Level>);
+    impl_dep_tracking_hash_via_hash!(Option<PathBuf>);
+    impl_dep_tracking_hash_via_hash!(CrateType);
+    impl_dep_tracking_hash_via_hash!(PanicStrategy);
+    impl_dep_tracking_hash_via_hash!(Passes);
+    impl_dep_tracking_hash_via_hash!(OptLevel);
+    impl_dep_tracking_hash_via_hash!(DebugInfoLevel);
+    impl_dep_tracking_hash_via_hash!(UnstableFeatures);
+    impl_dep_tracking_hash_via_hash!(Externs);
+    impl_dep_tracking_hash_via_hash!(OutputTypes);
+    impl_dep_tracking_hash_via_hash!(cstore::NativeLibraryKind);
+
+    impl_dep_tracking_hash_for_sortable_vec_of!(String);
+    impl_dep_tracking_hash_for_sortable_vec_of!(CrateType);
+    impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level));
+    impl_dep_tracking_hash_for_sortable_vec_of!((String, cstore::NativeLibraryKind));
+
+    impl DepTrackingHash for SearchPaths {
+        fn hash(&self, hasher: &mut SipHasher, _: ErrorOutputType) {
+            let mut elems: Vec<_> = self
+                .iter(PathKind::All)
+                .collect();
+            elems.sort();
+            Hash::hash(&elems, hasher);
+        }
+    }
+
+    impl<T1, T2> DepTrackingHash for (T1, T2)
+        where T1: DepTrackingHash,
+              T2: DepTrackingHash
+    {
+        fn hash(&self, hasher: &mut SipHasher, error_format: ErrorOutputType) {
+            Hash::hash(&0, hasher);
+            DepTrackingHash::hash(&self.0, hasher, error_format);
+            Hash::hash(&1, hasher);
+            DepTrackingHash::hash(&self.1, hasher, error_format);
+        }
+    }
+
+    // This is a stable hash because BTreeMap is a sorted container
+    pub fn stable_hash(sub_hashes: BTreeMap<&'static str, &DepTrackingHash>,
+                       hasher: &mut SipHasher,
+                       error_format: ErrorOutputType) {
+        for (key, sub_hash) in sub_hashes {
+            // Using Hash::hash() instead of DepTrackingHash::hash() is fine for
+            // the keys, as they are just plain strings
+            Hash::hash(&key.len(), hasher);
+            Hash::hash(key, hasher);
+            sub_hash.hash(hasher, error_format);
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use dep_graph::DepGraph;
-    use middle::cstore::DummyCrateStore;
-    use session::config::{build_configuration, build_session_options};
-    use session::build_session;
     use errors;
-    use std::rc::Rc;
     use getopts::{getopts, OptGroup};
+    use lint;
+    use middle::cstore::{self, DummyCrateStore};
+    use session::config::{build_configuration, build_session_options_and_crate_config};
+    use session::build_session;
+    use std::collections::{BTreeMap, BTreeSet};
+    use std::iter::FromIterator;
+    use std::path::PathBuf;
+    use std::rc::Rc;
+    use super::{OutputType, OutputTypes, Externs, PanicStrategy};
     use syntax::attr;
     use syntax::attr::AttrMetaMethods;
 
@@ -1548,6 +1813,14 @@ fn optgroups() -> Vec<OptGroup> {
                                 .collect()
     }
 
+    fn mk_map<K: Ord, V>(entries: Vec<(K, V)>) -> BTreeMap<K, V> {
+        BTreeMap::from_iter(entries.into_iter())
+    }
+
+    fn mk_set<V: Ord>(entries: Vec<V>) -> BTreeSet<V> {
+        BTreeSet::from_iter(entries.into_iter())
+    }
+
     // When the user supplies --test we should implicitly supply --cfg test
     #[test]
     fn test_switch_implies_cfg_test() {
@@ -1558,9 +1831,9 @@ fn test_switch_implies_cfg_test() {
               Err(f) => panic!("test_switch_implies_cfg_test: {}", f)
             };
         let registry = errors::registry::Registry::new(&[]);
-        let sessopts = build_session_options(matches);
+        let (sessopts, cfg) = build_session_options_and_crate_config(matches);
         let sess = build_session(sessopts, &dep_graph, None, registry, Rc::new(DummyCrateStore));
-        let cfg = build_configuration(&sess);
+        let cfg = build_configuration(&sess, cfg);
         assert!((attr::contains_name(&cfg[..], "test")));
     }
 
@@ -1578,10 +1851,10 @@ fn test_switch_implies_cfg_test_unless_cfg_test() {
               }
             };
         let registry = errors::registry::Registry::new(&[]);
-        let sessopts = build_session_options(matches);
+        let (sessopts, cfg) = build_session_options_and_crate_config(matches);
         let sess = build_session(sessopts, &dep_graph, None, registry,
                                  Rc::new(DummyCrateStore));
-        let cfg = build_configuration(&sess);
+        let cfg = build_configuration(&sess, cfg);
         let mut test_items = cfg.iter().filter(|m| m.name() == "test");
         assert!(test_items.next().is_some());
         assert!(test_items.next().is_none());
@@ -1595,7 +1868,7 @@ fn test_can_print_warnings() {
                 "-Awarnings".to_string()
             ], &optgroups()).unwrap();
             let registry = errors::registry::Registry::new(&[]);
-            let sessopts = build_session_options(&matches);
+            let (sessopts, _) = build_session_options_and_crate_config(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
             assert!(!sess.diagnostic().can_emit_warnings);
@@ -1607,7 +1880,7 @@ fn test_can_print_warnings() {
                 "-Dwarnings".to_string()
             ], &optgroups()).unwrap();
             let registry = errors::registry::Registry::new(&[]);
-            let sessopts = build_session_options(&matches);
+            let (sessopts, _) = build_session_options_and_crate_config(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
             assert!(sess.diagnostic().can_emit_warnings);
@@ -1618,10 +1891,589 @@ fn test_can_print_warnings() {
                 "-Adead_code".to_string()
             ], &optgroups()).unwrap();
             let registry = errors::registry::Registry::new(&[]);
-            let sessopts = build_session_options(&matches);
+            let (sessopts, _) = build_session_options_and_crate_config(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
             assert!(sess.diagnostic().can_emit_warnings);
         }
     }
+
+    #[test]
+    fn test_output_types_tracking_hash_different_paths() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+        let mut v3 = super::basic_options();
+
+        v1.output_types = OutputTypes::new(&[(OutputType::Exe,
+                                              Some(PathBuf::from("./some/thing")))]);
+        v2.output_types = OutputTypes::new(&[(OutputType::Exe,
+                                              Some(PathBuf::from("/some/thing")))]);
+        v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
+
+        assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash());
+        assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+        assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_output_types_tracking_hash_different_construction_order() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+
+        v1.output_types = OutputTypes::new(&[
+            (OutputType::Exe, Some(PathBuf::from("./some/thing"))),
+            (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))),
+        ]);
+
+        v2.output_types = OutputTypes::new(&[
+            (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))),
+            (OutputType::Exe, Some(PathBuf::from("./some/thing"))),
+        ]);
+
+        assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_externs_tracking_hash_different_values() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+        let mut v3 = super::basic_options();
+
+        v1.externs = Externs::new(mk_map(vec![
+            (String::from("a"), mk_set(vec![String::from("b"),
+                                            String::from("c")])),
+            (String::from("d"), mk_set(vec![String::from("e"),
+                                            String::from("f")])),
+        ]));
+
+        v2.externs = Externs::new(mk_map(vec![
+            (String::from("a"), mk_set(vec![String::from("b"),
+                                            String::from("c")])),
+            (String::from("X"), mk_set(vec![String::from("e"),
+                                            String::from("f")])),
+        ]));
+
+        v3.externs = Externs::new(mk_map(vec![
+            (String::from("a"), mk_set(vec![String::from("b"),
+                                            String::from("c")])),
+            (String::from("d"), mk_set(vec![String::from("X"),
+                                            String::from("f")])),
+        ]));
+
+        assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash());
+        assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+        assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_externs_tracking_hash_different_construction_order() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+        let mut v3 = super::basic_options();
+
+        v1.externs = Externs::new(mk_map(vec![
+            (String::from("a"), mk_set(vec![String::from("b"),
+                                            String::from("c")])),
+            (String::from("d"), mk_set(vec![String::from("e"),
+                                            String::from("f")])),
+        ]));
+
+        v2.externs = Externs::new(mk_map(vec![
+            (String::from("d"), mk_set(vec![String::from("e"),
+                                            String::from("f")])),
+            (String::from("a"), mk_set(vec![String::from("b"),
+                                            String::from("c")])),
+        ]));
+
+        v3.externs = Externs::new(mk_map(vec![
+            (String::from("a"), mk_set(vec![String::from("b"),
+                                            String::from("c")])),
+            (String::from("d"), mk_set(vec![String::from("f"),
+                                            String::from("e")])),
+        ]));
+
+        assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash());
+        assert_eq!(v1.dep_tracking_hash(), v3.dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v3.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+        assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_lints_tracking_hash_different_values() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+        let mut v3 = super::basic_options();
+
+        v1.lint_opts = vec![(String::from("a"), lint::Allow),
+                            (String::from("b"), lint::Warn),
+                            (String::from("c"), lint::Deny),
+                            (String::from("d"), lint::Forbid)];
+
+        v2.lint_opts = vec![(String::from("a"), lint::Allow),
+                            (String::from("b"), lint::Warn),
+                            (String::from("X"), lint::Deny),
+                            (String::from("d"), lint::Forbid)];
+
+        v3.lint_opts = vec![(String::from("a"), lint::Allow),
+                            (String::from("b"), lint::Warn),
+                            (String::from("c"), lint::Forbid),
+                            (String::from("d"), lint::Deny)];
+
+        assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash());
+        assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+        assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_lints_tracking_hash_different_construction_order() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+
+        v1.lint_opts = vec![(String::from("a"), lint::Allow),
+                            (String::from("b"), lint::Warn),
+                            (String::from("c"), lint::Deny),
+                            (String::from("d"), lint::Forbid)];
+
+        v2.lint_opts = vec![(String::from("a"), lint::Allow),
+                            (String::from("c"), lint::Deny),
+                            (String::from("b"), lint::Warn),
+                            (String::from("d"), lint::Forbid)];
+
+        assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_search_paths_tracking_hash_different_values() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+        let mut v3 = super::basic_options();
+        let mut v4 = super::basic_options();
+        let mut v5 = super::basic_options();
+
+        // Reference
+        v1.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v1.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v1.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v1.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+        v1.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+
+        // Native changed
+        v2.search_paths.add_path("native=XXX", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+
+        // Crate changed
+        v2.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("crate=XXX", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+
+        // Dependency changed
+        v3.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v3.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v3.search_paths.add_path("dependency=XXX", super::ErrorOutputType::Json);
+        v3.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+        v3.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+
+        // Framework changed
+        v4.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v4.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v4.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v4.search_paths.add_path("framework=XXX", super::ErrorOutputType::Json);
+        v4.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+
+        // All changed
+        v5.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v5.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v5.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v5.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+        v5.search_paths.add_path("all=XXX", super::ErrorOutputType::Json);
+
+        assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() != v4.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() != v5.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+        assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+        assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash());
+        assert_eq!(v5.dep_tracking_hash(), v5.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_search_paths_tracking_hash_different_order() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+        let mut v3 = super::basic_options();
+        let mut v4 = super::basic_options();
+
+        // Reference
+        v1.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v1.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v1.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v1.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+        v1.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+
+        v2.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+        v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+
+        v3.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v3.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+        v3.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v3.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v3.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+
+        v4.search_paths.add_path("all=mno", super::ErrorOutputType::Json);
+        v4.search_paths.add_path("native=abc", super::ErrorOutputType::Json);
+        v4.search_paths.add_path("crate=def", super::ErrorOutputType::Json);
+        v4.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json);
+        v4.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json);
+
+        assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() == v4.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+        assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+        assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_native_libs_tracking_hash_different_values() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+        let mut v3 = super::basic_options();
+
+        // Reference
+        v1.libs = vec![(String::from("a"), cstore::NativeStatic),
+                       (String::from("b"), cstore::NativeFramework),
+                       (String::from("c"), cstore::NativeUnknown)];
+
+        // Change label
+        v2.libs = vec![(String::from("a"), cstore::NativeStatic),
+                       (String::from("X"), cstore::NativeFramework),
+                       (String::from("c"), cstore::NativeUnknown)];
+
+        // Change kind
+        v3.libs = vec![(String::from("a"), cstore::NativeStatic),
+                       (String::from("b"), cstore::NativeStatic),
+                       (String::from("c"), cstore::NativeUnknown)];
+
+        assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+        assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_native_libs_tracking_hash_different_order() {
+        let mut v1 = super::basic_options();
+        let mut v2 = super::basic_options();
+        let mut v3 = super::basic_options();
+
+        // Reference
+        v1.libs = vec![(String::from("a"), cstore::NativeStatic),
+                       (String::from("b"), cstore::NativeFramework),
+                       (String::from("c"), cstore::NativeUnknown)];
+
+        v2.libs = vec![(String::from("b"), cstore::NativeFramework),
+                       (String::from("a"), cstore::NativeStatic),
+                       (String::from("c"), cstore::NativeUnknown)];
+
+        v3.libs = vec![(String::from("c"), cstore::NativeUnknown),
+                       (String::from("a"), cstore::NativeStatic),
+                       (String::from("b"), cstore::NativeFramework)];
+
+        assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash());
+        assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash());
+        assert!(v2.dep_tracking_hash() == v3.dep_tracking_hash());
+
+        // Check clone
+        assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+        assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+        assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_codegen_options_tracking_hash() {
+        let reference = super::basic_options();
+        let mut opts = super::basic_options();
+
+        // Make sure the changing an [UNTRACKED] option leaves the hash unchanged
+        opts.cg.ar = Some(String::from("abc"));
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        opts.cg.linker = Some(String::from("linker"));
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        opts.cg.link_args = Some(vec![String::from("abc"), String::from("def")]);
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        opts.cg.link_dead_code = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        opts.cg.rpath = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        opts.cg.extra_filename = String::from("extra-filename");
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        opts.cg.codegen_units = 42;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        opts.cg.remark = super::SomePasses(vec![String::from("pass1"),
+                                                String::from("pass2")]);
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        opts.cg.save_temps = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+
+        // Make sure changing a [TRACKED] option changes the hash
+        opts = reference.clone();
+        opts.cg.lto = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.target_cpu = Some(String::from("abc"));
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.target_feature = String::from("all the features, all of them");
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.passes = vec![String::from("1"), String::from("2")];
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.llvm_args = vec![String::from("1"), String::from("2")];
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.no_prepopulate_passes = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.no_vectorize_loops = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.no_vectorize_slp = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.soft_float = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.prefer_dynamic = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.no_integrated_as = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.no_redzone = Some(true);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.relocation_model = Some(String::from("relocation model"));
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.code_model = Some(String::from("code model"));
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.metadata = vec![String::from("A"), String::from("B")];
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.debuginfo = Some(0xdeadbeef);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.debuginfo = Some(0xba5eba11);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.debug_assertions = Some(true);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.inline_threshold = Some(0xf007ba11);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.cg.panic = PanicStrategy::Abort;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+    }
+
+    #[test]
+    fn test_debugging_options_tracking_hash() {
+        let reference = super::basic_options();
+        let mut opts = super::basic_options();
+
+        // Make sure the changing an [UNTRACKED] option leaves the hash unchanged
+        opts.debugging_opts.verbose = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.time_passes = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.count_llvm_insns = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.time_llvm_passes = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.input_stats = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.trans_stats = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.borrowck_stats = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.debug_llvm = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.meta_stats = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.print_link_args = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.print_llvm_passes = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.ast_json = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.ast_json_noexpand = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.ls = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.save_analysis = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.save_analysis_csv = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.print_move_fragments = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.flowgraph_print_loans = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.flowgraph_print_moves = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.flowgraph_print_assigns = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.flowgraph_print_all = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.print_region_graph = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.parse_only = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.incremental = Some(String::from("abc"));
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.dump_dep_graph = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.query_dep_graph = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.no_analysis = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.unstable_options = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.trace_macros = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.keep_hygiene_data = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.keep_ast = true;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.print_trans_items = Some(String::from("abc"));
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.dump_mir = Some(String::from("abc"));
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.dump_mir_dir = Some(String::from("abc"));
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        opts.debugging_opts.orbit = false;
+        assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+
+        // Make sure changing a [TRACKED] option changes the hash
+        opts = reference.clone();
+        opts.debugging_opts.asm_comments = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.no_verify = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.no_landing_pads = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.no_trans = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.treat_err_as_bug = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.continue_parse_after_error = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.extra_plugins = vec![String::from("plugin1"), String::from("plugin2")];
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.force_overflow_checks = Some(true);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.force_dropflag_checks = Some(true);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.enable_nonzeroing_move_hints = true;
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.show_span = Some(String::from("abc"));
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.mir_opt_level = Some(1);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+    }
 }
index 9ab75c8a5a20c8e17a9c47c3d5c70c18d779ccc0..c71253aee568f9a1e0cd8dcc905557cd7fa7bb20 100644 (file)
@@ -363,7 +363,7 @@ pub fn build_session_with_codemap(sopts: config::Options,
         .map(|&(_, ref level)| *level != lint::Allow)
         .last()
         .unwrap_or(true);
-    let treat_err_as_bug = sopts.treat_err_as_bug;
+    let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug;
 
     let emitter: Box<Emitter> = match sopts.error_format {
         config::ErrorOutputType::HumanReadable(color_config) => {
index 3c6cd26bef6ce87bfc853f040abae95d848a14a2..5bbc6841693ea3628c75c8ebf35988b046485ac1 100644 (file)
@@ -22,7 +22,7 @@ pub struct Iter<'a> {
     iter: slice::Iter<'a, (PathKind, PathBuf)>,
 }
 
-#[derive(Eq, PartialEq, Clone, Copy, Debug)]
+#[derive(Eq, PartialEq, Clone, Copy, Debug, PartialOrd, Ord, Hash)]
 pub enum PathKind {
     Native,
     Crate,
index 70b1261730b07092aca68f7cd721d75bb7a7121f..c6100004786bef8b97f9018cf30d7cddbd71cd49 100644 (file)
@@ -15,7 +15,8 @@
 use rustc_mir as mir;
 use rustc::mir::mir_map::MirMap;
 use rustc::session::{Session, CompileResult, compile_result_from_err_count};
-use rustc::session::config::{self, Input, OutputFilenames, OutputType};
+use rustc::session::config::{self, Input, OutputFilenames, OutputType,
+                             OutputTypes};
 use rustc::session::search_paths::PathKind;
 use rustc::lint;
 use rustc::middle::{self, dependency_format, stability, reachable};
@@ -42,7 +43,6 @@
 
 use serialize::json;
 
-use std::collections::HashMap;
 use std::env;
 use std::ffi::{OsString, OsStr};
 use std::fs;
@@ -478,7 +478,7 @@ pub fn phase_1_parse_input<'a>(sess: &'a Session,
                                cfg: ast::CrateConfig,
                                input: &Input)
                                -> PResult<'a, ast::Crate> {
-    let continue_after_error = sess.opts.continue_parse_after_error;
+    let continue_after_error = sess.opts.debugging_opts.continue_parse_after_error;
     sess.diagnostic().set_continue_after_error(continue_after_error);
 
     let krate = time(sess.time_passes(), "parsing", || {
@@ -667,7 +667,10 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
             trace_mac: sess.opts.debugging_opts.trace_macros,
             should_test: sess.opts.test,
         };
-        let mut loader = macro_import::MacroLoader::new(sess, &cstore, crate_name);
+        let mut loader = macro_import::MacroLoader::new(sess,
+                                                        &cstore,
+                                                        crate_name,
+                                                        krate.config.clone());
         let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess,
                                                       krate.config.clone(),
                                                       cfg,
@@ -1024,11 +1027,10 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
                                trans: &trans::CrateTranslation,
                                outputs: &OutputFilenames) -> CompileResult {
     if sess.opts.cg.no_integrated_as {
-        let mut map = HashMap::new();
-        map.insert(OutputType::Assembly, None);
+        let output_types = OutputTypes::new(&[(OutputType::Assembly, None)]);
         time(sess.time_passes(),
              "LLVM passes",
-             || write::run_passes(sess, trans, &map, outputs));
+             || write::run_passes(sess, trans, &output_types, outputs));
 
         write::run_assembler(sess, outputs);
 
index 98860c8f900eb397a68851509144182c89bc8364..6f57ae2941838d8185afde07b900a4b2aa635a5c 100644 (file)
@@ -181,7 +181,7 @@ macro_rules! do_or_return {($expr: expr, $sess: expr) => {
         None => return (Ok(()), None),
     };
 
-    let sopts = config::build_session_options(&matches);
+    let (sopts, cfg) = config::build_session_options_and_crate_config(&matches);
 
     if sopts.debugging_opts.debug_llvm {
         unsafe { llvm::LLVMRustSetDebug(1); }
@@ -191,6 +191,7 @@ macro_rules! do_or_return {($expr: expr, $sess: expr) => {
 
     do_or_return!(callbacks.early_callback(&matches,
                                            &sopts,
+                                           &cfg,
                                            &descriptions,
                                            sopts.error_format),
                                            None);
@@ -198,7 +199,7 @@ macro_rules! do_or_return {($expr: expr, $sess: expr) => {
     let (odir, ofile) = make_output(&matches);
     let (input, input_file_path) = match make_input(&matches.free) {
         Some((input, input_file_path)) => callbacks.some_input(input, input_file_path),
-        None => match callbacks.no_input(&matches, &sopts, &odir, &ofile, &descriptions) {
+        None => match callbacks.no_input(&matches, &sopts, &cfg, &odir, &ofile, &descriptions) {
             Some((input, input_file_path)) => (input, input_file_path),
             None => return (Ok(()), None),
         },
@@ -214,10 +215,11 @@ macro_rules! do_or_return {($expr: expr, $sess: expr) => {
                                                    cstore.clone(),
                                                    codemap);
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
-    let mut cfg = config::build_configuration(&sess);
+    let mut cfg = config::build_configuration(&sess, cfg);
     target_features::add_configuration(&mut cfg, &sess);
 
-    do_or_return!(callbacks.late_callback(&matches, &sess, &input, &odir, &ofile), Some(sess));
+    do_or_return!(callbacks.late_callback(&matches, &sess, &cfg, &input, &odir, &ofile),
+                  Some(sess));
 
     let plugins = sess.opts.debugging_opts.extra_plugins.clone();
     let control = callbacks.build_controller(&sess, &matches);
@@ -297,6 +299,7 @@ pub trait CompilerCalls<'a> {
     fn early_callback(&mut self,
                       _: &getopts::Matches,
                       _: &config::Options,
+                      _: &ast::CrateConfig,
                       _: &errors::registry::Registry,
                       _: ErrorOutputType)
                       -> Compilation {
@@ -309,6 +312,7 @@ fn early_callback(&mut self,
     fn late_callback(&mut self,
                      _: &getopts::Matches,
                      _: &Session,
+                     _: &ast::CrateConfig,
                      _: &Input,
                      _: &Option<PathBuf>,
                      _: &Option<PathBuf>)
@@ -334,6 +338,7 @@ fn some_input(&mut self,
     fn no_input(&mut self,
                 _: &getopts::Matches,
                 _: &config::Options,
+                _: &ast::CrateConfig,
                 _: &Option<PathBuf>,
                 _: &Option<PathBuf>,
                 _: &errors::registry::Registry)
@@ -375,7 +380,7 @@ fn handle_explain(code: &str,
     }
 }
 
-fn check_cfg(sopts: &config::Options,
+fn check_cfg(cfg: &ast::CrateConfig,
              output: ErrorOutputType) {
     let emitter: Box<Emitter> = match output {
         config::ErrorOutputType::HumanReadable(color_config) => {
@@ -386,7 +391,7 @@ fn check_cfg(sopts: &config::Options,
     let handler = errors::Handler::with_emitter(true, false, emitter);
 
     let mut saw_invalid_predicate = false;
-    for item in sopts.cfg.iter() {
+    for item in cfg.iter() {
         if item.is_meta_item_list() {
             saw_invalid_predicate = true;
             handler.emit(&MultiSpan::new(),
@@ -404,7 +409,8 @@ fn check_cfg(sopts: &config::Options,
 impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
     fn early_callback(&mut self,
                       matches: &getopts::Matches,
-                      sopts: &config::Options,
+                      _: &config::Options,
+                      cfg: &ast::CrateConfig,
                       descriptions: &errors::registry::Registry,
                       output: ErrorOutputType)
                       -> Compilation {
@@ -413,13 +419,14 @@ fn early_callback(&mut self,
             return Compilation::Stop;
         }
 
-        check_cfg(sopts, output);
+        check_cfg(cfg, output);
         Compilation::Continue
     }
 
     fn no_input(&mut self,
                 matches: &getopts::Matches,
                 sopts: &config::Options,
+                cfg: &ast::CrateConfig,
                 odir: &Option<PathBuf>,
                 ofile: &Option<PathBuf>,
                 descriptions: &errors::registry::Registry)
@@ -440,7 +447,13 @@ fn no_input(&mut self,
                     descriptions.clone(),
                     cstore.clone());
                 rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
-                let should_stop = RustcDefaultCalls::print_crate_info(&sess, None, odir, ofile);
+                let mut cfg = config::build_configuration(&sess, cfg.clone());
+                target_features::add_configuration(&mut cfg, &sess);
+                let should_stop = RustcDefaultCalls::print_crate_info(&sess,
+                                                                      &cfg,
+                                                                      None,
+                                                                      odir,
+                                                                      ofile);
                 if should_stop == Compilation::Stop {
                     return None;
                 }
@@ -456,11 +469,12 @@ fn no_input(&mut self,
     fn late_callback(&mut self,
                      matches: &getopts::Matches,
                      sess: &Session,
+                     cfg: &ast::CrateConfig,
                      input: &Input,
                      odir: &Option<PathBuf>,
                      ofile: &Option<PathBuf>)
                      -> Compilation {
-        RustcDefaultCalls::print_crate_info(sess, Some(input), odir, ofile)
+        RustcDefaultCalls::print_crate_info(sess, cfg, Some(input), odir, ofile)
             .and_then(|| RustcDefaultCalls::list_metadata(sess, matches, input))
     }
 
@@ -506,12 +520,14 @@ fn build_controller(&mut self,
             return control;
         }
 
-        if sess.opts.parse_only || sess.opts.debugging_opts.show_span.is_some() ||
+        if sess.opts.debugging_opts.parse_only ||
+           sess.opts.debugging_opts.show_span.is_some() ||
            sess.opts.debugging_opts.ast_json_noexpand {
             control.after_parse.stop = Compilation::Stop;
         }
 
-        if sess.opts.no_analysis || sess.opts.debugging_opts.ast_json {
+        if sess.opts.debugging_opts.no_analysis ||
+           sess.opts.debugging_opts.ast_json {
             control.after_hir_lowering.stop = Compilation::Stop;
         }
 
@@ -577,6 +593,7 @@ pub fn list_metadata(sess: &Session, matches: &getopts::Matches, input: &Input)
 
 
     fn print_crate_info(sess: &Session,
+                        cfg: &ast::CrateConfig,
                         input: Option<&Input>,
                         odir: &Option<PathBuf>,
                         ofile: &Option<PathBuf>)
@@ -629,9 +646,6 @@ fn print_crate_info(sess: &Session,
                     }
                 }
                 PrintRequest::Cfg => {
-                    let mut cfg = config::build_configuration(&sess);
-                    target_features::add_configuration(&mut cfg, &sess);
-
                     let allow_unstable_cfg = match get_unstable_features_setting() {
                         UnstableFeatures::Disallow => false,
                         _ => true,
index 084b6714b67b95544ff0e98fd5b4b70bef1f74f3..89a79d1a487e0f8106d99270c5bec649f9bc6c61 100644 (file)
@@ -158,6 +158,10 @@ pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> DefIdDirectoryBuilder<'a, 'tcx> {
         }
     }
 
+    pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
+        self.tcx
+    }
+
     pub fn add(&mut self, def_id: DefId) -> DefPathIndex {
         debug!("DefIdDirectoryBuilder: def_id={:?}", def_id);
         let tcx = self.tcx;
index 79b90b63dc608d8b238eea13d3b84ac3f890c9d8..c736437df1a9e8b178f36e574bc98e425c128eb8 100644 (file)
@@ -101,8 +101,25 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                   work_products_data: &[u8])
                                   -> Result<(), Error>
 {
+    // Decode the list of work_products
+    let mut work_product_decoder = Decoder::new(work_products_data, 0);
+    let work_products = try!(<Vec<SerializedWorkProduct>>::decode(&mut work_product_decoder));
+
     // Deserialize the directory and dep-graph.
     let mut dep_graph_decoder = Decoder::new(dep_graph_data, 0);
+    let prev_commandline_args_hash = try!(u64::decode(&mut dep_graph_decoder));
+
+    if prev_commandline_args_hash != tcx.sess.opts.dep_tracking_hash() {
+        // We can't reuse the cache, purge it.
+        debug!("decode_dep_graph: differing commandline arg hashes");
+        for swp in work_products {
+            delete_dirty_work_product(tcx, swp);
+        }
+
+        // No need to do any further work
+        return Ok(());
+    }
+
     let directory = try!(DefIdDirectory::decode(&mut dep_graph_decoder));
     let serialized_dep_graph = try!(SerializedDepGraph::decode(&mut dep_graph_decoder));
 
@@ -179,8 +196,6 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     // Add in work-products that are still clean, and delete those that are
     // dirty.
-    let mut work_product_decoder = Decoder::new(work_products_data, 0);
-    let work_products = try!(<Vec<SerializedWorkProduct>>::decode(&mut work_product_decoder));
     reconcile_work_products(tcx, work_products, &dirty_target_nodes);
 
     dirty_clean::check_dirty_clean_annotations(tcx, &dirty_raw_source_nodes, &retraced);
index f296cd3172fb0262ea82d7a877a423d012c9260a..a9523a81fbaf7b9da78a0f11056fde88b61677bb 100644 (file)
@@ -105,6 +105,10 @@ pub fn encode_dep_graph(preds: &Predecessors,
                         builder: &mut DefIdDirectoryBuilder,
                         encoder: &mut Encoder)
                         -> io::Result<()> {
+    // First encode the commandline arguments hash
+    let tcx = builder.tcx();
+    try!(tcx.sess.opts.dep_tracking_hash().encode(encoder));
+
     // Create a flat list of (Input, WorkProduct) edges for
     // serialization.
     let mut edges = vec![];
index 0b60fc386a7bbd8a84d2392fadc448499ae00851..4a656b180f259edc3bd05a49dd4e36f304e71327 100644 (file)
@@ -56,6 +56,7 @@ pub struct CrateReader<'a> {
     next_crate_num: ast::CrateNum,
     foreign_item_map: FnvHashMap<String, Vec<ast::NodeId>>,
     local_crate_name: String,
+    local_crate_config: ast::CrateConfig,
 }
 
 impl<'a> visit::Visitor for LocalCrateReader<'a> {
@@ -152,13 +153,16 @@ enum LoadResult {
 impl<'a> CrateReader<'a> {
     pub fn new(sess: &'a Session,
                cstore: &'a CStore,
-               local_crate_name: &str) -> CrateReader<'a> {
+               local_crate_name: &str,
+               local_crate_config: ast::CrateConfig)
+               -> CrateReader<'a> {
         CrateReader {
             sess: sess,
             cstore: cstore,
             next_crate_num: cstore.next_crate_num(),
             foreign_item_map: FnvHashMap(),
             local_crate_name: local_crate_name.to_owned(),
+            local_crate_config: local_crate_config,
         }
     }
 
@@ -561,7 +565,7 @@ pub fn read_exported_macros(&mut self, item: &ast::Item) -> Vec<ast::MacroDef> {
                 // NB: Don't use parse::parse_tts_from_source_str because it parses with
                 // quote_depth > 0.
                 let mut p = parse::new_parser_from_source_str(&self.sess.parse_sess,
-                                                              self.sess.opts.cfg.clone(),
+                                                              self.local_crate_config.clone(),
                                                               source_name.clone(),
                                                               body);
                 let lo = p.span.lo;
@@ -863,7 +867,7 @@ fn new(sess: &'a Session,
         LocalCrateReader {
             sess: sess,
             cstore: cstore,
-            creader: CrateReader::new(sess, cstore, local_crate_name),
+            creader: CrateReader::new(sess, cstore, local_crate_name, krate.config.clone()),
             krate: krate,
             definitions: defs,
         }
index cf1dd71a0a12acbc8477f37438d67647dd135f13..2345cd9a92aea2792c0cc77cedc90726370fb5c9 100644 (file)
@@ -400,7 +400,7 @@ fn find_library_crate(&mut self) -> Option<Library> {
         if self.hash.is_none() {
             self.should_match_name = false;
             if let Some(s) = self.sess.opts.externs.get(self.crate_name) {
-                return self.find_commandline_library(s);
+                return self.find_commandline_library(s.iter());
             }
             self.should_match_name = true;
         }
@@ -661,7 +661,9 @@ fn staticlibname(&self) -> (String, String) {
         (t.options.staticlib_prefix.clone(), t.options.staticlib_suffix.clone())
     }
 
-    fn find_commandline_library(&mut self, locs: &[String]) -> Option<Library> {
+    fn find_commandline_library<'b, LOCS> (&mut self, locs: LOCS) -> Option<Library>
+        where LOCS: Iterator<Item=&'b String>
+    {
         // First, filter out all libraries that look suspicious. We only accept
         // files which actually exist that have the correct naming scheme for
         // rlibs/dylibs.
@@ -670,7 +672,7 @@ fn find_commandline_library(&mut self, locs: &[String]) -> Option<Library> {
         let mut rlibs = HashMap::new();
         let mut dylibs = HashMap::new();
         {
-            let locs = locs.iter().map(|l| PathBuf::from(l)).filter(|loc| {
+            let locs = locs.map(|l| PathBuf::from(l)).filter(|loc| {
                 if !loc.exists() {
                     sess.err(&format!("extern location for {} does not exist: {}",
                                      self.crate_name, loc.display()));
index 4be044c1df30720770e913e41b70d10328937a77..b2a2dcf90fa4b65fe4f3a1f981ede966b79e702a 100644 (file)
@@ -29,10 +29,14 @@ pub struct MacroLoader<'a> {
 }
 
 impl<'a> MacroLoader<'a> {
-    pub fn new(sess: &'a Session, cstore: &'a CStore, crate_name: &str) -> MacroLoader<'a> {
+    pub fn new(sess: &'a Session,
+               cstore: &'a CStore,
+               crate_name: &str,
+               crate_config: ast::CrateConfig)
+               -> MacroLoader<'a> {
         MacroLoader {
             sess: sess,
-            reader: CrateReader::new(sess, cstore, crate_name),
+            reader: CrateReader::new(sess, cstore, crate_name, crate_config),
         }
     }
 }
index a3cd9b5da02bc3b2df506cc166614784826529d5..fb68eae96476f718d3bd19976e80ddb989b3952a 100644 (file)
@@ -49,7 +49,7 @@ pub fn load_plugins(sess: &Session,
                     krate: &ast::Crate,
                     crate_name: &str,
                     addl_plugins: Option<Vec<String>>) -> Vec<PluginRegistrar> {
-    let mut loader = PluginLoader::new(sess, cstore, crate_name);
+    let mut loader = PluginLoader::new(sess, cstore, crate_name, krate.config.clone());
 
     // do not report any error now. since crate attributes are
     // not touched by expansion, every use of plugin without
@@ -90,10 +90,14 @@ pub fn load_plugins(sess: &Session,
 }
 
 impl<'a> PluginLoader<'a> {
-    fn new(sess: &'a Session, cstore: &'a CStore, crate_name: &str) -> PluginLoader<'a> {
+    fn new(sess: &'a Session,
+           cstore: &'a CStore,
+           crate_name: &str,
+           crate_config: ast::CrateConfig)
+            -> PluginLoader<'a> {
         PluginLoader {
             sess: sess,
-            reader: CrateReader::new(sess, cstore, crate_name),
+            reader: CrateReader::new(sess, cstore, crate_name, crate_config),
             plugins: vec![],
         }
     }
index a9f3d2f8a175485b954c016e6d2e207a7c4084ff..f17d1a7f1cf31bd04aae8fe593632bef4f97edc0 100644 (file)
@@ -190,7 +190,8 @@ pub fn link_binary(sess: &Session,
     let mut out_filenames = Vec::new();
     for &crate_type in sess.crate_types.borrow().iter() {
         // Ignore executable crates if we have -Z no-trans, as they will error.
-        if sess.opts.no_trans && crate_type == config::CrateTypeExecutable {
+        if sess.opts.debugging_opts.no_trans &&
+           crate_type == config::CrateTypeExecutable {
             continue;
         }
 
index b5c993b86ecb2e0c09d015f9d93117ec02534b60..4b9c29d3d7db3a57b4f17063464315143175d6fb 100644 (file)
@@ -11,7 +11,7 @@
 use back::lto;
 use back::link::{get_linker, remove};
 use rustc_incremental::save_trans_partition;
-use session::config::{OutputFilenames, Passes, SomePasses, AllPasses};
+use session::config::{OutputFilenames, OutputTypes, Passes, SomePasses, AllPasses};
 use session::Session;
 use session::config::{self, OutputType};
 use llvm;
@@ -26,7 +26,6 @@
 use syntax_pos::MultiSpan;
 use context::{is_pie_binary, get_reloc_model};
 
-use std::collections::HashMap;
 use std::ffi::{CStr, CString};
 use std::fs;
 use std::path::{Path, PathBuf};
@@ -641,7 +640,7 @@ pub fn cleanup_llvm(trans: &CrateTranslation) {
 
 pub fn run_passes(sess: &Session,
                   trans: &CrateTranslation,
-                  output_types: &HashMap<OutputType, Option<PathBuf>>,
+                  output_types: &OutputTypes,
                   crate_output: &OutputFilenames) {
     // It's possible that we have `codegen_units > 1` but only one item in
     // `trans.modules`.  We could theoretically proceed and do LTO in that
index df893842337c47a36ff4ab851ea951d550737420..30618ff37273569ba6251f5780d76ae3dae1ad51 100644 (file)
@@ -2576,7 +2576,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     assert_module_sources::assert_module_sources(tcx, &modules);
 
     // Skip crate items and just output metadata in -Z no-trans mode.
-    if tcx.sess.opts.no_trans {
+    if tcx.sess.opts.debugging_opts.no_trans {
         let linker_info = LinkerInfo::new(&shared_ccx, &[]);
         return CrateTranslation {
             modules: modules,
index 7d8ebc403b08f1c5cc59f050a0df042ce42a5650..10736d2c827cdf8f59a207156332bd1a0e81886f 100644 (file)
@@ -45,7 +45,6 @@ pub enum MaybeTyped<'a, 'tcx: 'a> {
     NotTyped(&'a session::Session)
 }
 
-pub type Externs = HashMap<String, Vec<String>>;
 pub type ExternalPaths = HashMap<DefId, (Vec<String>, clean::TypeKind)>;
 
 pub struct DocContext<'a, 'tcx: 'a> {
@@ -99,7 +98,7 @@ fn is_doc_reachable(&self, did: DefId) -> bool {
 
 pub fn run_core(search_paths: SearchPaths,
                 cfgs: Vec<String>,
-                externs: Externs,
+                externs: config::Externs,
                 input: Input,
                 triple: Option<String>) -> (clean::Crate, RenderInfo)
 {
@@ -120,7 +119,6 @@ pub fn run_core(search_paths: SearchPaths,
         lint_cap: Some(lint::Allow),
         externs: externs,
         target_triple: triple.unwrap_or(config::host_triple().to_string()),
-        cfg: config::parse_cfgspecs(cfgs),
         // Ensure that rustdoc works even if rustc is feature-staged
         unstable_features: UnstableFeatures::Allow,
         ..config::basic_options().clone()
@@ -139,7 +137,7 @@ pub fn run_core(search_paths: SearchPaths,
                                        codemap, cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
-    let mut cfg = config::build_configuration(&sess);
+    let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs));
     target_features::add_configuration(&mut cfg, &sess);
 
     let krate = panictry!(driver::phase_1_parse_input(&sess, cfg, &input));
index d0c4f126550ca177356b4032497309372d3cb436..255e6b1e786dfd38f621eaee0d7d318a6519a45e 100644 (file)
@@ -51,7 +51,7 @@
 
 extern crate serialize as rustc_serialize; // used by deriving
 
-use std::collections::HashMap;
+use std::collections::{BTreeMap, BTreeSet};
 use std::default::Default;
 use std::env;
 use std::path::PathBuf;
@@ -60,7 +60,8 @@
 
 use externalfiles::ExternalHtml;
 use rustc::session::search_paths::SearchPaths;
-use rustc::session::config::{ErrorOutputType, RustcOptGroup, nightly_options};
+use rustc::session::config::{ErrorOutputType, RustcOptGroup, nightly_options,
+                             Externs};
 
 #[macro_use]
 pub mod externalfiles;
@@ -323,7 +324,7 @@ pub fn main_args(args: &[String]) -> isize {
 /// Looks inside the command line arguments to extract the relevant input format
 /// and files and then generates the necessary rustdoc output for formatting.
 fn acquire_input(input: &str,
-                 externs: core::Externs,
+                 externs: Externs,
                  matches: &getopts::Matches) -> Result<Output, String> {
     match matches.opt_str("r").as_ref().map(|s| &**s) {
         Some("rust") => Ok(rust_input(input, externs, matches)),
@@ -335,10 +336,10 @@ fn acquire_input(input: &str,
 }
 
 /// Extracts `--extern CRATE=PATH` arguments from `matches` and
-/// returns a `HashMap` mapping crate names to their paths or else an
+/// returns a map mapping crate names to their paths or else an
 /// error message.
-fn parse_externs(matches: &getopts::Matches) -> Result<core::Externs, String> {
-    let mut externs = HashMap::new();
+fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
+    let mut externs = BTreeMap::new();
     for arg in &matches.opt_strs("extern") {
         let mut parts = arg.splitn(2, '=');
         let name = parts.next().ok_or("--extern value must not be empty".to_string())?;
@@ -346,9 +347,9 @@ fn parse_externs(matches: &getopts::Matches) -> Result<core::Externs, String> {
                                  .ok_or("--extern value must be of the format `foo=bar`"
                                     .to_string())?;
         let name = name.to_string();
-        externs.entry(name).or_insert(vec![]).push(location.to_string());
+        externs.entry(name).or_insert_with(BTreeSet::new).insert(location.to_string());
     }
-    Ok(externs)
+    Ok(Externs::new(externs))
 }
 
 /// Interprets the input file as a rust source file, passing it through the
@@ -356,7 +357,7 @@ fn parse_externs(matches: &getopts::Matches) -> Result<core::Externs, String> {
 /// generated from the cleaned AST of the crate.
 ///
 /// This form of input will run all of the plug/cleaning passes
-fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matches) -> Output {
+fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) -> Output {
     let mut default_passes = !matches.opt_present("no-defaults");
     let mut passes = matches.opt_strs("passes");
     let mut plugins = matches.opt_strs("plugins");
index d21726dd40f08195ec366b989025a38c5b47b331..1421a3c78fc5a40a7c4803b2cc21ff73677cc4c4 100644 (file)
 use std::io;
 use std::path::{PathBuf, Path};
 
-use core;
 use getopts;
 use testing;
 use rustc::session::search_paths::SearchPaths;
+use rustc::session::config::Externs;
 
 use externalfiles::ExternalHtml;
 
@@ -142,7 +142,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
 }
 
 /// Run any tests/code examples in the markdown file `input`.
-pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: core::Externs,
+pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
             mut test_args: Vec<String>) -> isize {
     let input_str = load_or_return!(input, 1, 2);
 
index 5f1d28c8d316ad35014bd2f4a56f124449a6fdfc..7d1dbbe5dc07d9d645656ff7cb35bf1d6165ed23 100644 (file)
@@ -26,7 +26,8 @@
 use rustc::dep_graph::DepGraph;
 use rustc::hir::map as hir_map;
 use rustc::session::{self, config};
-use rustc::session::config::{get_unstable_features_setting, OutputType};
+use rustc::session::config::{get_unstable_features_setting, OutputType,
+                             OutputTypes, Externs};
 use rustc::session::search_paths::{SearchPaths, PathKind};
 use rustc_back::dynamic_lib::DynamicLibrary;
 use rustc_back::tempdir::TempDir;
@@ -55,7 +56,7 @@ pub struct TestOptions {
 pub fn run(input: &str,
            cfgs: Vec<String>,
            libs: SearchPaths,
-           externs: core::Externs,
+           externs: Externs,
            mut test_args: Vec<String>,
            crate_name: Option<String>)
            -> isize {
@@ -89,8 +90,7 @@ pub fn run(input: &str,
                                        cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
-    let mut cfg = config::build_configuration(&sess);
-    cfg.extend(config::parse_cfgspecs(cfgs.clone()));
+    let cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone()));
     let krate = panictry!(driver::phase_1_parse_input(&sess, cfg, &input));
     let driver::ExpansionResult { defs, mut hir_forest, .. } = {
         phase_2_configure_and_expand(
@@ -172,7 +172,7 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
 }
 
 fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
-           externs: core::Externs,
+           externs: Externs,
            should_panic: bool, no_run: bool, as_test_harness: bool,
            compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions) {
     // the test harness wants its own `main` & top level functions, so
@@ -182,8 +182,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
         name: driver::anon_src(),
         input: test.to_owned(),
     };
-    let mut outputs = HashMap::new();
-    outputs.insert(OutputType::Exe, None);
+    let outputs = OutputTypes::new(&[(OutputType::Exe, None)]);
 
     let sessopts = config::Options {
         maybe_sysroot: Some(env::current_exe().unwrap().parent().unwrap()
@@ -247,8 +246,7 @@ fn drop(&mut self) {
     let outdir = Mutex::new(TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir"));
     let libdir = sess.target_filesearch(PathKind::All).get_lib_path();
     let mut control = driver::CompileController::basic();
-    let mut cfg = config::build_configuration(&sess);
-    cfg.extend(config::parse_cfgspecs(cfgs.clone()));
+    let cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone()));
     let out = Some(outdir.lock().unwrap().path().to_path_buf());
 
     if no_run {
@@ -396,7 +394,7 @@ pub struct Collector {
     names: Vec<String>,
     cfgs: Vec<String>,
     libs: SearchPaths,
-    externs: core::Externs,
+    externs: Externs,
     cnt: usize,
     use_headers: bool,
     current_header: Option<String>,
@@ -405,7 +403,7 @@ pub struct Collector {
 }
 
 impl Collector {
-    pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: core::Externs,
+    pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
                use_headers: bool, opts: TestOptions) -> Collector {
         Collector {
             tests: Vec::new(),
index afda59c61ff76246b99a2bdc2458d07761d2ebfb..a6f0e0ca31e3e4fa94dec928990daab430ac29df 100644 (file)
@@ -1194,7 +1194,7 @@ pub fn check_crate(krate: &ast::Crate,
     visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate);
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub enum UnstableFeatures {
     /// Hard errors for unstable features are active, as on
     /// beta/stable channels.
diff --git a/src/test/incremental/commandline-args.rs b/src/test/incremental/commandline-args.rs
new file mode 100644 (file)
index 0000000..95187b8
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that changing a tracked commandline argument invalidates
+// the cache while changing an untracked one doesn't.
+
+// revisions:rpass1 rpass2 rpass3
+
+#![feature(rustc_attrs)]
+
+#![rustc_partition_translated(module="commandline_args", cfg="rpass2")]
+#![rustc_partition_reused(module="commandline_args", cfg="rpass3")]
+
+// Between revisions 1 and 2, we are changing the debuginfo-level, which should
+// invalidate the cache. Between revisions 2 and 3, we are adding `--verbose`
+// which should have no effect on the cache:
+//[rpass1] compile-flags: -C debuginfo=0
+//[rpass2] compile-flags: -C debuginfo=2
+//[rpass3] compile-flags: -C debuginfo=2 --verbose
+
+pub fn main() {
+    // empty
+}
index d5220316a20ff083381235e0bd499cff04721629..35043bdaddf21fcae3dccae36c4283939a494fec 100644 (file)
@@ -19,7 +19,8 @@
 
 use rustc::dep_graph::DepGraph;
 use rustc::session::{build_session, Session};
-use rustc::session::config::{basic_options, build_configuration, Input, OutputType};
+use rustc::session::config::{basic_options, build_configuration, Input,
+                             OutputType, OutputTypes};
 use rustc_driver::driver::{compile_input, CompileController, anon_src};
 use rustc_metadata::cstore::CStore;
 use rustc_errors::registry::Registry;
@@ -51,7 +52,7 @@ fn main() {}
 
 fn basic_sess(sysroot: PathBuf) -> (Session, Rc<CStore>) {
     let mut opts = basic_options();
-    opts.output_types.insert(OutputType::Exe, None);
+    opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
     opts.maybe_sysroot = Some(sysroot);
 
     let descriptions = Registry::new(&rustc::DIAGNOSTICS);
@@ -64,7 +65,7 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc<CStore>) {
 
 fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
     let (sess, cstore) = basic_sess(sysroot);
-    let cfg = build_configuration(&sess);
+    let cfg = build_configuration(&sess, vec![]);
     let control = CompileController::basic();
 
     compile_input(&sess, &cstore,
index ff57e9d6b73684dd6190dff73980e8d472e0a853..775ba38004e3a195375194b4a3b48e5993449549 100644 (file)
@@ -24,6 +24,7 @@
 use rustc::session::Session;
 use rustc::session::config::{self, Input};
 use rustc_driver::{driver, CompilerCalls, Compilation};
+use syntax::ast;
 
 use std::path::PathBuf;
 
@@ -35,6 +36,7 @@ impl<'a> CompilerCalls<'a> for TestCalls {
     fn early_callback(&mut self,
                       _: &getopts::Matches,
                       _: &config::Options,
+                      _: &ast::CrateConfig,
                       _: &errors::registry::Registry,
                       _: config::ErrorOutputType)
                       -> Compilation {
@@ -45,6 +47,7 @@ fn early_callback(&mut self,
     fn late_callback(&mut self,
                      _: &getopts::Matches,
                      _: &Session,
+                     _: &ast::CrateConfig,
                      _: &Input,
                      _: &Option<PathBuf>,
                      _: &Option<PathBuf>)
@@ -62,6 +65,7 @@ fn some_input(&mut self, input: Input, input_path: Option<PathBuf>)
     fn no_input(&mut self,
                 _: &getopts::Matches,
                 _: &config::Options,
+                _: &ast::CrateConfig,
                 _: &Option<PathBuf>,
                 _: &Option<PathBuf>,
                 _: &errors::registry::Registry)
index e9ccc029bc3cf4ee0efcfbef5d64ab94882edc54..0d081b267bb7084301f001f4a3f61861b8b7ddf3 100644 (file)
@@ -2008,6 +2008,7 @@ fn run_incremental_test(&self) {
         // Add an extra flag pointing at the incremental directory.
         let mut revision_props = self.props.clone();
         revision_props.incremental_dir = Some(incremental_dir);
+        revision_props.compile_flags.push(String::from("-Zincremental-info"));
 
         let revision_cx = TestCx {
             config: self.config,