diff --git a/src/cargo/core/compiler/build_context/mod.rs b/src/cargo/core/compiler/build_context/mod.rs
index 18956632152..7eab9dea45f 100644
--- a/src/cargo/core/compiler/build_context/mod.rs
+++ b/src/cargo/core/compiler/build_context/mod.rs
@@ -16,6 +16,11 @@ mod target_info;
 pub use self::target_info::{FileFlavor, TargetInfo};
 
 /// The build context, containing all information about a build task.
+///
+/// It is intended that this is mostly static information. Stuff that mutates
+/// during the build can be found in the parent `Context`. (I say mostly,
+/// because this has internal caching, but nothing that should be observable
+/// or require &mut.)
 pub struct BuildContext<'a, 'cfg> {
     /// The workspace the build is for.
     pub ws: &'a Workspace<'cfg>,
@@ -183,6 +188,17 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> {
     pub fn extra_args_for(&self, unit: &Unit<'a>) -> Option<&Vec<String>> {
         self.extra_compiler_args.get(unit)
     }
+
+    /// If a build script is overridden, this returns the `BuildOutput` to use.
+    ///
+    /// `lib_name` is the `links` library name and `kind` is whether it is for
+    /// Host or Target.
+    pub fn script_override(&self, lib_name: &str, kind: Kind) -> Option<&BuildOutput> {
+        match kind {
+            Kind::Host => self.host_config.overrides.get(lib_name),
+            Kind::Target => self.target_config.overrides.get(lib_name),
+        }
+    }
 }
 
 /// Information required to build for a target.
@@ -192,7 +208,11 @@ pub struct TargetConfig {
     pub ar: Option<PathBuf>,
     /// The path of the linker for this target.
     pub linker: Option<PathBuf>,
-    /// Special build options for any necessary input files (filename -> options).
+    /// Build script override for the given library name.
+    ///
+    /// Any package with a `links` value for the given library name will skip
+    /// running its build script and instead use the given output from the
+    /// config file.
     pub overrides: HashMap<String, BuildOutput>,
 }
 
diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs
index 7ab5f5b74b1..47f03308cf4 100644
--- a/src/cargo/core/compiler/context/mod.rs
+++ b/src/cargo/core/compiler/context/mod.rs
@@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
 use std::ffi::OsStr;
 use std::fmt::Write;
 use std::path::PathBuf;
-use std::sync::Arc;
+use std::sync::{Arc, Mutex};
 
 use filetime::FileTime;
 use jobserver::Client;
@@ -15,7 +15,7 @@ use crate::util::errors::{CargoResult, CargoResultExt};
 use crate::util::{internal, profile, Config};
 
 use super::build_plan::BuildPlan;
-use super::custom_build::{self, BuildDeps, BuildScripts, BuildState};
+use super::custom_build::{self, BuildDeps, BuildScriptOutputs, BuildScripts};
 use super::fingerprint::Fingerprint;
 use super::job_queue::JobQueue;
 use super::layout::Layout;
@@ -28,21 +28,45 @@ mod compilation_files;
 use self::compilation_files::CompilationFiles;
 pub use self::compilation_files::{Metadata, OutputFile};
 
+/// Collection of all the stuff that is needed to perform a build.
 pub struct Context<'a, 'cfg> {
+    /// Mostly static information about the build task.
     pub bcx: &'a BuildContext<'a, 'cfg>,
+    /// A large collection of information about the result of the entire compilation.
     pub compilation: Compilation<'cfg>,
-    pub build_state: Arc<BuildState>,
-    pub build_script_overridden: HashSet<(PackageId, Kind)>,
+    /// Output from build scripts, updated after each build script runs.
+    pub build_script_outputs: Arc<Mutex<BuildScriptOutputs>>,
+    /// Dependencies (like rerun-if-changed) declared by a build script.
+    /// This is *only* populated from the output from previous runs.
+    /// If the build script hasn't ever been run, then it must be run.
     pub build_explicit_deps: HashMap<Unit<'a>, BuildDeps>,
+    /// Fingerprints used to detect if a unit is out-of-date.
     pub fingerprints: HashMap<Unit<'a>, Arc<Fingerprint>>,
+    /// Cache of file mtimes to reduce filesystem hits.
     pub mtime_cache: HashMap<PathBuf, FileTime>,
+    /// A set used to track which units have been compiled.
+    /// A unit may appear in the job graph multiple times as a dependency of
+    /// multiple packages, but it only needs to run once.
     pub compiled: HashSet<Unit<'a>>,
+    /// Linking information for each `Unit`.
+    /// See `build_map` for details.
     pub build_scripts: HashMap<Unit<'a>, Arc<BuildScripts>>,
+    /// Used to check the `links` field in the manifest is not duplicated and
+    /// is used correctly.
     pub links: Links,
+    /// Job server client to manage concurrency with other processes.
     pub jobserver: Client,
+    /// "Primary" packages are the ones the user selected on the command-line
+    /// with `-p` flags. If no flags are specified, then it is the defaults
+    /// based on the current directory and the default workspace members.
     primary_packages: HashSet<PackageId>,
+    /// The dependency graph of units to compile.
     unit_dependencies: HashMap<Unit<'a>, Vec<Unit<'a>>>,
+    /// An abstraction of the files and directories that will be generated by
+    /// the compilation. This is `None` until after `unit_dependencies` has
+    /// been computed.
     files: Option<CompilationFiles<'a, 'cfg>>,
+    /// Cache of packages, populated when they are downloaded.
     package_cache: HashMap<PackageId, &'a Package>,
 
     /// A flag indicating whether pipelining is enabled for this compilation
@@ -82,7 +106,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         Ok(Self {
             bcx,
             compilation: Compilation::new(bcx)?,
-            build_state: Arc::new(BuildState::new(&bcx.host_config, &bcx.target_config)),
+            build_script_outputs: Arc::new(Mutex::new(BuildScriptOutputs::default())),
             fingerprints: HashMap::new(),
             mtime_cache: HashMap::new(),
             compiled: HashSet::new(),
@@ -90,8 +114,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
             build_explicit_deps: HashMap::new(),
             links: Links::new(),
             jobserver,
-            build_script_overridden: HashSet::new(),
-
             primary_packages: HashSet::new(),
             unit_dependencies: HashMap::new(),
             files: None,
@@ -228,7 +250,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
             super::output_depinfo(&mut self, unit)?;
         }
 
-        for (&(ref pkg, _), output) in self.build_state.outputs.lock().unwrap().iter() {
+        for (&(ref pkg, _), output) in self.build_script_outputs.lock().unwrap().iter() {
             self.compilation
                 .cfgs
                 .entry(pkg.clone())
@@ -338,22 +360,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
     //
     // TODO: this ideally should be `-> &[Unit<'a>]`.
     pub fn dep_targets(&self, unit: &Unit<'a>) -> Vec<Unit<'a>> {
-        // If this build script's execution has been overridden then we don't
-        // actually depend on anything, we've reached the end of the dependency
-        // chain as we've got all the info we're gonna get.
-        //
-        // Note there's a subtlety about this piece of code! The
-        // `build_script_overridden` map here is populated in
-        // `custom_build::build_map` which you need to call before inspecting
-        // dependencies. However, that code itself calls this method and
-        // gets a full pre-filtered set of dependencies. This is not super
-        // obvious, and clear, but it does work at the moment.
-        if unit.target.is_custom_build() {
-            let key = (unit.pkg.package_id(), unit.kind);
-            if self.build_script_overridden.contains(&key) {
-                return Vec::new();
-            }
-        }
         self.unit_dependencies[unit].clone()
     }
 
diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs
index 6e80f31a195..5cb41e6385d 100644
--- a/src/cargo/core/compiler/context/unit_dependencies.rs
+++ b/src/cargo/core/compiler/context/unit_dependencies.rs
@@ -258,6 +258,13 @@ fn compute_deps_custom_build<'a, 'cfg>(
     unit: &Unit<'a>,
     bcx: &BuildContext<'a, 'cfg>,
 ) -> CargoResult<Vec<(Unit<'a>, UnitFor)>> {
+    if let Some(links) = unit.pkg.manifest().links() {
+        if bcx.script_override(links, unit.kind).is_some() {
+            // Overridden build scripts don't have any dependencies.
+            return Ok(Vec::new());
+        }
+    }
+
     // When not overridden, then the dependencies to run a build script are:
     //
     // 1. Compiling the build script itself.
diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs
index 7978d1d480e..db621aa7eb8 100644
--- a/src/cargo/core/compiler/custom_build.rs
+++ b/src/cargo/core/compiler/custom_build.rs
@@ -3,7 +3,7 @@ use std::collections::{BTreeSet, HashSet};
 use std::fs;
 use std::path::{Path, PathBuf};
 use std::str;
-use std::sync::{Arc, Mutex};
+use std::sync::Arc;
 
 use crate::core::compiler::job_queue::JobState;
 use crate::core::PackageId;
@@ -13,7 +13,7 @@ use crate::util::Cfg;
 use crate::util::{self, internal, paths, profile};
 
 use super::job::{Freshness, Job, Work};
-use super::{fingerprint, Context, Kind, TargetConfig, Unit};
+use super::{fingerprint, Context, Kind, Unit};
 
 /// Contains the parsed output of a custom build script.
 #[derive(Clone, Debug, Hash)]
@@ -39,48 +39,57 @@ pub struct BuildOutput {
     pub warnings: Vec<String>,
 }
 
-/// Map of packages to build info.
-pub type BuildMap = HashMap<(PackageId, Kind), BuildOutput>;
-
-/// Build info and overrides.
-pub struct BuildState {
-    pub outputs: Mutex<BuildMap>,
-    overrides: HashMap<(String, Kind), BuildOutput>,
-}
+/// Map of packages to build script output.
+///
+/// This initially starts out as empty. Overridden build scripts get
+/// inserted during `build_map`. The rest of the entries are added
+/// immediately after each build script runs.
+pub type BuildScriptOutputs = HashMap<(PackageId, Kind), BuildOutput>;
 
+/// Linking information for a `Unit`.
+///
+/// See `build_map` for more details.
 #[derive(Default)]
 pub struct BuildScripts {
-    // Cargo will use this `to_link` vector to add `-L` flags to compiles as we
-    // propagate them upwards towards the final build. Note, however, that we
-    // need to preserve the ordering of `to_link` to be topologically sorted.
-    // This will ensure that build scripts which print their paths properly will
-    // correctly pick up the files they generated (if there are duplicates
-    // elsewhere).
-    //
-    // To preserve this ordering, the (id, kind) is stored in two places, once
-    // in the `Vec` and once in `seen_to_link` for a fast lookup. We maintain
-    // this as we're building interactively below to ensure that the memory
-    // usage here doesn't blow up too much.
-    //
-    // For more information, see #2354.
+    /// Cargo will use this `to_link` vector to add `-L` flags to compiles as we
+    /// propagate them upwards towards the final build. Note, however, that we
+    /// need to preserve the ordering of `to_link` to be topologically sorted.
+    /// This will ensure that build scripts which print their paths properly will
+    /// correctly pick up the files they generated (if there are duplicates
+    /// elsewhere).
+    ///
+    /// To preserve this ordering, the (id, kind) is stored in two places, once
+    /// in the `Vec` and once in `seen_to_link` for a fast lookup. We maintain
+    /// this as we're building interactively below to ensure that the memory
+    /// usage here doesn't blow up too much.
+    ///
+    /// For more information, see #2354.
     pub to_link: Vec<(PackageId, Kind)>,
+    /// This is only used while constructing `to_link` to avoid duplicates.
     seen_to_link: HashSet<(PackageId, Kind)>,
+    /// Host-only dependencies that have build scripts.
+    ///
+    /// This is the set of transitive dependencies that are host-only
+    /// (proc-macro, plugin, build-dependency) that contain a build script.
+    /// Any `BuildOutput::library_paths` path relative to `target` will be
+    /// added to LD_LIBRARY_PATH so that the compiler can find any dynamic
+    /// libraries a build script may have generated.
     pub plugins: BTreeSet<PackageId>,
 }
 
+/// Dependency information as declared by a build script.
 #[derive(Debug)]
 pub struct BuildDeps {
+    /// Absolute path to the file in the target directory that stores the
+    /// output of the build script.
     pub build_script_output: PathBuf,
+    /// Files that trigger a rebuild if they change.
     pub rerun_if_changed: Vec<PathBuf>,
+    /// Environment variables that trigger a rebuild if they change.
     pub rerun_if_env_changed: Vec<String>,
 }
 
 /// Prepares a `Work` that executes the target as a custom build script.
-///
-/// The `req` given is the requirement which this run of the build script will
-/// prepare work for. If the requirement is specified as both the target and the
-/// host platforms it is assumed that the two are equal and the build script is
-/// only run once (not twice).
 pub fn prepare<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<Job> {
     let _p = profile::start(format!(
         "build script prepare: {}/{}",
@@ -90,7 +99,8 @@ pub fn prepare<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRe
 
     let key = (unit.pkg.package_id(), unit.kind);
 
-    if cx.build_script_overridden.contains(&key) {
+    if cx.build_script_outputs.lock().unwrap().contains_key(&key) {
+        // The output is already set, thus the build script is overridden.
         fingerprint::prepare_target(cx, unit, false)
     } else {
         build_work(cx, unit)
@@ -233,7 +243,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
             .collect::<Vec<_>>()
     };
     let pkg_name = unit.pkg.to_string();
-    let build_state = Arc::clone(&cx.build_state);
+    let build_script_outputs = Arc::clone(&cx.build_script_outputs);
     let id = unit.pkg.package_id();
     let output_file = script_run_dir.join("output");
     let err_file = script_run_dir.join("stderr");
@@ -242,11 +252,11 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
     let all = (
         id,
         pkg_name.clone(),
-        Arc::clone(&build_state),
+        Arc::clone(&build_script_outputs),
         output_file.clone(),
         script_out_dir.clone(),
     );
-    let build_scripts = super::load_build_deps(cx, unit);
+    let build_scripts = cx.build_scripts.get(unit).cloned();
     let kind = unit.kind;
     let json_messages = bcx.build_config.emit_json();
     let extra_verbose = bcx.config.extra_verbose();
@@ -279,17 +289,17 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
         // dynamic library search path in case the build script depended on any
         // native dynamic libraries.
         if !build_plan {
-            let build_state = build_state.outputs.lock().unwrap();
+            let build_script_outputs = build_script_outputs.lock().unwrap();
             for (name, id) in lib_deps {
                 let key = (id, kind);
-                let state = build_state.get(&key).ok_or_else(|| {
+                let script_output = build_script_outputs.get(&key).ok_or_else(|| {
                     internal(format!(
                         "failed to locate build state for env \
                          vars: {}/{:?}",
                         id, kind
                     ))
                 })?;
-                let data = &state.metadata;
+                let data = &script_output.metadata;
                 for &(ref key, ref value) in data.iter() {
                     cmd.env(
                         &format!("DEP_{}_{}", super::envify(&name), super::envify(key)),
@@ -298,7 +308,12 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
                 }
             }
             if let Some(build_scripts) = build_scripts {
-                super::add_plugin_deps(&mut cmd, &build_state, &build_scripts, &host_target_root)?;
+                super::add_plugin_deps(
+                    &mut cmd,
+                    &build_script_outputs,
+                    &build_scripts,
+                    &host_target_root,
+                )?;
             }
         }
 
@@ -346,7 +361,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
         if json_messages {
             emit_build_output(state, &parsed_output, id);
         }
-        build_state.insert(id, kind, parsed_output);
+        build_script_outputs
+            .lock()
+            .unwrap()
+            .insert((id, kind), parsed_output);
         Ok(())
     });
 
@@ -354,7 +372,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
     // itself to run when we actually end up just discarding what we calculated
     // above.
     let fresh = Work::new(move |state| {
-        let (id, pkg_name, build_state, output_file, script_out_dir) = all;
+        let (id, pkg_name, build_script_outputs, output_file, script_out_dir) = all;
         let output = match prev_output {
             Some(output) => output,
             None => BuildOutput::parse_file(
@@ -369,7 +387,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
             emit_build_output(state, &output, id);
         }
 
-        build_state.insert(id, kind, output);
+        build_script_outputs
+            .lock()
+            .unwrap()
+            .insert((id, kind), output);
         Ok(())
     });
 
@@ -386,25 +407,6 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
     Ok(job)
 }
 
-impl BuildState {
-    pub fn new(host_config: &TargetConfig, target_config: &TargetConfig) -> BuildState {
-        let mut overrides = HashMap::new();
-        let i1 = host_config.overrides.iter().map(|p| (p, Kind::Host));
-        let i2 = target_config.overrides.iter().map(|p| (p, Kind::Target));
-        for ((name, output), kind) in i1.chain(i2) {
-            overrides.insert((name.clone(), kind), output.clone());
-        }
-        BuildState {
-            outputs: Mutex::new(HashMap::new()),
-            overrides,
-        }
-    }
-
-    fn insert(&self, id: PackageId, kind: Kind, output: BuildOutput) {
-        self.outputs.lock().unwrap().insert((id, kind), output);
-    }
-}
-
 impl BuildOutput {
     pub fn parse_file(
         path: &Path,
@@ -471,6 +473,7 @@ impl BuildOutput {
                 script_out_dir.to_str().unwrap(),
             );
 
+            // Keep in sync with TargetConfig::new.
             match key {
                 "rustc-flags" => {
                     let (paths, links) = BuildOutput::parse_rustc_flags(&value, &whence)?;
@@ -597,14 +600,21 @@ impl BuildDeps {
     }
 }
 
-/// Computes the `build_scripts` map in the `Context` which tracks what build
-/// scripts each package depends on.
+/// Computes several maps in `Context`:
+/// - `build_scripts`: A map that tracks which build scripts each package
+///   depends on.
+/// - `build_explicit_deps`: Dependency statements emitted by build scripts
+///   from a previous run.
+/// - `build_script_outputs`: Pre-populates this with any overridden build
+///   scripts.
 ///
-/// The global `build_scripts` map lists for all (package, kind) tuples what set
-/// of packages' build script outputs must be considered. For example this lists
-/// all dependencies' `-L` flags which need to be propagated transitively.
+/// The important one here is `build_scripts`, which for each `(package,
+/// kind)` stores a `BuildScripts` object which contains a list of
+/// dependencies with build scripts that the unit should consider when
+/// linking. For example this lists all dependencies' `-L` flags which need to
+/// be propagated transitively.
 ///
-/// The given set of targets to this function is the initial set of
+/// The given set of units to this function is the initial set of
 /// targets/profiles which are being built.
 pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> CargoResult<()> {
     let mut ret = HashMap::new();
@@ -628,20 +638,15 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
             return Ok(&out[unit]);
         }
 
-        let key = unit
-            .pkg
-            .manifest()
-            .links()
-            .map(|l| (l.to_string(), unit.kind));
-        let build_state = &cx.build_state;
-        if let Some(output) = key.and_then(|k| build_state.overrides.get(&k)) {
-            let key = (unit.pkg.package_id(), unit.kind);
-            cx.build_script_overridden.insert(key);
-            build_state
-                .outputs
-                .lock()
-                .unwrap()
-                .insert(key, output.clone());
+        // If there is a build script override, pre-fill the build output.
+        if let Some(links) = unit.pkg.manifest().links() {
+            if let Some(output) = cx.bcx.script_override(links, unit.kind) {
+                let key = (unit.pkg.package_id(), unit.kind);
+                cx.build_script_outputs
+                    .lock()
+                    .unwrap()
+                    .insert(key, output.clone());
+            }
         }
 
         let mut ret = BuildScripts::default();
@@ -650,6 +655,7 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
             add_to_link(&mut ret, unit.pkg.package_id(), unit.kind);
         }
 
+        // Load any dependency declarations from a previous run.
         if unit.mode.is_run_custom_build() {
             parse_previous_explicit_deps(cx, unit)?;
         }
@@ -658,16 +664,16 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
         // to rustc invocation caching schemes, so be sure to generate the same
         // set of build script dependency orderings via sorting the targets that
         // come out of the `Context`.
-        let mut targets = cx.dep_targets(unit);
-        targets.sort_by_key(|u| u.pkg.package_id());
+        let mut dependencies = cx.dep_targets(unit);
+        dependencies.sort_by_key(|u| u.pkg.package_id());
 
-        for unit in targets.iter() {
-            let dep_scripts = build(out, cx, unit)?;
+        for dep_unit in dependencies.iter() {
+            let dep_scripts = build(out, cx, dep_unit)?;
 
-            if unit.target.for_host() {
+            if dep_unit.target.for_host() {
                 ret.plugins
                     .extend(dep_scripts.to_link.iter().map(|p| &p.0).cloned());
-            } else if unit.target.linkable() {
+            } else if dep_unit.target.linkable() {
                 for &(pkg, kind) in dep_scripts.to_link.iter() {
                     add_to_link(&mut ret, pkg, kind);
                 }
diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs
index 3ce3c04a0f4..e7cccc818f8 100644
--- a/src/cargo/core/compiler/fingerprint.rs
+++ b/src/cargo/core/compiler/fingerprint.rs
@@ -286,12 +286,12 @@ pub fn prepare_target<'a, 'cfg>(
         // build script's fingerprint after it's executed. We do this by
         // using the `build_script_local_fingerprints` function which returns a
         // thunk we can invoke on a foreign thread to calculate this.
-        let state = Arc::clone(&cx.build_state);
+        let build_script_outputs = Arc::clone(&cx.build_script_outputs);
         let key = (unit.pkg.package_id(), unit.kind);
         let (gen_local, _overridden) = build_script_local_fingerprints(cx, unit);
         let output_path = cx.build_explicit_deps[unit].build_script_output.clone();
         Work::new(move |_| {
-            let outputs = state.outputs.lock().unwrap();
+            let outputs = build_script_outputs.lock().unwrap();
             let outputs = &outputs[&key];
             let deps = BuildDeps::new(&output_path, Some(outputs));
 
@@ -1264,8 +1264,11 @@ fn build_script_override_fingerprint<'a, 'cfg>(
     cx: &mut Context<'a, 'cfg>,
     unit: &Unit<'a>,
 ) -> Option<LocalFingerprint> {
-    let state = cx.build_state.outputs.lock().unwrap();
-    let output = state.get(&(unit.pkg.package_id(), unit.kind))?;
+    // Build script output is only populated at this stage when it is
+    // overridden.
+    let build_script_outputs = cx.build_script_outputs.lock().unwrap();
+    // Returns None if it is not overridden.
+    let output = build_script_outputs.get(&(unit.pkg.package_id(), unit.kind))?;
     let s = format!(
         "overridden build state with hash: {}",
         util::hash_u64(output)
diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs
index 1b999be7f42..cea9e4e23db 100644
--- a/src/cargo/core/compiler/job_queue.rs
+++ b/src/cargo/core/compiler/job_queue.rs
@@ -534,9 +534,9 @@ impl<'a, 'cfg> JobQueue<'a, 'cfg> {
         unit: &Unit<'a>,
         cx: &mut Context<'_, '_>,
     ) -> CargoResult<()> {
-        let output = cx.build_state.outputs.lock().unwrap();
+        let outputs = cx.build_script_outputs.lock().unwrap();
         let bcx = &mut cx.bcx;
-        if let Some(output) = output.get(&(unit.pkg.package_id(), unit.kind)) {
+        if let Some(output) = outputs.get(&(unit.pkg.package_id(), unit.kind)) {
             if !output.warnings.is_empty() {
                 if let Some(msg) = msg {
                     writeln!(bcx.config.shell().err(), "{}\n", msg)?;
diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs
index efe332a93eb..cf885e77595 100644
--- a/src/cargo/core/compiler/mod.rs
+++ b/src/cargo/core/compiler/mod.rs
@@ -29,7 +29,7 @@ pub use self::build_context::{BuildContext, FileFlavor, TargetConfig, TargetInfo
 use self::build_plan::BuildPlan;
 pub use self::compilation::{Compilation, Doctest};
 pub use self::context::Context;
-pub use self::custom_build::{BuildMap, BuildOutput, BuildScripts};
+pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
 pub use self::job::Freshness;
 use self::job::{Job, Work};
 use self::job_queue::{JobQueue, JobState};
@@ -192,9 +192,9 @@ fn rustc<'a, 'cfg>(
     let kind = unit.kind;
 
     // Prepare the native lib state (extra `-L` and `-l` flags).
-    let build_state = cx.build_state.clone();
+    let build_script_outputs = Arc::clone(&cx.build_script_outputs);
     let current_id = unit.pkg.package_id();
-    let build_deps = load_build_deps(cx, unit);
+    let build_scripts = cx.build_scripts.get(unit).cloned();
 
     // If we are a binary and the package also contains a library, then we
     // don't pass the `-l` flags.
@@ -242,20 +242,20 @@ fn rustc<'a, 'cfg>(
         // located somewhere in there.
         // Finally, if custom environment variables have been produced by
         // previous build scripts, we include them in the rustc invocation.
-        if let Some(build_deps) = build_deps {
-            let build_state = build_state.outputs.lock().unwrap();
+        if let Some(build_scripts) = build_scripts {
+            let script_outputs = build_script_outputs.lock().unwrap();
             if !build_plan {
                 add_native_deps(
                     &mut rustc,
-                    &build_state,
-                    &build_deps,
+                    &script_outputs,
+                    &build_scripts,
                     pass_l_flag,
                     pass_cdylib_link_args,
                     current_id,
                 )?;
-                add_plugin_deps(&mut rustc, &build_state, &build_deps, &root_output)?;
+                add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
             }
-            add_custom_env(&mut rustc, &build_state, current_id, kind)?;
+            add_custom_env(&mut rustc, &script_outputs, current_id, kind)?;
         }
 
         for output in outputs.iter() {
@@ -341,16 +341,16 @@ fn rustc<'a, 'cfg>(
     // present in `state`) to the command provided.
     fn add_native_deps(
         rustc: &mut ProcessBuilder,
-        build_state: &BuildMap,
+        build_script_outputs: &BuildScriptOutputs,
         build_scripts: &BuildScripts,
         pass_l_flag: bool,
         pass_cdylib_link_args: bool,
         current_id: PackageId,
     ) -> CargoResult<()> {
         for key in build_scripts.to_link.iter() {
-            let output = build_state.get(key).ok_or_else(|| {
+            let output = build_script_outputs.get(key).ok_or_else(|| {
                 internal(format!(
-                    "couldn't find build state for {}/{:?}",
+                    "couldn't find build script output for {}/{:?}",
                     key.0, key.1
                 ))
             })?;
@@ -381,12 +381,12 @@ fn rustc<'a, 'cfg>(
     // been put there by one of the `build_scripts`) to the command provided.
     fn add_custom_env(
         rustc: &mut ProcessBuilder,
-        build_state: &BuildMap,
+        build_script_outputs: &BuildScriptOutputs,
         current_id: PackageId,
         kind: Kind,
     ) -> CargoResult<()> {
         let key = (current_id, kind);
-        if let Some(output) = build_state.get(&key) {
+        if let Some(output) = build_script_outputs.get(&key) {
             for &(ref name, ref value) in output.env.iter() {
                 rustc.env(name, value);
             }
@@ -522,16 +522,12 @@ fn hardlink_or_copy(src: &Path, dst: &Path) -> CargoResult<()> {
     Ok(())
 }
 
-fn load_build_deps(cx: &Context<'_, '_>, unit: &Unit<'_>) -> Option<Arc<BuildScripts>> {
-    cx.build_scripts.get(unit).cloned()
-}
-
-// For all plugin dependencies, add their -L paths (now calculated and
-// present in `state`) to the dynamic library load path for the command to
-// execute.
+// For all plugin dependencies, add their -L paths (now calculated and present
+// in `build_script_outputs`) to the dynamic library load path for the command
+// to execute.
 fn add_plugin_deps(
     rustc: &mut ProcessBuilder,
-    build_state: &BuildMap,
+    build_script_outputs: &BuildScriptOutputs,
     build_scripts: &BuildScripts,
     root_output: &PathBuf,
 ) -> CargoResult<()> {
@@ -539,7 +535,7 @@ fn add_plugin_deps(
     let search_path = rustc.get_env(var).unwrap_or_default();
     let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
     for &id in build_scripts.plugins.iter() {
-        let output = build_state
+        let output = build_script_outputs
             .get(&(id, Kind::Host))
             .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", id)))?;
         search_path.append(&mut filter_dynamic_search_path(
@@ -645,14 +641,14 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
     rustdoc.args(bcx.rustdocflags_args(unit));
 
     let name = unit.pkg.name().to_string();
-    let build_state = cx.build_state.clone();
+    let build_script_outputs = Arc::clone(&cx.build_script_outputs);
     let key = (unit.pkg.package_id(), unit.kind);
     let package_id = unit.pkg.package_id();
     let target = unit.target.clone();
     let mut output_options = OutputOptions::new(cx, unit);
 
     Ok(Work::new(move |state| {
-        if let Some(output) = build_state.outputs.lock().unwrap().get(&key) {
+        if let Some(output) = build_script_outputs.lock().unwrap().get(&key) {
             for cfg in output.cfgs.iter() {
                 rustdoc.arg("--cfg").arg(cfg);
             }
diff --git a/src/cargo/core/compiler/output_depinfo.rs b/src/cargo/core/compiler/output_depinfo.rs
index 86cb7b2180d..a7acc8c4e08 100644
--- a/src/cargo/core/compiler/output_depinfo.rs
+++ b/src/cargo/core/compiler/output_depinfo.rs
@@ -59,7 +59,7 @@ fn add_deps_for_unit<'a, 'b>(
 
     // Add rerun-if-changed dependencies
     let key = (unit.pkg.package_id(), unit.kind);
-    if let Some(output) = context.build_state.outputs.lock().unwrap().get(&key) {
+    if let Some(output) = context.build_script_outputs.lock().unwrap().get(&key) {
         for path in &output.rerun_if_changed {
             deps.insert(path.into());
         }
diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs
index 2c57c9e4a43..acda0e5700b 100644
--- a/src/cargo/core/manifest.rs
+++ b/src/cargo/core/manifest.rs
@@ -761,6 +761,7 @@ impl Target {
     pub fn documented(&self) -> bool {
         self.doc
     }
+    // A plugin, proc-macro, or build-script.
     pub fn for_host(&self) -> bool {
         self.for_host
     }