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