]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/tool.rs
Rollup merge of #56168 - sfackler:raw-entry-tracking, r=kennytm
[rust.git] / src / bootstrap / tool.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::fs;
12 use std::env;
13 use std::iter;
14 use std::path::PathBuf;
15 use std::process::{Command, exit};
16 use std::collections::HashSet;
17
18 use Mode;
19 use Compiler;
20 use builder::{Step, RunConfig, ShouldRun, Builder};
21 use util::{exe, add_lib_path};
22 use compile;
23 use native;
24 use channel::GitInfo;
25 use channel;
26 use cache::Interned;
27 use toolstate::ToolState;
28
29 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
30 pub enum SourceType {
31     InTree,
32     Submodule,
33 }
34
35 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
36 struct ToolBuild {
37     compiler: Compiler,
38     target: Interned<String>,
39     tool: &'static str,
40     path: &'static str,
41     mode: Mode,
42     is_optional_tool: bool,
43     source_type: SourceType,
44     extra_features: Vec<String>,
45 }
46
47 impl Step for ToolBuild {
48     type Output = Option<PathBuf>;
49
50     fn should_run(run: ShouldRun) -> ShouldRun {
51         run.never()
52     }
53
54     /// Build a tool in `src/tools`
55     ///
56     /// This will build the specified tool with the specified `host` compiler in
57     /// `stage` into the normal cargo output directory.
58     fn run(self, builder: &Builder) -> Option<PathBuf> {
59         let compiler = self.compiler;
60         let target = self.target;
61         let tool = self.tool;
62         let path = self.path;
63         let is_optional_tool = self.is_optional_tool;
64
65         match self.mode {
66             Mode::ToolRustc => {
67                 builder.ensure(compile::Rustc { compiler, target })
68             }
69             Mode::ToolStd => {
70                 builder.ensure(compile::Std { compiler, target })
71             }
72             Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs
73             _ => panic!("unexpected Mode for tool build")
74         }
75
76         let mut cargo = prepare_tool_cargo(
77             builder,
78             compiler,
79             self.mode,
80             target,
81             "build",
82             path,
83             self.source_type,
84             &self.extra_features,
85         );
86
87         let _folder = builder.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
88         builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target));
89         let mut duplicates = Vec::new();
90         let is_expected = compile::stream_cargo(builder, &mut cargo, vec![], &mut |msg| {
91             // Only care about big things like the RLS/Cargo for now
92             match tool {
93                 | "rls"
94                 | "cargo"
95                 | "clippy-driver"
96                 => {}
97
98                 _ => return,
99             }
100             let (id, features, filenames) = match msg {
101                 compile::CargoMessage::CompilerArtifact {
102                     package_id,
103                     features,
104                     filenames
105                 } => {
106                     (package_id, features, filenames)
107                 }
108                 _ => return,
109             };
110             let features = features.iter().map(|s| s.to_string()).collect::<Vec<_>>();
111
112             for path in filenames {
113                 let val = (tool, PathBuf::from(&*path), features.clone());
114                 // we're only interested in deduplicating rlibs for now
115                 if val.1.extension().and_then(|s| s.to_str()) != Some("rlib") {
116                     continue
117                 }
118
119                 // Don't worry about libs that turn out to be host dependencies
120                 // or build scripts, we only care about target dependencies that
121                 // are in `deps`.
122                 if let Some(maybe_target) = val.1
123                     .parent()                   // chop off file name
124                     .and_then(|p| p.parent())   // chop off `deps`
125                     .and_then(|p| p.parent())   // chop off `release`
126                     .and_then(|p| p.file_name())
127                     .and_then(|p| p.to_str())
128                 {
129                     if maybe_target != &*target {
130                         continue
131                     }
132                 }
133
134                 let mut artifacts = builder.tool_artifacts.borrow_mut();
135                 let prev_artifacts = artifacts
136                     .entry(target)
137                     .or_default();
138                 if let Some(prev) = prev_artifacts.get(&*id) {
139                     if prev.1 != val.1 {
140                         duplicates.push((
141                             id.to_string(),
142                             val,
143                             prev.clone(),
144                         ));
145                     }
146                     return
147                 }
148                 prev_artifacts.insert(id.to_string(), val);
149             }
150         });
151
152         if is_expected && !duplicates.is_empty() {
153             println!("duplicate artfacts found when compiling a tool, this \
154                       typically means that something was recompiled because \
155                       a transitive dependency has different features activated \
156                       than in a previous build:\n");
157             println!("the following dependencies are duplicated although they \
158                       have the same features enabled:");
159             for (id, cur, prev) in duplicates.drain_filter(|(_, cur, prev)| cur.2 == prev.2) {
160                 println!("  {}", id);
161                 // same features
162                 println!("    `{}` ({:?})\n    `{}` ({:?})", cur.0, cur.1, prev.0, prev.1);
163             }
164             println!("the following dependencies have different features:");
165             for (id, cur, prev) in duplicates {
166                 println!("  {}", id);
167                 let cur_features: HashSet<_> = cur.2.into_iter().collect();
168                 let prev_features: HashSet<_> = prev.2.into_iter().collect();
169                 println!("    `{}` additionally enabled features {:?} at {:?}",
170                          cur.0, &cur_features - &prev_features, cur.1);
171                 println!("    `{}` additionally enabled features {:?} at {:?}",
172                          prev.0, &prev_features - &cur_features, prev.1);
173             }
174             println!();
175             println!("to fix this you will probably want to edit the local \
176                       src/tools/rustc-workspace-hack/Cargo.toml crate, as \
177                       that will update the dependency graph to ensure that \
178                       these crates all share the same feature set");
179             panic!("tools should not compile multiple copies of the same crate");
180         }
181
182         builder.save_toolstate(tool, if is_expected {
183             ToolState::TestFail
184         } else {
185             ToolState::BuildFail
186         });
187
188         if !is_expected {
189             if !is_optional_tool {
190                 exit(1);
191             } else {
192                 None
193             }
194         } else {
195             let cargo_out = builder.cargo_out(compiler, self.mode, target)
196                 .join(exe(tool, &compiler.host));
197             let bin = builder.tools_dir(compiler).join(exe(tool, &compiler.host));
198             builder.copy(&cargo_out, &bin);
199             Some(bin)
200         }
201     }
202 }
203
204 pub fn prepare_tool_cargo(
205     builder: &Builder,
206     compiler: Compiler,
207     mode: Mode,
208     target: Interned<String>,
209     command: &'static str,
210     path: &'static str,
211     source_type: SourceType,
212     extra_features: &[String],
213 ) -> Command {
214     let mut cargo = builder.cargo(compiler, mode, target, command);
215     let dir = builder.src.join(path);
216     cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
217
218     // We don't want to build tools dynamically as they'll be running across
219     // stages and such and it's just easier if they're not dynamically linked.
220     cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
221
222     if source_type == SourceType::Submodule {
223         cargo.env("RUSTC_EXTERNAL_TOOL", "1");
224     }
225
226     let mut features = extra_features.iter().cloned().collect::<Vec<_>>();
227     if builder.build.config.cargo_native_static {
228         if path.ends_with("cargo") ||
229             path.ends_with("rls") ||
230             path.ends_with("clippy") ||
231             path.ends_with("rustfmt")
232         {
233             cargo.env("LIBZ_SYS_STATIC", "1");
234             features.push("rustc-workspace-hack/all-static".to_string());
235         }
236     }
237
238     // if tools are using lzma we want to force the build script to build its
239     // own copy
240     cargo.env("LZMA_API_STATIC", "1");
241
242     cargo.env("CFG_RELEASE_CHANNEL", &builder.config.channel);
243     cargo.env("CFG_VERSION", builder.rust_version());
244     cargo.env("CFG_RELEASE_NUM", channel::CFG_RELEASE_NUM);
245
246     let info = GitInfo::new(&builder.config, &dir);
247     if let Some(sha) = info.sha() {
248         cargo.env("CFG_COMMIT_HASH", sha);
249     }
250     if let Some(sha_short) = info.sha_short() {
251         cargo.env("CFG_SHORT_COMMIT_HASH", sha_short);
252     }
253     if let Some(date) = info.commit_date() {
254         cargo.env("CFG_COMMIT_DATE", date);
255     }
256     if !features.is_empty() {
257         cargo.arg("--features").arg(&features.join(", "));
258     }
259     cargo
260 }
261
262 macro_rules! tool {
263     ($(
264         $name:ident, $path:expr, $tool_name:expr, $mode:expr
265         $(,llvm_tools = $llvm:expr)*
266         $(,is_external_tool = $external:expr)*
267         $(,cargo_test_root = $cargo_test_root:expr)*
268         ;
269     )+) => {
270         #[derive(Copy, PartialEq, Eq, Clone)]
271         pub enum Tool {
272             $(
273                 $name,
274             )+
275         }
276
277         impl Tool {
278             pub fn get_mode(&self) -> Mode {
279                 let mode = match self {
280                     $(Tool::$name => $mode,)+
281                 };
282                 mode
283             }
284
285             /// Whether this tool requires LLVM to run
286             pub fn uses_llvm_tools(&self) -> bool {
287                 match self {
288                     $(Tool::$name => false $(|| $llvm)*,)+
289                 }
290             }
291
292             /// Whether this tool requires may run Cargo for test crates,
293             /// which currently needs setting the environment variable
294             /// `__CARGO_TEST_ROOT` to separate it from the workspace.
295             pub fn needs_cargo_test_root(&self) -> bool {
296                 match self {
297                     $(Tool::$name => false $(|| $cargo_test_root)*,)+
298                 }
299             }
300         }
301
302         impl<'a> Builder<'a> {
303             pub fn tool_exe(&self, tool: Tool) -> PathBuf {
304                 let stage = self.tool_default_stage(tool);
305                 match tool {
306                     $(Tool::$name =>
307                         self.ensure($name {
308                             compiler: self.compiler(stage, self.config.build),
309                             target: self.config.build,
310                         }),
311                     )+
312                 }
313             }
314
315             pub fn tool_default_stage(&self, tool: Tool) -> u32 {
316                 // Compile the error-index in the same stage as rustdoc to avoid
317                 // recompiling rustdoc twice if we can. Otherwise compile
318                 // everything else in stage0 as there's no need to rebootstrap
319                 // everything.
320                 match tool {
321                     Tool::ErrorIndex if self.top_stage >= 2 => self.top_stage,
322                     _ => 0,
323                 }
324             }
325         }
326
327         $(
328             #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
329         pub struct $name {
330             pub compiler: Compiler,
331             pub target: Interned<String>,
332         }
333
334         impl Step for $name {
335             type Output = PathBuf;
336
337             fn should_run(run: ShouldRun) -> ShouldRun {
338                 run.path($path)
339             }
340
341             fn make_run(run: RunConfig) {
342                 run.builder.ensure($name {
343                     compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
344                     target: run.target,
345                 });
346             }
347
348             fn run(self, builder: &Builder) -> PathBuf {
349                 builder.ensure(ToolBuild {
350                     compiler: self.compiler,
351                     target: self.target,
352                     tool: $tool_name,
353                     mode: $mode,
354                     path: $path,
355                     is_optional_tool: false,
356                     source_type: if false $(|| $external)* {
357                         SourceType::Submodule
358                     } else {
359                         SourceType::InTree
360                     },
361                     extra_features: Vec::new(),
362                 }).expect("expected to build -- essential tool")
363             }
364         }
365         )+
366     }
367 }
368
369 tool!(
370     Rustbook, "src/tools/rustbook", "rustbook", Mode::ToolBootstrap;
371     ErrorIndex, "src/tools/error_index_generator", "error_index_generator", Mode::ToolRustc;
372     UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen", Mode::ToolBootstrap;
373     Tidy, "src/tools/tidy", "tidy", Mode::ToolBootstrap;
374     Linkchecker, "src/tools/linkchecker", "linkchecker", Mode::ToolBootstrap;
375     CargoTest, "src/tools/cargotest", "cargotest", Mode::ToolBootstrap, cargo_test_root = true;
376     Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolBootstrap,
377         llvm_tools = true, cargo_test_root = true;
378     BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolBootstrap;
379     RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolBootstrap;
380     RustInstaller, "src/tools/rust-installer", "fabricate", Mode::ToolBootstrap,
381         is_external_tool = true;
382     RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes", Mode::ToolBootstrap;
383 );
384
385 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
386 pub struct RemoteTestServer {
387     pub compiler: Compiler,
388     pub target: Interned<String>,
389 }
390
391 impl Step for RemoteTestServer {
392     type Output = PathBuf;
393
394     fn should_run(run: ShouldRun) -> ShouldRun {
395         run.path("src/tools/remote-test-server")
396     }
397
398     fn make_run(run: RunConfig) {
399         run.builder.ensure(RemoteTestServer {
400             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
401             target: run.target,
402         });
403     }
404
405     fn run(self, builder: &Builder) -> PathBuf {
406         builder.ensure(ToolBuild {
407             compiler: self.compiler,
408             target: self.target,
409             tool: "remote-test-server",
410             mode: Mode::ToolStd,
411             path: "src/tools/remote-test-server",
412             is_optional_tool: false,
413             source_type: SourceType::InTree,
414             extra_features: Vec::new(),
415         }).expect("expected to build -- essential tool")
416     }
417 }
418
419 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
420 pub struct Rustdoc {
421     pub host: Interned<String>,
422 }
423
424 impl Step for Rustdoc {
425     type Output = PathBuf;
426     const DEFAULT: bool = true;
427     const ONLY_HOSTS: bool = true;
428
429     fn should_run(run: ShouldRun) -> ShouldRun {
430         run.path("src/tools/rustdoc")
431     }
432
433     fn make_run(run: RunConfig) {
434         run.builder.ensure(Rustdoc {
435             host: run.host,
436         });
437     }
438
439     fn run(self, builder: &Builder) -> PathBuf {
440         let target_compiler = builder.compiler(builder.top_stage, self.host);
441         let target = target_compiler.host;
442         let build_compiler = if target_compiler.stage == 0 {
443             builder.compiler(0, builder.config.build)
444         } else if target_compiler.stage >= 2 {
445             // Past stage 2, we consider the compiler to be ABI-compatible and hence capable of
446             // building rustdoc itself.
447             builder.compiler(target_compiler.stage, builder.config.build)
448         } else {
449             // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
450             // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage
451             // compilers, which isn't what we want.
452             builder.compiler(target_compiler.stage - 1, builder.config.build)
453         };
454
455         builder.ensure(compile::Rustc { compiler: build_compiler, target });
456         builder.ensure(compile::Rustc {
457             compiler: build_compiler,
458             target: builder.config.build,
459         });
460
461         let mut cargo = prepare_tool_cargo(
462             builder,
463             build_compiler,
464             Mode::ToolRustc,
465             target,
466             "build",
467             "src/tools/rustdoc",
468             SourceType::InTree,
469             &[],
470         );
471
472         // Most tools don't get debuginfo, but rustdoc should.
473         cargo.env("RUSTC_DEBUGINFO", builder.config.rust_debuginfo.to_string())
474              .env("RUSTC_DEBUGINFO_LINES", builder.config.rust_debuginfo_lines.to_string());
475
476         let _folder = builder.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
477         builder.info(&format!("Building rustdoc for stage{} ({})",
478             target_compiler.stage, target_compiler.host));
479         builder.run(&mut cargo);
480
481         // Cargo adds a number of paths to the dylib search path on windows, which results in
482         // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
483         // rustdoc a different name.
484         let tool_rustdoc = builder.cargo_out(build_compiler, Mode::ToolRustc, target)
485             .join(exe("rustdoc_tool_binary", &target_compiler.host));
486
487         // don't create a stage0-sysroot/bin directory.
488         if target_compiler.stage > 0 {
489             let sysroot = builder.sysroot(target_compiler);
490             let bindir = sysroot.join("bin");
491             t!(fs::create_dir_all(&bindir));
492             let bin_rustdoc = bindir.join(exe("rustdoc", &*target_compiler.host));
493             let _ = fs::remove_file(&bin_rustdoc);
494             builder.copy(&tool_rustdoc, &bin_rustdoc);
495             bin_rustdoc
496         } else {
497             tool_rustdoc
498         }
499     }
500 }
501
502 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
503 pub struct Cargo {
504     pub compiler: Compiler,
505     pub target: Interned<String>,
506 }
507
508 impl Step for Cargo {
509     type Output = PathBuf;
510     const DEFAULT: bool = true;
511     const ONLY_HOSTS: bool = true;
512
513     fn should_run(run: ShouldRun) -> ShouldRun {
514         let builder = run.builder;
515         run.path("src/tools/cargo").default_condition(builder.config.extended)
516     }
517
518     fn make_run(run: RunConfig) {
519         run.builder.ensure(Cargo {
520             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
521             target: run.target,
522         });
523     }
524
525     fn run(self, builder: &Builder) -> PathBuf {
526         // Cargo depends on procedural macros, which requires a full host
527         // compiler to be available, so we need to depend on that.
528         builder.ensure(compile::Rustc {
529             compiler: self.compiler,
530             target: builder.config.build,
531         });
532         builder.ensure(ToolBuild {
533             compiler: self.compiler,
534             target: self.target,
535             tool: "cargo",
536             mode: Mode::ToolRustc,
537             path: "src/tools/cargo",
538             is_optional_tool: false,
539             source_type: SourceType::Submodule,
540             extra_features: Vec::new(),
541         }).expect("expected to build -- essential tool")
542     }
543 }
544
545 macro_rules! tool_extended {
546     (($sel:ident, $builder:ident),
547        $($name:ident,
548        $toolstate:ident,
549        $path:expr,
550        $tool_name:expr,
551        $extra_deps:block;)+) => {
552         $(
553             #[derive(Debug, Clone, Hash, PartialEq, Eq)]
554         pub struct $name {
555             pub compiler: Compiler,
556             pub target: Interned<String>,
557             pub extra_features: Vec<String>,
558         }
559
560         impl Step for $name {
561             type Output = Option<PathBuf>;
562             const DEFAULT: bool = true;
563             const ONLY_HOSTS: bool = true;
564
565             fn should_run(run: ShouldRun) -> ShouldRun {
566                 let builder = run.builder;
567                 run.path($path).default_condition(builder.config.extended)
568             }
569
570             fn make_run(run: RunConfig) {
571                 run.builder.ensure($name {
572                     compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
573                     target: run.target,
574                     extra_features: Vec::new(),
575                 });
576             }
577
578             #[allow(unused_mut)]
579             fn run(mut $sel, $builder: &Builder) -> Option<PathBuf> {
580                 $extra_deps
581                 $builder.ensure(ToolBuild {
582                     compiler: $sel.compiler,
583                     target: $sel.target,
584                     tool: $tool_name,
585                     mode: Mode::ToolRustc,
586                     path: $path,
587                     extra_features: $sel.extra_features,
588                     is_optional_tool: true,
589                     source_type: SourceType::Submodule,
590                 })
591             }
592         }
593         )+
594     }
595 }
596
597 tool_extended!((self, builder),
598     Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {};
599     CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", {
600         // Clippy depends on procedural macros (serde), which requires a full host
601         // compiler to be available, so we need to depend on that.
602         builder.ensure(compile::Rustc {
603             compiler: self.compiler,
604             target: builder.config.build,
605         });
606     };
607     Clippy, clippy, "src/tools/clippy", "clippy-driver", {
608         // Clippy depends on procedural macros (serde), which requires a full host
609         // compiler to be available, so we need to depend on that.
610         builder.ensure(compile::Rustc {
611             compiler: self.compiler,
612             target: builder.config.build,
613         });
614     };
615     Miri, miri, "src/tools/miri", "miri", {};
616     Rls, rls, "src/tools/rls", "rls", {
617         let clippy = builder.ensure(Clippy {
618             compiler: self.compiler,
619             target: self.target,
620             extra_features: Vec::new(),
621         });
622         if clippy.is_some() {
623             self.extra_features.push("clippy".to_owned());
624         }
625         // RLS depends on procedural macros, which requires a full host
626         // compiler to be available, so we need to depend on that.
627         builder.ensure(compile::Rustc {
628             compiler: self.compiler,
629             target: builder.config.build,
630         });
631     };
632     Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {};
633 );
634
635 impl<'a> Builder<'a> {
636     /// Get a `Command` which is ready to run `tool` in `stage` built for
637     /// `host`.
638     pub fn tool_cmd(&self, tool: Tool) -> Command {
639         let mut cmd = Command::new(self.tool_exe(tool));
640         let compiler = self.compiler(self.tool_default_stage(tool), self.config.build);
641         self.prepare_tool_cmd(compiler, tool, &mut cmd);
642         cmd
643     }
644
645     /// Prepares the `cmd` provided to be able to run the `compiler` provided.
646     ///
647     /// Notably this munges the dynamic library lookup path to point to the
648     /// right location to run `compiler`.
649     fn prepare_tool_cmd(&self, compiler: Compiler, tool: Tool, cmd: &mut Command) {
650         let host = &compiler.host;
651         let mut lib_paths: Vec<PathBuf> = vec![
652             if compiler.stage == 0 && tool != Tool::ErrorIndex {
653                 self.build.rustc_snapshot_libdir()
654             } else {
655                 PathBuf::from(&self.sysroot_libdir(compiler, compiler.host))
656             },
657             self.cargo_out(compiler, tool.get_mode(), *host).join("deps"),
658         ];
659
660         // On MSVC a tool may invoke a C compiler (e.g. compiletest in run-make
661         // mode) and that C compiler may need some extra PATH modification. Do
662         // so here.
663         if compiler.host.contains("msvc") {
664             let curpaths = env::var_os("PATH").unwrap_or_default();
665             let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
666             for &(ref k, ref v) in self.cc[&compiler.host].env() {
667                 if k != "PATH" {
668                     continue
669                 }
670                 for path in env::split_paths(v) {
671                     if !curpaths.contains(&path) {
672                         lib_paths.push(path);
673                     }
674                 }
675             }
676         }
677
678         // Add the llvm/bin directory to PATH since it contains lots of
679         // useful, platform-independent tools
680         if tool.uses_llvm_tools() {
681             if let Some(llvm_bin_path) = self.llvm_bin_path() {
682                 if host.contains("windows") {
683                     // On Windows, PATH and the dynamic library path are the same,
684                     // so we just add the LLVM bin path to lib_path
685                     lib_paths.push(llvm_bin_path);
686                 } else {
687                     let old_path = env::var_os("PATH").unwrap_or_default();
688                     let new_path = env::join_paths(iter::once(llvm_bin_path)
689                             .chain(env::split_paths(&old_path)))
690                         .expect("Could not add LLVM bin path to PATH");
691                     cmd.env("PATH", new_path);
692                 }
693             }
694         }
695
696         // Set `__CARGO_TEST_ROOT` to the build directory if needed.
697         if tool.needs_cargo_test_root() {
698             cmd.env("__CARGO_TEST_ROOT", &self.config.out);
699         }
700
701         add_lib_path(lib_paths, cmd);
702     }
703
704     fn llvm_bin_path(&self) -> Option<PathBuf> {
705         if self.config.llvm_enabled && !self.config.dry_run {
706             let llvm_config = self.ensure(native::Llvm {
707                 target: self.config.build,
708                 emscripten: false,
709             });
710
711             // Add the llvm/bin directory to PATH since it contains lots of
712             // useful, platform-independent tools
713             let llvm_bin_path = llvm_config.parent()
714                 .expect("Expected llvm-config to be contained in directory");
715             assert!(llvm_bin_path.is_dir());
716             Some(llvm_bin_path.to_path_buf())
717         } else {
718             None
719         }
720     }
721 }