blob: b8111fbc17f84e73051e46d006ad20a5e01579bf [file] [log] [blame]
//! Code for dealing with `--print` requests.
use std::fmt;
use std::sync::LazyLock;
use rustc_data_structures::fx::FxHashSet;
use crate::EarlyDiagCtxt;
use crate::config::{
CodegenOptions, OutFileName, UnstableOptions, nightly_options, split_out_file_name,
};
use crate::macros::AllVariants;
#[derive(Clone, PartialEq, Debug)]
pub struct PrintRequest {
pub kind: PrintKind,
pub out: OutFileName,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(AllVariants)]
pub enum PrintKind {
// tidy-alphabetical-start
AllTargetSpecsJson,
CallingConventions,
Cfg,
CheckCfg,
CodeModels,
CrateName,
CrateRootLintLevels,
DeploymentTarget,
FileNames,
HostTuple,
LinkArgs,
NativeStaticLibs,
RelocationModels,
SplitDebuginfo,
StackProtectorStrategies,
SupportedCrateTypes,
Sysroot,
TargetCPUs,
TargetFeatures,
TargetLibdir,
TargetList,
TargetSpecJson,
TargetSpecJsonSchema,
TlsModels,
// tidy-alphabetical-end
}
impl PrintKind {
/// FIXME: rust-analyzer doesn't support `#![feature(macro_derive)]` yet
/// (<https://github.com/rust-lang/rust-analyzer/issues/21043>), which breaks autocomplete.
/// Work around that by aliasing the trait constant to a regular constant.
const ALL_VARIANTS: &[Self] = <Self as AllVariants>::ALL_VARIANTS;
fn name(self) -> &'static str {
use PrintKind::*;
match self {
// tidy-alphabetical-start
AllTargetSpecsJson => "all-target-specs-json",
CallingConventions => "calling-conventions",
Cfg => "cfg",
CheckCfg => "check-cfg",
CodeModels => "code-models",
CrateName => "crate-name",
CrateRootLintLevels => "crate-root-lint-levels",
DeploymentTarget => "deployment-target",
FileNames => "file-names",
HostTuple => "host-tuple",
LinkArgs => "link-args",
NativeStaticLibs => "native-static-libs",
RelocationModels => "relocation-models",
SplitDebuginfo => "split-debuginfo",
StackProtectorStrategies => "stack-protector-strategies",
SupportedCrateTypes => "supported-crate-types",
Sysroot => "sysroot",
TargetCPUs => "target-cpus",
TargetFeatures => "target-features",
TargetLibdir => "target-libdir",
TargetList => "target-list",
TargetSpecJson => "target-spec-json",
TargetSpecJsonSchema => "target-spec-json-schema",
TlsModels => "tls-models",
// tidy-alphabetical-end
}
}
fn is_stable(self) -> bool {
use PrintKind::*;
match self {
// Stable values:
CallingConventions
| Cfg
| CodeModels
| CrateName
| DeploymentTarget
| FileNames
| HostTuple
| LinkArgs
| NativeStaticLibs
| RelocationModels
| SplitDebuginfo
| StackProtectorStrategies
| Sysroot
| TargetCPUs
| TargetFeatures
| TargetLibdir
| TargetList
| TlsModels => true,
// Unstable values:
AllTargetSpecsJson => false,
CheckCfg => false,
CrateRootLintLevels => false,
SupportedCrateTypes => false,
TargetSpecJson => false,
TargetSpecJsonSchema => false,
}
}
fn from_str(s: &str) -> Option<Self> {
Self::ALL_VARIANTS.iter().find(|kind| kind.name() == s).copied()
}
}
impl fmt::Display for PrintKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name().fmt(f)
}
}
pub(crate) static PRINT_HELP: LazyLock<String> = LazyLock::new(|| {
let print_kinds =
PrintKind::ALL_VARIANTS.iter().map(|kind| kind.name()).collect::<Vec<_>>().join("|");
format!(
"Compiler information to print on stdout (or to a file)\n\
INFO may be one of <{print_kinds}>.",
)
});
pub(crate) fn collect_print_requests(
early_dcx: &EarlyDiagCtxt,
cg: &mut CodegenOptions,
unstable_opts: &UnstableOptions,
matches: &getopts::Matches,
) -> Vec<PrintRequest> {
let mut prints = Vec::<PrintRequest>::new();
if cg.target_cpu.as_deref() == Some("help") {
prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout });
cg.target_cpu = None;
};
if cg.target_feature == "help" {
prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout });
cg.target_feature = String::new();
}
// We disallow reusing the same path in multiple prints, such as `--print
// cfg=output.txt --print link-args=output.txt`, because outputs are printed
// by disparate pieces of the compiler, and keeping track of which files
// need to be overwritten vs appended to is annoying.
let mut printed_paths = FxHashSet::default();
prints.extend(matches.opt_strs("print").into_iter().map(|req| {
let (req, out) = split_out_file_name(&req);
let kind = if let Some(print_kind) = PrintKind::from_str(req) {
check_print_request_stability(early_dcx, unstable_opts, print_kind);
print_kind
} else {
let is_nightly = nightly_options::match_is_nightly_build(matches);
emit_unknown_print_request_help(early_dcx, req, is_nightly)
};
let out = out.unwrap_or(OutFileName::Stdout);
if let OutFileName::Real(path) = &out {
if !printed_paths.insert(path.clone()) {
early_dcx.early_fatal(format!(
"cannot print multiple outputs to the same path: {}",
path.display(),
));
}
}
PrintRequest { kind, out }
}));
prints
}
fn check_print_request_stability(
early_dcx: &EarlyDiagCtxt,
unstable_opts: &UnstableOptions,
print_kind: PrintKind,
) {
if !print_kind.is_stable() && !unstable_opts.unstable_options {
early_dcx.early_fatal(format!(
"the `-Z unstable-options` flag must also be passed to enable the `{print_kind}` print option"
));
}
}
fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! {
let prints = PrintKind::ALL_VARIANTS
.iter()
// If we're not on nightly, we don't want to print unstable options
.filter(|kind| is_nightly || kind.is_stable())
.map(|kind| format!("`{kind}`"))
.collect::<Vec<_>>()
.join(", ");
let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`"));
#[allow(rustc::diagnostic_outside_of_impl)]
diag.help(format!("valid print requests are: {prints}"));
if req == "lints" {
diag.help(format!("use `-Whelp` to print a list of lints"));
}
diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information"));
diag.emit()
}