]>
Commit | Line | Data |
---|---|---|
1 | //! Contains infrastructure for configuring the compiler, including parsing | |
2 | //! command-line options. | |
3 | ||
4 | pub use crate::options::*; | |
5 | ||
6 | use crate::lint; | |
7 | use crate::search_paths::SearchPath; | |
8 | use crate::utils::NativeLibKind; | |
9 | use crate::{early_error, early_warn, Session}; | |
10 | ||
11 | use rustc_data_structures::fx::FxHashSet; | |
12 | use rustc_data_structures::impl_stable_hash_via_hash; | |
13 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; | |
14 | ||
15 | use rustc_target::spec::{Target, TargetTriple}; | |
16 | ||
17 | use crate::parse::CrateConfig; | |
18 | use rustc_feature::UnstableFeatures; | |
19 | use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST}; | |
20 | use rustc_span::source_map::{FileName, FilePathMapping}; | |
21 | use rustc_span::symbol::{sym, Symbol}; | |
22 | use rustc_span::SourceFileHashAlgorithm; | |
23 | ||
24 | use rustc_errors::emitter::HumanReadableErrorType; | |
25 | use rustc_errors::{ColorConfig, HandlerFlags}; | |
26 | ||
27 | use std::collections::btree_map::{ | |
28 | Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, | |
29 | }; | |
30 | use std::collections::{BTreeMap, BTreeSet}; | |
31 | use std::fmt; | |
32 | use std::iter::{self, FromIterator}; | |
33 | use std::path::{Path, PathBuf}; | |
34 | use std::str::{self, FromStr}; | |
35 | ||
36 | pub struct Config { | |
37 | pub target: Target, | |
38 | pub ptr_width: u32, | |
39 | } | |
40 | ||
41 | bitflags! { | |
42 | #[derive(Default, Encodable, Decodable)] | |
43 | pub struct SanitizerSet: u8 { | |
44 | const ADDRESS = 1 << 0; | |
45 | const LEAK = 1 << 1; | |
46 | const MEMORY = 1 << 2; | |
47 | const THREAD = 1 << 3; | |
48 | } | |
49 | } | |
50 | ||
51 | /// Formats a sanitizer set as a comma separated list of sanitizers' names. | |
52 | impl fmt::Display for SanitizerSet { | |
53 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
54 | let mut first = true; | |
55 | for s in *self { | |
56 | let name = match s { | |
57 | SanitizerSet::ADDRESS => "address", | |
58 | SanitizerSet::LEAK => "leak", | |
59 | SanitizerSet::MEMORY => "memory", | |
60 | SanitizerSet::THREAD => "thread", | |
61 | _ => panic!("unrecognized sanitizer {:?}", s), | |
62 | }; | |
63 | if !first { | |
64 | f.write_str(",")?; | |
65 | } | |
66 | f.write_str(name)?; | |
67 | first = false; | |
68 | } | |
69 | Ok(()) | |
70 | } | |
71 | } | |
72 | ||
73 | impl IntoIterator for SanitizerSet { | |
74 | type Item = SanitizerSet; | |
75 | type IntoIter = std::vec::IntoIter<SanitizerSet>; | |
76 | ||
77 | fn into_iter(self) -> Self::IntoIter { | |
78 | [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] | |
79 | .iter() | |
80 | .copied() | |
81 | .filter(|&s| self.contains(s)) | |
82 | .collect::<Vec<_>>() | |
83 | .into_iter() | |
84 | } | |
85 | } | |
86 | ||
87 | impl<CTX> HashStable<CTX> for SanitizerSet { | |
88 | fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { | |
89 | self.bits().hash_stable(ctx, hasher); | |
90 | } | |
91 | } | |
92 | ||
93 | /// The different settings that the `-Z strip` flag can have. | |
94 | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | |
95 | pub enum Strip { | |
96 | /// Do not strip at all. | |
97 | None, | |
98 | ||
99 | /// Strip debuginfo. | |
100 | Debuginfo, | |
101 | ||
102 | /// Strip all symbols. | |
103 | Symbols, | |
104 | } | |
105 | ||
106 | /// The different settings that the `-C control-flow-guard` flag can have. | |
107 | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | |
108 | pub enum CFGuard { | |
109 | /// Do not emit Control Flow Guard metadata or checks. | |
110 | Disabled, | |
111 | ||
112 | /// Emit Control Flow Guard metadata but no checks. | |
113 | NoChecks, | |
114 | ||
115 | /// Emit Control Flow Guard metadata and checks. | |
116 | Checks, | |
117 | } | |
118 | ||
119 | #[derive(Clone, Copy, Debug, PartialEq, Hash)] | |
120 | pub enum OptLevel { | |
121 | No, // -O0 | |
122 | Less, // -O1 | |
123 | Default, // -O2 | |
124 | Aggressive, // -O3 | |
125 | Size, // -Os | |
126 | SizeMin, // -Oz | |
127 | } | |
128 | ||
129 | impl_stable_hash_via_hash!(OptLevel); | |
130 | ||
131 | /// This is what the `LtoCli` values get mapped to after resolving defaults and | |
132 | /// and taking other command line options into account. | |
133 | #[derive(Clone, PartialEq)] | |
134 | pub enum Lto { | |
135 | /// Don't do any LTO whatsoever | |
136 | No, | |
137 | ||
138 | /// Do a full crate graph LTO with ThinLTO | |
139 | Thin, | |
140 | ||
141 | /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen | |
142 | /// units). | |
143 | ThinLocal, | |
144 | ||
145 | /// Do a full crate graph LTO with "fat" LTO | |
146 | Fat, | |
147 | } | |
148 | ||
149 | /// The different settings that the `-C lto` flag can have. | |
150 | #[derive(Clone, Copy, PartialEq, Hash, Debug)] | |
151 | pub enum LtoCli { | |
152 | /// `-C lto=no` | |
153 | No, | |
154 | /// `-C lto=yes` | |
155 | Yes, | |
156 | /// `-C lto` | |
157 | NoParam, | |
158 | /// `-C lto=thin` | |
159 | Thin, | |
160 | /// `-C lto=fat` | |
161 | Fat, | |
162 | /// No `-C lto` flag passed | |
163 | Unspecified, | |
164 | } | |
165 | ||
166 | #[derive(Clone, PartialEq, Hash)] | |
167 | pub enum LinkerPluginLto { | |
168 | LinkerPlugin(PathBuf), | |
169 | LinkerPluginAuto, | |
170 | Disabled, | |
171 | } | |
172 | ||
173 | impl LinkerPluginLto { | |
174 | pub fn enabled(&self) -> bool { | |
175 | match *self { | |
176 | LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true, | |
177 | LinkerPluginLto::Disabled => false, | |
178 | } | |
179 | } | |
180 | } | |
181 | ||
182 | #[derive(Clone, PartialEq, Hash)] | |
183 | pub enum SwitchWithOptPath { | |
184 | Enabled(Option<PathBuf>), | |
185 | Disabled, | |
186 | } | |
187 | ||
188 | impl SwitchWithOptPath { | |
189 | pub fn enabled(&self) -> bool { | |
190 | match *self { | |
191 | SwitchWithOptPath::Enabled(_) => true, | |
192 | SwitchWithOptPath::Disabled => false, | |
193 | } | |
194 | } | |
195 | } | |
196 | ||
197 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
198 | #[derive(Encodable, Decodable)] | |
199 | pub enum SymbolManglingVersion { | |
200 | Legacy, | |
201 | V0, | |
202 | } | |
203 | ||
204 | impl_stable_hash_via_hash!(SymbolManglingVersion); | |
205 | ||
206 | #[derive(Clone, Copy, PartialEq, Hash)] | |
207 | pub enum DebugInfo { | |
208 | None, | |
209 | Limited, | |
210 | Full, | |
211 | } | |
212 | ||
213 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] | |
214 | #[derive(Encodable, Decodable)] | |
215 | pub enum OutputType { | |
216 | Bitcode, | |
217 | Assembly, | |
218 | LlvmAssembly, | |
219 | Mir, | |
220 | Metadata, | |
221 | Object, | |
222 | Exe, | |
223 | DepInfo, | |
224 | } | |
225 | ||
226 | impl_stable_hash_via_hash!(OutputType); | |
227 | ||
228 | impl OutputType { | |
229 | fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { | |
230 | match *self { | |
231 | OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true, | |
232 | OutputType::Bitcode | |
233 | | OutputType::Assembly | |
234 | | OutputType::LlvmAssembly | |
235 | | OutputType::Mir | |
236 | | OutputType::Object => false, | |
237 | } | |
238 | } | |
239 | ||
240 | fn shorthand(&self) -> &'static str { | |
241 | match *self { | |
242 | OutputType::Bitcode => "llvm-bc", | |
243 | OutputType::Assembly => "asm", | |
244 | OutputType::LlvmAssembly => "llvm-ir", | |
245 | OutputType::Mir => "mir", | |
246 | OutputType::Object => "obj", | |
247 | OutputType::Metadata => "metadata", | |
248 | OutputType::Exe => "link", | |
249 | OutputType::DepInfo => "dep-info", | |
250 | } | |
251 | } | |
252 | ||
253 | fn from_shorthand(shorthand: &str) -> Option<Self> { | |
254 | Some(match shorthand { | |
255 | "asm" => OutputType::Assembly, | |
256 | "llvm-ir" => OutputType::LlvmAssembly, | |
257 | "mir" => OutputType::Mir, | |
258 | "llvm-bc" => OutputType::Bitcode, | |
259 | "obj" => OutputType::Object, | |
260 | "metadata" => OutputType::Metadata, | |
261 | "link" => OutputType::Exe, | |
262 | "dep-info" => OutputType::DepInfo, | |
263 | _ => return None, | |
264 | }) | |
265 | } | |
266 | ||
267 | fn shorthands_display() -> String { | |
268 | format!( | |
269 | "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", | |
270 | OutputType::Bitcode.shorthand(), | |
271 | OutputType::Assembly.shorthand(), | |
272 | OutputType::LlvmAssembly.shorthand(), | |
273 | OutputType::Mir.shorthand(), | |
274 | OutputType::Object.shorthand(), | |
275 | OutputType::Metadata.shorthand(), | |
276 | OutputType::Exe.shorthand(), | |
277 | OutputType::DepInfo.shorthand(), | |
278 | ) | |
279 | } | |
280 | ||
281 | pub fn extension(&self) -> &'static str { | |
282 | match *self { | |
283 | OutputType::Bitcode => "bc", | |
284 | OutputType::Assembly => "s", | |
285 | OutputType::LlvmAssembly => "ll", | |
286 | OutputType::Mir => "mir", | |
287 | OutputType::Object => "o", | |
288 | OutputType::Metadata => "rmeta", | |
289 | OutputType::DepInfo => "d", | |
290 | OutputType::Exe => "", | |
291 | } | |
292 | } | |
293 | } | |
294 | ||
295 | /// The type of diagnostics output to generate. | |
296 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | |
297 | pub enum ErrorOutputType { | |
298 | /// Output meant for the consumption of humans. | |
299 | HumanReadable(HumanReadableErrorType), | |
300 | /// Output that's consumed by other tools such as `rustfix` or the `RLS`. | |
301 | Json { | |
302 | /// Render the JSON in a human readable way (with indents and newlines). | |
303 | pretty: bool, | |
304 | /// The JSON output includes a `rendered` field that includes the rendered | |
305 | /// human output. | |
306 | json_rendered: HumanReadableErrorType, | |
307 | }, | |
308 | } | |
309 | ||
310 | impl Default for ErrorOutputType { | |
311 | fn default() -> Self { | |
312 | Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) | |
313 | } | |
314 | } | |
315 | ||
316 | /// Use tree-based collections to cheaply get a deterministic `Hash` implementation. | |
317 | /// *Do not* switch `BTreeMap` out for an unsorted container type! That would break | |
318 | /// dependency tracking for command-line arguments. | |
319 | #[derive(Clone, Hash)] | |
320 | pub struct OutputTypes(BTreeMap<OutputType, Option<PathBuf>>); | |
321 | ||
322 | impl_stable_hash_via_hash!(OutputTypes); | |
323 | ||
324 | impl OutputTypes { | |
325 | pub fn new(entries: &[(OutputType, Option<PathBuf>)]) -> OutputTypes { | |
326 | OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) | |
327 | } | |
328 | ||
329 | pub fn get(&self, key: &OutputType) -> Option<&Option<PathBuf>> { | |
330 | self.0.get(key) | |
331 | } | |
332 | ||
333 | pub fn contains_key(&self, key: &OutputType) -> bool { | |
334 | self.0.contains_key(key) | |
335 | } | |
336 | ||
337 | pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<PathBuf>> { | |
338 | self.0.keys() | |
339 | } | |
340 | ||
341 | pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<PathBuf>> { | |
342 | self.0.values() | |
343 | } | |
344 | ||
345 | pub fn len(&self) -> usize { | |
346 | self.0.len() | |
347 | } | |
348 | ||
349 | // Returns `true` if any of the output types require codegen or linking. | |
350 | pub fn should_codegen(&self) -> bool { | |
351 | self.0.keys().any(|k| match *k { | |
352 | OutputType::Bitcode | |
353 | | OutputType::Assembly | |
354 | | OutputType::LlvmAssembly | |
355 | | OutputType::Mir | |
356 | | OutputType::Object | |
357 | | OutputType::Exe => true, | |
358 | OutputType::Metadata | OutputType::DepInfo => false, | |
359 | }) | |
360 | } | |
361 | } | |
362 | ||
363 | /// Use tree-based collections to cheaply get a deterministic `Hash` implementation. | |
364 | /// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That | |
365 | /// would break dependency tracking for command-line arguments. | |
366 | #[derive(Clone)] | |
367 | pub struct Externs(BTreeMap<String, ExternEntry>); | |
368 | ||
369 | #[derive(Clone, Debug)] | |
370 | pub struct ExternEntry { | |
371 | pub location: ExternLocation, | |
372 | /// Indicates this is a "private" dependency for the | |
373 | /// `exported_private_dependencies` lint. | |
374 | /// | |
375 | /// This can be set with the `priv` option like | |
376 | /// `--extern priv:name=foo.rlib`. | |
377 | pub is_private_dep: bool, | |
378 | /// Add the extern entry to the extern prelude. | |
379 | /// | |
380 | /// This can be disabled with the `noprelude` option like | |
381 | /// `--extern noprelude:name`. | |
382 | pub add_prelude: bool, | |
383 | } | |
384 | ||
385 | #[derive(Clone, Debug)] | |
386 | pub enum ExternLocation { | |
387 | /// Indicates to look for the library in the search paths. | |
388 | /// | |
389 | /// Added via `--extern name`. | |
390 | FoundInLibrarySearchDirectories, | |
391 | /// The locations where this extern entry must be found. | |
392 | /// | |
393 | /// The `CrateLoader` is responsible for loading these and figuring out | |
394 | /// which one to use. | |
395 | /// | |
396 | /// Added via `--extern prelude_name=some_file.rlib` | |
397 | ExactPaths(BTreeSet<String>), | |
398 | } | |
399 | ||
400 | impl Externs { | |
401 | pub fn new(data: BTreeMap<String, ExternEntry>) -> Externs { | |
402 | Externs(data) | |
403 | } | |
404 | ||
405 | pub fn get(&self, key: &str) -> Option<&ExternEntry> { | |
406 | self.0.get(key) | |
407 | } | |
408 | ||
409 | pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> { | |
410 | self.0.iter() | |
411 | } | |
412 | } | |
413 | ||
414 | impl ExternEntry { | |
415 | fn new(location: ExternLocation) -> ExternEntry { | |
416 | ExternEntry { location, is_private_dep: false, add_prelude: false } | |
417 | } | |
418 | ||
419 | pub fn files(&self) -> Option<impl Iterator<Item = &String>> { | |
420 | match &self.location { | |
421 | ExternLocation::ExactPaths(set) => Some(set.iter()), | |
422 | _ => None, | |
423 | } | |
424 | } | |
425 | } | |
426 | ||
427 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
428 | pub enum PrintRequest { | |
429 | FileNames, | |
430 | Sysroot, | |
431 | TargetLibdir, | |
432 | CrateName, | |
433 | Cfg, | |
434 | TargetList, | |
435 | TargetCPUs, | |
436 | TargetFeatures, | |
437 | RelocationModels, | |
438 | CodeModels, | |
439 | TlsModels, | |
440 | TargetSpec, | |
441 | NativeStaticLibs, | |
442 | } | |
443 | ||
444 | #[derive(Copy, Clone)] | |
445 | pub enum BorrowckMode { | |
446 | Mir, | |
447 | Migrate, | |
448 | } | |
449 | ||
450 | impl BorrowckMode { | |
451 | /// Returns whether we should run the MIR-based borrow check, but also fall back | |
452 | /// on the AST borrow check if the MIR-based one errors. | |
453 | pub fn migrate(self) -> bool { | |
454 | match self { | |
455 | BorrowckMode::Mir => false, | |
456 | BorrowckMode::Migrate => true, | |
457 | } | |
458 | } | |
459 | } | |
460 | ||
461 | pub enum Input { | |
462 | /// Load source code from a file. | |
463 | File(PathBuf), | |
464 | /// Load source code from a string. | |
465 | Str { | |
466 | /// A string that is shown in place of a filename. | |
467 | name: FileName, | |
468 | /// An anonymous string containing the source code. | |
469 | input: String, | |
470 | }, | |
471 | } | |
472 | ||
473 | impl Input { | |
474 | pub fn filestem(&self) -> &str { | |
475 | match *self { | |
476 | Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), | |
477 | Input::Str { .. } => "rust_out", | |
478 | } | |
479 | } | |
480 | ||
481 | pub fn get_input(&mut self) -> Option<&mut String> { | |
482 | match *self { | |
483 | Input::File(_) => None, | |
484 | Input::Str { ref mut input, .. } => Some(input), | |
485 | } | |
486 | } | |
487 | ||
488 | pub fn source_name(&self) -> FileName { | |
489 | match *self { | |
490 | Input::File(ref ifile) => ifile.clone().into(), | |
491 | Input::Str { ref name, .. } => name.clone(), | |
492 | } | |
493 | } | |
494 | } | |
495 | ||
496 | #[derive(Clone, Hash)] | |
497 | pub struct OutputFilenames { | |
498 | pub out_directory: PathBuf, | |
499 | filestem: String, | |
500 | pub single_output_file: Option<PathBuf>, | |
501 | pub outputs: OutputTypes, | |
502 | } | |
503 | ||
504 | impl_stable_hash_via_hash!(OutputFilenames); | |
505 | ||
506 | pub const RLINK_EXT: &str = "rlink"; | |
507 | pub const RUST_CGU_EXT: &str = "rcgu"; | |
508 | ||
509 | impl OutputFilenames { | |
510 | pub fn new( | |
511 | out_directory: PathBuf, | |
512 | out_filestem: String, | |
513 | single_output_file: Option<PathBuf>, | |
514 | extra: String, | |
515 | outputs: OutputTypes, | |
516 | ) -> Self { | |
517 | OutputFilenames { | |
518 | out_directory, | |
519 | single_output_file, | |
520 | outputs, | |
521 | filestem: format!("{}{}", out_filestem, extra), | |
522 | } | |
523 | } | |
524 | ||
525 | pub fn path(&self, flavor: OutputType) -> PathBuf { | |
526 | self.outputs | |
527 | .get(&flavor) | |
528 | .and_then(|p| p.to_owned()) | |
529 | .or_else(|| self.single_output_file.clone()) | |
530 | .unwrap_or_else(|| self.temp_path(flavor, None)) | |
531 | } | |
532 | ||
533 | /// Gets the path where a compilation artifact of the given type for the | |
534 | /// given codegen unit should be placed on disk. If codegen_unit_name is | |
535 | /// None, a path distinct from those of any codegen unit will be generated. | |
536 | pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf { | |
537 | let extension = flavor.extension(); | |
538 | self.temp_path_ext(extension, codegen_unit_name) | |
539 | } | |
540 | ||
541 | /// Like temp_path, but also supports things where there is no corresponding | |
542 | /// OutputType, like noopt-bitcode or lto-bitcode. | |
543 | pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { | |
544 | let mut extension = String::new(); | |
545 | ||
546 | if let Some(codegen_unit_name) = codegen_unit_name { | |
547 | extension.push_str(codegen_unit_name); | |
548 | } | |
549 | ||
550 | if !ext.is_empty() { | |
551 | if !extension.is_empty() { | |
552 | extension.push_str("."); | |
553 | extension.push_str(RUST_CGU_EXT); | |
554 | extension.push_str("."); | |
555 | } | |
556 | ||
557 | extension.push_str(ext); | |
558 | } | |
559 | ||
560 | self.with_extension(&extension) | |
561 | } | |
562 | ||
563 | pub fn with_extension(&self, extension: &str) -> PathBuf { | |
564 | let mut path = self.out_directory.join(&self.filestem); | |
565 | path.set_extension(extension); | |
566 | path | |
567 | } | |
568 | } | |
569 | ||
570 | pub fn host_triple() -> &'static str { | |
571 | // Get the host triple out of the build environment. This ensures that our | |
572 | // idea of the host triple is the same as for the set of libraries we've | |
573 | // actually built. We can't just take LLVM's host triple because they | |
574 | // normalize all ix86 architectures to i386. | |
575 | // | |
576 | // Instead of grabbing the host triple (for the current host), we grab (at | |
577 | // compile time) the target triple that this rustc is built with and | |
578 | // calling that (at runtime) the host triple. | |
579 | (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE") | |
580 | } | |
581 | ||
582 | impl Default for Options { | |
583 | fn default() -> Options { | |
584 | Options { | |
585 | crate_types: Vec::new(), | |
586 | optimize: OptLevel::No, | |
587 | debuginfo: DebugInfo::None, | |
588 | lint_opts: Vec::new(), | |
589 | lint_cap: None, | |
590 | describe_lints: false, | |
591 | output_types: OutputTypes(BTreeMap::new()), | |
592 | search_paths: vec![], | |
593 | maybe_sysroot: None, | |
594 | target_triple: TargetTriple::from_triple(host_triple()), | |
595 | test: false, | |
596 | incremental: None, | |
597 | debugging_opts: basic_debugging_options(), | |
598 | prints: Vec::new(), | |
599 | borrowck_mode: BorrowckMode::Migrate, | |
600 | cg: basic_codegen_options(), | |
601 | error_format: ErrorOutputType::default(), | |
602 | externs: Externs(BTreeMap::new()), | |
603 | crate_name: None, | |
604 | alt_std_name: None, | |
605 | libs: Vec::new(), | |
606 | unstable_features: UnstableFeatures::Disallow, | |
607 | debug_assertions: true, | |
608 | actually_rustdoc: false, | |
609 | cli_forced_codegen_units: None, | |
610 | cli_forced_thinlto_off: false, | |
611 | remap_path_prefix: Vec::new(), | |
612 | edition: DEFAULT_EDITION, | |
613 | json_artifact_notifications: false, | |
614 | pretty: None, | |
615 | } | |
616 | } | |
617 | } | |
618 | ||
619 | impl Options { | |
620 | /// Returns `true` if there is a reason to build the dep graph. | |
621 | pub fn build_dep_graph(&self) -> bool { | |
622 | self.incremental.is_some() | |
623 | || self.debugging_opts.dump_dep_graph | |
624 | || self.debugging_opts.query_dep_graph | |
625 | } | |
626 | ||
627 | #[inline(always)] | |
628 | pub fn enable_dep_node_debug_strs(&self) -> bool { | |
629 | cfg!(debug_assertions) | |
630 | && (self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info) | |
631 | } | |
632 | ||
633 | pub fn file_path_mapping(&self) -> FilePathMapping { | |
634 | FilePathMapping::new(self.remap_path_prefix.clone()) | |
635 | } | |
636 | ||
637 | /// Returns `true` if there will be an output file generated. | |
638 | pub fn will_create_output_file(&self) -> bool { | |
639 | !self.debugging_opts.parse_only && // The file is just being parsed | |
640 | !self.debugging_opts.ls // The file is just being queried | |
641 | } | |
642 | ||
643 | #[inline] | |
644 | pub fn share_generics(&self) -> bool { | |
645 | match self.debugging_opts.share_generics { | |
646 | Some(setting) => setting, | |
647 | None => match self.optimize { | |
648 | OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, | |
649 | OptLevel::Default | OptLevel::Aggressive => false, | |
650 | }, | |
651 | } | |
652 | } | |
653 | } | |
654 | ||
655 | impl DebuggingOptions { | |
656 | pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags { | |
657 | HandlerFlags { | |
658 | can_emit_warnings, | |
659 | treat_err_as_bug: self.treat_err_as_bug, | |
660 | dont_buffer_diagnostics: self.dont_buffer_diagnostics, | |
661 | report_delayed_bugs: self.report_delayed_bugs, | |
662 | macro_backtrace: self.macro_backtrace, | |
663 | deduplicate_diagnostics: self.deduplicate_diagnostics, | |
664 | } | |
665 | } | |
666 | } | |
667 | ||
668 | // The type of entry function, so users can have their own entry functions | |
669 | #[derive(Copy, Clone, PartialEq, Hash, Debug)] | |
670 | pub enum EntryFnType { | |
671 | Main, | |
672 | Start, | |
673 | } | |
674 | ||
675 | impl_stable_hash_via_hash!(EntryFnType); | |
676 | ||
677 | #[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] | |
678 | pub enum CrateType { | |
679 | Executable, | |
680 | Dylib, | |
681 | Rlib, | |
682 | Staticlib, | |
683 | Cdylib, | |
684 | ProcMacro, | |
685 | } | |
686 | ||
687 | impl_stable_hash_via_hash!(CrateType); | |
688 | ||
689 | #[derive(Clone, Hash)] | |
690 | pub enum Passes { | |
691 | Some(Vec<String>), | |
692 | All, | |
693 | } | |
694 | ||
695 | impl Passes { | |
696 | pub fn is_empty(&self) -> bool { | |
697 | match *self { | |
698 | Passes::Some(ref v) => v.is_empty(), | |
699 | Passes::All => false, | |
700 | } | |
701 | } | |
702 | } | |
703 | ||
704 | pub const fn default_lib_output() -> CrateType { | |
705 | CrateType::Rlib | |
706 | } | |
707 | ||
708 | pub fn default_configuration(sess: &Session) -> CrateConfig { | |
709 | let end = &sess.target.target.target_endian; | |
710 | let arch = &sess.target.target.arch; | |
711 | let wordsz = &sess.target.target.target_pointer_width; | |
712 | let os = &sess.target.target.target_os; | |
713 | let env = &sess.target.target.target_env; | |
714 | let vendor = &sess.target.target.target_vendor; | |
715 | let min_atomic_width = sess.target.target.min_atomic_width(); | |
716 | let max_atomic_width = sess.target.target.max_atomic_width(); | |
717 | let atomic_cas = sess.target.target.options.atomic_cas; | |
718 | ||
719 | let mut ret = FxHashSet::default(); | |
720 | ret.reserve(6); // the minimum number of insertions | |
721 | // Target bindings. | |
722 | ret.insert((sym::target_os, Some(Symbol::intern(os)))); | |
723 | if let Some(ref fam) = sess.target.target.options.target_family { | |
724 | ret.insert((sym::target_family, Some(Symbol::intern(fam)))); | |
725 | if fam == "windows" { | |
726 | ret.insert((sym::windows, None)); | |
727 | } else if fam == "unix" { | |
728 | ret.insert((sym::unix, None)); | |
729 | } | |
730 | } | |
731 | ret.insert((sym::target_arch, Some(Symbol::intern(arch)))); | |
732 | ret.insert((sym::target_endian, Some(Symbol::intern(end)))); | |
733 | ret.insert((sym::target_pointer_width, Some(Symbol::intern(wordsz)))); | |
734 | ret.insert((sym::target_env, Some(Symbol::intern(env)))); | |
735 | ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); | |
736 | if sess.target.target.options.has_elf_tls { | |
737 | ret.insert((sym::target_thread_local, None)); | |
738 | } | |
739 | for &i in &[8, 16, 32, 64, 128] { | |
740 | if i >= min_atomic_width && i <= max_atomic_width { | |
741 | let mut insert_atomic = |s| { | |
742 | ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s)))); | |
743 | if atomic_cas { | |
744 | ret.insert((sym::target_has_atomic, Some(Symbol::intern(s)))); | |
745 | } | |
746 | }; | |
747 | let s = i.to_string(); | |
748 | insert_atomic(&s); | |
749 | if &s == wordsz { | |
750 | insert_atomic("ptr"); | |
751 | } | |
752 | } | |
753 | } | |
754 | ||
755 | for s in sess.opts.debugging_opts.sanitizer { | |
756 | let symbol = Symbol::intern(&s.to_string()); | |
757 | ret.insert((sym::sanitize, Some(symbol))); | |
758 | } | |
759 | ||
760 | if sess.opts.debug_assertions { | |
761 | ret.insert((sym::debug_assertions, None)); | |
762 | } | |
763 | if sess.opts.crate_types.contains(&CrateType::ProcMacro) { | |
764 | ret.insert((sym::proc_macro, None)); | |
765 | } | |
766 | ret | |
767 | } | |
768 | ||
769 | /// Converts the crate `cfg!` configuration from `String` to `Symbol`. | |
770 | /// `rustc_interface::interface::Config` accepts this in the compiler configuration, | |
771 | /// but the symbol interner is not yet set up then, so we must convert it later. | |
772 | pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig { | |
773 | cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect() | |
774 | } | |
775 | ||
776 | pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig { | |
777 | // Combine the configuration requested by the session (command line) with | |
778 | // some default and generated configuration items. | |
779 | let default_cfg = default_configuration(sess); | |
780 | // If the user wants a test runner, then add the test cfg. | |
781 | if sess.opts.test { | |
782 | user_cfg.insert((sym::test, None)); | |
783 | } | |
784 | user_cfg.extend(default_cfg.iter().cloned()); | |
785 | user_cfg | |
786 | } | |
787 | ||
788 | pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Config { | |
789 | let target = Target::search(&opts.target_triple).unwrap_or_else(|e| { | |
790 | early_error( | |
791 | error_format, | |
792 | &format!( | |
793 | "Error loading target specification: {}. \ | |
794 | Use `--print target-list` for a list of built-in targets", | |
795 | e | |
796 | ), | |
797 | ) | |
798 | }); | |
799 | ||
800 | let ptr_width = match &target.target_pointer_width[..] { | |
801 | "16" => 16, | |
802 | "32" => 32, | |
803 | "64" => 64, | |
804 | w => early_error( | |
805 | error_format, | |
806 | &format!( | |
807 | "target specification was invalid: \ | |
808 | unrecognized target-pointer-width {}", | |
809 | w | |
810 | ), | |
811 | ), | |
812 | }; | |
813 | ||
814 | Config { target, ptr_width } | |
815 | } | |
816 | ||
817 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
818 | pub enum OptionStability { | |
819 | Stable, | |
820 | Unstable, | |
821 | } | |
822 | ||
823 | pub struct RustcOptGroup { | |
824 | pub apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>, | |
825 | pub name: &'static str, | |
826 | pub stability: OptionStability, | |
827 | } | |
828 | ||
829 | impl RustcOptGroup { | |
830 | pub fn is_stable(&self) -> bool { | |
831 | self.stability == OptionStability::Stable | |
832 | } | |
833 | ||
834 | pub fn stable<F>(name: &'static str, f: F) -> RustcOptGroup | |
835 | where | |
836 | F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, | |
837 | { | |
838 | RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Stable } | |
839 | } | |
840 | ||
841 | pub fn unstable<F>(name: &'static str, f: F) -> RustcOptGroup | |
842 | where | |
843 | F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, | |
844 | { | |
845 | RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Unstable } | |
846 | } | |
847 | } | |
848 | ||
849 | // The `opt` local module holds wrappers around the `getopts` API that | |
850 | // adds extra rustc-specific metadata to each option; such metadata | |
851 | // is exposed by . The public | |
852 | // functions below ending with `_u` are the functions that return | |
853 | // *unstable* options, i.e., options that are only enabled when the | |
854 | // user also passes the `-Z unstable-options` debugging flag. | |
855 | mod opt { | |
856 | // The `fn flag*` etc below are written so that we can use them | |
857 | // in the future; do not warn about them not being used right now. | |
858 | #![allow(dead_code)] | |
859 | ||
860 | use super::RustcOptGroup; | |
861 | ||
862 | pub type R = RustcOptGroup; | |
863 | pub type S = &'static str; | |
864 | ||
865 | fn stable<F>(name: S, f: F) -> R | |
866 | where | |
867 | F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, | |
868 | { | |
869 | RustcOptGroup::stable(name, f) | |
870 | } | |
871 | ||
872 | fn unstable<F>(name: S, f: F) -> R | |
873 | where | |
874 | F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, | |
875 | { | |
876 | RustcOptGroup::unstable(name, f) | |
877 | } | |
878 | ||
879 | fn longer(a: S, b: S) -> S { | |
880 | if a.len() > b.len() { a } else { b } | |
881 | } | |
882 | ||
883 | pub fn opt_s(a: S, b: S, c: S, d: S) -> R { | |
884 | stable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) | |
885 | } | |
886 | pub fn multi_s(a: S, b: S, c: S, d: S) -> R { | |
887 | stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) | |
888 | } | |
889 | pub fn flag_s(a: S, b: S, c: S) -> R { | |
890 | stable(longer(a, b), move |opts| opts.optflag(a, b, c)) | |
891 | } | |
892 | pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R { | |
893 | stable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) | |
894 | } | |
895 | pub fn flagmulti_s(a: S, b: S, c: S) -> R { | |
896 | stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) | |
897 | } | |
898 | ||
899 | pub fn opt(a: S, b: S, c: S, d: S) -> R { | |
900 | unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) | |
901 | } | |
902 | pub fn multi(a: S, b: S, c: S, d: S) -> R { | |
903 | unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) | |
904 | } | |
905 | pub fn flag(a: S, b: S, c: S) -> R { | |
906 | unstable(longer(a, b), move |opts| opts.optflag(a, b, c)) | |
907 | } | |
908 | pub fn flagopt(a: S, b: S, c: S, d: S) -> R { | |
909 | unstable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) | |
910 | } | |
911 | pub fn flagmulti(a: S, b: S, c: S) -> R { | |
912 | unstable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) | |
913 | } | |
914 | } | |
915 | ||
916 | /// Returns the "short" subset of the rustc command line options, | |
917 | /// including metadata for each option, such as whether the option is | |
918 | /// part of the stable long-term interface for rustc. | |
919 | pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> { | |
920 | vec![ | |
921 | opt::flag_s("h", "help", "Display this message"), | |
922 | opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), | |
923 | opt::multi_s( | |
924 | "L", | |
925 | "", | |
926 | "Add a directory to the library search path. The | |
927 | optional KIND can be one of dependency, crate, native, | |
928 | framework, or all (the default).", | |
929 | "[KIND=]PATH", | |
930 | ), | |
931 | opt::multi_s( | |
932 | "l", | |
933 | "", | |
934 | "Link the generated crate(s) to the specified native | |
935 | library NAME. The optional KIND can be one of | |
936 | static, framework, or dylib (the default).", | |
937 | "[KIND=]NAME", | |
938 | ), | |
939 | make_crate_type_option(), | |
940 | opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"), | |
941 | opt::opt_s( | |
942 | "", | |
943 | "edition", | |
944 | "Specify which edition of the compiler to use when compiling code.", | |
945 | EDITION_NAME_LIST, | |
946 | ), | |
947 | opt::multi_s( | |
948 | "", | |
949 | "emit", | |
950 | "Comma separated list of types of output for \ | |
951 | the compiler to emit", | |
952 | "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]", | |
953 | ), | |
954 | opt::multi_s( | |
955 | "", | |
956 | "print", | |
957 | "Compiler information to print on stdout", | |
958 | "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\ | |
959 | target-cpus|target-features|relocation-models|\ | |
960 | code-models|tls-models|target-spec-json|native-static-libs]", | |
961 | ), | |
962 | opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), | |
963 | opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), | |
964 | opt::opt_s("o", "", "Write output to <filename>", "FILENAME"), | |
965 | opt::opt_s( | |
966 | "", | |
967 | "out-dir", | |
968 | "Write output to compiler-chosen filename \ | |
969 | in <dir>", | |
970 | "DIR", | |
971 | ), | |
972 | opt::opt_s( | |
973 | "", | |
974 | "explain", | |
975 | "Provide a detailed explanation of an error \ | |
976 | message", | |
977 | "OPT", | |
978 | ), | |
979 | opt::flag_s("", "test", "Build a test harness"), | |
980 | opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"), | |
981 | opt::multi_s("W", "warn", "Set lint warnings", "OPT"), | |
982 | opt::multi_s("A", "allow", "Set lint allowed", "OPT"), | |
983 | opt::multi_s("D", "deny", "Set lint denied", "OPT"), | |
984 | opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"), | |
985 | opt::multi_s( | |
986 | "", | |
987 | "cap-lints", | |
988 | "Set the most restrictive lint level. \ | |
989 | More restrictive lints are capped at this \ | |
990 | level", | |
991 | "LEVEL", | |
992 | ), | |
993 | opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), | |
994 | opt::flag_s("V", "version", "Print version info and exit"), | |
995 | opt::flag_s("v", "verbose", "Use verbose output"), | |
996 | ] | |
997 | } | |
998 | ||
999 | /// Returns all rustc command line options, including metadata for | |
1000 | /// each option, such as whether the option is part of the stable | |
1001 | /// long-term interface for rustc. | |
1002 | pub fn rustc_optgroups() -> Vec<RustcOptGroup> { | |
1003 | let mut opts = rustc_short_optgroups(); | |
1004 | opts.extend(vec![ | |
1005 | opt::multi_s( | |
1006 | "", | |
1007 | "extern", | |
1008 | "Specify where an external rust library is located", | |
1009 | "NAME[=PATH]", | |
1010 | ), | |
1011 | opt::opt_s("", "sysroot", "Override the system root", "PATH"), | |
1012 | opt::multi("Z", "", "Set internal debugging options", "FLAG"), | |
1013 | opt::opt_s( | |
1014 | "", | |
1015 | "error-format", | |
1016 | "How errors and other messages are produced", | |
1017 | "human|json|short", | |
1018 | ), | |
1019 | opt::multi_s("", "json", "Configure the JSON output of the compiler", "CONFIG"), | |
1020 | opt::opt_s( | |
1021 | "", | |
1022 | "color", | |
1023 | "Configure coloring of output: | |
1024 | auto = colorize, if output goes to a tty (default); | |
1025 | always = always colorize output; | |
1026 | never = never colorize output", | |
1027 | "auto|always|never", | |
1028 | ), | |
1029 | opt::opt( | |
1030 | "", | |
1031 | "pretty", | |
1032 | "Pretty-print the input instead of compiling; | |
1033 | valid types are: `normal` (un-annotated source), | |
1034 | `expanded` (crates expanded), or | |
1035 | `expanded,identified` (fully parenthesized, AST nodes with IDs).", | |
1036 | "TYPE", | |
1037 | ), | |
1038 | opt::multi_s( | |
1039 | "", | |
1040 | "remap-path-prefix", | |
1041 | "Remap source names in all output (compiler messages and output files)", | |
1042 | "FROM=TO", | |
1043 | ), | |
1044 | ]); | |
1045 | opts | |
1046 | } | |
1047 | ||
1048 | pub fn get_cmd_lint_options( | |
1049 | matches: &getopts::Matches, | |
1050 | error_format: ErrorOutputType, | |
1051 | ) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) { | |
1052 | let mut lint_opts_with_position = vec![]; | |
1053 | let mut describe_lints = false; | |
1054 | ||
1055 | for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] { | |
1056 | for (passed_arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { | |
1057 | let arg_pos = if let lint::Forbid = level { | |
1058 | // HACK: forbid is always specified last, so it can't be overridden. | |
1059 | // FIXME: remove this once <https://github.com/rust-lang/rust/issues/70819> is | |
1060 | // fixed and `forbid` works as expected. | |
1061 | usize::MAX | |
1062 | } else { | |
1063 | passed_arg_pos | |
1064 | }; | |
1065 | if lint_name == "help" { | |
1066 | describe_lints = true; | |
1067 | } else { | |
1068 | lint_opts_with_position.push((arg_pos, lint_name.replace("-", "_"), level)); | |
1069 | } | |
1070 | } | |
1071 | } | |
1072 | ||
1073 | lint_opts_with_position.sort_by_key(|x| x.0); | |
1074 | let lint_opts = lint_opts_with_position | |
1075 | .iter() | |
1076 | .cloned() | |
1077 | .map(|(_, lint_name, level)| (lint_name, level)) | |
1078 | .collect(); | |
1079 | ||
1080 | let lint_cap = matches.opt_str("cap-lints").map(|cap| { | |
1081 | lint::Level::from_str(&cap) | |
1082 | .unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{}`", cap))) | |
1083 | }); | |
1084 | (lint_opts, describe_lints, lint_cap) | |
1085 | } | |
1086 | ||
1087 | /// Parses the `--color` flag. | |
1088 | pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { | |
1089 | match matches.opt_str("color").as_ref().map(|s| &s[..]) { | |
1090 | Some("auto") => ColorConfig::Auto, | |
1091 | Some("always") => ColorConfig::Always, | |
1092 | Some("never") => ColorConfig::Never, | |
1093 | ||
1094 | None => ColorConfig::Auto, | |
1095 | ||
1096 | Some(arg) => early_error( | |
1097 | ErrorOutputType::default(), | |
1098 | &format!( | |
1099 | "argument for `--color` must be auto, \ | |
1100 | always or never (instead was `{}`)", | |
1101 | arg | |
1102 | ), | |
1103 | ), | |
1104 | } | |
1105 | } | |
1106 | ||
1107 | /// Parse the `--json` flag. | |
1108 | /// | |
1109 | /// The first value returned is how to render JSON diagnostics, and the second | |
1110 | /// is whether or not artifact notifications are enabled. | |
1111 | pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) { | |
1112 | let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = | |
1113 | HumanReadableErrorType::Default; | |
1114 | let mut json_color = ColorConfig::Never; | |
1115 | let mut json_artifact_notifications = false; | |
1116 | for option in matches.opt_strs("json") { | |
1117 | // For now conservatively forbid `--color` with `--json` since `--json` | |
1118 | // won't actually be emitting any colors and anything colorized is | |
1119 | // embedded in a diagnostic message anyway. | |
1120 | if matches.opt_str("color").is_some() { | |
1121 | early_error( | |
1122 | ErrorOutputType::default(), | |
1123 | "cannot specify the `--color` option with `--json`", | |
1124 | ); | |
1125 | } | |
1126 | ||
1127 | for sub_option in option.split(',') { | |
1128 | match sub_option { | |
1129 | "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, | |
1130 | "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, | |
1131 | "artifacts" => json_artifact_notifications = true, | |
1132 | s => early_error( | |
1133 | ErrorOutputType::default(), | |
1134 | &format!("unknown `--json` option `{}`", s), | |
1135 | ), | |
1136 | } | |
1137 | } | |
1138 | } | |
1139 | (json_rendered(json_color), json_artifact_notifications) | |
1140 | } | |
1141 | ||
1142 | /// Parses the `--error-format` flag. | |
1143 | pub fn parse_error_format( | |
1144 | matches: &getopts::Matches, | |
1145 | color: ColorConfig, | |
1146 | json_rendered: HumanReadableErrorType, | |
1147 | ) -> ErrorOutputType { | |
1148 | // We need the `opts_present` check because the driver will send us Matches | |
1149 | // with only stable options if no unstable options are used. Since error-format | |
1150 | // is unstable, it will not be present. We have to use `opts_present` not | |
1151 | // `opt_present` because the latter will panic. | |
1152 | let error_format = if matches.opts_present(&["error-format".to_owned()]) { | |
1153 | match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { | |
1154 | None | Some("human") => { | |
1155 | ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) | |
1156 | } | |
1157 | Some("human-annotate-rs") => { | |
1158 | ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) | |
1159 | } | |
1160 | Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, | |
1161 | Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, | |
1162 | Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), | |
1163 | ||
1164 | Some(arg) => early_error( | |
1165 | ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), | |
1166 | &format!( | |
1167 | "argument for `--error-format` must be `human`, `json` or \ | |
1168 | `short` (instead was `{}`)", | |
1169 | arg | |
1170 | ), | |
1171 | ), | |
1172 | } | |
1173 | } else { | |
1174 | ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) | |
1175 | }; | |
1176 | ||
1177 | match error_format { | |
1178 | ErrorOutputType::Json { .. } => {} | |
1179 | ||
1180 | // Conservatively require that the `--json` argument is coupled with | |
1181 | // `--error-format=json`. This means that `--json` is specified we | |
1182 | // should actually be emitting JSON blobs. | |
1183 | _ if !matches.opt_strs("json").is_empty() => { | |
1184 | early_error( | |
1185 | ErrorOutputType::default(), | |
1186 | "using `--json` requires also using `--error-format=json`", | |
1187 | ); | |
1188 | } | |
1189 | ||
1190 | _ => {} | |
1191 | } | |
1192 | ||
1193 | error_format | |
1194 | } | |
1195 | ||
1196 | fn parse_crate_edition(matches: &getopts::Matches) -> Edition { | |
1197 | let edition = match matches.opt_str("edition") { | |
1198 | Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { | |
1199 | early_error( | |
1200 | ErrorOutputType::default(), | |
1201 | &format!( | |
1202 | "argument for `--edition` must be one of: \ | |
1203 | {}. (instead was `{}`)", | |
1204 | EDITION_NAME_LIST, arg | |
1205 | ), | |
1206 | ) | |
1207 | }), | |
1208 | None => DEFAULT_EDITION, | |
1209 | }; | |
1210 | ||
1211 | if !edition.is_stable() && !nightly_options::is_nightly_build() { | |
1212 | early_error( | |
1213 | ErrorOutputType::default(), | |
1214 | &format!( | |
1215 | "edition {} is unstable and only \ | |
1216 | available for nightly builds of rustc.", | |
1217 | edition, | |
1218 | ), | |
1219 | ) | |
1220 | } | |
1221 | ||
1222 | edition | |
1223 | } | |
1224 | ||
1225 | fn check_debug_option_stability( | |
1226 | debugging_opts: &DebuggingOptions, | |
1227 | error_format: ErrorOutputType, | |
1228 | json_rendered: HumanReadableErrorType, | |
1229 | ) { | |
1230 | if !debugging_opts.unstable_options { | |
1231 | if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { | |
1232 | early_error( | |
1233 | ErrorOutputType::Json { pretty: false, json_rendered }, | |
1234 | "`--error-format=pretty-json` is unstable", | |
1235 | ); | |
1236 | } | |
1237 | if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = | |
1238 | error_format | |
1239 | { | |
1240 | early_error( | |
1241 | ErrorOutputType::Json { pretty: false, json_rendered }, | |
1242 | "`--error-format=human-annotate-rs` is unstable", | |
1243 | ); | |
1244 | } | |
1245 | } | |
1246 | } | |
1247 | ||
1248 | fn parse_output_types( | |
1249 | debugging_opts: &DebuggingOptions, | |
1250 | matches: &getopts::Matches, | |
1251 | error_format: ErrorOutputType, | |
1252 | ) -> OutputTypes { | |
1253 | let mut output_types = BTreeMap::new(); | |
1254 | if !debugging_opts.parse_only { | |
1255 | for list in matches.opt_strs("emit") { | |
1256 | for output_type in list.split(',') { | |
1257 | let mut parts = output_type.splitn(2, '='); | |
1258 | let shorthand = parts.next().unwrap(); | |
1259 | let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { | |
1260 | early_error( | |
1261 | error_format, | |
1262 | &format!( | |
1263 | "unknown emission type: `{}` - expected one of: {}", | |
1264 | shorthand, | |
1265 | OutputType::shorthands_display(), | |
1266 | ), | |
1267 | ) | |
1268 | }); | |
1269 | let path = parts.next().map(PathBuf::from); | |
1270 | output_types.insert(output_type, path); | |
1271 | } | |
1272 | } | |
1273 | }; | |
1274 | if output_types.is_empty() { | |
1275 | output_types.insert(OutputType::Exe, None); | |
1276 | } | |
1277 | OutputTypes(output_types) | |
1278 | } | |
1279 | ||
1280 | fn should_override_cgus_and_disable_thinlto( | |
1281 | output_types: &OutputTypes, | |
1282 | matches: &getopts::Matches, | |
1283 | error_format: ErrorOutputType, | |
1284 | mut codegen_units: Option<usize>, | |
1285 | ) -> (bool, Option<usize>) { | |
1286 | let mut disable_thinlto = false; | |
1287 | // Issue #30063: if user requests LLVM-related output to one | |
1288 | // particular path, disable codegen-units. | |
1289 | let incompatible: Vec<_> = output_types | |
1290 | .0 | |
1291 | .iter() | |
1292 | .map(|ot_path| ot_path.0) | |
1293 | .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file()) | |
1294 | .map(|ot| ot.shorthand()) | |
1295 | .collect(); | |
1296 | if !incompatible.is_empty() { | |
1297 | match codegen_units { | |
1298 | Some(n) if n > 1 => { | |
1299 | if matches.opt_present("o") { | |
1300 | for ot in &incompatible { | |
1301 | early_warn( | |
1302 | error_format, | |
1303 | &format!( | |
1304 | "`--emit={}` with `-o` incompatible with \ | |
1305 | `-C codegen-units=N` for N > 1", | |
1306 | ot | |
1307 | ), | |
1308 | ); | |
1309 | } | |
1310 | early_warn(error_format, "resetting to default -C codegen-units=1"); | |
1311 | codegen_units = Some(1); | |
1312 | disable_thinlto = true; | |
1313 | } | |
1314 | } | |
1315 | _ => { | |
1316 | codegen_units = Some(1); | |
1317 | disable_thinlto = true; | |
1318 | } | |
1319 | } | |
1320 | } | |
1321 | ||
1322 | if codegen_units == Some(0) { | |
1323 | early_error(error_format, "value for codegen units must be a positive non-zero integer"); | |
1324 | } | |
1325 | ||
1326 | (disable_thinlto, codegen_units) | |
1327 | } | |
1328 | ||
1329 | fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) { | |
1330 | if debugging_opts.threads == 0 { | |
1331 | early_error(error_format, "value for threads must be a positive non-zero integer"); | |
1332 | } | |
1333 | ||
1334 | if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() { | |
1335 | early_error(error_format, "optimization fuel is incompatible with multiple threads"); | |
1336 | } | |
1337 | } | |
1338 | ||
1339 | fn collect_print_requests( | |
1340 | cg: &mut CodegenOptions, | |
1341 | dopts: &mut DebuggingOptions, | |
1342 | matches: &getopts::Matches, | |
1343 | error_format: ErrorOutputType, | |
1344 | ) -> Vec<PrintRequest> { | |
1345 | let mut prints = Vec::<PrintRequest>::new(); | |
1346 | if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { | |
1347 | prints.push(PrintRequest::TargetCPUs); | |
1348 | cg.target_cpu = None; | |
1349 | }; | |
1350 | if cg.target_feature == "help" { | |
1351 | prints.push(PrintRequest::TargetFeatures); | |
1352 | cg.target_feature = String::new(); | |
1353 | } | |
1354 | ||
1355 | prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { | |
1356 | "crate-name" => PrintRequest::CrateName, | |
1357 | "file-names" => PrintRequest::FileNames, | |
1358 | "sysroot" => PrintRequest::Sysroot, | |
1359 | "target-libdir" => PrintRequest::TargetLibdir, | |
1360 | "cfg" => PrintRequest::Cfg, | |
1361 | "target-list" => PrintRequest::TargetList, | |
1362 | "target-cpus" => PrintRequest::TargetCPUs, | |
1363 | "target-features" => PrintRequest::TargetFeatures, | |
1364 | "relocation-models" => PrintRequest::RelocationModels, | |
1365 | "code-models" => PrintRequest::CodeModels, | |
1366 | "tls-models" => PrintRequest::TlsModels, | |
1367 | "native-static-libs" => PrintRequest::NativeStaticLibs, | |
1368 | "target-spec-json" => { | |
1369 | if dopts.unstable_options { | |
1370 | PrintRequest::TargetSpec | |
1371 | } else { | |
1372 | early_error( | |
1373 | error_format, | |
1374 | "the `-Z unstable-options` flag must also be passed to \ | |
1375 | enable the target-spec-json print option", | |
1376 | ); | |
1377 | } | |
1378 | } | |
1379 | req => early_error(error_format, &format!("unknown print request `{}`", req)), | |
1380 | })); | |
1381 | ||
1382 | prints | |
1383 | } | |
1384 | ||
1385 | fn parse_target_triple(matches: &getopts::Matches, error_format: ErrorOutputType) -> TargetTriple { | |
1386 | match matches.opt_str("target") { | |
1387 | Some(target) if target.ends_with(".json") => { | |
1388 | let path = Path::new(&target); | |
1389 | TargetTriple::from_path(&path).unwrap_or_else(|_| { | |
1390 | early_error(error_format, &format!("target file {:?} does not exist", path)) | |
1391 | }) | |
1392 | } | |
1393 | Some(target) => TargetTriple::TargetTriple(target), | |
1394 | _ => TargetTriple::from_triple(host_triple()), | |
1395 | } | |
1396 | } | |
1397 | ||
1398 | fn parse_opt_level( | |
1399 | matches: &getopts::Matches, | |
1400 | cg: &CodegenOptions, | |
1401 | error_format: ErrorOutputType, | |
1402 | ) -> OptLevel { | |
1403 | // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able | |
1404 | // to use them interchangeably. However, because they're technically different flags, | |
1405 | // we need to work out manually which should take precedence if both are supplied (i.e. | |
1406 | // the rightmost flag). We do this by finding the (rightmost) position of both flags and | |
1407 | // comparing them. Note that if a flag is not found, its position will be `None`, which | |
1408 | // always compared less than `Some(_)`. | |
1409 | let max_o = matches.opt_positions("O").into_iter().max(); | |
1410 | let max_c = matches | |
1411 | .opt_strs_pos("C") | |
1412 | .into_iter() | |
1413 | .flat_map( | |
1414 | |(i, s)| { | |
1415 | if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } | |
1416 | }, | |
1417 | ) | |
1418 | .max(); | |
1419 | if max_o > max_c { | |
1420 | OptLevel::Default | |
1421 | } else { | |
1422 | match cg.opt_level.as_ref() { | |
1423 | "0" => OptLevel::No, | |
1424 | "1" => OptLevel::Less, | |
1425 | "2" => OptLevel::Default, | |
1426 | "3" => OptLevel::Aggressive, | |
1427 | "s" => OptLevel::Size, | |
1428 | "z" => OptLevel::SizeMin, | |
1429 | arg => { | |
1430 | early_error( | |
1431 | error_format, | |
1432 | &format!( | |
1433 | "optimization level needs to be \ | |
1434 | between 0-3, s or z (instead was `{}`)", | |
1435 | arg | |
1436 | ), | |
1437 | ); | |
1438 | } | |
1439 | } | |
1440 | } | |
1441 | } | |
1442 | ||
1443 | fn select_debuginfo( | |
1444 | matches: &getopts::Matches, | |
1445 | cg: &CodegenOptions, | |
1446 | error_format: ErrorOutputType, | |
1447 | ) -> DebugInfo { | |
1448 | let max_g = matches.opt_positions("g").into_iter().max(); | |
1449 | let max_c = matches | |
1450 | .opt_strs_pos("C") | |
1451 | .into_iter() | |
1452 | .flat_map( | |
1453 | |(i, s)| { | |
1454 | if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } | |
1455 | }, | |
1456 | ) | |
1457 | .max(); | |
1458 | if max_g > max_c { | |
1459 | DebugInfo::Full | |
1460 | } else { | |
1461 | match cg.debuginfo { | |
1462 | 0 => DebugInfo::None, | |
1463 | 1 => DebugInfo::Limited, | |
1464 | 2 => DebugInfo::Full, | |
1465 | arg => { | |
1466 | early_error( | |
1467 | error_format, | |
1468 | &format!( | |
1469 | "debug info level needs to be between \ | |
1470 | 0-2 (instead was `{}`)", | |
1471 | arg | |
1472 | ), | |
1473 | ); | |
1474 | } | |
1475 | } | |
1476 | } | |
1477 | } | |
1478 | ||
1479 | fn parse_libs( | |
1480 | matches: &getopts::Matches, | |
1481 | error_format: ErrorOutputType, | |
1482 | ) -> Vec<(String, Option<String>, NativeLibKind)> { | |
1483 | matches | |
1484 | .opt_strs("l") | |
1485 | .into_iter() | |
1486 | .map(|s| { | |
1487 | // Parse string of the form "[KIND=]lib[:new_name]", | |
1488 | // where KIND is one of "dylib", "framework", "static". | |
1489 | let mut parts = s.splitn(2, '='); | |
1490 | let kind = parts.next().unwrap(); | |
1491 | let (name, kind) = match (parts.next(), kind) { | |
1492 | (None, name) => (name, NativeLibKind::Unspecified), | |
1493 | (Some(name), "dylib") => (name, NativeLibKind::Dylib), | |
1494 | (Some(name), "framework") => (name, NativeLibKind::Framework), | |
1495 | (Some(name), "static") => (name, NativeLibKind::StaticBundle), | |
1496 | (Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle), | |
1497 | (_, s) => { | |
1498 | early_error( | |
1499 | error_format, | |
1500 | &format!( | |
1501 | "unknown library kind `{}`, expected \ | |
1502 | one of dylib, framework, or static", | |
1503 | s | |
1504 | ), | |
1505 | ); | |
1506 | } | |
1507 | }; | |
1508 | if kind == NativeLibKind::StaticNoBundle && !nightly_options::is_nightly_build() { | |
1509 | early_error( | |
1510 | error_format, | |
1511 | "the library kind 'static-nobundle' is only \ | |
1512 | accepted on the nightly compiler", | |
1513 | ); | |
1514 | } | |
1515 | let mut name_parts = name.splitn(2, ':'); | |
1516 | let name = name_parts.next().unwrap(); | |
1517 | let new_name = name_parts.next(); | |
1518 | (name.to_owned(), new_name.map(|n| n.to_owned()), kind) | |
1519 | }) | |
1520 | .collect() | |
1521 | } | |
1522 | ||
1523 | fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) -> BorrowckMode { | |
1524 | match dopts.borrowck.as_ref() { | |
1525 | "migrate" => BorrowckMode::Migrate, | |
1526 | "mir" => BorrowckMode::Mir, | |
1527 | m => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), | |
1528 | } | |
1529 | } | |
1530 | ||
1531 | pub fn parse_externs( | |
1532 | matches: &getopts::Matches, | |
1533 | debugging_opts: &DebuggingOptions, | |
1534 | error_format: ErrorOutputType, | |
1535 | ) -> Externs { | |
1536 | let is_unstable_enabled = debugging_opts.unstable_options; | |
1537 | let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new(); | |
1538 | for arg in matches.opt_strs("extern") { | |
1539 | let mut parts = arg.splitn(2, '='); | |
1540 | let name = parts | |
1541 | .next() | |
1542 | .unwrap_or_else(|| early_error(error_format, "--extern value must not be empty")); | |
1543 | let path = parts.next().map(|s| s.to_string()); | |
1544 | ||
1545 | let mut name_parts = name.splitn(2, ':'); | |
1546 | let first_part = name_parts.next(); | |
1547 | let second_part = name_parts.next(); | |
1548 | let (options, name) = match (first_part, second_part) { | |
1549 | (Some(opts), Some(name)) => (Some(opts), name), | |
1550 | (Some(name), None) => (None, name), | |
1551 | (None, None) => early_error(error_format, "--extern name must not be empty"), | |
1552 | _ => unreachable!(), | |
1553 | }; | |
1554 | ||
1555 | let entry = externs.entry(name.to_owned()); | |
1556 | ||
1557 | use std::collections::btree_map::Entry; | |
1558 | ||
1559 | let entry = if let Some(path) = path { | |
1560 | // --extern prelude_name=some_file.rlib | |
1561 | match entry { | |
1562 | Entry::Vacant(vacant) => { | |
1563 | let files = BTreeSet::from_iter(iter::once(path)); | |
1564 | vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files))) | |
1565 | } | |
1566 | Entry::Occupied(occupied) => { | |
1567 | let ext_ent = occupied.into_mut(); | |
1568 | match ext_ent { | |
1569 | ExternEntry { location: ExternLocation::ExactPaths(files), .. } => { | |
1570 | files.insert(path); | |
1571 | } | |
1572 | ExternEntry { | |
1573 | location: location @ ExternLocation::FoundInLibrarySearchDirectories, | |
1574 | .. | |
1575 | } => { | |
1576 | // Exact paths take precedence over search directories. | |
1577 | let files = BTreeSet::from_iter(iter::once(path)); | |
1578 | *location = ExternLocation::ExactPaths(files); | |
1579 | } | |
1580 | } | |
1581 | ext_ent | |
1582 | } | |
1583 | } | |
1584 | } else { | |
1585 | // --extern prelude_name | |
1586 | match entry { | |
1587 | Entry::Vacant(vacant) => { | |
1588 | vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories)) | |
1589 | } | |
1590 | Entry::Occupied(occupied) => { | |
1591 | // Ignore if already specified. | |
1592 | occupied.into_mut() | |
1593 | } | |
1594 | } | |
1595 | }; | |
1596 | ||
1597 | let mut is_private_dep = false; | |
1598 | let mut add_prelude = true; | |
1599 | if let Some(opts) = options { | |
1600 | if !is_unstable_enabled { | |
1601 | early_error( | |
1602 | error_format, | |
1603 | "the `-Z unstable-options` flag must also be passed to \ | |
1604 | enable `--extern options", | |
1605 | ); | |
1606 | } | |
1607 | for opt in opts.split(',') { | |
1608 | match opt { | |
1609 | "priv" => is_private_dep = true, | |
1610 | "noprelude" => { | |
1611 | if let ExternLocation::ExactPaths(_) = &entry.location { | |
1612 | add_prelude = false; | |
1613 | } else { | |
1614 | early_error( | |
1615 | error_format, | |
1616 | "the `noprelude` --extern option requires a file path", | |
1617 | ); | |
1618 | } | |
1619 | } | |
1620 | _ => early_error(error_format, &format!("unknown --extern option `{}`", opt)), | |
1621 | } | |
1622 | } | |
1623 | } | |
1624 | ||
1625 | // Crates start out being not private, and go to being private `priv` | |
1626 | // is specified. | |
1627 | entry.is_private_dep |= is_private_dep; | |
1628 | // If any flag is missing `noprelude`, then add to the prelude. | |
1629 | entry.add_prelude |= add_prelude; | |
1630 | } | |
1631 | Externs(externs) | |
1632 | } | |
1633 | ||
1634 | fn parse_remap_path_prefix( | |
1635 | matches: &getopts::Matches, | |
1636 | error_format: ErrorOutputType, | |
1637 | ) -> Vec<(PathBuf, PathBuf)> { | |
1638 | matches | |
1639 | .opt_strs("remap-path-prefix") | |
1640 | .into_iter() | |
1641 | .map(|remap| { | |
1642 | let mut parts = remap.rsplitn(2, '='); // reverse iterator | |
1643 | let to = parts.next(); | |
1644 | let from = parts.next(); | |
1645 | match (from, to) { | |
1646 | (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), | |
1647 | _ => early_error( | |
1648 | error_format, | |
1649 | "--remap-path-prefix must contain '=' between FROM and TO", | |
1650 | ), | |
1651 | } | |
1652 | }) | |
1653 | .collect() | |
1654 | } | |
1655 | ||
1656 | pub fn build_session_options(matches: &getopts::Matches) -> Options { | |
1657 | let color = parse_color(matches); | |
1658 | ||
1659 | let edition = parse_crate_edition(matches); | |
1660 | ||
1661 | let (json_rendered, json_artifact_notifications) = parse_json(matches); | |
1662 | ||
1663 | let error_format = parse_error_format(matches, color, json_rendered); | |
1664 | ||
1665 | let unparsed_crate_types = matches.opt_strs("crate-type"); | |
1666 | let crate_types = parse_crate_types_from_list(unparsed_crate_types) | |
1667 | .unwrap_or_else(|e| early_error(error_format, &e[..])); | |
1668 | ||
1669 | let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); | |
1670 | ||
1671 | let mut debugging_opts = build_debugging_options(matches, error_format); | |
1672 | check_debug_option_stability(&debugging_opts, error_format, json_rendered); | |
1673 | ||
1674 | let output_types = parse_output_types(&debugging_opts, matches, error_format); | |
1675 | ||
1676 | let mut cg = build_codegen_options(matches, error_format); | |
1677 | let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( | |
1678 | &output_types, | |
1679 | matches, | |
1680 | error_format, | |
1681 | cg.codegen_units, | |
1682 | ); | |
1683 | ||
1684 | check_thread_count(&debugging_opts, error_format); | |
1685 | ||
1686 | let incremental = cg.incremental.as_ref().map(PathBuf::from); | |
1687 | ||
1688 | if debugging_opts.profile && incremental.is_some() { | |
1689 | early_error( | |
1690 | error_format, | |
1691 | "can't instrument with gcov profiling when compiling incrementally", | |
1692 | ); | |
1693 | } | |
1694 | if debugging_opts.profile { | |
1695 | match codegen_units { | |
1696 | Some(1) => {} | |
1697 | None => codegen_units = Some(1), | |
1698 | Some(_) => early_error( | |
1699 | error_format, | |
1700 | "can't instrument with gcov profiling with multiple codegen units", | |
1701 | ), | |
1702 | } | |
1703 | } | |
1704 | ||
1705 | if cg.profile_generate.enabled() && cg.profile_use.is_some() { | |
1706 | early_error( | |
1707 | error_format, | |
1708 | "options `-C profile-generate` and `-C profile-use` are exclusive", | |
1709 | ); | |
1710 | } | |
1711 | ||
1712 | if debugging_opts.instrument_coverage { | |
1713 | if cg.profile_generate.enabled() || cg.profile_use.is_some() { | |
1714 | early_error( | |
1715 | error_format, | |
1716 | "option `-Z instrument-coverage` is not compatible with either `-C profile-use` \ | |
1717 | or `-C profile-generate`", | |
1718 | ); | |
1719 | } | |
1720 | ||
1721 | // `-Z instrument-coverage` implies: | |
1722 | // * `-Z symbol-mangling-version=v0` - to ensure consistent and reversible name mangling. | |
1723 | // Note, LLVM coverage tools can analyze coverage over multiple runs, including some | |
1724 | // changes to source code; so mangled names must be consistent across compilations. | |
1725 | // * `-C link-dead-code` - so unexecuted code is still counted as zero, rather than be | |
1726 | // optimized out. Note that instrumenting dead code can be explicitly disabled with: | |
1727 | // `-Z instrument-coverage -C link-dead-code=no`. | |
1728 | debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; | |
1729 | if cg.link_dead_code == None { | |
1730 | // FIXME(richkadel): Investigate if the `instrument-coverage` implementation can | |
1731 | // inject ["zero counters"](https://llvm.org/docs/CoverageMappingFormat.html#counter) | |
1732 | // in the coverage map when "dead code" is removed, rather than forcing `link-dead-code`. | |
1733 | cg.link_dead_code = Some(true); | |
1734 | } | |
1735 | } | |
1736 | ||
1737 | if !cg.embed_bitcode { | |
1738 | match cg.lto { | |
1739 | LtoCli::No | LtoCli::Unspecified => {} | |
1740 | LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( | |
1741 | error_format, | |
1742 | "options `-C embed-bitcode=no` and `-C lto` are incompatible", | |
1743 | ), | |
1744 | } | |
1745 | } | |
1746 | ||
1747 | let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format); | |
1748 | ||
1749 | let cg = cg; | |
1750 | ||
1751 | let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); | |
1752 | let target_triple = parse_target_triple(matches, error_format); | |
1753 | let opt_level = parse_opt_level(matches, &cg, error_format); | |
1754 | // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able | |
1755 | // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) | |
1756 | // for more details. | |
1757 | let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); | |
1758 | let debuginfo = select_debuginfo(matches, &cg, error_format); | |
1759 | ||
1760 | let mut search_paths = vec![]; | |
1761 | for s in &matches.opt_strs("L") { | |
1762 | search_paths.push(SearchPath::from_cli_opt(&s[..], error_format)); | |
1763 | } | |
1764 | ||
1765 | let libs = parse_libs(matches, error_format); | |
1766 | ||
1767 | let test = matches.opt_present("test"); | |
1768 | ||
1769 | let borrowck_mode = parse_borrowck_mode(&debugging_opts, error_format); | |
1770 | ||
1771 | if !cg.remark.is_empty() && debuginfo == DebugInfo::None { | |
1772 | early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations"); | |
1773 | } | |
1774 | ||
1775 | let externs = parse_externs(matches, &debugging_opts, error_format); | |
1776 | ||
1777 | let crate_name = matches.opt_str("crate-name"); | |
1778 | ||
1779 | let remap_path_prefix = parse_remap_path_prefix(matches, error_format); | |
1780 | ||
1781 | let pretty = parse_pretty(matches, &debugging_opts, error_format); | |
1782 | ||
1783 | Options { | |
1784 | crate_types, | |
1785 | optimize: opt_level, | |
1786 | debuginfo, | |
1787 | lint_opts, | |
1788 | lint_cap, | |
1789 | describe_lints, | |
1790 | output_types, | |
1791 | search_paths, | |
1792 | maybe_sysroot: sysroot_opt, | |
1793 | target_triple, | |
1794 | test, | |
1795 | incremental, | |
1796 | debugging_opts, | |
1797 | prints, | |
1798 | borrowck_mode, | |
1799 | cg, | |
1800 | error_format, | |
1801 | externs, | |
1802 | crate_name, | |
1803 | alt_std_name: None, | |
1804 | libs, | |
1805 | unstable_features: UnstableFeatures::from_environment(), | |
1806 | debug_assertions, | |
1807 | actually_rustdoc: false, | |
1808 | cli_forced_codegen_units: codegen_units, | |
1809 | cli_forced_thinlto_off: disable_thinlto, | |
1810 | remap_path_prefix, | |
1811 | edition, | |
1812 | json_artifact_notifications, | |
1813 | pretty, | |
1814 | } | |
1815 | } | |
1816 | ||
1817 | fn parse_pretty( | |
1818 | matches: &getopts::Matches, | |
1819 | debugging_opts: &DebuggingOptions, | |
1820 | efmt: ErrorOutputType, | |
1821 | ) -> Option<PpMode> { | |
1822 | let pretty = if debugging_opts.unstable_options { | |
1823 | matches.opt_default("pretty", "normal").map(|a| { | |
1824 | // stable pretty-print variants only | |
1825 | parse_pretty_inner(efmt, &a, false) | |
1826 | }) | |
1827 | } else { | |
1828 | None | |
1829 | }; | |
1830 | ||
1831 | return if pretty.is_none() { | |
1832 | debugging_opts.unpretty.as_ref().map(|a| { | |
1833 | // extended with unstable pretty-print variants | |
1834 | parse_pretty_inner(efmt, &a, true) | |
1835 | }) | |
1836 | } else { | |
1837 | pretty | |
1838 | }; | |
1839 | ||
1840 | fn parse_pretty_inner(efmt: ErrorOutputType, name: &str, extended: bool) -> PpMode { | |
1841 | use PpMode::*; | |
1842 | use PpSourceMode::*; | |
1843 | let first = match (name, extended) { | |
1844 | ("normal", _) => PpmSource(PpmNormal), | |
1845 | ("identified", _) => PpmSource(PpmIdentified), | |
1846 | ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops), | |
1847 | ("expanded", _) => PpmSource(PpmExpanded), | |
1848 | ("expanded,identified", _) => PpmSource(PpmExpandedIdentified), | |
1849 | ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene), | |
1850 | ("hir", true) => PpmHir(PpmNormal), | |
1851 | ("hir,identified", true) => PpmHir(PpmIdentified), | |
1852 | ("hir,typed", true) => PpmHir(PpmTyped), | |
1853 | ("hir-tree", true) => PpmHirTree(PpmNormal), | |
1854 | ("mir", true) => PpmMir, | |
1855 | ("mir-cfg", true) => PpmMirCFG, | |
1856 | _ => { | |
1857 | if extended { | |
1858 | early_error( | |
1859 | efmt, | |
1860 | &format!( | |
1861 | "argument to `unpretty` must be one of `normal`, \ | |
1862 | `expanded`, `identified`, `expanded,identified`, \ | |
1863 | `expanded,hygiene`, `everybody_loops`, \ | |
1864 | `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \ | |
1865 | `mir` or `mir-cfg`; got {}", | |
1866 | name | |
1867 | ), | |
1868 | ); | |
1869 | } else { | |
1870 | early_error( | |
1871 | efmt, | |
1872 | &format!( | |
1873 | "argument to `pretty` must be one of `normal`, \ | |
1874 | `expanded`, `identified`, or `expanded,identified`; got {}", | |
1875 | name | |
1876 | ), | |
1877 | ); | |
1878 | } | |
1879 | } | |
1880 | }; | |
1881 | tracing::debug!("got unpretty option: {:?}", first); | |
1882 | first | |
1883 | } | |
1884 | } | |
1885 | ||
1886 | pub fn make_crate_type_option() -> RustcOptGroup { | |
1887 | opt::multi_s( | |
1888 | "", | |
1889 | "crate-type", | |
1890 | "Comma separated list of types of crates | |
1891 | for the compiler to emit", | |
1892 | "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", | |
1893 | ) | |
1894 | } | |
1895 | ||
1896 | pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> { | |
1897 | let mut crate_types: Vec<CrateType> = Vec::new(); | |
1898 | for unparsed_crate_type in &list_list { | |
1899 | for part in unparsed_crate_type.split(',') { | |
1900 | let new_part = match part { | |
1901 | "lib" => default_lib_output(), | |
1902 | "rlib" => CrateType::Rlib, | |
1903 | "staticlib" => CrateType::Staticlib, | |
1904 | "dylib" => CrateType::Dylib, | |
1905 | "cdylib" => CrateType::Cdylib, | |
1906 | "bin" => CrateType::Executable, | |
1907 | "proc-macro" => CrateType::ProcMacro, | |
1908 | _ => return Err(format!("unknown crate type: `{}`", part)), | |
1909 | }; | |
1910 | if !crate_types.contains(&new_part) { | |
1911 | crate_types.push(new_part) | |
1912 | } | |
1913 | } | |
1914 | } | |
1915 | ||
1916 | Ok(crate_types) | |
1917 | } | |
1918 | ||
1919 | pub mod nightly_options { | |
1920 | use super::{ErrorOutputType, OptionStability, RustcOptGroup}; | |
1921 | use crate::early_error; | |
1922 | use rustc_feature::UnstableFeatures; | |
1923 | ||
1924 | pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { | |
1925 | is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options") | |
1926 | } | |
1927 | ||
1928 | pub fn is_nightly_build() -> bool { | |
1929 | UnstableFeatures::from_environment().is_nightly_build() | |
1930 | } | |
1931 | ||
1932 | pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { | |
1933 | let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options"); | |
1934 | let really_allows_unstable_options = | |
1935 | UnstableFeatures::from_environment().is_nightly_build(); | |
1936 | ||
1937 | for opt in flags.iter() { | |
1938 | if opt.stability == OptionStability::Stable { | |
1939 | continue; | |
1940 | } | |
1941 | if !matches.opt_present(opt.name) { | |
1942 | continue; | |
1943 | } | |
1944 | if opt.name != "Z" && !has_z_unstable_option { | |
1945 | early_error( | |
1946 | ErrorOutputType::default(), | |
1947 | &format!( | |
1948 | "the `-Z unstable-options` flag must also be passed to enable \ | |
1949 | the flag `{}`", | |
1950 | opt.name | |
1951 | ), | |
1952 | ); | |
1953 | } | |
1954 | if really_allows_unstable_options { | |
1955 | continue; | |
1956 | } | |
1957 | match opt.stability { | |
1958 | OptionStability::Unstable => { | |
1959 | let msg = format!( | |
1960 | "the option `{}` is only accepted on the \ | |
1961 | nightly compiler", | |
1962 | opt.name | |
1963 | ); | |
1964 | early_error(ErrorOutputType::default(), &msg); | |
1965 | } | |
1966 | OptionStability::Stable => {} | |
1967 | } | |
1968 | } | |
1969 | } | |
1970 | } | |
1971 | ||
1972 | impl fmt::Display for CrateType { | |
1973 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1974 | match *self { | |
1975 | CrateType::Executable => "bin".fmt(f), | |
1976 | CrateType::Dylib => "dylib".fmt(f), | |
1977 | CrateType::Rlib => "rlib".fmt(f), | |
1978 | CrateType::Staticlib => "staticlib".fmt(f), | |
1979 | CrateType::Cdylib => "cdylib".fmt(f), | |
1980 | CrateType::ProcMacro => "proc-macro".fmt(f), | |
1981 | } | |
1982 | } | |
1983 | } | |
1984 | ||
1985 | #[derive(Copy, Clone, PartialEq, Debug)] | |
1986 | pub enum PpSourceMode { | |
1987 | PpmNormal, | |
1988 | PpmEveryBodyLoops, | |
1989 | PpmExpanded, | |
1990 | PpmIdentified, | |
1991 | PpmExpandedIdentified, | |
1992 | PpmExpandedHygiene, | |
1993 | PpmTyped, | |
1994 | } | |
1995 | ||
1996 | #[derive(Copy, Clone, PartialEq, Debug)] | |
1997 | pub enum PpMode { | |
1998 | PpmSource(PpSourceMode), | |
1999 | PpmHir(PpSourceMode), | |
2000 | PpmHirTree(PpSourceMode), | |
2001 | PpmMir, | |
2002 | PpmMirCFG, | |
2003 | } | |
2004 | ||
2005 | impl PpMode { | |
2006 | pub fn needs_ast_map(&self) -> bool { | |
2007 | use PpMode::*; | |
2008 | use PpSourceMode::*; | |
2009 | match *self { | |
2010 | PpmSource(PpmNormal | PpmIdentified) => false, | |
2011 | ||
2012 | PpmSource( | |
2013 | PpmExpanded | PpmEveryBodyLoops | PpmExpandedIdentified | PpmExpandedHygiene, | |
2014 | ) | |
2015 | | PpmHir(_) | |
2016 | | PpmHirTree(_) | |
2017 | | PpmMir | |
2018 | | PpmMirCFG => true, | |
2019 | PpmSource(PpmTyped) => panic!("invalid state"), | |
2020 | } | |
2021 | } | |
2022 | ||
2023 | pub fn needs_analysis(&self) -> bool { | |
2024 | use PpMode::*; | |
2025 | match *self { | |
2026 | PpmMir | PpmMirCFG => true, | |
2027 | _ => false, | |
2028 | } | |
2029 | } | |
2030 | } | |
2031 | ||
2032 | /// Command-line arguments passed to the compiler have to be incorporated with | |
2033 | /// the dependency tracking system for incremental compilation. This module | |
2034 | /// provides some utilities to make this more convenient. | |
2035 | /// | |
2036 | /// The values of all command-line arguments that are relevant for dependency | |
2037 | /// tracking are hashed into a single value that determines whether the | |
2038 | /// incremental compilation cache can be re-used or not. This hashing is done | |
2039 | /// via the `DepTrackingHash` trait defined below, since the standard `Hash` | |
2040 | /// implementation might not be suitable (e.g., arguments are stored in a `Vec`, | |
2041 | /// the hash of which is order dependent, but we might not want the order of | |
2042 | /// arguments to make a difference for the hash). | |
2043 | /// | |
2044 | /// However, since the value provided by `Hash::hash` often *is* suitable, | |
2045 | /// especially for primitive types, there is the | |
2046 | /// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the | |
2047 | /// `Hash` implementation for `DepTrackingHash`. It's important though that | |
2048 | /// we have an opt-in scheme here, so one is hopefully forced to think about | |
2049 | /// how the hash should be calculated when adding a new command-line argument. | |
2050 | crate mod dep_tracking { | |
2051 | use super::{ | |
2052 | CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, | |
2053 | OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath, | |
2054 | SymbolManglingVersion, | |
2055 | }; | |
2056 | use crate::lint; | |
2057 | use crate::utils::NativeLibKind; | |
2058 | use rustc_feature::UnstableFeatures; | |
2059 | use rustc_span::edition::Edition; | |
2060 | use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; | |
2061 | use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel}; | |
2062 | use std::collections::hash_map::DefaultHasher; | |
2063 | use std::collections::BTreeMap; | |
2064 | use std::hash::Hash; | |
2065 | use std::path::PathBuf; | |
2066 | ||
2067 | pub trait DepTrackingHash { | |
2068 | fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType); | |
2069 | } | |
2070 | ||
2071 | macro_rules! impl_dep_tracking_hash_via_hash { | |
2072 | ($t:ty) => { | |
2073 | impl DepTrackingHash for $t { | |
2074 | fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType) { | |
2075 | Hash::hash(self, hasher); | |
2076 | } | |
2077 | } | |
2078 | }; | |
2079 | } | |
2080 | ||
2081 | macro_rules! impl_dep_tracking_hash_for_sortable_vec_of { | |
2082 | ($t:ty) => { | |
2083 | impl DepTrackingHash for Vec<$t> { | |
2084 | fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { | |
2085 | let mut elems: Vec<&$t> = self.iter().collect(); | |
2086 | elems.sort(); | |
2087 | Hash::hash(&elems.len(), hasher); | |
2088 | for (index, elem) in elems.iter().enumerate() { | |
2089 | Hash::hash(&index, hasher); | |
2090 | DepTrackingHash::hash(*elem, hasher, error_format); | |
2091 | } | |
2092 | } | |
2093 | } | |
2094 | }; | |
2095 | } | |
2096 | ||
2097 | impl_dep_tracking_hash_via_hash!(bool); | |
2098 | impl_dep_tracking_hash_via_hash!(usize); | |
2099 | impl_dep_tracking_hash_via_hash!(u64); | |
2100 | impl_dep_tracking_hash_via_hash!(String); | |
2101 | impl_dep_tracking_hash_via_hash!(PathBuf); | |
2102 | impl_dep_tracking_hash_via_hash!(lint::Level); | |
2103 | impl_dep_tracking_hash_via_hash!(Option<bool>); | |
2104 | impl_dep_tracking_hash_via_hash!(Option<usize>); | |
2105 | impl_dep_tracking_hash_via_hash!(Option<String>); | |
2106 | impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); | |
2107 | impl_dep_tracking_hash_via_hash!(Option<Vec<String>>); | |
2108 | impl_dep_tracking_hash_via_hash!(Option<MergeFunctions>); | |
2109 | impl_dep_tracking_hash_via_hash!(Option<RelocModel>); | |
2110 | impl_dep_tracking_hash_via_hash!(Option<CodeModel>); | |
2111 | impl_dep_tracking_hash_via_hash!(Option<TlsModel>); | |
2112 | impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>); | |
2113 | impl_dep_tracking_hash_via_hash!(Option<RelroLevel>); | |
2114 | impl_dep_tracking_hash_via_hash!(Option<lint::Level>); | |
2115 | impl_dep_tracking_hash_via_hash!(Option<PathBuf>); | |
2116 | impl_dep_tracking_hash_via_hash!(CrateType); | |
2117 | impl_dep_tracking_hash_via_hash!(MergeFunctions); | |
2118 | impl_dep_tracking_hash_via_hash!(PanicStrategy); | |
2119 | impl_dep_tracking_hash_via_hash!(RelroLevel); | |
2120 | impl_dep_tracking_hash_via_hash!(Passes); | |
2121 | impl_dep_tracking_hash_via_hash!(OptLevel); | |
2122 | impl_dep_tracking_hash_via_hash!(LtoCli); | |
2123 | impl_dep_tracking_hash_via_hash!(DebugInfo); | |
2124 | impl_dep_tracking_hash_via_hash!(UnstableFeatures); | |
2125 | impl_dep_tracking_hash_via_hash!(OutputTypes); | |
2126 | impl_dep_tracking_hash_via_hash!(NativeLibKind); | |
2127 | impl_dep_tracking_hash_via_hash!(SanitizerSet); | |
2128 | impl_dep_tracking_hash_via_hash!(CFGuard); | |
2129 | impl_dep_tracking_hash_via_hash!(TargetTriple); | |
2130 | impl_dep_tracking_hash_via_hash!(Edition); | |
2131 | impl_dep_tracking_hash_via_hash!(LinkerPluginLto); | |
2132 | impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); | |
2133 | impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); | |
2134 | impl_dep_tracking_hash_via_hash!(Option<SourceFileHashAlgorithm>); | |
2135 | ||
2136 | impl_dep_tracking_hash_for_sortable_vec_of!(String); | |
2137 | impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); | |
2138 | impl_dep_tracking_hash_for_sortable_vec_of!(CrateType); | |
2139 | impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); | |
2140 | impl_dep_tracking_hash_for_sortable_vec_of!((String, Option<String>, NativeLibKind)); | |
2141 | impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); | |
2142 | ||
2143 | impl<T1, T2> DepTrackingHash for (T1, T2) | |
2144 | where | |
2145 | T1: DepTrackingHash, | |
2146 | T2: DepTrackingHash, | |
2147 | { | |
2148 | fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { | |
2149 | Hash::hash(&0, hasher); | |
2150 | DepTrackingHash::hash(&self.0, hasher, error_format); | |
2151 | Hash::hash(&1, hasher); | |
2152 | DepTrackingHash::hash(&self.1, hasher, error_format); | |
2153 | } | |
2154 | } | |
2155 | ||
2156 | impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3) | |
2157 | where | |
2158 | T1: DepTrackingHash, | |
2159 | T2: DepTrackingHash, | |
2160 | T3: DepTrackingHash, | |
2161 | { | |
2162 | fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { | |
2163 | Hash::hash(&0, hasher); | |
2164 | DepTrackingHash::hash(&self.0, hasher, error_format); | |
2165 | Hash::hash(&1, hasher); | |
2166 | DepTrackingHash::hash(&self.1, hasher, error_format); | |
2167 | Hash::hash(&2, hasher); | |
2168 | DepTrackingHash::hash(&self.2, hasher, error_format); | |
2169 | } | |
2170 | } | |
2171 | ||
2172 | // This is a stable hash because BTreeMap is a sorted container | |
2173 | pub fn stable_hash( | |
2174 | sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, | |
2175 | hasher: &mut DefaultHasher, | |
2176 | error_format: ErrorOutputType, | |
2177 | ) { | |
2178 | for (key, sub_hash) in sub_hashes { | |
2179 | // Using Hash::hash() instead of DepTrackingHash::hash() is fine for | |
2180 | // the keys, as they are just plain strings | |
2181 | Hash::hash(&key.len(), hasher); | |
2182 | Hash::hash(key, hasher); | |
2183 | sub_hash.hash(hasher, error_format); | |
2184 | } | |
2185 | } | |
2186 | } |