e820ecdba1
Remove implicit names and values from `--cfg` in `--check-cfg` This PR remove the implicit names and values from `--cfg` in `--check-cfg` because the behavior is quite surprising but also because it's really easy to inadvertently really on the implicitness and when the `--cfg` is not set anymore to have an unexpected warning from an unexpected condition that pass with the implicitness. This change in behavior will also enable us to warn when an unexpected `--cfg` is passed, ex: the user wrote `--cfg=unstabl` instead of `--cfg=unstable`. The implementation of the warning will be done in a follow-up PR. cc `@petrochenkov`
2971 lines
100 KiB
Rust
2971 lines
100 KiB
Rust
//! Contains infrastructure for configuring the compiler, including parsing
|
|
//! command-line options.
|
|
|
|
pub use crate::options::*;
|
|
|
|
use crate::search_paths::SearchPath;
|
|
use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
|
|
use crate::{early_error, early_warn, Session};
|
|
use crate::{lint, HashStableContext};
|
|
|
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
|
|
|
use rustc_data_structures::stable_hasher::ToStableHashKey;
|
|
use rustc_target::abi::{Align, TargetDataLayout};
|
|
use rustc_target::spec::{LinkerFlavor, SplitDebuginfo, Target, TargetTriple, TargetWarnings};
|
|
use rustc_target::spec::{PanicStrategy, SanitizerSet, TARGETS};
|
|
|
|
use crate::parse::{CrateCheckConfig, CrateConfig};
|
|
use rustc_feature::UnstableFeatures;
|
|
use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION};
|
|
use rustc_span::source_map::{FileName, FilePathMapping};
|
|
use rustc_span::symbol::{sym, Symbol};
|
|
use rustc_span::RealFileName;
|
|
use rustc_span::SourceFileHashAlgorithm;
|
|
|
|
use rustc_errors::emitter::HumanReadableErrorType;
|
|
use rustc_errors::{ColorConfig, HandlerFlags};
|
|
|
|
use std::collections::btree_map::{
|
|
Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter,
|
|
};
|
|
use std::collections::{BTreeMap, BTreeSet};
|
|
use std::fmt;
|
|
use std::hash::Hash;
|
|
use std::iter::{self, FromIterator};
|
|
use std::path::{Path, PathBuf};
|
|
use std::str::{self, FromStr};
|
|
|
|
/// The different settings that the `-C strip` flag can have.
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
|
pub enum Strip {
|
|
/// Do not strip at all.
|
|
None,
|
|
|
|
/// Strip debuginfo.
|
|
Debuginfo,
|
|
|
|
/// Strip all symbols.
|
|
Symbols,
|
|
}
|
|
|
|
/// The different settings that the `-C control-flow-guard` flag can have.
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
|
pub enum CFGuard {
|
|
/// Do not emit Control Flow Guard metadata or checks.
|
|
Disabled,
|
|
|
|
/// Emit Control Flow Guard metadata but no checks.
|
|
NoChecks,
|
|
|
|
/// Emit Control Flow Guard metadata and checks.
|
|
Checks,
|
|
}
|
|
|
|
/// The different settings that the `-Z cf-protection` flag can have.
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
|
pub enum CFProtection {
|
|
/// Do not enable control-flow protection
|
|
None,
|
|
|
|
/// Emit control-flow protection for branches (enables indirect branch tracking).
|
|
Branch,
|
|
|
|
/// Emit control-flow protection for returns.
|
|
Return,
|
|
|
|
/// Emit control-flow protection for both branches and returns.
|
|
Full,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)]
|
|
pub enum OptLevel {
|
|
No, // -O0
|
|
Less, // -O1
|
|
Default, // -O2
|
|
Aggressive, // -O3
|
|
Size, // -Os
|
|
SizeMin, // -Oz
|
|
}
|
|
|
|
/// This is what the `LtoCli` values get mapped to after resolving defaults and
|
|
/// and taking other command line options into account.
|
|
///
|
|
/// Note that linker plugin-based LTO is a different mechanism entirely.
|
|
#[derive(Clone, PartialEq)]
|
|
pub enum Lto {
|
|
/// Don't do any LTO whatsoever.
|
|
No,
|
|
|
|
/// Do a full-crate-graph (inter-crate) LTO with ThinLTO.
|
|
Thin,
|
|
|
|
/// Do a local ThinLTO (intra-crate, over the CodeGen Units of the local crate only). This is
|
|
/// only relevant if multiple CGUs are used.
|
|
ThinLocal,
|
|
|
|
/// Do a full-crate-graph (inter-crate) LTO with "fat" LTO.
|
|
Fat,
|
|
}
|
|
|
|
/// The different settings that the `-C lto` flag can have.
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
|
pub enum LtoCli {
|
|
/// `-C lto=no`
|
|
No,
|
|
/// `-C lto=yes`
|
|
Yes,
|
|
/// `-C lto`
|
|
NoParam,
|
|
/// `-C lto=thin`
|
|
Thin,
|
|
/// `-C lto=fat`
|
|
Fat,
|
|
/// No `-C lto` flag passed
|
|
Unspecified,
|
|
}
|
|
|
|
/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a
|
|
/// document highlighting each span of every statement (including terminators). `Terminator` and
|
|
/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a
|
|
/// computed span for the block, representing the entire range, covering the block's terminator and
|
|
/// all of its statements.
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
|
pub enum MirSpanview {
|
|
/// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement`
|
|
Statement,
|
|
/// `-Z dump_mir_spanview=terminator`
|
|
Terminator,
|
|
/// `-Z dump_mir_spanview=block`
|
|
Block,
|
|
}
|
|
|
|
/// The different settings that the `-C instrument-coverage` flag can have.
|
|
///
|
|
/// Coverage instrumentation now supports combining `-C instrument-coverage`
|
|
/// with compiler and linker optimization (enabled with `-O` or `-C opt-level=1`
|
|
/// and higher). Nevertheless, there are many variables, depending on options
|
|
/// selected, code structure, and enabled attributes. If errors are encountered,
|
|
/// either while compiling or when generating `llvm-cov show` reports, consider
|
|
/// lowering the optimization level, including or excluding `-C link-dead-code`,
|
|
/// or using `-Zunstable-options -C instrument-coverage=except-unused-functions`
|
|
/// or `-Zunstable-options -C instrument-coverage=except-unused-generics`.
|
|
///
|
|
/// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the
|
|
/// coverage map, it will not attempt to generate synthetic functions for unused
|
|
/// (and not code-generated) functions (whether they are generic or not). As a
|
|
/// result, non-codegenned functions will not be included in the coverage map,
|
|
/// and will not appear, as covered or uncovered, in coverage reports.
|
|
///
|
|
/// `ExceptUnusedGenerics` will add synthetic functions to the coverage map,
|
|
/// unless the function has type parameters.
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
|
pub enum InstrumentCoverage {
|
|
/// Default `-C instrument-coverage` or `-C instrument-coverage=statement`
|
|
All,
|
|
/// `-Zunstable-options -C instrument-coverage=except-unused-generics`
|
|
ExceptUnusedGenerics,
|
|
/// `-Zunstable-options -C instrument-coverage=except-unused-functions`
|
|
ExceptUnusedFunctions,
|
|
/// `-C instrument-coverage=off` (or `no`, etc.)
|
|
Off,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Hash, Debug)]
|
|
pub enum LinkerPluginLto {
|
|
LinkerPlugin(PathBuf),
|
|
LinkerPluginAuto,
|
|
Disabled,
|
|
}
|
|
|
|
/// Used with `-Z assert-incr-state`.
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
|
pub enum IncrementalStateAssertion {
|
|
/// Found and loaded an existing session directory.
|
|
///
|
|
/// Note that this says nothing about whether any particular query
|
|
/// will be found to be red or green.
|
|
Loaded,
|
|
/// Did not load an existing session directory.
|
|
NotLoaded,
|
|
}
|
|
|
|
impl LinkerPluginLto {
|
|
pub fn enabled(&self) -> bool {
|
|
match *self {
|
|
LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true,
|
|
LinkerPluginLto::Disabled => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The different settings that can be enabled via the `-Z location-detail` flag.
|
|
#[derive(Clone, PartialEq, Hash, Debug)]
|
|
pub struct LocationDetail {
|
|
pub file: bool,
|
|
pub line: bool,
|
|
pub column: bool,
|
|
}
|
|
|
|
impl LocationDetail {
|
|
pub fn all() -> Self {
|
|
Self { file: true, line: true, column: true }
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Hash, Debug)]
|
|
pub enum SwitchWithOptPath {
|
|
Enabled(Option<PathBuf>),
|
|
Disabled,
|
|
}
|
|
|
|
impl SwitchWithOptPath {
|
|
pub fn enabled(&self) -> bool {
|
|
match *self {
|
|
SwitchWithOptPath::Enabled(_) => true,
|
|
SwitchWithOptPath::Disabled => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable_Generic)]
|
|
#[derive(Encodable, Decodable)]
|
|
pub enum SymbolManglingVersion {
|
|
Legacy,
|
|
V0,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
|
|
pub enum DebugInfo {
|
|
None,
|
|
Limited,
|
|
Full,
|
|
}
|
|
|
|
/// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split
|
|
/// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform
|
|
/// uses DWARF for debug-information.
|
|
///
|
|
/// Some debug-information requires link-time relocation and some does not. LLVM can partition
|
|
/// the debuginfo into sections depending on whether or not it requires link-time relocation. Split
|
|
/// DWARF provides a mechanism which allows the linker to skip the sections which don't require
|
|
/// link-time relocation - either by putting those sections in DWARF object files, or by keeping
|
|
/// them in the object file in such a way that the linker will skip them.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
|
|
pub enum SplitDwarfKind {
|
|
/// Sections which do not require relocation are written into object file but ignored by the
|
|
/// linker.
|
|
Single,
|
|
/// Sections which do not require relocation are written into a DWARF object (`.dwo`) file
|
|
/// which is ignored by the linker.
|
|
Split,
|
|
}
|
|
|
|
impl FromStr for SplitDwarfKind {
|
|
type Err = ();
|
|
|
|
fn from_str(s: &str) -> Result<Self, ()> {
|
|
Ok(match s {
|
|
"single" => SplitDwarfKind::Single,
|
|
"split" => SplitDwarfKind::Split,
|
|
_ => return Err(()),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)]
|
|
#[derive(Encodable, Decodable)]
|
|
pub enum OutputType {
|
|
Bitcode,
|
|
Assembly,
|
|
LlvmAssembly,
|
|
Mir,
|
|
Metadata,
|
|
Object,
|
|
Exe,
|
|
DepInfo,
|
|
}
|
|
|
|
impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
|
|
type KeyType = Self;
|
|
|
|
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
|
|
*self
|
|
}
|
|
}
|
|
|
|
impl OutputType {
|
|
fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
|
|
match *self {
|
|
OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true,
|
|
OutputType::Bitcode
|
|
| OutputType::Assembly
|
|
| OutputType::LlvmAssembly
|
|
| OutputType::Mir
|
|
| OutputType::Object => false,
|
|
}
|
|
}
|
|
|
|
fn shorthand(&self) -> &'static str {
|
|
match *self {
|
|
OutputType::Bitcode => "llvm-bc",
|
|
OutputType::Assembly => "asm",
|
|
OutputType::LlvmAssembly => "llvm-ir",
|
|
OutputType::Mir => "mir",
|
|
OutputType::Object => "obj",
|
|
OutputType::Metadata => "metadata",
|
|
OutputType::Exe => "link",
|
|
OutputType::DepInfo => "dep-info",
|
|
}
|
|
}
|
|
|
|
fn from_shorthand(shorthand: &str) -> Option<Self> {
|
|
Some(match shorthand {
|
|
"asm" => OutputType::Assembly,
|
|
"llvm-ir" => OutputType::LlvmAssembly,
|
|
"mir" => OutputType::Mir,
|
|
"llvm-bc" => OutputType::Bitcode,
|
|
"obj" => OutputType::Object,
|
|
"metadata" => OutputType::Metadata,
|
|
"link" => OutputType::Exe,
|
|
"dep-info" => OutputType::DepInfo,
|
|
_ => return None,
|
|
})
|
|
}
|
|
|
|
fn shorthands_display() -> String {
|
|
format!(
|
|
"`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`",
|
|
OutputType::Bitcode.shorthand(),
|
|
OutputType::Assembly.shorthand(),
|
|
OutputType::LlvmAssembly.shorthand(),
|
|
OutputType::Mir.shorthand(),
|
|
OutputType::Object.shorthand(),
|
|
OutputType::Metadata.shorthand(),
|
|
OutputType::Exe.shorthand(),
|
|
OutputType::DepInfo.shorthand(),
|
|
)
|
|
}
|
|
|
|
pub fn extension(&self) -> &'static str {
|
|
match *self {
|
|
OutputType::Bitcode => "bc",
|
|
OutputType::Assembly => "s",
|
|
OutputType::LlvmAssembly => "ll",
|
|
OutputType::Mir => "mir",
|
|
OutputType::Object => "o",
|
|
OutputType::Metadata => "rmeta",
|
|
OutputType::DepInfo => "d",
|
|
OutputType::Exe => "",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The type of diagnostics output to generate.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum ErrorOutputType {
|
|
/// Output meant for the consumption of humans.
|
|
HumanReadable(HumanReadableErrorType),
|
|
/// Output that's consumed by other tools such as `rustfix` or the `RLS`.
|
|
Json {
|
|
/// Render the JSON in a human readable way (with indents and newlines).
|
|
pretty: bool,
|
|
/// The JSON output includes a `rendered` field that includes the rendered
|
|
/// human output.
|
|
json_rendered: HumanReadableErrorType,
|
|
},
|
|
}
|
|
|
|
impl Default for ErrorOutputType {
|
|
fn default() -> Self {
|
|
Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto))
|
|
}
|
|
}
|
|
|
|
/// Parameter to control path trimming.
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
|
|
pub enum TrimmedDefPaths {
|
|
/// `try_print_trimmed_def_path` never prints a trimmed path and never calls the expensive query
|
|
#[default]
|
|
Never,
|
|
/// `try_print_trimmed_def_path` calls the expensive query, the query doesn't call `delay_good_path_bug`
|
|
Always,
|
|
/// `try_print_trimmed_def_path` calls the expensive query, the query calls `delay_good_path_bug`
|
|
GoodPath,
|
|
}
|
|
|
|
/// Use tree-based collections to cheaply get a deterministic `Hash` implementation.
|
|
/// *Do not* switch `BTreeMap` out for an unsorted container type! That would break
|
|
/// dependency tracking for command-line arguments. Also only hash keys, since tracking
|
|
/// should only depend on the output types, not the paths they're written to.
|
|
#[derive(Clone, Debug, Hash, HashStable_Generic)]
|
|
pub struct OutputTypes(BTreeMap<OutputType, Option<PathBuf>>);
|
|
|
|
impl OutputTypes {
|
|
pub fn new(entries: &[(OutputType, Option<PathBuf>)]) -> OutputTypes {
|
|
OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone()))))
|
|
}
|
|
|
|
pub fn get(&self, key: &OutputType) -> Option<&Option<PathBuf>> {
|
|
self.0.get(key)
|
|
}
|
|
|
|
pub fn contains_key(&self, key: &OutputType) -> bool {
|
|
self.0.contains_key(key)
|
|
}
|
|
|
|
pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<PathBuf>> {
|
|
self.0.keys()
|
|
}
|
|
|
|
pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<PathBuf>> {
|
|
self.0.values()
|
|
}
|
|
|
|
pub fn len(&self) -> usize {
|
|
self.0.len()
|
|
}
|
|
|
|
/// Returns `true` if any of the output types require codegen or linking.
|
|
pub fn should_codegen(&self) -> bool {
|
|
self.0.keys().any(|k| match *k {
|
|
OutputType::Bitcode
|
|
| OutputType::Assembly
|
|
| OutputType::LlvmAssembly
|
|
| OutputType::Mir
|
|
| OutputType::Object
|
|
| OutputType::Exe => true,
|
|
OutputType::Metadata | OutputType::DepInfo => false,
|
|
})
|
|
}
|
|
|
|
/// Returns `true` if any of the output types require linking.
|
|
pub fn should_link(&self) -> bool {
|
|
self.0.keys().any(|k| match *k {
|
|
OutputType::Bitcode
|
|
| OutputType::Assembly
|
|
| OutputType::LlvmAssembly
|
|
| OutputType::Mir
|
|
| OutputType::Metadata
|
|
| OutputType::Object
|
|
| OutputType::DepInfo => false,
|
|
OutputType::Exe => true,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Use tree-based collections to cheaply get a deterministic `Hash` implementation.
|
|
/// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That
|
|
/// would break dependency tracking for command-line arguments.
|
|
#[derive(Clone)]
|
|
pub struct Externs(BTreeMap<String, ExternEntry>);
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ExternEntry {
|
|
pub location: ExternLocation,
|
|
/// Indicates this is a "private" dependency for the
|
|
/// `exported_private_dependencies` lint.
|
|
///
|
|
/// This can be set with the `priv` option like
|
|
/// `--extern priv:name=foo.rlib`.
|
|
pub is_private_dep: bool,
|
|
/// Add the extern entry to the extern prelude.
|
|
///
|
|
/// This can be disabled with the `noprelude` option like
|
|
/// `--extern noprelude:name`.
|
|
pub add_prelude: bool,
|
|
/// The extern entry shouldn't be considered for unused dependency warnings.
|
|
///
|
|
/// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to
|
|
/// suppress `unused-crate-dependencies` warnings.
|
|
pub nounused_dep: bool,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum ExternLocation {
|
|
/// Indicates to look for the library in the search paths.
|
|
///
|
|
/// Added via `--extern name`.
|
|
FoundInLibrarySearchDirectories,
|
|
/// The locations where this extern entry must be found.
|
|
///
|
|
/// The `CrateLoader` is responsible for loading these and figuring out
|
|
/// which one to use.
|
|
///
|
|
/// Added via `--extern prelude_name=some_file.rlib`
|
|
ExactPaths(BTreeSet<CanonicalizedPath>),
|
|
}
|
|
|
|
impl Externs {
|
|
/// Used for testing.
|
|
pub fn new(data: BTreeMap<String, ExternEntry>) -> Externs {
|
|
Externs(data)
|
|
}
|
|
|
|
pub fn get(&self, key: &str) -> Option<&ExternEntry> {
|
|
self.0.get(key)
|
|
}
|
|
|
|
pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> {
|
|
self.0.iter()
|
|
}
|
|
|
|
pub fn len(&self) -> usize {
|
|
self.0.len()
|
|
}
|
|
}
|
|
|
|
impl ExternEntry {
|
|
fn new(location: ExternLocation) -> ExternEntry {
|
|
ExternEntry { location, is_private_dep: false, add_prelude: false, nounused_dep: false }
|
|
}
|
|
|
|
pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
|
|
match &self.location {
|
|
ExternLocation::ExactPaths(set) => Some(set.iter()),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
pub enum PrintRequest {
|
|
FileNames,
|
|
Sysroot,
|
|
TargetLibdir,
|
|
CrateName,
|
|
Cfg,
|
|
TargetList,
|
|
TargetCPUs,
|
|
TargetFeatures,
|
|
RelocationModels,
|
|
CodeModels,
|
|
TlsModels,
|
|
TargetSpec,
|
|
NativeStaticLibs,
|
|
StackProtectorStrategies,
|
|
LinkArgs,
|
|
}
|
|
|
|
pub enum Input {
|
|
/// Load source code from a file.
|
|
File(PathBuf),
|
|
/// Load source code from a string.
|
|
Str {
|
|
/// A string that is shown in place of a filename.
|
|
name: FileName,
|
|
/// An anonymous string containing the source code.
|
|
input: String,
|
|
},
|
|
}
|
|
|
|
impl Input {
|
|
pub fn filestem(&self) -> &str {
|
|
match *self {
|
|
Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(),
|
|
Input::Str { .. } => "rust_out",
|
|
}
|
|
}
|
|
|
|
pub fn source_name(&self) -> FileName {
|
|
match *self {
|
|
Input::File(ref ifile) => ifile.clone().into(),
|
|
Input::Str { ref name, .. } => name.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Hash, Debug, HashStable_Generic)]
|
|
pub struct OutputFilenames {
|
|
pub out_directory: PathBuf,
|
|
filestem: String,
|
|
pub single_output_file: Option<PathBuf>,
|
|
pub temps_directory: Option<PathBuf>,
|
|
pub outputs: OutputTypes,
|
|
}
|
|
|
|
pub const RLINK_EXT: &str = "rlink";
|
|
pub const RUST_CGU_EXT: &str = "rcgu";
|
|
pub const DWARF_OBJECT_EXT: &str = "dwo";
|
|
|
|
impl OutputFilenames {
|
|
pub fn new(
|
|
out_directory: PathBuf,
|
|
out_filestem: String,
|
|
single_output_file: Option<PathBuf>,
|
|
temps_directory: Option<PathBuf>,
|
|
extra: String,
|
|
outputs: OutputTypes,
|
|
) -> Self {
|
|
OutputFilenames {
|
|
out_directory,
|
|
single_output_file,
|
|
temps_directory,
|
|
outputs,
|
|
filestem: format!("{out_filestem}{extra}"),
|
|
}
|
|
}
|
|
|
|
pub fn path(&self, flavor: OutputType) -> PathBuf {
|
|
self.outputs
|
|
.get(&flavor)
|
|
.and_then(|p| p.to_owned())
|
|
.or_else(|| self.single_output_file.clone())
|
|
.unwrap_or_else(|| self.output_path(flavor))
|
|
}
|
|
|
|
/// Gets the output path where a compilation artifact of the given type
|
|
/// should be placed on disk.
|
|
pub fn output_path(&self, flavor: OutputType) -> PathBuf {
|
|
let extension = flavor.extension();
|
|
self.with_directory_and_extension(&self.out_directory, &extension)
|
|
}
|
|
|
|
/// Gets the path where a compilation artifact of the given type for the
|
|
/// given codegen unit should be placed on disk. If codegen_unit_name is
|
|
/// None, a path distinct from those of any codegen unit will be generated.
|
|
pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf {
|
|
let extension = flavor.extension();
|
|
self.temp_path_ext(extension, codegen_unit_name)
|
|
}
|
|
|
|
/// Like `temp_path`, but specifically for dwarf objects.
|
|
pub fn temp_path_dwo(&self, codegen_unit_name: Option<&str>) -> PathBuf {
|
|
self.temp_path_ext(DWARF_OBJECT_EXT, codegen_unit_name)
|
|
}
|
|
|
|
/// Like `temp_path`, but also supports things where there is no corresponding
|
|
/// OutputType, like noopt-bitcode or lto-bitcode.
|
|
pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf {
|
|
let mut extension = String::new();
|
|
|
|
if let Some(codegen_unit_name) = codegen_unit_name {
|
|
extension.push_str(codegen_unit_name);
|
|
}
|
|
|
|
if !ext.is_empty() {
|
|
if !extension.is_empty() {
|
|
extension.push('.');
|
|
extension.push_str(RUST_CGU_EXT);
|
|
extension.push('.');
|
|
}
|
|
|
|
extension.push_str(ext);
|
|
}
|
|
|
|
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
|
|
|
|
self.with_directory_and_extension(&temps_directory, &extension)
|
|
}
|
|
|
|
pub fn with_extension(&self, extension: &str) -> PathBuf {
|
|
self.with_directory_and_extension(&self.out_directory, extension)
|
|
}
|
|
|
|
fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
|
|
let mut path = directory.join(&self.filestem);
|
|
path.set_extension(extension);
|
|
path
|
|
}
|
|
|
|
/// Returns the path for the Split DWARF file - this can differ depending on which Split DWARF
|
|
/// mode is being used, which is the logic that this function is intended to encapsulate.
|
|
pub fn split_dwarf_path(
|
|
&self,
|
|
split_debuginfo_kind: SplitDebuginfo,
|
|
split_dwarf_kind: SplitDwarfKind,
|
|
cgu_name: Option<&str>,
|
|
) -> Option<PathBuf> {
|
|
let obj_out = self.temp_path(OutputType::Object, cgu_name);
|
|
let dwo_out = self.temp_path_dwo(cgu_name);
|
|
match (split_debuginfo_kind, split_dwarf_kind) {
|
|
(SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
|
|
// Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes
|
|
// (pointing at the path which is being determined here). Use the path to the current
|
|
// object file.
|
|
(SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => {
|
|
Some(obj_out)
|
|
}
|
|
// Split mode emits the DWARF into a different file, use that path.
|
|
(SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => {
|
|
Some(dwo_out)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn host_triple() -> &'static str {
|
|
// Get the host triple out of the build environment. This ensures that our
|
|
// idea of the host triple is the same as for the set of libraries we've
|
|
// actually built. We can't just take LLVM's host triple because they
|
|
// normalize all ix86 architectures to i386.
|
|
//
|
|
// Instead of grabbing the host triple (for the current host), we grab (at
|
|
// compile time) the target triple that this rustc is built with and
|
|
// calling that (at runtime) the host triple.
|
|
(option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE")
|
|
}
|
|
|
|
impl Default for Options {
|
|
fn default() -> Options {
|
|
Options {
|
|
assert_incr_state: None,
|
|
crate_types: Vec::new(),
|
|
optimize: OptLevel::No,
|
|
debuginfo: DebugInfo::None,
|
|
lint_opts: Vec::new(),
|
|
lint_cap: None,
|
|
describe_lints: false,
|
|
output_types: OutputTypes(BTreeMap::new()),
|
|
search_paths: vec![],
|
|
maybe_sysroot: None,
|
|
target_triple: TargetTriple::from_triple(host_triple()),
|
|
test: false,
|
|
incremental: None,
|
|
unstable_opts: Default::default(),
|
|
prints: Vec::new(),
|
|
cg: Default::default(),
|
|
error_format: ErrorOutputType::default(),
|
|
diagnostic_width: None,
|
|
externs: Externs(BTreeMap::new()),
|
|
crate_name: None,
|
|
libs: Vec::new(),
|
|
unstable_features: UnstableFeatures::Disallow,
|
|
debug_assertions: true,
|
|
actually_rustdoc: false,
|
|
trimmed_def_paths: TrimmedDefPaths::default(),
|
|
cli_forced_codegen_units: None,
|
|
cli_forced_thinlto_off: false,
|
|
remap_path_prefix: Vec::new(),
|
|
real_rust_source_base_dir: None,
|
|
edition: DEFAULT_EDITION,
|
|
json_artifact_notifications: false,
|
|
json_unused_externs: JsonUnusedExterns::No,
|
|
json_future_incompat: false,
|
|
pretty: None,
|
|
working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Options {
|
|
/// Returns `true` if there is a reason to build the dep graph.
|
|
pub fn build_dep_graph(&self) -> bool {
|
|
self.incremental.is_some()
|
|
|| self.unstable_opts.dump_dep_graph
|
|
|| self.unstable_opts.query_dep_graph
|
|
}
|
|
|
|
pub fn file_path_mapping(&self) -> FilePathMapping {
|
|
FilePathMapping::new(self.remap_path_prefix.clone())
|
|
}
|
|
|
|
/// Returns `true` if there will be an output file generated.
|
|
pub fn will_create_output_file(&self) -> bool {
|
|
!self.unstable_opts.parse_only && // The file is just being parsed
|
|
!self.unstable_opts.ls // The file is just being queried
|
|
}
|
|
|
|
#[inline]
|
|
pub fn share_generics(&self) -> bool {
|
|
match self.unstable_opts.share_generics {
|
|
Some(setting) => setting,
|
|
None => match self.optimize {
|
|
OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true,
|
|
OptLevel::Default | OptLevel::Aggressive => false,
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion {
|
|
self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy)
|
|
}
|
|
}
|
|
|
|
impl UnstableOptions {
|
|
pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags {
|
|
HandlerFlags {
|
|
can_emit_warnings,
|
|
treat_err_as_bug: self.treat_err_as_bug,
|
|
dont_buffer_diagnostics: self.dont_buffer_diagnostics,
|
|
report_delayed_bugs: self.report_delayed_bugs,
|
|
macro_backtrace: self.macro_backtrace,
|
|
deduplicate_diagnostics: self.deduplicate_diagnostics,
|
|
}
|
|
}
|
|
}
|
|
|
|
// The type of entry function, so users can have their own entry functions
|
|
#[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)]
|
|
pub enum EntryFnType {
|
|
Main,
|
|
Start,
|
|
}
|
|
|
|
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
|
|
#[derive(HashStable_Generic)]
|
|
pub enum CrateType {
|
|
Executable,
|
|
Dylib,
|
|
Rlib,
|
|
Staticlib,
|
|
Cdylib,
|
|
ProcMacro,
|
|
}
|
|
|
|
impl CrateType {
|
|
/// When generated, is this crate type an archive?
|
|
pub fn is_archive(&self) -> bool {
|
|
match *self {
|
|
CrateType::Rlib | CrateType::Staticlib => true,
|
|
CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => {
|
|
false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
|
|
pub enum Passes {
|
|
Some(Vec<String>),
|
|
All,
|
|
}
|
|
|
|
impl Passes {
|
|
pub fn is_empty(&self) -> bool {
|
|
match *self {
|
|
Passes::Some(ref v) => v.is_empty(),
|
|
Passes::All => false,
|
|
}
|
|
}
|
|
|
|
pub fn extend(&mut self, passes: impl IntoIterator<Item = String>) {
|
|
match *self {
|
|
Passes::Some(ref mut v) => v.extend(passes),
|
|
Passes::All => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Hash, Debug, PartialEq)]
|
|
pub enum PAuthKey {
|
|
A,
|
|
B,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Hash, Debug, PartialEq)]
|
|
pub struct PacRet {
|
|
pub leaf: bool,
|
|
pub key: PAuthKey,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Hash, Debug, PartialEq)]
|
|
pub struct BranchProtection {
|
|
pub bti: bool,
|
|
pub pac_ret: Option<PacRet>,
|
|
}
|
|
|
|
impl Default for BranchProtection {
|
|
fn default() -> Self {
|
|
BranchProtection { bti: false, pac_ret: None }
|
|
}
|
|
}
|
|
|
|
pub const fn default_lib_output() -> CrateType {
|
|
CrateType::Rlib
|
|
}
|
|
|
|
fn default_configuration(sess: &Session) -> CrateConfig {
|
|
// NOTE: This should be kept in sync with `CrateCheckConfig::fill_well_known` below.
|
|
let end = &sess.target.endian;
|
|
let arch = &sess.target.arch;
|
|
let wordsz = sess.target.pointer_width.to_string();
|
|
let os = &sess.target.os;
|
|
let env = &sess.target.env;
|
|
let abi = &sess.target.abi;
|
|
let vendor = &sess.target.vendor;
|
|
let min_atomic_width = sess.target.min_atomic_width();
|
|
let max_atomic_width = sess.target.max_atomic_width();
|
|
let atomic_cas = sess.target.atomic_cas;
|
|
let layout = TargetDataLayout::parse(&sess.target).unwrap_or_else(|err| {
|
|
sess.fatal(&err);
|
|
});
|
|
|
|
let mut ret = FxHashSet::default();
|
|
ret.reserve(7); // the minimum number of insertions
|
|
// Target bindings.
|
|
ret.insert((sym::target_os, Some(Symbol::intern(os))));
|
|
for fam in sess.target.families.as_ref() {
|
|
ret.insert((sym::target_family, Some(Symbol::intern(fam))));
|
|
if fam == "windows" {
|
|
ret.insert((sym::windows, None));
|
|
} else if fam == "unix" {
|
|
ret.insert((sym::unix, None));
|
|
}
|
|
}
|
|
ret.insert((sym::target_arch, Some(Symbol::intern(arch))));
|
|
ret.insert((sym::target_endian, Some(Symbol::intern(end.as_str()))));
|
|
ret.insert((sym::target_pointer_width, Some(Symbol::intern(&wordsz))));
|
|
ret.insert((sym::target_env, Some(Symbol::intern(env))));
|
|
ret.insert((sym::target_abi, Some(Symbol::intern(abi))));
|
|
ret.insert((sym::target_vendor, Some(Symbol::intern(vendor))));
|
|
if sess.target.has_thread_local {
|
|
ret.insert((sym::target_thread_local, None));
|
|
}
|
|
for (i, align) in [
|
|
(8, layout.i8_align.abi),
|
|
(16, layout.i16_align.abi),
|
|
(32, layout.i32_align.abi),
|
|
(64, layout.i64_align.abi),
|
|
(128, layout.i128_align.abi),
|
|
] {
|
|
if i >= min_atomic_width && i <= max_atomic_width {
|
|
let mut insert_atomic = |s, align: Align| {
|
|
ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s))));
|
|
if atomic_cas {
|
|
ret.insert((sym::target_has_atomic, Some(Symbol::intern(s))));
|
|
}
|
|
if align.bits() == i {
|
|
ret.insert((sym::target_has_atomic_equal_alignment, Some(Symbol::intern(s))));
|
|
}
|
|
};
|
|
let s = i.to_string();
|
|
insert_atomic(&s, align);
|
|
if s == wordsz {
|
|
insert_atomic("ptr", layout.pointer_align.abi);
|
|
}
|
|
}
|
|
}
|
|
|
|
let panic_strategy = sess.panic_strategy();
|
|
ret.insert((sym::panic, Some(panic_strategy.desc_symbol())));
|
|
|
|
for s in sess.opts.unstable_opts.sanitizer {
|
|
let symbol = Symbol::intern(&s.to_string());
|
|
ret.insert((sym::sanitize, Some(symbol)));
|
|
}
|
|
|
|
if sess.opts.debug_assertions {
|
|
ret.insert((sym::debug_assertions, None));
|
|
}
|
|
// JUSTIFICATION: before wrapper fn is available
|
|
#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
|
|
if sess.opts.crate_types.contains(&CrateType::ProcMacro) {
|
|
ret.insert((sym::proc_macro, None));
|
|
}
|
|
ret
|
|
}
|
|
|
|
/// Converts the crate `cfg!` configuration from `String` to `Symbol`.
|
|
/// `rustc_interface::interface::Config` accepts this in the compiler configuration,
|
|
/// but the symbol interner is not yet set up then, so we must convert it later.
|
|
pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig {
|
|
cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect()
|
|
}
|
|
|
|
/// The parsed `--check-cfg` options
|
|
pub struct CheckCfg<T = String> {
|
|
/// The set of all `names()`, if None no name checking is performed
|
|
pub names_valid: Option<FxHashSet<T>>,
|
|
/// Is well known values activated
|
|
pub well_known_values: bool,
|
|
/// The set of all `values()`
|
|
pub values_valid: FxHashMap<T, FxHashSet<T>>,
|
|
}
|
|
|
|
impl<T> Default for CheckCfg<T> {
|
|
fn default() -> Self {
|
|
CheckCfg {
|
|
names_valid: Default::default(),
|
|
values_valid: Default::default(),
|
|
well_known_values: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> CheckCfg<T> {
|
|
fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> {
|
|
CheckCfg {
|
|
names_valid: self
|
|
.names_valid
|
|
.as_ref()
|
|
.map(|names_valid| names_valid.iter().map(|a| f(a)).collect()),
|
|
values_valid: self
|
|
.values_valid
|
|
.iter()
|
|
.map(|(a, b)| (f(a), b.iter().map(|b| f(b)).collect()))
|
|
.collect(),
|
|
well_known_values: self.well_known_values,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Converts the crate `--check-cfg` options from `String` to `Symbol`.
|
|
/// `rustc_interface::interface::Config` accepts this in the compiler configuration,
|
|
/// but the symbol interner is not yet set up then, so we must convert it later.
|
|
pub fn to_crate_check_config(cfg: CheckCfg) -> CrateCheckConfig {
|
|
cfg.map_data(|s| Symbol::intern(s))
|
|
}
|
|
|
|
impl CrateCheckConfig {
|
|
/// Fills a `CrateCheckConfig` with well-known configuration names.
|
|
fn fill_well_known_names(&mut self) {
|
|
// NOTE: This should be kept in sync with `default_configuration` and
|
|
// `fill_well_known_values`
|
|
const WELL_KNOWN_NAMES: &[Symbol] = &[
|
|
// rustc
|
|
sym::unix,
|
|
sym::windows,
|
|
sym::target_os,
|
|
sym::target_family,
|
|
sym::target_arch,
|
|
sym::target_endian,
|
|
sym::target_pointer_width,
|
|
sym::target_env,
|
|
sym::target_abi,
|
|
sym::target_vendor,
|
|
sym::target_thread_local,
|
|
sym::target_has_atomic_load_store,
|
|
sym::target_has_atomic,
|
|
sym::target_has_atomic_equal_alignment,
|
|
sym::target_feature,
|
|
sym::panic,
|
|
sym::sanitize,
|
|
sym::debug_assertions,
|
|
sym::proc_macro,
|
|
sym::test,
|
|
sym::feature,
|
|
// rustdoc
|
|
sym::doc,
|
|
sym::doctest,
|
|
// miri
|
|
sym::miri,
|
|
];
|
|
|
|
// We only insert well-known names if `names()` was activated
|
|
if let Some(names_valid) = &mut self.names_valid {
|
|
names_valid.extend(WELL_KNOWN_NAMES);
|
|
}
|
|
}
|
|
|
|
/// Fills a `CrateCheckConfig` with well-known configuration values.
|
|
fn fill_well_known_values(&mut self) {
|
|
if !self.well_known_values {
|
|
return;
|
|
}
|
|
|
|
// NOTE: This should be kept in sync with `default_configuration` and
|
|
// `fill_well_known_names`
|
|
|
|
let panic_values = &PanicStrategy::all();
|
|
|
|
let atomic_values = &[
|
|
sym::ptr,
|
|
sym::integer(8usize),
|
|
sym::integer(16usize),
|
|
sym::integer(32usize),
|
|
sym::integer(64usize),
|
|
sym::integer(128usize),
|
|
];
|
|
|
|
let sanitize_values = SanitizerSet::all()
|
|
.into_iter()
|
|
.map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap()));
|
|
|
|
// Unknown possible values:
|
|
// - `feature`
|
|
// - `target_feature`
|
|
|
|
// No-values
|
|
for name in [
|
|
sym::doc,
|
|
sym::miri,
|
|
sym::unix,
|
|
sym::test,
|
|
sym::doctest,
|
|
sym::windows,
|
|
sym::proc_macro,
|
|
sym::debug_assertions,
|
|
sym::target_thread_local,
|
|
] {
|
|
self.values_valid.entry(name).or_default();
|
|
}
|
|
|
|
// Pre-defined values
|
|
self.values_valid.entry(sym::panic).or_default().extend(panic_values);
|
|
self.values_valid.entry(sym::sanitize).or_default().extend(sanitize_values);
|
|
self.values_valid.entry(sym::target_has_atomic).or_default().extend(atomic_values);
|
|
self.values_valid
|
|
.entry(sym::target_has_atomic_load_store)
|
|
.or_default()
|
|
.extend(atomic_values);
|
|
self.values_valid
|
|
.entry(sym::target_has_atomic_equal_alignment)
|
|
.or_default()
|
|
.extend(atomic_values);
|
|
|
|
// Target specific values
|
|
{
|
|
const VALUES: [&Symbol; 8] = [
|
|
&sym::target_os,
|
|
&sym::target_family,
|
|
&sym::target_arch,
|
|
&sym::target_endian,
|
|
&sym::target_env,
|
|
&sym::target_abi,
|
|
&sym::target_vendor,
|
|
&sym::target_pointer_width,
|
|
];
|
|
|
|
// Initialize (if not already initialized)
|
|
for &e in VALUES {
|
|
self.values_valid.entry(e).or_default();
|
|
}
|
|
|
|
// Get all values map at once otherwise it would be costly.
|
|
// (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
|
|
let [
|
|
values_target_os,
|
|
values_target_family,
|
|
values_target_arch,
|
|
values_target_endian,
|
|
values_target_env,
|
|
values_target_abi,
|
|
values_target_vendor,
|
|
values_target_pointer_width,
|
|
] = self
|
|
.values_valid
|
|
.get_many_mut(VALUES)
|
|
.expect("unable to get all the check-cfg values buckets");
|
|
|
|
for target in TARGETS
|
|
.iter()
|
|
.map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
|
|
{
|
|
values_target_os.insert(Symbol::intern(&target.options.os));
|
|
values_target_family
|
|
.extend(target.options.families.iter().map(|family| Symbol::intern(family)));
|
|
values_target_arch.insert(Symbol::intern(&target.arch));
|
|
values_target_endian.insert(Symbol::intern(&target.options.endian.as_str()));
|
|
values_target_env.insert(Symbol::intern(&target.options.env));
|
|
values_target_abi.insert(Symbol::intern(&target.options.abi));
|
|
values_target_vendor.insert(Symbol::intern(&target.options.vendor));
|
|
values_target_pointer_width.insert(sym::integer(target.pointer_width));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn fill_well_known(&mut self) {
|
|
self.fill_well_known_names();
|
|
self.fill_well_known_values();
|
|
}
|
|
}
|
|
|
|
pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig {
|
|
// Combine the configuration requested by the session (command line) with
|
|
// some default and generated configuration items.
|
|
let default_cfg = default_configuration(sess);
|
|
// If the user wants a test runner, then add the test cfg.
|
|
if sess.opts.test {
|
|
user_cfg.insert((sym::test, None));
|
|
}
|
|
user_cfg.extend(default_cfg.iter().cloned());
|
|
user_cfg
|
|
}
|
|
|
|
pub(super) fn build_target_config(
|
|
opts: &Options,
|
|
target_override: Option<Target>,
|
|
sysroot: &Path,
|
|
) -> Target {
|
|
let target_result = target_override.map_or_else(
|
|
|| Target::search(&opts.target_triple, sysroot),
|
|
|t| Ok((t, TargetWarnings::empty())),
|
|
);
|
|
let (target, target_warnings) = target_result.unwrap_or_else(|e| {
|
|
early_error(
|
|
opts.error_format,
|
|
&format!(
|
|
"Error loading target specification: {}. \
|
|
Run `rustc --print target-list` for a list of built-in targets",
|
|
e
|
|
),
|
|
)
|
|
});
|
|
for warning in target_warnings.warning_messages() {
|
|
early_warn(opts.error_format, &warning)
|
|
}
|
|
|
|
if !matches!(target.pointer_width, 16 | 32 | 64) {
|
|
early_error(
|
|
opts.error_format,
|
|
&format!(
|
|
"target specification was invalid: \
|
|
unrecognized target-pointer-width {}",
|
|
target.pointer_width
|
|
),
|
|
)
|
|
}
|
|
|
|
target
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
pub enum OptionStability {
|
|
Stable,
|
|
Unstable,
|
|
}
|
|
|
|
pub struct RustcOptGroup {
|
|
pub apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>,
|
|
pub name: &'static str,
|
|
pub stability: OptionStability,
|
|
}
|
|
|
|
impl RustcOptGroup {
|
|
pub fn is_stable(&self) -> bool {
|
|
self.stability == OptionStability::Stable
|
|
}
|
|
|
|
pub fn stable<F>(name: &'static str, f: F) -> RustcOptGroup
|
|
where
|
|
F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
|
|
{
|
|
RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Stable }
|
|
}
|
|
|
|
pub fn unstable<F>(name: &'static str, f: F) -> RustcOptGroup
|
|
where
|
|
F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
|
|
{
|
|
RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Unstable }
|
|
}
|
|
}
|
|
|
|
// The `opt` local module holds wrappers around the `getopts` API that
|
|
// adds extra rustc-specific metadata to each option; such metadata
|
|
// is exposed by . The public
|
|
// functions below ending with `_u` are the functions that return
|
|
// *unstable* options, i.e., options that are only enabled when the
|
|
// user also passes the `-Z unstable-options` debugging flag.
|
|
mod opt {
|
|
// The `fn flag*` etc below are written so that we can use them
|
|
// in the future; do not warn about them not being used right now.
|
|
#![allow(dead_code)]
|
|
|
|
use super::RustcOptGroup;
|
|
|
|
pub type R = RustcOptGroup;
|
|
pub type S = &'static str;
|
|
|
|
fn stable<F>(name: S, f: F) -> R
|
|
where
|
|
F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
|
|
{
|
|
RustcOptGroup::stable(name, f)
|
|
}
|
|
|
|
fn unstable<F>(name: S, f: F) -> R
|
|
where
|
|
F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static,
|
|
{
|
|
RustcOptGroup::unstable(name, f)
|
|
}
|
|
|
|
fn longer(a: S, b: S) -> S {
|
|
if a.len() > b.len() { a } else { b }
|
|
}
|
|
|
|
pub fn opt_s(a: S, b: S, c: S, d: S) -> R {
|
|
stable(longer(a, b), move |opts| opts.optopt(a, b, c, d))
|
|
}
|
|
pub fn multi_s(a: S, b: S, c: S, d: S) -> R {
|
|
stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d))
|
|
}
|
|
pub fn flag_s(a: S, b: S, c: S) -> R {
|
|
stable(longer(a, b), move |opts| opts.optflag(a, b, c))
|
|
}
|
|
pub fn flagmulti_s(a: S, b: S, c: S) -> R {
|
|
stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c))
|
|
}
|
|
|
|
pub fn opt(a: S, b: S, c: S, d: S) -> R {
|
|
unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d))
|
|
}
|
|
pub fn multi(a: S, b: S, c: S, d: S) -> R {
|
|
unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d))
|
|
}
|
|
}
|
|
|
|
/// Returns the "short" subset of the rustc command line options,
|
|
/// including metadata for each option, such as whether the option is
|
|
/// part of the stable long-term interface for rustc.
|
|
pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
|
|
vec![
|
|
opt::flag_s("h", "help", "Display this message"),
|
|
opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"),
|
|
opt::multi("", "check-cfg", "Provide list of valid cfg options for checking", "SPEC"),
|
|
opt::multi_s(
|
|
"L",
|
|
"",
|
|
"Add a directory to the library search path. The
|
|
optional KIND can be one of dependency, crate, native,
|
|
framework, or all (the default).",
|
|
"[KIND=]PATH",
|
|
),
|
|
opt::multi_s(
|
|
"l",
|
|
"",
|
|
"Link the generated crate(s) to the specified native
|
|
library NAME. The optional KIND can be one of
|
|
static, framework, or dylib (the default).
|
|
Optional comma separated MODIFIERS (bundle|verbatim|whole-archive|as-needed)
|
|
may be specified each with a prefix of either '+' to
|
|
enable or '-' to disable.",
|
|
"[KIND[:MODIFIERS]=]NAME[:RENAME]",
|
|
),
|
|
make_crate_type_option(),
|
|
opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"),
|
|
opt::opt_s(
|
|
"",
|
|
"edition",
|
|
"Specify which edition of the compiler to use when compiling code.",
|
|
EDITION_NAME_LIST,
|
|
),
|
|
opt::multi_s(
|
|
"",
|
|
"emit",
|
|
"Comma separated list of types of output for \
|
|
the compiler to emit",
|
|
"[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]",
|
|
),
|
|
opt::multi_s(
|
|
"",
|
|
"print",
|
|
"Compiler information to print on stdout",
|
|
"[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\
|
|
target-cpus|target-features|relocation-models|code-models|\
|
|
tls-models|target-spec-json|native-static-libs|stack-protector-strategies|\
|
|
link-args]",
|
|
),
|
|
opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"),
|
|
opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"),
|
|
opt::opt_s("o", "", "Write output to <filename>", "FILENAME"),
|
|
opt::opt_s(
|
|
"",
|
|
"out-dir",
|
|
"Write output to compiler-chosen filename \
|
|
in <dir>",
|
|
"DIR",
|
|
),
|
|
opt::opt_s(
|
|
"",
|
|
"explain",
|
|
"Provide a detailed explanation of an error \
|
|
message",
|
|
"OPT",
|
|
),
|
|
opt::flag_s("", "test", "Build a test harness"),
|
|
opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"),
|
|
opt::multi_s("A", "allow", "Set lint allowed", "LINT"),
|
|
opt::multi_s("W", "warn", "Set lint warnings", "LINT"),
|
|
opt::multi_s("", "force-warn", "Set lint force-warn", "LINT"),
|
|
opt::multi_s("D", "deny", "Set lint denied", "LINT"),
|
|
opt::multi_s("F", "forbid", "Set lint forbidden", "LINT"),
|
|
opt::multi_s(
|
|
"",
|
|
"cap-lints",
|
|
"Set the most restrictive lint level. \
|
|
More restrictive lints are capped at this \
|
|
level",
|
|
"LEVEL",
|
|
),
|
|
opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
|
|
opt::flag_s("V", "version", "Print version info and exit"),
|
|
opt::flag_s("v", "verbose", "Use verbose output"),
|
|
]
|
|
}
|
|
|
|
/// Returns all rustc command line options, including metadata for
|
|
/// each option, such as whether the option is part of the stable
|
|
/// long-term interface for rustc.
|
|
pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
|
|
let mut opts = rustc_short_optgroups();
|
|
// FIXME: none of these descriptions are actually used
|
|
opts.extend(vec![
|
|
opt::multi_s(
|
|
"",
|
|
"extern",
|
|
"Specify where an external rust library is located",
|
|
"NAME[=PATH]",
|
|
),
|
|
opt::opt_s("", "sysroot", "Override the system root", "PATH"),
|
|
opt::multi("Z", "", "Set unstable / perma-unstable options", "FLAG"),
|
|
opt::opt_s(
|
|
"",
|
|
"error-format",
|
|
"How errors and other messages are produced",
|
|
"human|json|short",
|
|
),
|
|
opt::multi_s("", "json", "Configure the JSON output of the compiler", "CONFIG"),
|
|
opt::opt_s(
|
|
"",
|
|
"color",
|
|
"Configure coloring of output:
|
|
auto = colorize, if output goes to a tty (default);
|
|
always = always colorize output;
|
|
never = never colorize output",
|
|
"auto|always|never",
|
|
),
|
|
opt::opt_s(
|
|
"",
|
|
"diagnostic-width",
|
|
"Inform rustc of the width of the output so that diagnostics can be truncated to fit",
|
|
"WIDTH",
|
|
),
|
|
opt::multi_s(
|
|
"",
|
|
"remap-path-prefix",
|
|
"Remap source names in all output (compiler messages and output files)",
|
|
"FROM=TO",
|
|
),
|
|
]);
|
|
opts
|
|
}
|
|
|
|
pub fn get_cmd_lint_options(
|
|
matches: &getopts::Matches,
|
|
error_format: ErrorOutputType,
|
|
) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
|
|
let mut lint_opts_with_position = vec![];
|
|
let mut describe_lints = false;
|
|
|
|
for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] {
|
|
for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
|
|
if lint_name == "help" {
|
|
describe_lints = true;
|
|
} else {
|
|
lint_opts_with_position.push((arg_pos, lint_name.replace('-', "_"), level));
|
|
}
|
|
}
|
|
}
|
|
|
|
lint_opts_with_position.sort_by_key(|x| x.0);
|
|
let lint_opts = lint_opts_with_position
|
|
.iter()
|
|
.cloned()
|
|
.map(|(_, lint_name, level)| (lint_name, level))
|
|
.collect();
|
|
|
|
let lint_cap = matches.opt_str("cap-lints").map(|cap| {
|
|
lint::Level::from_str(&cap)
|
|
.unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{cap}`")))
|
|
});
|
|
|
|
(lint_opts, describe_lints, lint_cap)
|
|
}
|
|
|
|
/// Parses the `--color` flag.
|
|
pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
|
|
match matches.opt_str("color").as_ref().map(|s| &s[..]) {
|
|
Some("auto") => ColorConfig::Auto,
|
|
Some("always") => ColorConfig::Always,
|
|
Some("never") => ColorConfig::Never,
|
|
|
|
None => ColorConfig::Auto,
|
|
|
|
Some(arg) => early_error(
|
|
ErrorOutputType::default(),
|
|
&format!(
|
|
"argument for `--color` must be auto, \
|
|
always or never (instead was `{arg}`)"
|
|
),
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Possible json config files
|
|
pub struct JsonConfig {
|
|
pub json_rendered: HumanReadableErrorType,
|
|
pub json_artifact_notifications: bool,
|
|
pub json_unused_externs: JsonUnusedExterns,
|
|
pub json_future_incompat: bool,
|
|
}
|
|
|
|
/// Report unused externs in event stream
|
|
#[derive(Copy, Clone)]
|
|
pub enum JsonUnusedExterns {
|
|
/// Do not
|
|
No,
|
|
/// Report, but do not exit with failure status for deny/forbid
|
|
Silent,
|
|
/// Report, and also exit with failure status for deny/forbid
|
|
Loud,
|
|
}
|
|
|
|
impl JsonUnusedExterns {
|
|
pub fn is_enabled(&self) -> bool {
|
|
match self {
|
|
JsonUnusedExterns::No => false,
|
|
JsonUnusedExterns::Loud | JsonUnusedExterns::Silent => true,
|
|
}
|
|
}
|
|
|
|
pub fn is_loud(&self) -> bool {
|
|
match self {
|
|
JsonUnusedExterns::No | JsonUnusedExterns::Silent => false,
|
|
JsonUnusedExterns::Loud => true,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Parse the `--json` flag.
|
|
///
|
|
/// The first value returned is how to render JSON diagnostics, and the second
|
|
/// is whether or not artifact notifications are enabled.
|
|
pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
|
|
let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType =
|
|
HumanReadableErrorType::Default;
|
|
let mut json_color = ColorConfig::Never;
|
|
let mut json_artifact_notifications = false;
|
|
let mut json_unused_externs = JsonUnusedExterns::No;
|
|
let mut json_future_incompat = false;
|
|
for option in matches.opt_strs("json") {
|
|
// For now conservatively forbid `--color` with `--json` since `--json`
|
|
// won't actually be emitting any colors and anything colorized is
|
|
// embedded in a diagnostic message anyway.
|
|
if matches.opt_str("color").is_some() {
|
|
early_error(
|
|
ErrorOutputType::default(),
|
|
"cannot specify the `--color` option with `--json`",
|
|
);
|
|
}
|
|
|
|
for sub_option in option.split(',') {
|
|
match sub_option {
|
|
"diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
|
|
"diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
|
|
"artifacts" => json_artifact_notifications = true,
|
|
"unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
|
|
"unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
|
|
"future-incompat" => json_future_incompat = true,
|
|
s => early_error(
|
|
ErrorOutputType::default(),
|
|
&format!("unknown `--json` option `{s}`"),
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
JsonConfig {
|
|
json_rendered: json_rendered(json_color),
|
|
json_artifact_notifications,
|
|
json_unused_externs,
|
|
json_future_incompat,
|
|
}
|
|
}
|
|
|
|
/// Parses the `--error-format` flag.
|
|
pub fn parse_error_format(
|
|
matches: &getopts::Matches,
|
|
color: ColorConfig,
|
|
json_rendered: HumanReadableErrorType,
|
|
) -> ErrorOutputType {
|
|
// We need the `opts_present` check because the driver will send us Matches
|
|
// with only stable options if no unstable options are used. Since error-format
|
|
// is unstable, it will not be present. We have to use `opts_present` not
|
|
// `opt_present` because the latter will panic.
|
|
let error_format = if matches.opts_present(&["error-format".to_owned()]) {
|
|
match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
|
|
None | Some("human") => {
|
|
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color))
|
|
}
|
|
Some("human-annotate-rs") => {
|
|
ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color))
|
|
}
|
|
Some("json") => ErrorOutputType::Json { pretty: false, json_rendered },
|
|
Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered },
|
|
Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)),
|
|
|
|
Some(arg) => early_error(
|
|
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)),
|
|
&format!(
|
|
"argument for `--error-format` must be `human`, `json` or \
|
|
`short` (instead was `{arg}`)"
|
|
),
|
|
),
|
|
}
|
|
} else {
|
|
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color))
|
|
};
|
|
|
|
match error_format {
|
|
ErrorOutputType::Json { .. } => {}
|
|
|
|
// Conservatively require that the `--json` argument is coupled with
|
|
// `--error-format=json`. This means that `--json` is specified we
|
|
// should actually be emitting JSON blobs.
|
|
_ if !matches.opt_strs("json").is_empty() => {
|
|
early_error(
|
|
ErrorOutputType::default(),
|
|
"using `--json` requires also using `--error-format=json`",
|
|
);
|
|
}
|
|
|
|
_ => {}
|
|
}
|
|
|
|
error_format
|
|
}
|
|
|
|
pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition {
|
|
let edition = match matches.opt_str("edition") {
|
|
Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
|
|
early_error(
|
|
ErrorOutputType::default(),
|
|
&format!(
|
|
"argument for `--edition` must be one of: \
|
|
{EDITION_NAME_LIST}. (instead was `{arg}`)"
|
|
),
|
|
)
|
|
}),
|
|
None => DEFAULT_EDITION,
|
|
};
|
|
|
|
if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) {
|
|
let is_nightly = nightly_options::match_is_nightly_build(matches);
|
|
let msg = if !is_nightly {
|
|
format!(
|
|
"the crate requires edition {}, but the latest edition supported by this Rust version is {}",
|
|
edition, LATEST_STABLE_EDITION
|
|
)
|
|
} else {
|
|
format!("edition {edition} is unstable and only available with -Z unstable-options")
|
|
};
|
|
early_error(ErrorOutputType::default(), &msg)
|
|
}
|
|
|
|
edition
|
|
}
|
|
|
|
fn check_error_format_stability(
|
|
unstable_opts: &UnstableOptions,
|
|
error_format: ErrorOutputType,
|
|
json_rendered: HumanReadableErrorType,
|
|
) {
|
|
if !unstable_opts.unstable_options {
|
|
if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format {
|
|
early_error(
|
|
ErrorOutputType::Json { pretty: false, json_rendered },
|
|
"`--error-format=pretty-json` is unstable",
|
|
);
|
|
}
|
|
if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) =
|
|
error_format
|
|
{
|
|
early_error(
|
|
ErrorOutputType::Json { pretty: false, json_rendered },
|
|
"`--error-format=human-annotate-rs` is unstable",
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse_output_types(
|
|
unstable_opts: &UnstableOptions,
|
|
matches: &getopts::Matches,
|
|
error_format: ErrorOutputType,
|
|
) -> OutputTypes {
|
|
let mut output_types = BTreeMap::new();
|
|
if !unstable_opts.parse_only {
|
|
for list in matches.opt_strs("emit") {
|
|
for output_type in list.split(',') {
|
|
let (shorthand, path) = match output_type.split_once('=') {
|
|
None => (output_type, None),
|
|
Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))),
|
|
};
|
|
let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
|
|
early_error(
|
|
error_format,
|
|
&format!(
|
|
"unknown emission type: `{shorthand}` - expected one of: {display}",
|
|
display = OutputType::shorthands_display(),
|
|
),
|
|
)
|
|
});
|
|
output_types.insert(output_type, path);
|
|
}
|
|
}
|
|
};
|
|
if output_types.is_empty() {
|
|
output_types.insert(OutputType::Exe, None);
|
|
}
|
|
OutputTypes(output_types)
|
|
}
|
|
|
|
fn should_override_cgus_and_disable_thinlto(
|
|
output_types: &OutputTypes,
|
|
matches: &getopts::Matches,
|
|
error_format: ErrorOutputType,
|
|
mut codegen_units: Option<usize>,
|
|
) -> (bool, Option<usize>) {
|
|
let mut disable_thinlto = false;
|
|
// Issue #30063: if user requests LLVM-related output to one
|
|
// particular path, disable codegen-units.
|
|
let incompatible: Vec<_> = output_types
|
|
.0
|
|
.iter()
|
|
.map(|ot_path| ot_path.0)
|
|
.filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file())
|
|
.map(|ot| ot.shorthand())
|
|
.collect();
|
|
if !incompatible.is_empty() {
|
|
match codegen_units {
|
|
Some(n) if n > 1 => {
|
|
if matches.opt_present("o") {
|
|
for ot in &incompatible {
|
|
early_warn(
|
|
error_format,
|
|
&format!(
|
|
"`--emit={ot}` with `-o` incompatible with \
|
|
`-C codegen-units=N` for N > 1",
|
|
),
|
|
);
|
|
}
|
|
early_warn(error_format, "resetting to default -C codegen-units=1");
|
|
codegen_units = Some(1);
|
|
disable_thinlto = true;
|
|
}
|
|
}
|
|
_ => {
|
|
codegen_units = Some(1);
|
|
disable_thinlto = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if codegen_units == Some(0) {
|
|
early_error(error_format, "value for codegen units must be a positive non-zero integer");
|
|
}
|
|
|
|
(disable_thinlto, codegen_units)
|
|
}
|
|
|
|
fn check_thread_count(unstable_opts: &UnstableOptions, error_format: ErrorOutputType) {
|
|
if unstable_opts.threads == 0 {
|
|
early_error(error_format, "value for threads must be a positive non-zero integer");
|
|
}
|
|
|
|
if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() {
|
|
early_error(error_format, "optimization fuel is incompatible with multiple threads");
|
|
}
|
|
}
|
|
|
|
fn collect_print_requests(
|
|
cg: &mut CodegenOptions,
|
|
unstable_opts: &mut UnstableOptions,
|
|
matches: &getopts::Matches,
|
|
error_format: ErrorOutputType,
|
|
) -> Vec<PrintRequest> {
|
|
let mut prints = Vec::<PrintRequest>::new();
|
|
if cg.target_cpu.as_ref().map_or(false, |s| s == "help") {
|
|
prints.push(PrintRequest::TargetCPUs);
|
|
cg.target_cpu = None;
|
|
};
|
|
if cg.target_feature == "help" {
|
|
prints.push(PrintRequest::TargetFeatures);
|
|
cg.target_feature = String::new();
|
|
}
|
|
|
|
prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s {
|
|
"crate-name" => PrintRequest::CrateName,
|
|
"file-names" => PrintRequest::FileNames,
|
|
"sysroot" => PrintRequest::Sysroot,
|
|
"target-libdir" => PrintRequest::TargetLibdir,
|
|
"cfg" => PrintRequest::Cfg,
|
|
"target-list" => PrintRequest::TargetList,
|
|
"target-cpus" => PrintRequest::TargetCPUs,
|
|
"target-features" => PrintRequest::TargetFeatures,
|
|
"relocation-models" => PrintRequest::RelocationModels,
|
|
"code-models" => PrintRequest::CodeModels,
|
|
"tls-models" => PrintRequest::TlsModels,
|
|
"native-static-libs" => PrintRequest::NativeStaticLibs,
|
|
"stack-protector-strategies" => PrintRequest::StackProtectorStrategies,
|
|
"target-spec-json" => {
|
|
if unstable_opts.unstable_options {
|
|
PrintRequest::TargetSpec
|
|
} else {
|
|
early_error(
|
|
error_format,
|
|
"the `-Z unstable-options` flag must also be passed to \
|
|
enable the target-spec-json print option",
|
|
);
|
|
}
|
|
}
|
|
"link-args" => PrintRequest::LinkArgs,
|
|
req => early_error(error_format, &format!("unknown print request `{req}`")),
|
|
}));
|
|
|
|
prints
|
|
}
|
|
|
|
pub fn parse_target_triple(
|
|
matches: &getopts::Matches,
|
|
error_format: ErrorOutputType,
|
|
) -> TargetTriple {
|
|
match matches.opt_str("target") {
|
|
Some(target) if target.ends_with(".json") => {
|
|
let path = Path::new(&target);
|
|
TargetTriple::from_path(&path).unwrap_or_else(|_| {
|
|
early_error(error_format, &format!("target file {path:?} does not exist"))
|
|
})
|
|
}
|
|
Some(target) => TargetTriple::TargetTriple(target),
|
|
_ => TargetTriple::from_triple(host_triple()),
|
|
}
|
|
}
|
|
|
|
fn parse_opt_level(
|
|
matches: &getopts::Matches,
|
|
cg: &CodegenOptions,
|
|
error_format: ErrorOutputType,
|
|
) -> OptLevel {
|
|
// The `-O` and `-C opt-level` flags specify the same setting, so we want to be able
|
|
// to use them interchangeably. However, because they're technically different flags,
|
|
// we need to work out manually which should take precedence if both are supplied (i.e.
|
|
// the rightmost flag). We do this by finding the (rightmost) position of both flags and
|
|
// comparing them. Note that if a flag is not found, its position will be `None`, which
|
|
// always compared less than `Some(_)`.
|
|
let max_o = matches.opt_positions("O").into_iter().max();
|
|
let max_c = matches
|
|
.opt_strs_pos("C")
|
|
.into_iter()
|
|
.flat_map(|(i, s)| {
|
|
// NB: This can match a string without `=`.
|
|
if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None }
|
|
})
|
|
.max();
|
|
if max_o > max_c {
|
|
OptLevel::Default
|
|
} else {
|
|
match cg.opt_level.as_ref() {
|
|
"0" => OptLevel::No,
|
|
"1" => OptLevel::Less,
|
|
"2" => OptLevel::Default,
|
|
"3" => OptLevel::Aggressive,
|
|
"s" => OptLevel::Size,
|
|
"z" => OptLevel::SizeMin,
|
|
arg => {
|
|
early_error(
|
|
error_format,
|
|
&format!(
|
|
"optimization level needs to be \
|
|
between 0-3, s or z (instead was `{arg}`)"
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn select_debuginfo(
|
|
matches: &getopts::Matches,
|
|
cg: &CodegenOptions,
|
|
error_format: ErrorOutputType,
|
|
) -> DebugInfo {
|
|
let max_g = matches.opt_positions("g").into_iter().max();
|
|
let max_c = matches
|
|
.opt_strs_pos("C")
|
|
.into_iter()
|
|
.flat_map(|(i, s)| {
|
|
// NB: This can match a string without `=`.
|
|
if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None }
|
|
})
|
|
.max();
|
|
if max_g > max_c {
|
|
DebugInfo::Full
|
|
} else {
|
|
match cg.debuginfo {
|
|
0 => DebugInfo::None,
|
|
1 => DebugInfo::Limited,
|
|
2 => DebugInfo::Full,
|
|
arg => {
|
|
early_error(
|
|
error_format,
|
|
&format!(
|
|
"debug info level needs to be between \
|
|
0-2 (instead was `{arg}`)"
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_assert_incr_state(
|
|
opt_assertion: &Option<String>,
|
|
error_format: ErrorOutputType,
|
|
) -> Option<IncrementalStateAssertion> {
|
|
match opt_assertion {
|
|
Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded),
|
|
Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded),
|
|
Some(s) => {
|
|
early_error(error_format, &format!("unexpected incremental state assertion value: {s}"))
|
|
}
|
|
None => None,
|
|
}
|
|
}
|
|
|
|
fn parse_native_lib_kind(
|
|
matches: &getopts::Matches,
|
|
kind: &str,
|
|
error_format: ErrorOutputType,
|
|
) -> (NativeLibKind, Option<bool>) {
|
|
let (kind, modifiers) = match kind.split_once(':') {
|
|
None => (kind, None),
|
|
Some((kind, modifiers)) => (kind, Some(modifiers)),
|
|
};
|
|
|
|
let kind = match kind {
|
|
"static" => NativeLibKind::Static { bundle: None, whole_archive: None },
|
|
"dylib" => NativeLibKind::Dylib { as_needed: None },
|
|
"framework" => NativeLibKind::Framework { as_needed: None },
|
|
"link-arg" => {
|
|
if !nightly_options::is_unstable_enabled(matches) {
|
|
let why = if nightly_options::match_is_nightly_build(matches) {
|
|
" and only accepted on the nightly compiler"
|
|
} else {
|
|
", the `-Z unstable-options` flag must also be passed to use it"
|
|
};
|
|
early_error(error_format, &format!("library kind `link-arg` is unstable{why}"))
|
|
}
|
|
NativeLibKind::LinkArg
|
|
}
|
|
_ => early_error(
|
|
error_format,
|
|
&format!(
|
|
"unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
|
|
),
|
|
),
|
|
};
|
|
match modifiers {
|
|
None => (kind, None),
|
|
Some(modifiers) => parse_native_lib_modifiers(kind, modifiers, error_format, matches),
|
|
}
|
|
}
|
|
|
|
fn parse_native_lib_modifiers(
|
|
mut kind: NativeLibKind,
|
|
modifiers: &str,
|
|
error_format: ErrorOutputType,
|
|
matches: &getopts::Matches,
|
|
) -> (NativeLibKind, Option<bool>) {
|
|
let mut verbatim = None;
|
|
for modifier in modifiers.split(',') {
|
|
let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
|
|
Some(m) => (m, modifier.starts_with('+')),
|
|
None => early_error(
|
|
error_format,
|
|
"invalid linking modifier syntax, expected '+' or '-' prefix \
|
|
before one of: bundle, verbatim, whole-archive, as-needed",
|
|
),
|
|
};
|
|
|
|
let report_unstable_modifier = || {
|
|
if !nightly_options::is_unstable_enabled(matches) {
|
|
let why = if nightly_options::match_is_nightly_build(matches) {
|
|
" and only accepted on the nightly compiler"
|
|
} else {
|
|
", the `-Z unstable-options` flag must also be passed to use it"
|
|
};
|
|
early_error(
|
|
error_format,
|
|
&format!("linking modifier `{modifier}` is unstable{why}"),
|
|
)
|
|
}
|
|
};
|
|
let assign_modifier = |dst: &mut Option<bool>| {
|
|
if dst.is_some() {
|
|
let msg = format!("multiple `{modifier}` modifiers in a single `-l` option");
|
|
early_error(error_format, &msg)
|
|
} else {
|
|
*dst = Some(value);
|
|
}
|
|
};
|
|
match (modifier, &mut kind) {
|
|
("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
|
|
("bundle", _) => early_error(
|
|
error_format,
|
|
"linking modifier `bundle` is only compatible with `static` linking kind",
|
|
),
|
|
|
|
("verbatim", _) => {
|
|
report_unstable_modifier();
|
|
assign_modifier(&mut verbatim)
|
|
}
|
|
|
|
("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
|
|
assign_modifier(whole_archive)
|
|
}
|
|
("whole-archive", _) => early_error(
|
|
error_format,
|
|
"linking modifier `whole-archive` is only compatible with `static` linking kind",
|
|
),
|
|
|
|
("as-needed", NativeLibKind::Dylib { as_needed })
|
|
| ("as-needed", NativeLibKind::Framework { as_needed }) => {
|
|
report_unstable_modifier();
|
|
assign_modifier(as_needed)
|
|
}
|
|
("as-needed", _) => early_error(
|
|
error_format,
|
|
"linking modifier `as-needed` is only compatible with \
|
|
`dylib` and `framework` linking kinds",
|
|
),
|
|
|
|
// Note: this error also excludes the case with empty modifier
|
|
// string, like `modifiers = ""`.
|
|
_ => early_error(
|
|
error_format,
|
|
&format!(
|
|
"unknown linking modifier `{modifier}`, expected one \
|
|
of: bundle, verbatim, whole-archive, as-needed"
|
|
),
|
|
),
|
|
}
|
|
}
|
|
|
|
(kind, verbatim)
|
|
}
|
|
|
|
fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<NativeLib> {
|
|
matches
|
|
.opt_strs("l")
|
|
.into_iter()
|
|
.map(|s| {
|
|
// Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]",
|
|
// where KIND is one of "dylib", "framework", "static", "link-arg" and
|
|
// where MODIFIERS are a comma separated list of supported modifiers
|
|
// (bundle, verbatim, whole-archive, as-needed). Each modifier is prefixed
|
|
// with either + or - to indicate whether it is enabled or disabled.
|
|
// The last value specified for a given modifier wins.
|
|
let (name, kind, verbatim) = match s.split_once('=') {
|
|
None => (s, NativeLibKind::Unspecified, None),
|
|
Some((kind, name)) => {
|
|
let (kind, verbatim) = parse_native_lib_kind(matches, kind, error_format);
|
|
(name.to_string(), kind, verbatim)
|
|
}
|
|
};
|
|
|
|
let (name, new_name) = match name.split_once(':') {
|
|
None => (name, None),
|
|
Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
|
|
};
|
|
if name.is_empty() {
|
|
early_error(error_format, "library name must not be empty");
|
|
}
|
|
NativeLib { name, new_name, kind, verbatim }
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn parse_externs(
|
|
matches: &getopts::Matches,
|
|
unstable_opts: &UnstableOptions,
|
|
error_format: ErrorOutputType,
|
|
) -> Externs {
|
|
let is_unstable_enabled = unstable_opts.unstable_options;
|
|
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
|
|
for arg in matches.opt_strs("extern") {
|
|
let (name, path) = match arg.split_once('=') {
|
|
None => (arg, None),
|
|
Some((name, path)) => (name.to_string(), Some(Path::new(path))),
|
|
};
|
|
let (options, name) = match name.split_once(':') {
|
|
None => (None, name),
|
|
Some((opts, name)) => (Some(opts), name.to_string()),
|
|
};
|
|
|
|
let path = path.map(|p| CanonicalizedPath::new(p));
|
|
|
|
let entry = externs.entry(name.to_owned());
|
|
|
|
use std::collections::btree_map::Entry;
|
|
|
|
let entry = if let Some(path) = path {
|
|
// --extern prelude_name=some_file.rlib
|
|
match entry {
|
|
Entry::Vacant(vacant) => {
|
|
let files = BTreeSet::from_iter(iter::once(path));
|
|
vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files)))
|
|
}
|
|
Entry::Occupied(occupied) => {
|
|
let ext_ent = occupied.into_mut();
|
|
match ext_ent {
|
|
ExternEntry { location: ExternLocation::ExactPaths(files), .. } => {
|
|
files.insert(path);
|
|
}
|
|
ExternEntry {
|
|
location: location @ ExternLocation::FoundInLibrarySearchDirectories,
|
|
..
|
|
} => {
|
|
// Exact paths take precedence over search directories.
|
|
let files = BTreeSet::from_iter(iter::once(path));
|
|
*location = ExternLocation::ExactPaths(files);
|
|
}
|
|
}
|
|
ext_ent
|
|
}
|
|
}
|
|
} else {
|
|
// --extern prelude_name
|
|
match entry {
|
|
Entry::Vacant(vacant) => {
|
|
vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories))
|
|
}
|
|
Entry::Occupied(occupied) => {
|
|
// Ignore if already specified.
|
|
occupied.into_mut()
|
|
}
|
|
}
|
|
};
|
|
|
|
let mut is_private_dep = false;
|
|
let mut add_prelude = true;
|
|
let mut nounused_dep = false;
|
|
if let Some(opts) = options {
|
|
if !is_unstable_enabled {
|
|
early_error(
|
|
error_format,
|
|
"the `-Z unstable-options` flag must also be passed to \
|
|
enable `--extern options",
|
|
);
|
|
}
|
|
for opt in opts.split(',') {
|
|
match opt {
|
|
"priv" => is_private_dep = true,
|
|
"noprelude" => {
|
|
if let ExternLocation::ExactPaths(_) = &entry.location {
|
|
add_prelude = false;
|
|
} else {
|
|
early_error(
|
|
error_format,
|
|
"the `noprelude` --extern option requires a file path",
|
|
);
|
|
}
|
|
}
|
|
"nounused" => nounused_dep = true,
|
|
_ => early_error(error_format, &format!("unknown --extern option `{opt}`")),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Crates start out being not private, and go to being private `priv`
|
|
// is specified.
|
|
entry.is_private_dep |= is_private_dep;
|
|
// likewise `nounused`
|
|
entry.nounused_dep |= nounused_dep;
|
|
// If any flag is missing `noprelude`, then add to the prelude.
|
|
entry.add_prelude |= add_prelude;
|
|
}
|
|
Externs(externs)
|
|
}
|
|
|
|
fn parse_remap_path_prefix(
|
|
matches: &getopts::Matches,
|
|
unstable_opts: &UnstableOptions,
|
|
error_format: ErrorOutputType,
|
|
) -> Vec<(PathBuf, PathBuf)> {
|
|
let mut mapping: Vec<(PathBuf, PathBuf)> = matches
|
|
.opt_strs("remap-path-prefix")
|
|
.into_iter()
|
|
.map(|remap| match remap.rsplit_once('=') {
|
|
None => early_error(
|
|
error_format,
|
|
"--remap-path-prefix must contain '=' between FROM and TO",
|
|
),
|
|
Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
|
|
})
|
|
.collect();
|
|
match &unstable_opts.remap_cwd_prefix {
|
|
Some(to) => match std::env::current_dir() {
|
|
Ok(cwd) => mapping.push((cwd, to.clone())),
|
|
Err(_) => (),
|
|
},
|
|
None => (),
|
|
};
|
|
mapping
|
|
}
|
|
|
|
// JUSTIFICATION: before wrapper fn is available
|
|
#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
|
|
pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
|
let color = parse_color(matches);
|
|
|
|
let edition = parse_crate_edition(matches);
|
|
|
|
let JsonConfig {
|
|
json_rendered,
|
|
json_artifact_notifications,
|
|
json_unused_externs,
|
|
json_future_incompat,
|
|
} = parse_json(matches);
|
|
|
|
let error_format = parse_error_format(matches, color, json_rendered);
|
|
|
|
let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
|
|
early_error(error_format, "`--diagnostic-width` must be an positive integer");
|
|
});
|
|
|
|
let unparsed_crate_types = matches.opt_strs("crate-type");
|
|
let crate_types = parse_crate_types_from_list(unparsed_crate_types)
|
|
.unwrap_or_else(|e| early_error(error_format, &e));
|
|
|
|
let mut unstable_opts = UnstableOptions::build(matches, error_format);
|
|
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
|
|
|
|
check_error_format_stability(&unstable_opts, error_format, json_rendered);
|
|
|
|
if !unstable_opts.unstable_options && json_unused_externs.is_enabled() {
|
|
early_error(
|
|
error_format,
|
|
"the `-Z unstable-options` flag must also be passed to enable \
|
|
the flag `--json=unused-externs`",
|
|
);
|
|
}
|
|
|
|
let output_types = parse_output_types(&unstable_opts, matches, error_format);
|
|
|
|
let mut cg = CodegenOptions::build(matches, error_format);
|
|
let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto(
|
|
&output_types,
|
|
matches,
|
|
error_format,
|
|
cg.codegen_units,
|
|
);
|
|
|
|
check_thread_count(&unstable_opts, error_format);
|
|
|
|
let incremental = cg.incremental.as_ref().map(PathBuf::from);
|
|
|
|
let assert_incr_state = parse_assert_incr_state(&unstable_opts.assert_incr_state, error_format);
|
|
|
|
if unstable_opts.profile && incremental.is_some() {
|
|
early_error(
|
|
error_format,
|
|
"can't instrument with gcov profiling when compiling incrementally",
|
|
);
|
|
}
|
|
if unstable_opts.profile {
|
|
match codegen_units {
|
|
Some(1) => {}
|
|
None => codegen_units = Some(1),
|
|
Some(_) => early_error(
|
|
error_format,
|
|
"can't instrument with gcov profiling with multiple codegen units",
|
|
),
|
|
}
|
|
}
|
|
|
|
if cg.profile_generate.enabled() && cg.profile_use.is_some() {
|
|
early_error(
|
|
error_format,
|
|
"options `-C profile-generate` and `-C profile-use` are exclusive",
|
|
);
|
|
}
|
|
|
|
if unstable_opts.profile_sample_use.is_some()
|
|
&& (cg.profile_generate.enabled() || cg.profile_use.is_some())
|
|
{
|
|
early_error(
|
|
error_format,
|
|
"option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
|
|
);
|
|
}
|
|
|
|
// Handle both `-Z symbol-mangling-version` and `-C symbol-mangling-version`; the latter takes
|
|
// precedence.
|
|
match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) {
|
|
(Some(smv_c), Some(smv_z)) if smv_c != smv_z => {
|
|
early_error(
|
|
error_format,
|
|
"incompatible values passed for `-C symbol-mangling-version` \
|
|
and `-Z symbol-mangling-version`",
|
|
);
|
|
}
|
|
(Some(SymbolManglingVersion::V0), _) => {}
|
|
(Some(_), _) if !unstable_opts.unstable_options => {
|
|
early_error(
|
|
error_format,
|
|
"`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
|
|
);
|
|
}
|
|
(None, None) => {}
|
|
(None, smv) => {
|
|
early_warn(
|
|
error_format,
|
|
"`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`",
|
|
);
|
|
cg.symbol_mangling_version = smv;
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
// Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes
|
|
// precedence.
|
|
match (cg.instrument_coverage, unstable_opts.instrument_coverage) {
|
|
(Some(ic_c), Some(ic_z)) if ic_c != ic_z => {
|
|
early_error(
|
|
error_format,
|
|
"incompatible values passed for `-C instrument-coverage` \
|
|
and `-Z instrument-coverage`",
|
|
);
|
|
}
|
|
(Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {}
|
|
(Some(_), _) if !unstable_opts.unstable_options => {
|
|
early_error(
|
|
error_format,
|
|
"`-C instrument-coverage=except-*` requires `-Z unstable-options`",
|
|
);
|
|
}
|
|
(None, None) => {}
|
|
(None, ic) => {
|
|
early_warn(
|
|
error_format,
|
|
"`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`",
|
|
);
|
|
cg.instrument_coverage = ic;
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) {
|
|
if cg.profile_generate.enabled() || cg.profile_use.is_some() {
|
|
early_error(
|
|
error_format,
|
|
"option `-C instrument-coverage` is not compatible with either `-C profile-use` \
|
|
or `-C profile-generate`",
|
|
);
|
|
}
|
|
|
|
// `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent
|
|
// and reversible name mangling. Note, LLVM coverage tools can analyze coverage over
|
|
// multiple runs, including some changes to source code; so mangled names must be consistent
|
|
// across compilations.
|
|
match cg.symbol_mangling_version {
|
|
None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0),
|
|
Some(SymbolManglingVersion::Legacy) => {
|
|
early_warn(
|
|
error_format,
|
|
"-C instrument-coverage requires symbol mangling version `v0`, \
|
|
but `-C symbol-mangling-version=legacy` was specified",
|
|
);
|
|
}
|
|
Some(SymbolManglingVersion::V0) => {}
|
|
}
|
|
}
|
|
|
|
if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") {
|
|
unstable_opts.graphviz_font = graphviz_font;
|
|
}
|
|
|
|
if !cg.embed_bitcode {
|
|
match cg.lto {
|
|
LtoCli::No | LtoCli::Unspecified => {}
|
|
LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error(
|
|
error_format,
|
|
"options `-C embed-bitcode=no` and `-C lto` are incompatible",
|
|
),
|
|
}
|
|
}
|
|
|
|
if cg.linker_flavor == Some(LinkerFlavor::L4Bender)
|
|
&& !nightly_options::is_unstable_enabled(matches)
|
|
{
|
|
early_error(
|
|
error_format,
|
|
"`l4-bender` linker flavor is unstable, `-Z unstable-options` \
|
|
flag must also be passed to explicitly use it",
|
|
);
|
|
}
|
|
|
|
let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format);
|
|
|
|
let cg = cg;
|
|
|
|
let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
|
|
let target_triple = parse_target_triple(matches, error_format);
|
|
let opt_level = parse_opt_level(matches, &cg, error_format);
|
|
// The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
|
|
// to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`)
|
|
// for more details.
|
|
let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
|
|
let debuginfo = select_debuginfo(matches, &cg, error_format);
|
|
|
|
let mut search_paths = vec![];
|
|
for s in &matches.opt_strs("L") {
|
|
search_paths.push(SearchPath::from_cli_opt(&s, error_format));
|
|
}
|
|
|
|
let libs = parse_libs(matches, error_format);
|
|
|
|
let test = matches.opt_present("test");
|
|
|
|
if !cg.remark.is_empty() && debuginfo == DebugInfo::None {
|
|
early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations");
|
|
}
|
|
|
|
let externs = parse_externs(matches, &unstable_opts, error_format);
|
|
|
|
let crate_name = matches.opt_str("crate-name");
|
|
|
|
let remap_path_prefix = parse_remap_path_prefix(matches, &unstable_opts, error_format);
|
|
|
|
let pretty = parse_pretty(&unstable_opts, error_format);
|
|
|
|
if !unstable_opts.unstable_options
|
|
&& !target_triple.triple().contains("apple")
|
|
&& cg.split_debuginfo.is_some()
|
|
{
|
|
early_error(error_format, "`-Csplit-debuginfo` is unstable on this platform");
|
|
}
|
|
|
|
// Try to find a directory containing the Rust `src`, for more details see
|
|
// the doc comment on the `real_rust_source_base_dir` field.
|
|
let tmp_buf;
|
|
let sysroot = match &sysroot_opt {
|
|
Some(s) => s,
|
|
None => {
|
|
tmp_buf = crate::filesearch::get_or_default_sysroot();
|
|
&tmp_buf
|
|
}
|
|
};
|
|
let real_rust_source_base_dir = {
|
|
// This is the location used by the `rust-src` `rustup` component.
|
|
let mut candidate = sysroot.join("lib/rustlib/src/rust");
|
|
if let Ok(metadata) = candidate.symlink_metadata() {
|
|
// Replace the symlink rustbuild creates, with its destination.
|
|
// We could try to use `fs::canonicalize` instead, but that might
|
|
// produce unnecessarily verbose path.
|
|
if metadata.file_type().is_symlink() {
|
|
if let Ok(symlink_dest) = std::fs::read_link(&candidate) {
|
|
candidate = symlink_dest;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Only use this directory if it has a file we can expect to always find.
|
|
if candidate.join("library/std/src/lib.rs").is_file() { Some(candidate) } else { None }
|
|
};
|
|
|
|
let working_dir = std::env::current_dir().unwrap_or_else(|e| {
|
|
early_error(error_format, &format!("Current directory is invalid: {e}"));
|
|
});
|
|
|
|
let (path, remapped) =
|
|
FilePathMapping::new(remap_path_prefix.clone()).map_prefix(working_dir.clone());
|
|
let working_dir = if remapped {
|
|
RealFileName::Remapped { local_path: Some(working_dir), virtual_name: path }
|
|
} else {
|
|
RealFileName::LocalPath(path)
|
|
};
|
|
|
|
Options {
|
|
assert_incr_state,
|
|
crate_types,
|
|
optimize: opt_level,
|
|
debuginfo,
|
|
lint_opts,
|
|
lint_cap,
|
|
describe_lints,
|
|
output_types,
|
|
search_paths,
|
|
maybe_sysroot: sysroot_opt,
|
|
target_triple,
|
|
test,
|
|
incremental,
|
|
unstable_opts,
|
|
prints,
|
|
cg,
|
|
error_format,
|
|
diagnostic_width,
|
|
externs,
|
|
unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()),
|
|
crate_name,
|
|
libs,
|
|
debug_assertions,
|
|
actually_rustdoc: false,
|
|
trimmed_def_paths: TrimmedDefPaths::default(),
|
|
cli_forced_codegen_units: codegen_units,
|
|
cli_forced_thinlto_off: disable_thinlto,
|
|
remap_path_prefix,
|
|
real_rust_source_base_dir,
|
|
edition,
|
|
json_artifact_notifications,
|
|
json_unused_externs,
|
|
json_future_incompat,
|
|
pretty,
|
|
working_dir,
|
|
}
|
|
}
|
|
|
|
fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Option<PpMode> {
|
|
use PpMode::*;
|
|
|
|
let first = match unstable_opts.unpretty.as_deref()? {
|
|
"normal" => Source(PpSourceMode::Normal),
|
|
"identified" => Source(PpSourceMode::Identified),
|
|
"expanded" => Source(PpSourceMode::Expanded),
|
|
"expanded,identified" => Source(PpSourceMode::ExpandedIdentified),
|
|
"expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene),
|
|
"ast-tree" => AstTree(PpAstTreeMode::Normal),
|
|
"ast-tree,expanded" => AstTree(PpAstTreeMode::Expanded),
|
|
"hir" => Hir(PpHirMode::Normal),
|
|
"hir,identified" => Hir(PpHirMode::Identified),
|
|
"hir,typed" => Hir(PpHirMode::Typed),
|
|
"hir-tree" => HirTree,
|
|
"thir-tree" => ThirTree,
|
|
"mir" => Mir,
|
|
"mir-cfg" => MirCFG,
|
|
name => early_error(
|
|
efmt,
|
|
&format!(
|
|
"argument to `unpretty` must be one of `normal`, `identified`, \
|
|
`expanded`, `expanded,identified`, `expanded,hygiene`, \
|
|
`ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
|
|
`hir,typed`, `hir-tree`, `thir-tree`, `mir` or `mir-cfg`; got {name}"
|
|
),
|
|
),
|
|
};
|
|
tracing::debug!("got unpretty option: {first:?}");
|
|
Some(first)
|
|
}
|
|
|
|
pub fn make_crate_type_option() -> RustcOptGroup {
|
|
opt::multi_s(
|
|
"",
|
|
"crate-type",
|
|
"Comma separated list of types of crates
|
|
for the compiler to emit",
|
|
"[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]",
|
|
)
|
|
}
|
|
|
|
pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> {
|
|
let mut crate_types: Vec<CrateType> = Vec::new();
|
|
for unparsed_crate_type in &list_list {
|
|
for part in unparsed_crate_type.split(',') {
|
|
let new_part = match part {
|
|
"lib" => default_lib_output(),
|
|
"rlib" => CrateType::Rlib,
|
|
"staticlib" => CrateType::Staticlib,
|
|
"dylib" => CrateType::Dylib,
|
|
"cdylib" => CrateType::Cdylib,
|
|
"bin" => CrateType::Executable,
|
|
"proc-macro" => CrateType::ProcMacro,
|
|
_ => return Err(format!("unknown crate type: `{part}`")),
|
|
};
|
|
if !crate_types.contains(&new_part) {
|
|
crate_types.push(new_part)
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(crate_types)
|
|
}
|
|
|
|
pub mod nightly_options {
|
|
use super::{ErrorOutputType, OptionStability, RustcOptGroup};
|
|
use crate::early_error;
|
|
use rustc_feature::UnstableFeatures;
|
|
|
|
pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
|
|
match_is_nightly_build(matches)
|
|
&& matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
|
|
}
|
|
|
|
pub fn match_is_nightly_build(matches: &getopts::Matches) -> bool {
|
|
is_nightly_build(matches.opt_str("crate-name").as_deref())
|
|
}
|
|
|
|
pub fn is_nightly_build(krate: Option<&str>) -> bool {
|
|
UnstableFeatures::from_environment(krate).is_nightly_build()
|
|
}
|
|
|
|
pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) {
|
|
let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
|
|
let really_allows_unstable_options = match_is_nightly_build(matches);
|
|
|
|
for opt in flags.iter() {
|
|
if opt.stability == OptionStability::Stable {
|
|
continue;
|
|
}
|
|
if !matches.opt_present(opt.name) {
|
|
continue;
|
|
}
|
|
if opt.name != "Z" && !has_z_unstable_option {
|
|
early_error(
|
|
ErrorOutputType::default(),
|
|
&format!(
|
|
"the `-Z unstable-options` flag must also be passed to enable \
|
|
the flag `{}`",
|
|
opt.name
|
|
),
|
|
);
|
|
}
|
|
if really_allows_unstable_options {
|
|
continue;
|
|
}
|
|
match opt.stability {
|
|
OptionStability::Unstable => {
|
|
let msg = format!(
|
|
"the option `{}` is only accepted on the \
|
|
nightly compiler",
|
|
opt.name
|
|
);
|
|
early_error(ErrorOutputType::default(), &msg);
|
|
}
|
|
OptionStability::Stable => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for CrateType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match *self {
|
|
CrateType::Executable => "bin".fmt(f),
|
|
CrateType::Dylib => "dylib".fmt(f),
|
|
CrateType::Rlib => "rlib".fmt(f),
|
|
CrateType::Staticlib => "staticlib".fmt(f),
|
|
CrateType::Cdylib => "cdylib".fmt(f),
|
|
CrateType::ProcMacro => "proc-macro".fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
pub enum PpSourceMode {
|
|
/// `-Zunpretty=normal`
|
|
Normal,
|
|
/// `-Zunpretty=expanded`
|
|
Expanded,
|
|
/// `-Zunpretty=identified`
|
|
Identified,
|
|
/// `-Zunpretty=expanded,identified`
|
|
ExpandedIdentified,
|
|
/// `-Zunpretty=expanded,hygiene`
|
|
ExpandedHygiene,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
pub enum PpAstTreeMode {
|
|
/// `-Zunpretty=ast`
|
|
Normal,
|
|
/// `-Zunpretty=ast,expanded`
|
|
Expanded,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
pub enum PpHirMode {
|
|
/// `-Zunpretty=hir`
|
|
Normal,
|
|
/// `-Zunpretty=hir,identified`
|
|
Identified,
|
|
/// `-Zunpretty=hir,typed`
|
|
Typed,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
pub enum PpMode {
|
|
/// Options that print the source code, i.e.
|
|
/// `-Zunpretty=normal` and `-Zunpretty=expanded`
|
|
Source(PpSourceMode),
|
|
AstTree(PpAstTreeMode),
|
|
/// Options that print the HIR, i.e. `-Zunpretty=hir`
|
|
Hir(PpHirMode),
|
|
/// `-Zunpretty=hir-tree`
|
|
HirTree,
|
|
/// `-Zunpretty=thir-tree`
|
|
ThirTree,
|
|
/// `-Zunpretty=mir`
|
|
Mir,
|
|
/// `-Zunpretty=mir-cfg`
|
|
MirCFG,
|
|
}
|
|
|
|
impl PpMode {
|
|
pub fn needs_ast_map(&self) -> bool {
|
|
use PpMode::*;
|
|
use PpSourceMode::*;
|
|
match *self {
|
|
Source(Normal | Identified) | AstTree(PpAstTreeMode::Normal) => false,
|
|
|
|
Source(Expanded | ExpandedIdentified | ExpandedHygiene)
|
|
| AstTree(PpAstTreeMode::Expanded)
|
|
| Hir(_)
|
|
| HirTree
|
|
| ThirTree
|
|
| Mir
|
|
| MirCFG => true,
|
|
}
|
|
}
|
|
pub fn needs_hir(&self) -> bool {
|
|
use PpMode::*;
|
|
match *self {
|
|
Source(_) | AstTree(_) => false,
|
|
|
|
Hir(_) | HirTree | ThirTree | Mir | MirCFG => true,
|
|
}
|
|
}
|
|
|
|
pub fn needs_analysis(&self) -> bool {
|
|
use PpMode::*;
|
|
matches!(*self, Mir | MirCFG | ThirTree)
|
|
}
|
|
}
|
|
|
|
/// Command-line arguments passed to the compiler have to be incorporated with
|
|
/// the dependency tracking system for incremental compilation. This module
|
|
/// provides some utilities to make this more convenient.
|
|
///
|
|
/// The values of all command-line arguments that are relevant for dependency
|
|
/// tracking are hashed into a single value that determines whether the
|
|
/// incremental compilation cache can be re-used or not. This hashing is done
|
|
/// via the `DepTrackingHash` trait defined below, since the standard `Hash`
|
|
/// implementation might not be suitable (e.g., arguments are stored in a `Vec`,
|
|
/// the hash of which is order dependent, but we might not want the order of
|
|
/// arguments to make a difference for the hash).
|
|
///
|
|
/// However, since the value provided by `Hash::hash` often *is* suitable,
|
|
/// especially for primitive types, there is the
|
|
/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the
|
|
/// `Hash` implementation for `DepTrackingHash`. It's important though that
|
|
/// we have an opt-in scheme here, so one is hopefully forced to think about
|
|
/// how the hash should be calculated when adding a new command-line argument.
|
|
pub(crate) mod dep_tracking {
|
|
use super::{
|
|
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType,
|
|
InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel,
|
|
OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SplitDwarfKind,
|
|
SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths,
|
|
};
|
|
use crate::lint;
|
|
use crate::options::WasiExecModel;
|
|
use crate::utils::{NativeLib, NativeLibKind};
|
|
use rustc_errors::LanguageIdentifier;
|
|
use rustc_feature::UnstableFeatures;
|
|
use rustc_span::edition::Edition;
|
|
use rustc_span::RealFileName;
|
|
use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
|
|
use rustc_target::spec::{
|
|
RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
|
|
};
|
|
use std::collections::hash_map::DefaultHasher;
|
|
use std::collections::BTreeMap;
|
|
use std::hash::Hash;
|
|
use std::num::NonZeroUsize;
|
|
use std::path::PathBuf;
|
|
|
|
pub trait DepTrackingHash {
|
|
fn hash(
|
|
&self,
|
|
hasher: &mut DefaultHasher,
|
|
error_format: ErrorOutputType,
|
|
for_crate_hash: bool,
|
|
);
|
|
}
|
|
|
|
macro_rules! impl_dep_tracking_hash_via_hash {
|
|
($($t:ty),+ $(,)?) => {$(
|
|
impl DepTrackingHash for $t {
|
|
fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType, _for_crate_hash: bool) {
|
|
Hash::hash(self, hasher);
|
|
}
|
|
}
|
|
)+};
|
|
}
|
|
|
|
impl<T: DepTrackingHash> DepTrackingHash for Option<T> {
|
|
fn hash(
|
|
&self,
|
|
hasher: &mut DefaultHasher,
|
|
error_format: ErrorOutputType,
|
|
for_crate_hash: bool,
|
|
) {
|
|
match self {
|
|
Some(x) => {
|
|
Hash::hash(&1, hasher);
|
|
DepTrackingHash::hash(x, hasher, error_format, for_crate_hash);
|
|
}
|
|
None => Hash::hash(&0, hasher),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl_dep_tracking_hash_via_hash!(
|
|
bool,
|
|
usize,
|
|
NonZeroUsize,
|
|
u64,
|
|
String,
|
|
PathBuf,
|
|
lint::Level,
|
|
WasiExecModel,
|
|
u32,
|
|
RelocModel,
|
|
CodeModel,
|
|
TlsModel,
|
|
InstrumentCoverage,
|
|
CrateType,
|
|
MergeFunctions,
|
|
PanicStrategy,
|
|
RelroLevel,
|
|
Passes,
|
|
OptLevel,
|
|
LtoCli,
|
|
DebugInfo,
|
|
UnstableFeatures,
|
|
NativeLib,
|
|
NativeLibKind,
|
|
SanitizerSet,
|
|
CFGuard,
|
|
CFProtection,
|
|
TargetTriple,
|
|
Edition,
|
|
LinkerPluginLto,
|
|
SplitDebuginfo,
|
|
SplitDwarfKind,
|
|
StackProtector,
|
|
SwitchWithOptPath,
|
|
SymbolManglingVersion,
|
|
SourceFileHashAlgorithm,
|
|
TrimmedDefPaths,
|
|
Option<LdImpl>,
|
|
OutputType,
|
|
RealFileName,
|
|
LocationDetail,
|
|
BranchProtection,
|
|
OomStrategy,
|
|
LanguageIdentifier,
|
|
);
|
|
|
|
impl<T1, T2> DepTrackingHash for (T1, T2)
|
|
where
|
|
T1: DepTrackingHash,
|
|
T2: DepTrackingHash,
|
|
{
|
|
fn hash(
|
|
&self,
|
|
hasher: &mut DefaultHasher,
|
|
error_format: ErrorOutputType,
|
|
for_crate_hash: bool,
|
|
) {
|
|
Hash::hash(&0, hasher);
|
|
DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
|
|
Hash::hash(&1, hasher);
|
|
DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
|
|
}
|
|
}
|
|
|
|
impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3)
|
|
where
|
|
T1: DepTrackingHash,
|
|
T2: DepTrackingHash,
|
|
T3: DepTrackingHash,
|
|
{
|
|
fn hash(
|
|
&self,
|
|
hasher: &mut DefaultHasher,
|
|
error_format: ErrorOutputType,
|
|
for_crate_hash: bool,
|
|
) {
|
|
Hash::hash(&0, hasher);
|
|
DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
|
|
Hash::hash(&1, hasher);
|
|
DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
|
|
Hash::hash(&2, hasher);
|
|
DepTrackingHash::hash(&self.2, hasher, error_format, for_crate_hash);
|
|
}
|
|
}
|
|
|
|
impl<T: DepTrackingHash> DepTrackingHash for Vec<T> {
|
|
fn hash(
|
|
&self,
|
|
hasher: &mut DefaultHasher,
|
|
error_format: ErrorOutputType,
|
|
for_crate_hash: bool,
|
|
) {
|
|
Hash::hash(&self.len(), hasher);
|
|
for (index, elem) in self.iter().enumerate() {
|
|
Hash::hash(&index, hasher);
|
|
DepTrackingHash::hash(elem, hasher, error_format, for_crate_hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DepTrackingHash for OutputTypes {
|
|
fn hash(
|
|
&self,
|
|
hasher: &mut DefaultHasher,
|
|
error_format: ErrorOutputType,
|
|
for_crate_hash: bool,
|
|
) {
|
|
Hash::hash(&self.0.len(), hasher);
|
|
for (key, val) in &self.0 {
|
|
DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
|
|
if !for_crate_hash {
|
|
DepTrackingHash::hash(val, hasher, error_format, for_crate_hash);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is a stable hash because BTreeMap is a sorted container
|
|
pub(crate) fn stable_hash(
|
|
sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>,
|
|
hasher: &mut DefaultHasher,
|
|
error_format: ErrorOutputType,
|
|
for_crate_hash: bool,
|
|
) {
|
|
for (key, sub_hash) in sub_hashes {
|
|
// Using Hash::hash() instead of DepTrackingHash::hash() is fine for
|
|
// the keys, as they are just plain strings
|
|
Hash::hash(&key.len(), hasher);
|
|
Hash::hash(key, hasher);
|
|
sub_hash.hash(hasher, error_format, for_crate_hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Default behavior to use in out-of-memory situations.
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
|
|
pub enum OomStrategy {
|
|
/// Generate a panic that can be caught by `catch_unwind`.
|
|
Panic,
|
|
|
|
/// Abort the process immediately.
|
|
Abort,
|
|
}
|
|
|
|
impl OomStrategy {
|
|
pub const SYMBOL: &'static str = "__rust_alloc_error_handler_should_panic";
|
|
|
|
pub fn should_panic(self) -> u8 {
|
|
match self {
|
|
OomStrategy::Panic => 1,
|
|
OomStrategy::Abort => 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// How to run proc-macro code when building this crate
|
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
|
pub enum ProcMacroExecutionStrategy {
|
|
/// Run the proc-macro code on the same thread as the server.
|
|
SameThread,
|
|
|
|
/// Run the proc-macro code on a different thread.
|
|
CrossThread,
|
|
}
|