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