rustc_target/spec/
json.rs

1use std::collections::BTreeMap;
2use std::str::FromStr;
3
4use rustc_abi::{Align, AlignFromBytesError};
5
6use super::crt_objects::CrtObjects;
7use super::{
8    BinaryFormat, CodeModel, DebuginfoKind, FloatAbi, FramePointer, LinkArgsCli,
9    LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFlavorCli, LldFlavor,
10    MergeFunctions, PanicStrategy, RelocModel, RelroLevel, RustcAbi, SanitizerSet,
11    SmallDataThresholdSupport, SplitDebuginfo, StackProbeType, StaticCow, SymbolVisibility, Target,
12    TargetKind, TargetOptions, TargetWarnings, TlsModel,
13};
14use crate::json::{Json, ToJson};
15use crate::spec::AbiMap;
16
17impl Target {
18    /// Loads a target descriptor from a JSON object.
19    pub fn from_json(json: &str) -> Result<(Target, TargetWarnings), String> {
20        let json_deserializer = &mut serde_json::Deserializer::from_str(json);
21
22        let json: TargetSpecJson =
23            serde_path_to_error::deserialize(json_deserializer).map_err(|err| err.to_string())?;
24
25        let mut base = Target {
26            llvm_target: json.llvm_target,
27            metadata: Default::default(),
28            pointer_width: json
29                .target_pointer_width
30                .parse()
31                .map_err(|err| format!("invalid target-pointer-width: {err}"))?,
32            data_layout: json.data_layout,
33            arch: json.arch,
34            options: Default::default(),
35        };
36
37        // FIXME: This doesn't properly validate anything and just ignores the data if it's invalid.
38        // That's okay for now, the only use of this is when generating docs, which we don't do for
39        // custom targets.
40        if let Some(metadata) = json.metadata {
41            base.metadata.description = metadata.description;
42            base.metadata.tier = metadata.tier.filter(|tier| (1..=3).contains(tier));
43            base.metadata.host_tools = metadata.host_tools;
44            base.metadata.std = metadata.std;
45        }
46
47        let alignment_error = |field_name: &str, error: AlignFromBytesError| -> String {
48            let msg = match error {
49                AlignFromBytesError::NotPowerOfTwo(_) => "not a power of 2 number of bytes",
50                AlignFromBytesError::TooLarge(_) => "too large",
51            };
52            format!("`{}` bits is not a valid value for {field_name}: {msg}", error.align() * 8)
53        };
54
55        macro_rules! forward {
56            ($name:ident) => {
57                if let Some($name) = json.$name {
58                    base.$name = $name;
59                }
60            };
61        }
62        macro_rules! forward_opt {
63            ($name:ident) => {
64                if let Some($name) = json.$name {
65                    base.$name = Some($name);
66                }
67            };
68        }
69
70        if let Some(target_endian) = json.target_endian {
71            base.endian = target_endian.0;
72        }
73
74        forward!(frame_pointer);
75        forward!(c_int_width);
76        forward_opt!(c_enum_min_bits); // if None, matches c_int_width
77        forward!(os);
78        forward!(env);
79        forward!(abi);
80        forward!(vendor);
81        forward_opt!(linker);
82        forward!(linker_flavor_json);
83        forward!(lld_flavor_json);
84        forward!(linker_is_gnu_json);
85        forward!(pre_link_objects);
86        forward!(post_link_objects);
87        forward!(pre_link_objects_self_contained);
88        forward!(post_link_objects_self_contained);
89
90        // Deserializes the backwards-compatible variants of `-Clink-self-contained`
91        if let Some(link_self_contained) = json.link_self_contained_backwards_compatible {
92            base.link_self_contained = link_self_contained;
93        }
94        // Deserializes the components variant of `-Clink-self-contained`
95        if let Some(link_self_contained) = json.link_self_contained {
96            let components = link_self_contained
97                .components
98                .into_iter()
99                .fold(LinkSelfContainedComponents::empty(), |a, b| a | b);
100            base.link_self_contained = LinkSelfContainedDefault::WithComponents(components);
101        }
102
103        forward!(pre_link_args_json);
104        forward!(late_link_args_json);
105        forward!(late_link_args_dynamic_json);
106        forward!(late_link_args_static_json);
107        forward!(post_link_args_json);
108        forward_opt!(link_script);
109
110        if let Some(link_env) = json.link_env {
111            for s in link_env {
112                if let [k, v] = *s.split('=').collect::<Vec<_>>() {
113                    base.link_env.to_mut().push((k.to_string().into(), v.to_string().into()))
114                } else {
115                    return Err(format!("link-env value '{s}' must be of the pattern 'KEY=VALUE'"));
116                }
117            }
118        }
119
120        forward!(link_env_remove);
121        forward!(asm_args);
122        forward!(cpu);
123        forward!(need_explicit_cpu);
124        forward!(features);
125        forward!(dynamic_linking);
126        forward_opt!(direct_access_external_data);
127        forward!(dll_tls_export);
128        forward!(only_cdylib);
129        forward!(executables);
130        forward!(relocation_model);
131        forward_opt!(code_model);
132        forward!(tls_model);
133        forward!(disable_redzone);
134        forward!(function_sections);
135        forward!(dll_prefix);
136        forward!(dll_suffix);
137        forward!(exe_suffix);
138        forward!(staticlib_prefix);
139        forward!(staticlib_suffix);
140
141        if let Some(target_family) = json.target_family {
142            match target_family {
143                TargetFamiliesJson::Array(families) => base.families = families,
144                TargetFamiliesJson::String(family) => base.families = vec![family].into(),
145            }
146        }
147
148        forward!(abi_return_struct_as_int);
149        forward!(is_like_aix);
150        forward!(is_like_darwin);
151        forward!(is_like_solaris);
152        forward!(is_like_windows);
153        forward!(is_like_msvc);
154        forward!(is_like_wasm);
155        forward!(is_like_android);
156        forward!(is_like_vexos);
157        forward!(binary_format);
158        forward!(default_dwarf_version);
159        forward!(allows_weak_linkage);
160        forward!(has_rpath);
161        forward!(no_default_libraries);
162        forward!(position_independent_executables);
163        forward!(static_position_independent_executables);
164        forward!(plt_by_default);
165        forward!(relro_level);
166        forward!(archive_format);
167        forward!(allow_asm);
168        forward!(main_needs_argc_argv);
169        forward!(has_thread_local);
170        forward!(obj_is_bitcode);
171        forward!(bitcode_llvm_cmdline);
172        forward_opt!(max_atomic_width);
173        forward_opt!(min_atomic_width);
174        forward!(atomic_cas);
175        forward!(panic_strategy);
176        forward!(crt_static_allows_dylibs);
177        forward!(crt_static_default);
178        forward!(crt_static_respected);
179        forward!(stack_probes);
180
181        if let Some(min_global_align) = json.min_global_align {
182            match Align::from_bits(min_global_align) {
183                Ok(align) => base.min_global_align = Some(align),
184                Err(e) => return Err(alignment_error("min-global-align", e)),
185            }
186        }
187
188        forward_opt!(default_codegen_units);
189        forward_opt!(default_codegen_backend);
190        forward!(trap_unreachable);
191        forward!(requires_lto);
192        forward!(singlethread);
193        forward!(no_builtins);
194        forward_opt!(default_visibility);
195        forward!(emit_debug_gdb_scripts);
196        forward!(requires_uwtable);
197        forward!(default_uwtable);
198        forward!(simd_types_indirect);
199        forward!(limit_rdylib_exports);
200        forward_opt!(override_export_symbols);
201        forward!(merge_functions);
202        forward!(mcount);
203        forward_opt!(llvm_mcount_intrinsic);
204        forward!(llvm_abiname);
205        forward_opt!(llvm_floatabi);
206        forward_opt!(rustc_abi);
207        forward!(relax_elf_relocations);
208        forward!(llvm_args);
209        forward!(use_ctors_section);
210        forward!(eh_frame_header);
211        forward!(has_thumb_interworking);
212        forward!(debuginfo_kind);
213        forward!(split_debuginfo);
214        forward!(supported_split_debuginfo);
215
216        if let Some(supported_sanitizers) = json.supported_sanitizers {
217            base.supported_sanitizers =
218                supported_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b);
219        }
220
221        forward!(generate_arange_section);
222        forward!(supports_stack_protector);
223        forward!(small_data_threshold_support);
224        forward!(entry_name);
225        forward!(supports_xray);
226
227        // we're going to run `update_from_cli`, but that won't change the target's AbiMap
228        // FIXME: better factor the Target definition so we enforce this on a type level
229        let abi_map = AbiMap::from_target(&base);
230        if let Some(entry_abi) = json.entry_abi {
231            base.options.entry_abi = abi_map.canonize_abi(entry_abi.0, false).unwrap();
232        }
233
234        base.update_from_cli();
235        base.check_consistency(TargetKind::Json)?;
236
237        Ok((base, TargetWarnings { unused_fields: vec![] }))
238    }
239}
240
241impl ToJson for Target {
242    fn to_json(&self) -> Json {
243        let mut d = serde_json::Map::new();
244        let default: TargetOptions = Default::default();
245        let mut target = self.clone();
246        target.update_to_cli();
247
248        macro_rules! target_val {
249            ($attr:ident) => {{
250                let name = (stringify!($attr)).replace("_", "-");
251                d.insert(name, target.$attr.to_json());
252            }};
253        }
254
255        macro_rules! target_option_val {
256            ($attr:ident) => {{
257                let name = (stringify!($attr)).replace("_", "-");
258                if default.$attr != target.$attr {
259                    d.insert(name, target.$attr.to_json());
260                }
261            }};
262            ($attr:ident, $json_name:expr) => {{
263                let name = $json_name;
264                if default.$attr != target.$attr {
265                    d.insert(name.into(), target.$attr.to_json());
266                }
267            }};
268            (link_args - $attr:ident, $json_name:expr) => {{
269                let name = $json_name;
270                if default.$attr != target.$attr {
271                    let obj = target
272                        .$attr
273                        .iter()
274                        .map(|(k, v)| (k.desc().to_string(), v.clone()))
275                        .collect::<BTreeMap<_, _>>();
276                    d.insert(name.to_string(), obj.to_json());
277                }
278            }};
279            (env - $attr:ident) => {{
280                let name = (stringify!($attr)).replace("_", "-");
281                if default.$attr != target.$attr {
282                    let obj = target
283                        .$attr
284                        .iter()
285                        .map(|&(ref k, ref v)| format!("{k}={v}"))
286                        .collect::<Vec<_>>();
287                    d.insert(name, obj.to_json());
288                }
289            }};
290        }
291
292        target_val!(llvm_target);
293        target_val!(metadata);
294        d.insert("target-pointer-width".to_string(), self.pointer_width.to_string().to_json());
295        target_val!(arch);
296        target_val!(data_layout);
297
298        target_option_val!(endian, "target-endian");
299        target_option_val!(c_int_width, "target-c-int-width");
300        target_option_val!(os);
301        target_option_val!(env);
302        target_option_val!(abi);
303        target_option_val!(vendor);
304        target_option_val!(linker);
305        target_option_val!(linker_flavor_json, "linker-flavor");
306        target_option_val!(lld_flavor_json, "lld-flavor");
307        target_option_val!(linker_is_gnu_json, "linker-is-gnu");
308        target_option_val!(pre_link_objects);
309        target_option_val!(post_link_objects);
310        target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback");
311        target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback");
312        target_option_val!(link_args - pre_link_args_json, "pre-link-args");
313        target_option_val!(link_args - late_link_args_json, "late-link-args");
314        target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic");
315        target_option_val!(link_args - late_link_args_static_json, "late-link-args-static");
316        target_option_val!(link_args - post_link_args_json, "post-link-args");
317        target_option_val!(link_script);
318        target_option_val!(env - link_env);
319        target_option_val!(link_env_remove);
320        target_option_val!(asm_args);
321        target_option_val!(cpu);
322        target_option_val!(need_explicit_cpu);
323        target_option_val!(features);
324        target_option_val!(dynamic_linking);
325        target_option_val!(direct_access_external_data);
326        target_option_val!(dll_tls_export);
327        target_option_val!(only_cdylib);
328        target_option_val!(executables);
329        target_option_val!(relocation_model);
330        target_option_val!(code_model);
331        target_option_val!(tls_model);
332        target_option_val!(disable_redzone);
333        target_option_val!(frame_pointer);
334        target_option_val!(function_sections);
335        target_option_val!(dll_prefix);
336        target_option_val!(dll_suffix);
337        target_option_val!(exe_suffix);
338        target_option_val!(staticlib_prefix);
339        target_option_val!(staticlib_suffix);
340        target_option_val!(families, "target-family");
341        target_option_val!(abi_return_struct_as_int);
342        target_option_val!(is_like_aix);
343        target_option_val!(is_like_darwin);
344        target_option_val!(is_like_solaris);
345        target_option_val!(is_like_windows);
346        target_option_val!(is_like_msvc);
347        target_option_val!(is_like_wasm);
348        target_option_val!(is_like_android);
349        target_option_val!(is_like_vexos);
350        target_option_val!(binary_format);
351        target_option_val!(default_dwarf_version);
352        target_option_val!(allows_weak_linkage);
353        target_option_val!(has_rpath);
354        target_option_val!(no_default_libraries);
355        target_option_val!(position_independent_executables);
356        target_option_val!(static_position_independent_executables);
357        target_option_val!(plt_by_default);
358        target_option_val!(relro_level);
359        target_option_val!(archive_format);
360        target_option_val!(allow_asm);
361        target_option_val!(main_needs_argc_argv);
362        target_option_val!(has_thread_local);
363        target_option_val!(obj_is_bitcode);
364        target_option_val!(bitcode_llvm_cmdline);
365        target_option_val!(min_atomic_width);
366        target_option_val!(max_atomic_width);
367        target_option_val!(atomic_cas);
368        target_option_val!(panic_strategy);
369        target_option_val!(crt_static_allows_dylibs);
370        target_option_val!(crt_static_default);
371        target_option_val!(crt_static_respected);
372        target_option_val!(stack_probes);
373        target_option_val!(min_global_align);
374        target_option_val!(default_codegen_units);
375        target_option_val!(default_codegen_backend);
376        target_option_val!(trap_unreachable);
377        target_option_val!(requires_lto);
378        target_option_val!(singlethread);
379        target_option_val!(no_builtins);
380        target_option_val!(default_visibility);
381        target_option_val!(emit_debug_gdb_scripts);
382        target_option_val!(requires_uwtable);
383        target_option_val!(default_uwtable);
384        target_option_val!(simd_types_indirect);
385        target_option_val!(limit_rdylib_exports);
386        target_option_val!(override_export_symbols);
387        target_option_val!(merge_functions);
388        target_option_val!(mcount, "target-mcount");
389        target_option_val!(llvm_mcount_intrinsic);
390        target_option_val!(llvm_abiname);
391        target_option_val!(llvm_floatabi);
392        target_option_val!(rustc_abi);
393        target_option_val!(relax_elf_relocations);
394        target_option_val!(llvm_args);
395        target_option_val!(use_ctors_section);
396        target_option_val!(eh_frame_header);
397        target_option_val!(has_thumb_interworking);
398        target_option_val!(debuginfo_kind);
399        target_option_val!(split_debuginfo);
400        target_option_val!(supported_split_debuginfo);
401        target_option_val!(supported_sanitizers);
402        target_option_val!(c_enum_min_bits);
403        target_option_val!(generate_arange_section);
404        target_option_val!(supports_stack_protector);
405        target_option_val!(small_data_threshold_support);
406        target_option_val!(entry_name);
407        target_option_val!(entry_abi);
408        target_option_val!(supports_xray);
409
410        // Serializing `-Clink-self-contained` needs a dynamic key to support the
411        // backwards-compatible variants.
412        d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json());
413
414        Json::Object(d)
415    }
416}
417
418#[derive(serde_derive::Deserialize)]
419struct LinkSelfContainedComponentsWrapper {
420    components: Vec<LinkSelfContainedComponents>,
421}
422
423#[derive(serde_derive::Deserialize)]
424#[serde(untagged)]
425enum TargetFamiliesJson {
426    Array(StaticCow<[StaticCow<str>]>),
427    String(StaticCow<str>),
428}
429
430/// `Endian` is in `rustc_abi`, which doesn't have access to the macro and serde.
431struct EndianWrapper(rustc_abi::Endian);
432impl FromStr for EndianWrapper {
433    type Err = String;
434    fn from_str(s: &str) -> Result<Self, Self::Err> {
435        rustc_abi::Endian::from_str(s).map(Self)
436    }
437}
438crate::json::serde_deserialize_from_str!(EndianWrapper);
439
440/// `ExternAbi` is in `rustc_abi`, which doesn't have access to the macro and serde.
441struct ExternAbiWrapper(rustc_abi::ExternAbi);
442impl FromStr for ExternAbiWrapper {
443    type Err = String;
444    fn from_str(s: &str) -> Result<Self, Self::Err> {
445        rustc_abi::ExternAbi::from_str(s)
446            .map(Self)
447            .map_err(|_| format!("{s} is not a valid extern ABI"))
448    }
449}
450crate::json::serde_deserialize_from_str!(ExternAbiWrapper);
451
452#[derive(serde_derive::Deserialize)]
453struct TargetSpecJsonMetadata {
454    description: Option<StaticCow<str>>,
455    tier: Option<u64>,
456    host_tools: Option<bool>,
457    std: Option<bool>,
458}
459
460#[derive(serde_derive::Deserialize)]
461#[serde(rename_all = "kebab-case")]
462// Ensure that all unexpected fields get turned into errors.
463// This helps users stay up to date when the schema changes instead of silently
464// ignoring their old values.
465#[serde(deny_unknown_fields)]
466struct TargetSpecJson {
467    llvm_target: StaticCow<str>,
468    target_pointer_width: String,
469    data_layout: StaticCow<str>,
470    arch: StaticCow<str>,
471
472    metadata: Option<TargetSpecJsonMetadata>,
473
474    // options:
475    target_endian: Option<EndianWrapper>,
476    frame_pointer: Option<FramePointer>,
477    #[serde(rename = "target-c-int-width")]
478    c_int_width: Option<u16>,
479    c_enum_min_bits: Option<u64>,
480    os: Option<StaticCow<str>>,
481    env: Option<StaticCow<str>>,
482    abi: Option<StaticCow<str>>,
483    vendor: Option<StaticCow<str>>,
484    linker: Option<StaticCow<str>>,
485    #[serde(rename = "linker-flavor")]
486    linker_flavor_json: Option<LinkerFlavorCli>,
487    #[serde(rename = "lld-flavor")]
488    lld_flavor_json: Option<LldFlavor>,
489    #[serde(rename = "linker-is-gnu")]
490    linker_is_gnu_json: Option<bool>,
491    #[serde(rename = "pre-link-objects")]
492    pre_link_objects: Option<CrtObjects>,
493    #[serde(rename = "post-link-objects")]
494    post_link_objects: Option<CrtObjects>,
495    #[serde(rename = "pre-link-objects-fallback")]
496    pre_link_objects_self_contained: Option<CrtObjects>,
497    #[serde(rename = "post-link-objects-fallback")]
498    post_link_objects_self_contained: Option<CrtObjects>,
499    #[serde(rename = "crt-objects-fallback")]
500    link_self_contained_backwards_compatible: Option<LinkSelfContainedDefault>,
501    link_self_contained: Option<LinkSelfContainedComponentsWrapper>,
502    #[serde(rename = "pre-link-args")]
503    pre_link_args_json: Option<LinkArgsCli>,
504    #[serde(rename = "late-link-args")]
505    late_link_args_json: Option<LinkArgsCli>,
506    #[serde(rename = "late-link-args-dynamic")]
507    late_link_args_dynamic_json: Option<LinkArgsCli>,
508    #[serde(rename = "late-link-args-static")]
509    late_link_args_static_json: Option<LinkArgsCli>,
510    #[serde(rename = "post-link-args")]
511    post_link_args_json: Option<LinkArgsCli>,
512    link_script: Option<StaticCow<str>>,
513    link_env: Option<Vec<StaticCow<str>>>,
514    link_env_remove: Option<StaticCow<[StaticCow<str>]>>,
515    asm_args: Option<StaticCow<[StaticCow<str>]>>,
516    cpu: Option<StaticCow<str>>,
517    need_explicit_cpu: Option<bool>,
518    features: Option<StaticCow<str>>,
519    dynamic_linking: Option<bool>,
520    direct_access_external_data: Option<bool>,
521    dll_tls_export: Option<bool>,
522    only_cdylib: Option<bool>,
523    executables: Option<bool>,
524    relocation_model: Option<RelocModel>,
525    code_model: Option<CodeModel>,
526    tls_model: Option<TlsModel>,
527    disable_redzone: Option<bool>,
528    function_sections: Option<bool>,
529    dll_prefix: Option<StaticCow<str>>,
530    dll_suffix: Option<StaticCow<str>>,
531    exe_suffix: Option<StaticCow<str>>,
532    staticlib_prefix: Option<StaticCow<str>>,
533    staticlib_suffix: Option<StaticCow<str>>,
534    target_family: Option<TargetFamiliesJson>,
535    abi_return_struct_as_int: Option<bool>,
536    is_like_aix: Option<bool>,
537    is_like_darwin: Option<bool>,
538    is_like_solaris: Option<bool>,
539    is_like_windows: Option<bool>,
540    is_like_msvc: Option<bool>,
541    is_like_wasm: Option<bool>,
542    is_like_android: Option<bool>,
543    is_like_vexos: Option<bool>,
544    binary_format: Option<BinaryFormat>,
545    default_dwarf_version: Option<u32>,
546    allows_weak_linkage: Option<bool>,
547    has_rpath: Option<bool>,
548    no_default_libraries: Option<bool>,
549    position_independent_executables: Option<bool>,
550    static_position_independent_executables: Option<bool>,
551    plt_by_default: Option<bool>,
552    relro_level: Option<RelroLevel>,
553    archive_format: Option<StaticCow<str>>,
554    allow_asm: Option<bool>,
555    main_needs_argc_argv: Option<bool>,
556    has_thread_local: Option<bool>,
557    obj_is_bitcode: Option<bool>,
558    bitcode_llvm_cmdline: Option<StaticCow<str>>,
559    max_atomic_width: Option<u64>,
560    min_atomic_width: Option<u64>,
561    atomic_cas: Option<bool>,
562    panic_strategy: Option<PanicStrategy>,
563    crt_static_allows_dylibs: Option<bool>,
564    crt_static_default: Option<bool>,
565    crt_static_respected: Option<bool>,
566    stack_probes: Option<StackProbeType>,
567    min_global_align: Option<u64>,
568    default_codegen_units: Option<u64>,
569    default_codegen_backend: Option<StaticCow<str>>,
570    trap_unreachable: Option<bool>,
571    requires_lto: Option<bool>,
572    singlethread: Option<bool>,
573    no_builtins: Option<bool>,
574    default_visibility: Option<SymbolVisibility>,
575    emit_debug_gdb_scripts: Option<bool>,
576    requires_uwtable: Option<bool>,
577    default_uwtable: Option<bool>,
578    simd_types_indirect: Option<bool>,
579    limit_rdylib_exports: Option<bool>,
580    override_export_symbols: Option<StaticCow<[StaticCow<str>]>>,
581    merge_functions: Option<MergeFunctions>,
582    #[serde(rename = "target-mcount")]
583    mcount: Option<StaticCow<str>>,
584    llvm_mcount_intrinsic: Option<StaticCow<str>>,
585    llvm_abiname: Option<StaticCow<str>>,
586    llvm_floatabi: Option<FloatAbi>,
587    rustc_abi: Option<RustcAbi>,
588    relax_elf_relocations: Option<bool>,
589    llvm_args: Option<StaticCow<[StaticCow<str>]>>,
590    use_ctors_section: Option<bool>,
591    eh_frame_header: Option<bool>,
592    has_thumb_interworking: Option<bool>,
593    debuginfo_kind: Option<DebuginfoKind>,
594    split_debuginfo: Option<SplitDebuginfo>,
595    supported_split_debuginfo: Option<StaticCow<[SplitDebuginfo]>>,
596    supported_sanitizers: Option<Vec<SanitizerSet>>,
597    generate_arange_section: Option<bool>,
598    supports_stack_protector: Option<bool>,
599    small_data_threshold_support: Option<SmallDataThresholdSupport>,
600    entry_name: Option<StaticCow<str>>,
601    supports_xray: Option<bool>,
602    entry_abi: Option<ExternAbiWrapper>,
603}