1use std::fmt;
4use std::sync::LazyLock;
5
6use rustc_data_structures::fx::FxHashSet;
7
8use crate::EarlyDiagCtxt;
9use crate::config::{
10 CodegenOptions, OutFileName, UnstableOptions, nightly_options, split_out_file_name,
11};
12use crate::macros::AllVariants;
13
14#[derive(Clone, PartialEq, Debug)]
15pub struct PrintRequest {
16 pub kind: PrintKind,
17 pub out: OutFileName,
18}
19
20#[derive(Copy, Clone, PartialEq, Eq, Debug)]
21#[derive(AllVariants)]
22pub enum PrintKind {
23 AllTargetSpecsJson,
25 CallingConventions,
26 Cfg,
27 CheckCfg,
28 CodeModels,
29 CrateName,
30 CrateRootLintLevels,
31 DeploymentTarget,
32 FileNames,
33 HostTuple,
34 LinkArgs,
35 NativeStaticLibs,
36 RelocationModels,
37 SplitDebuginfo,
38 StackProtectorStrategies,
39 SupportedCrateTypes,
40 Sysroot,
41 TargetCPUs,
42 TargetFeatures,
43 TargetLibdir,
44 TargetList,
45 TargetSpecJson,
46 TargetSpecJsonSchema,
47 TlsModels,
48 }
50
51impl PrintKind {
52 const ALL_VARIANTS: &[Self] = <Self as AllVariants>::ALL_VARIANTS;
56
57 fn name(self) -> &'static str {
58 use PrintKind::*;
59 match self {
60 AllTargetSpecsJson => "all-target-specs-json",
62 CallingConventions => "calling-conventions",
63 Cfg => "cfg",
64 CheckCfg => "check-cfg",
65 CodeModels => "code-models",
66 CrateName => "crate-name",
67 CrateRootLintLevels => "crate-root-lint-levels",
68 DeploymentTarget => "deployment-target",
69 FileNames => "file-names",
70 HostTuple => "host-tuple",
71 LinkArgs => "link-args",
72 NativeStaticLibs => "native-static-libs",
73 RelocationModels => "relocation-models",
74 SplitDebuginfo => "split-debuginfo",
75 StackProtectorStrategies => "stack-protector-strategies",
76 SupportedCrateTypes => "supported-crate-types",
77 Sysroot => "sysroot",
78 TargetCPUs => "target-cpus",
79 TargetFeatures => "target-features",
80 TargetLibdir => "target-libdir",
81 TargetList => "target-list",
82 TargetSpecJson => "target-spec-json",
83 TargetSpecJsonSchema => "target-spec-json-schema",
84 TlsModels => "tls-models",
85 }
87 }
88
89 fn is_stable(self) -> bool {
90 use PrintKind::*;
91 match self {
92 CallingConventions
94 | Cfg
95 | CodeModels
96 | CrateName
97 | DeploymentTarget
98 | FileNames
99 | HostTuple
100 | LinkArgs
101 | NativeStaticLibs
102 | RelocationModels
103 | SplitDebuginfo
104 | StackProtectorStrategies
105 | Sysroot
106 | TargetCPUs
107 | TargetFeatures
108 | TargetLibdir
109 | TargetList
110 | TlsModels => true,
111
112 AllTargetSpecsJson => false,
114 CheckCfg => false,
115 CrateRootLintLevels => false,
116 SupportedCrateTypes => false,
117 TargetSpecJson => false,
118 TargetSpecJsonSchema => false,
119 }
120 }
121
122 fn from_str(s: &str) -> Option<Self> {
123 Self::ALL_VARIANTS.iter().find(|kind| kind.name() == s).copied()
124 }
125}
126
127impl fmt::Display for PrintKind {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 self.name().fmt(f)
130 }
131}
132
133pub(crate) static PRINT_HELP: LazyLock<String> = LazyLock::new(|| {
134 let print_kinds =
135 PrintKind::ALL_VARIANTS.iter().map(|kind| kind.name()).collect::<Vec<_>>().join("|");
136 format!(
137 "Compiler information to print on stdout (or to a file)\n\
138 INFO may be one of <{print_kinds}>.",
139 )
140});
141
142pub(crate) fn collect_print_requests(
143 early_dcx: &EarlyDiagCtxt,
144 cg: &mut CodegenOptions,
145 unstable_opts: &UnstableOptions,
146 matches: &getopts::Matches,
147) -> Vec<PrintRequest> {
148 let mut prints = Vec::<PrintRequest>::new();
149 if cg.target_cpu.as_deref() == Some("help") {
150 prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout });
151 cg.target_cpu = None;
152 };
153 if cg.target_feature == "help" {
154 prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout });
155 cg.target_feature = String::new();
156 }
157
158 let mut printed_paths = FxHashSet::default();
163
164 prints.extend(matches.opt_strs("print").into_iter().map(|req| {
165 let (req, out) = split_out_file_name(&req);
166
167 let kind = if let Some(print_kind) = PrintKind::from_str(req) {
168 check_print_request_stability(early_dcx, unstable_opts, print_kind);
169 print_kind
170 } else {
171 let is_nightly = nightly_options::match_is_nightly_build(matches);
172 emit_unknown_print_request_help(early_dcx, req, is_nightly)
173 };
174
175 let out = out.unwrap_or(OutFileName::Stdout);
176 if let OutFileName::Real(path) = &out {
177 if !printed_paths.insert(path.clone()) {
178 early_dcx.early_fatal(format!(
179 "cannot print multiple outputs to the same path: {}",
180 path.display(),
181 ));
182 }
183 }
184
185 PrintRequest { kind, out }
186 }));
187
188 prints
189}
190
191fn check_print_request_stability(
192 early_dcx: &EarlyDiagCtxt,
193 unstable_opts: &UnstableOptions,
194 print_kind: PrintKind,
195) {
196 if !print_kind.is_stable() && !unstable_opts.unstable_options {
197 early_dcx.early_fatal(format!(
198 "the `-Z unstable-options` flag must also be passed to enable the `{print_kind}` print option"
199 ));
200 }
201}
202
203fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! {
204 let prints = PrintKind::ALL_VARIANTS
205 .iter()
206 .filter(|kind| is_nightly || kind.is_stable())
208 .map(|kind| format!("`{kind}`"))
209 .collect::<Vec<_>>()
210 .join(", ");
211
212 let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`"));
213 #[allow(rustc::diagnostic_outside_of_impl)]
214 diag.help(format!("valid print requests are: {prints}"));
215
216 if req == "lints" {
217 diag.help(format!("use `-Whelp` to print a list of lints"));
218 }
219
220 diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information"));
221 diag.emit()
222}