1#![no_std]
50#![doc(test(
51 no_crate_inject,
52 attr(allow(
53 dead_code,
54 unused_variables,
55 clippy::undocumented_unsafe_blocks,
56 clippy::unused_trait_names,
57 ))
58))]
59#![forbid(unsafe_code)]
60#![warn(
61 missing_debug_implementations,
63 clippy::alloc_instead_of_core,
65 clippy::exhaustive_enums,
66 clippy::exhaustive_structs,
67 clippy::impl_trait_in_params,
68 clippy::std_instead_of_alloc,
69 clippy::std_instead_of_core,
70 )]
72
73extern crate alloc;
74extern crate std;
75
76#[cfg(test)]
77#[path = "gen/tests/assert_impl.rs"]
78mod assert_impl;
79#[cfg(test)]
80#[path = "gen/tests/track_size.rs"]
81mod track_size;
82
83#[path = "gen/target_spec.rs"]
84mod target_spec;
85pub use self::target_spec::{
86 Arch, BinaryFormat, Env, Os, PanicStrategy, Sanitizer, TargetEndian, TargetFamily,
87};
88
89#[macro_use]
90mod process;
91
92mod error;
93
94use alloc::{collections::BTreeMap, string::String, vec::Vec};
95use core::ops;
96use std::process::Command;
97
98use serde_derive::{Deserialize, Serialize};
99
100pub use self::error::Error;
101use self::{error::Result, process::ProcessBuilder};
102
103pub type AllTargetSpecs = BTreeMap<String, TargetSpec>;
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(rename_all = "kebab-case")]
112#[cfg_attr(test, serde(deny_unknown_fields))]
113#[non_exhaustive]
114pub struct TargetSpec {
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub abi_return_struct_as_int: Option<bool>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub abi: Option<String>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub allows_weak_linkage: Option<bool>,
121 pub arch: Arch,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub archive_format: Option<String>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub asm_args: Option<Vec<String>>,
126 #[serde(default = "default_true", skip_serializing_if = "Clone::clone")]
127 pub atomic_cas: bool,
128 #[serde(default, skip_serializing_if = "BinaryFormat::is_elf")]
129 pub binary_format: BinaryFormat,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub bitcode_llvm_cmdline: Option<String>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub c_enum_min_bits: Option<u32>,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub code_model: Option<String>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub cpu: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub crt_objects_fallback: Option<String>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub crt_static_allows_dylibs: Option<bool>,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub crt_static_default: Option<bool>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub crt_static_respected: Option<bool>,
146 pub data_layout: String,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub debuginfo_kind: Option<String>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub default_adjusted_cabi: Option<String>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub default_codegen_units: Option<u32>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub default_dwarf_version: Option<u32>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub default_hidden_visibility: Option<bool>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub default_sanitizers: Option<Vec<Sanitizer>>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub default_uwtable: Option<bool>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub direct_access_external_data: Option<bool>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub disable_redzone: Option<bool>,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub dll_prefix: Option<String>,
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub dll_suffix: Option<String>,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub dll_tls_export: Option<bool>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub dynamic_linking: Option<bool>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub eh_frame_header: Option<bool>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub emit_debug_gdb_scripts: Option<bool>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub entry_abi: Option<String>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub entry_name: Option<String>,
181 #[serde(default, skip_serializing_if = "Env::is_none")]
182 pub env: Env,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub executables: Option<bool>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub exe_suffix: Option<String>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub features: Option<String>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub force_emulated_tls: Option<bool>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub forces_embed_bitcode: Option<bool>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub frame_pointer: Option<String>,
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub function_sections: Option<bool>,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub generate_arange_section: Option<bool>,
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub has_rpath: Option<bool>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub has_thread_local: Option<bool>,
203 #[serde(default, skip_serializing_if = "ops::Not::not")]
204 pub has_thumb_interworking: bool,
205 #[serde(default, skip_serializing_if = "ops::Not::not")]
206 pub is_builtin: bool,
207 #[serde(default, skip_serializing_if = "ops::Not::not")]
208 pub is_like_android: bool,
209 #[serde(default, skip_serializing_if = "ops::Not::not")]
210 pub is_like_aix: bool,
211 #[serde(default, skip_serializing_if = "ops::Not::not")]
212 pub is_like_darwin: bool,
213 #[serde(default, skip_serializing_if = "ops::Not::not")]
214 pub is_like_gpu: bool,
215 #[serde(default, skip_serializing_if = "ops::Not::not")]
217 pub is_like_osx: bool,
218 #[serde(default, skip_serializing_if = "ops::Not::not")]
219 pub is_like_solaris: bool,
220 #[serde(default, skip_serializing_if = "ops::Not::not")]
221 pub is_like_msvc: bool,
222 #[serde(default, skip_serializing_if = "ops::Not::not")]
223 pub is_like_vexos: bool,
224 #[serde(default, skip_serializing_if = "ops::Not::not")]
225 pub is_like_wasm: bool,
226 #[serde(default, skip_serializing_if = "ops::Not::not")]
227 pub is_like_windows: bool,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub late_link_args: Option<BTreeMap<String, Vec<String>>>,
230 #[serde(skip_serializing_if = "Option::is_none")]
231 pub late_link_args_dynamic: Option<BTreeMap<String, Vec<String>>>,
232 #[serde(skip_serializing_if = "Option::is_none")]
233 pub late_link_args_static: Option<BTreeMap<String, Vec<String>>>,
234 #[serde(skip_serializing_if = "Option::is_none")]
235 pub limit_rdylib_exports: Option<bool>,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 pub link_env: Option<Vec<String>>,
238 #[serde(skip_serializing_if = "Option::is_none")]
239 pub link_env_remove: Option<Vec<String>>,
240 #[serde(skip_serializing_if = "Option::is_none")]
241 pub link_self_contained: Option<BTreeMap<String, Vec<String>>>,
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub link_script: Option<String>,
244 #[serde(skip_serializing_if = "Option::is_none")]
245 pub linker: Option<String>,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub linker_flavor: Option<String>,
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub linker_is_gnu: Option<bool>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub lld_flavor: Option<String>,
252 #[serde(skip_serializing_if = "Option::is_none")]
253 pub llvm_abiname: Option<String>,
254 #[serde(skip_serializing_if = "Option::is_none")]
255 pub llvm_args: Option<Vec<String>>,
256 #[serde(skip_serializing_if = "Option::is_none")]
257 pub llvm_floatabi: Option<String>,
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub llvm_mcount_intrinsic: Option<String>,
260 pub llvm_target: String,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub main_needs_argc_argv: Option<bool>,
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub max_atomic_width: Option<u32>,
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub merge_functions: Option<String>,
267 pub metadata: Option<Metadata>,
268 #[serde(skip_serializing_if = "Option::is_none")]
269 pub min_atomic_width: Option<u32>,
270 #[serde(skip_serializing_if = "Option::is_none")]
271 pub min_global_align: Option<u32>,
272 #[serde(skip_serializing_if = "Option::is_none")]
273 pub need_explicit_cpu: Option<bool>,
274 #[serde(skip_serializing_if = "Option::is_none")]
275 pub needs_plt: Option<bool>,
276 #[serde(skip_serializing_if = "Option::is_none")]
277 pub no_builtins: Option<bool>,
278 #[serde(skip_serializing_if = "Option::is_none")]
279 pub no_default_libraries: Option<bool>,
280 #[serde(default, skip_serializing_if = "Os::is_none")]
281 pub os: Os,
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub obj_is_bitcode: Option<bool>,
284 #[serde(skip_serializing_if = "Option::is_none")]
285 pub only_cdylib: Option<bool>,
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub override_export_symbols: Option<Vec<String>>,
288 #[serde(default, skip_serializing_if = "PanicStrategy::is_unwind")]
289 pub panic_strategy: PanicStrategy,
290 #[serde(skip_serializing_if = "Option::is_none")]
291 pub plt_by_default: Option<bool>,
292 #[serde(skip_serializing_if = "Option::is_none")]
293 pub position_independent_executables: Option<bool>,
294 #[serde(skip_serializing_if = "Option::is_none")]
295 pub post_link_args: Option<BTreeMap<String, Vec<String>>>,
296 #[serde(skip_serializing_if = "Option::is_none")]
297 pub post_link_objects: Option<BTreeMap<String, Vec<String>>>,
298 #[serde(skip_serializing_if = "Option::is_none")]
299 pub post_link_objects_fallback: Option<BTreeMap<String, Vec<String>>>,
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub pre_link_args: Option<BTreeMap<String, Vec<String>>>,
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub pre_link_objects: Option<BTreeMap<String, Vec<String>>>,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub pre_link_objects_fallback: Option<BTreeMap<String, Vec<String>>>,
306 #[serde(skip_serializing_if = "Option::is_none")]
307 pub relax_elf_relocations: Option<bool>,
308 #[serde(skip_serializing_if = "Option::is_none")]
309 pub relocation_model: Option<String>,
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub relro_level: Option<String>,
312 #[serde(skip_serializing_if = "Option::is_none")]
313 pub requires_lto: Option<bool>,
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub requires_uwtable: Option<bool>,
316 #[serde(skip_serializing_if = "Option::is_none")]
317 pub rustc_abi: Option<String>,
318 #[serde(skip_serializing_if = "Option::is_none")]
319 pub split_debuginfo: Option<String>,
320 #[serde(skip_serializing_if = "Option::is_none")]
321 pub stack_probes: Option<StackProbes>,
322 #[serde(skip_serializing_if = "Option::is_none")]
323 pub static_initializer_must_be_acyclic: Option<bool>,
324 #[serde(skip_serializing_if = "Option::is_none")]
325 pub static_position_independent_executables: Option<bool>,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub staticlib_prefix: Option<String>,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub staticlib_suffix: Option<String>,
330 #[serde(default, skip_serializing_if = "Vec::is_empty")]
331 pub supported_sanitizers: Vec<Sanitizer>,
332 #[serde(default, skip_serializing_if = "Vec::is_empty")]
333 pub supported_split_debuginfo: Vec<String>,
334 #[serde(default = "default_true", skip_serializing_if = "Clone::clone")]
335 pub supports_stack_protector: bool,
336 #[serde(default, skip_serializing_if = "ops::Not::not")]
337 pub supports_fentry: bool,
338 #[serde(default, skip_serializing_if = "ops::Not::not")]
339 pub supports_xray: bool,
340 #[serde(skip_serializing_if = "Option::is_none")]
341 pub simd_types_indirect: Option<bool>,
342 #[serde(default, skip_serializing_if = "ops::Not::not")]
343 pub singlethread: bool,
344 #[serde(default, skip_serializing_if = "TargetEndian::is_little")]
345 pub target_endian: TargetEndian,
346 #[serde(default, skip_serializing_if = "Vec::is_empty")]
347 pub target_family: Vec<TargetFamily>,
348 #[serde(skip_serializing_if = "Option::is_none")]
349 pub target_mcount: Option<String>,
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub target_c_int_width: Option<u32>,
353 pub target_pointer_width: u32,
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub tls_model: Option<String>,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub trap_unreachable: Option<bool>,
359 #[serde(default, skip_serializing_if = "Vec::is_empty")]
360 pub unsupported_cpus: Vec<String>,
361 #[serde(skip_serializing_if = "Option::is_none")]
362 pub use_ctors_section: Option<bool>,
363 #[serde(skip_serializing_if = "Option::is_none")]
364 pub vendor: Option<String>,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
368#[cfg_attr(test, serde(deny_unknown_fields))]
369#[non_exhaustive]
370pub struct Metadata {
371 pub description: Option<String>,
372 pub host_tools: Option<bool>,
373 pub std: Option<bool>,
374 pub tier: Option<u32>,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
378#[serde(rename_all = "kebab-case")]
379#[cfg_attr(test, serde(deny_unknown_fields))]
380#[non_exhaustive]
381pub struct StackProbes {
382 pub kind: String,
383 #[serde(skip_serializing_if = "Option::is_none")]
384 pub min_llvm_version_for_inline: Option<(u32, u32, u32)>,
385}
386
387fn default_true() -> bool {
388 true
389}
390
391pub fn target_spec_json(rustc: Command, target: &str) -> Result<TargetSpec> {
393 let raw = ProcessBuilder::from_std(rustc)
394 .args(["-Z", "unstable-options", "--print", "target-spec-json", "--target", target])
395 .read()?;
396 serde_json::from_str(&raw).map_err(Error::new)
397}
398
399pub fn all_target_specs_json(rustc: Command) -> Result<AllTargetSpecs> {
401 let raw = ProcessBuilder::from_std(rustc)
402 .args(["-Z", "unstable-options", "--print", "all-target-specs-json"])
403 .read()?;
404 serde_json::from_str(&raw).map_err(Error::new)
405}
406
407#[cfg(test)]
408mod tests {
409 use std::eprintln;
410
411 use super::*;
412
413 fn target_spec_json(target: &str) -> Result<(TargetSpec, String)> {
414 let mut cmd = cmd!(
415 "rustc",
416 "-Z",
417 "unstable-options",
418 "--print",
419 "target-spec-json",
420 "--target",
421 target
422 );
423 if !rustversion::cfg!(nightly) {
424 cmd.env("RUSTC_BOOTSTRAP", "1");
425 }
426 let raw = cmd.read()?;
427 Ok((serde_json::from_str(&raw).map_err(Error::new)?, raw))
428 }
429
430 fn all_target_specs_json() -> Result<(AllTargetSpecs, String)> {
431 let mut cmd = cmd!("rustc", "-Z", "unstable-options", "--print", "all-target-specs-json");
432 if !rustversion::cfg!(nightly) {
433 cmd.env("RUSTC_BOOTSTRAP", "1");
434 }
435 let raw = cmd.read()?;
436 Ok((serde_json::from_str(&raw).map_err(Error::new)?, raw))
437 }
438
439 #[rustversion::attr(before(1.91), ignore)]
441 #[test]
442 #[cfg_attr(miri, ignore)] fn parse_target_spec_json() {
444 for target in cmd!("rustc", "--print", "target-list").read().unwrap().lines() {
446 eprintln!("target={target}:");
447 let (parsed, raw) = target_spec_json(target).unwrap();
448 let deserialized = serde_json::to_string(&parsed).unwrap();
449 assert_eq!(
450 serde_json::from_str::<serde_json::Value>(&raw).unwrap(),
451 serde_json::from_str::<serde_json::Value>(&deserialized).unwrap()
452 );
453 }
454 eprintln!("all-targets:");
455 let (parsed, raw) = all_target_specs_json().unwrap();
456 let deserialized = serde_json::to_string(&parsed).unwrap();
457 assert_eq!(
458 serde_json::from_str::<serde_json::Value>(&raw).unwrap(),
459 serde_json::from_str::<serde_json::Value>(&deserialized).unwrap()
460 );
461 }
468}