Add --print target-spec-json-schema

This schema is helpful for people writing custom target spec JSON. It
can provide autocomplete in the editor, and also serves as documentation
when there are documentation comments on the structs, as `schemars` will
put them in the schema.
This commit is contained in:
Noratrieb
2025-07-26 12:39:31 +02:00
parent a0bb9cc57d
commit f157ce994e
19 changed files with 229 additions and 16 deletions

View File

@@ -1191,6 +1191,12 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
[[package]]
name = "dyn-clone"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
[[package]]
name = "either"
version = "1.15.0"
@@ -3122,6 +3128,26 @@ dependencies = [
"thiserror 2.0.15",
]
[[package]]
name = "ref-cast"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "regex"
version = "1.11.1"
@@ -4577,6 +4603,7 @@ dependencies = [
"rustc_macros",
"rustc_serialize",
"rustc_span",
"schemars",
"serde",
"serde_derive",
"serde_json",
@@ -4900,6 +4927,31 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "schemars"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
dependencies = [
"dyn-clone",
"ref-cast",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.106",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
@@ -4974,6 +5026,17 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "serde_json"
version = "1.0.142"

View File

@@ -109,7 +109,7 @@ impl Command {
}
Program::Lld(ref p, flavor) => {
let mut c = process::Command::new(p);
c.arg("-flavor").arg(flavor.as_str());
c.arg("-flavor").arg(flavor.desc());
c
}
};

View File

@@ -668,6 +668,10 @@ fn print_crate_info(
TargetSpecJson => {
println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
}
TargetSpecJsonSchema => {
let schema = rustc_target::spec::json_schema();
println_info!("{}", serde_json::to_string_pretty(&schema).unwrap());
}
AllTargetSpecsJson => {
let mut targets = BTreeMap::new();
for name in rustc_target::spec::TARGETS {

View File

@@ -70,6 +70,7 @@ pub const PRINT_KINDS: &[(&str, PrintKind)] = &[
("target-libdir", PrintKind::TargetLibdir),
("target-list", PrintKind::TargetList),
("target-spec-json", PrintKind::TargetSpecJson),
("target-spec-json-schema", PrintKind::TargetSpecJsonSchema),
("tls-models", PrintKind::TlsModels),
// tidy-alphabetical-end
];
@@ -1043,6 +1044,7 @@ pub enum PrintKind {
TargetLibdir,
TargetList,
TargetSpecJson,
TargetSpecJsonSchema,
TlsModels,
// tidy-alphabetical-end
}
@@ -2323,7 +2325,8 @@ fn is_print_request_stable(print_kind: PrintKind) -> bool {
| PrintKind::CheckCfg
| PrintKind::CrateRootLintLevels
| PrintKind::SupportedCrateTypes
| PrintKind::TargetSpecJson => false,
| PrintKind::TargetSpecJson
| PrintKind::TargetSpecJsonSchema => false,
_ => true,
}
}

View File

@@ -14,6 +14,7 @@ rustc_fs_util = { path = "../rustc_fs_util" }
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_span = { path = "../rustc_span" }
schemars = "1.0.4"
serde = "1.0.219"
serde_derive = "1.0.219"
serde_json = "1.0.59"

View File

@@ -86,9 +86,11 @@ macro_rules! target_spec_enum {
) => {
$( #[$attr] )*
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
#[derive(schemars::JsonSchema)]
pub enum $name {
$(
$( #[$variant_attr] )*
#[serde(rename = $string)] // for JSON schema generation only
$variant,
)*
}

View File

@@ -408,12 +408,12 @@ impl ToJson for Target {
}
}
#[derive(serde_derive::Deserialize)]
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
struct LinkSelfContainedComponentsWrapper {
components: Vec<LinkSelfContainedComponents>,
}
#[derive(serde_derive::Deserialize)]
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
#[serde(untagged)]
enum TargetFamiliesJson {
Array(StaticCow<[StaticCow<str>]>),
@@ -429,6 +429,18 @@ impl FromStr for EndianWrapper {
}
}
crate::json::serde_deserialize_from_str!(EndianWrapper);
impl schemars::JsonSchema for EndianWrapper {
fn schema_name() -> std::borrow::Cow<'static, str> {
"Endian".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema! ({
"type": "string",
"enum": ["big", "little"]
})
.into()
}
}
/// `ExternAbi` is in `rustc_abi`, which doesn't have access to the macro and serde.
struct ExternAbiWrapper(rustc_abi::ExternAbi);
@@ -441,8 +453,22 @@ impl FromStr for ExternAbiWrapper {
}
}
crate::json::serde_deserialize_from_str!(ExternAbiWrapper);
impl schemars::JsonSchema for ExternAbiWrapper {
fn schema_name() -> std::borrow::Cow<'static, str> {
"ExternAbi".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
let all =
rustc_abi::ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect::<Vec<_>>();
schemars::json_schema! ({
"type": "string",
"enum": all,
})
.into()
}
}
#[derive(serde_derive::Deserialize)]
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
struct TargetSpecJsonMetadata {
description: Option<StaticCow<str>>,
tier: Option<u64>,
@@ -450,7 +476,7 @@ struct TargetSpecJsonMetadata {
std: Option<bool>,
}
#[derive(serde_derive::Deserialize)]
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "kebab-case")]
// Ensure that all unexpected fields get turned into errors.
// This helps users stay up to date when the schema changes instead of silently
@@ -593,3 +619,7 @@ struct TargetSpecJson {
supports_xray: Option<bool>,
entry_abi: Option<ExternAbiWrapper>,
}
pub fn json_schema() -> schemars::Schema {
schemars::schema_for!(TargetSpecJson)
}

View File

@@ -70,6 +70,7 @@ mod json;
pub use abi_map::{AbiMap, AbiMapping};
pub use base::apple;
pub use base::avr::ef_avr_arch;
pub use json::json_schema;
/// Linker is called through a C/C++ compiler.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
@@ -523,6 +524,20 @@ linker_flavor_cli_impls! {
}
crate::json::serde_deserialize_from_str!(LinkerFlavorCli);
impl schemars::JsonSchema for LinkerFlavorCli {
fn schema_name() -> std::borrow::Cow<'static, str> {
"LinkerFlavor".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
let all: Vec<&'static str> =
Self::all().iter().map(|flavor| flavor.desc()).collect::<Vec<_>>();
schemars::json_schema! ({
"type": "string",
"enum": all
})
.into()
}
}
impl ToJson for LinkerFlavorCli {
fn to_json(&self) -> Json {
@@ -576,6 +591,18 @@ impl FromStr for LinkSelfContainedDefault {
}
crate::json::serde_deserialize_from_str!(LinkSelfContainedDefault);
impl schemars::JsonSchema for LinkSelfContainedDefault {
fn schema_name() -> std::borrow::Cow<'static, str> {
"LinkSelfContainedDefault".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema! ({
"type": "string",
"enum": ["false", "true", "wasm", "musl", "mingw"]
})
.into()
}
}
impl ToJson for LinkSelfContainedDefault {
fn to_json(&self) -> Json {
@@ -708,6 +735,20 @@ impl FromStr for LinkSelfContainedComponents {
}
crate::json::serde_deserialize_from_str!(LinkSelfContainedComponents);
impl schemars::JsonSchema for LinkSelfContainedComponents {
fn schema_name() -> std::borrow::Cow<'static, str> {
"LinkSelfContainedComponents".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
let all =
Self::all_components().iter().map(|component| component.as_str()).collect::<Vec<_>>();
schemars::json_schema! ({
"type": "string",
"enum": all,
})
.into()
}
}
impl ToJson for LinkSelfContainedComponents {
fn to_json(&self) -> Json {
@@ -846,7 +887,6 @@ crate::target_spec_enum! {
parse_error_type = "symbol visibility";
}
#[derive(Clone, Debug, PartialEq, Hash)]
pub enum SmallDataThresholdSupport {
None,
@@ -874,6 +914,18 @@ impl FromStr for SmallDataThresholdSupport {
}
crate::json::serde_deserialize_from_str!(SmallDataThresholdSupport);
impl schemars::JsonSchema for SmallDataThresholdSupport {
fn schema_name() -> std::borrow::Cow<'static, str> {
"SmallDataThresholdSupport".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema! ({
"type": "string",
"pattern": r#"^none|default-for-arch|llvm-module-flag=.+|llvm-arg=.+$"#,
})
.into()
}
}
impl ToJson for SmallDataThresholdSupport {
fn to_json(&self) -> Value {
@@ -1074,7 +1126,7 @@ crate::target_spec_enum! {
into_diag_arg_using_display!(SplitDebuginfo);
#[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize, schemars::JsonSchema)]
#[serde(tag = "kind")]
#[serde(rename_all = "kebab-case")]
pub enum StackProbeType {
@@ -1235,6 +1287,19 @@ impl FromStr for SanitizerSet {
}
crate::json::serde_deserialize_from_str!(SanitizerSet);
impl schemars::JsonSchema for SanitizerSet {
fn schema_name() -> std::borrow::Cow<'static, str> {
"SanitizerSet".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
let all = Self::all().iter().map(|sanitizer| sanitizer.as_str()).collect::<Vec<_>>();
schemars::json_schema! ({
"type": "string",
"enum": all,
})
.into()
}
}
impl ToJson for SanitizerSet {
fn to_json(&self) -> Json {
@@ -1328,7 +1393,6 @@ impl BinaryFormat {
}
}
impl ToJson for Align {
fn to_json(&self) -> Json {
self.bits().to_json()

View File

@@ -590,6 +590,8 @@ impl Step for Rustc {
// Debugger scripts
builder.ensure(DebuggerScripts { sysroot: image.to_owned(), target });
generate_target_spec_json_schema(builder, image);
// HTML copyright files
let file_list = builder.ensure(super::run::GenerateCopyright);
for file in file_list {
@@ -618,6 +620,28 @@ impl Step for Rustc {
}
}
fn generate_target_spec_json_schema(builder: &Builder<'_>, sysroot: &Path) {
// Since we run rustc in bootstrap, we need to ensure that we use the host compiler.
// We do this by using the stage 1 compiler, which is always compiled for the host,
// even in a cross build.
let stage1_host = builder.compiler(1, builder.host_target);
let mut rustc = command(builder.rustc(stage1_host)).fail_fast();
rustc
.env("RUSTC_BOOTSTRAP", "1")
.args(["--print=target-spec-json-schema", "-Zunstable-options"]);
let schema = rustc.run_capture(builder).stdout();
let schema_dir = tmpdir(builder);
t!(fs::create_dir_all(&schema_dir));
let schema_file = schema_dir.join("target-spec-json-schema.json");
t!(std::fs::write(&schema_file, schema));
let dst = sysroot.join("etc");
t!(fs::create_dir_all(&dst));
builder.install(&schema_file, &dst, FileType::Regular);
}
/// Copies debugger scripts for `target` into the given compiler `sysroot`.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct DebuggerScripts {

View File

@@ -43,6 +43,7 @@ pub static CRATES: &[&str] = &[
"rustc-hash",
"self_cell",
"serde",
"serde_derive_internals",
"sha2",
"smallvec",
"stable_deref_trait",

View File

@@ -16,6 +16,21 @@ rustc +nightly -Z unstable-options --target=wasm32-unknown-unknown --print targe
To use a custom target, see the (unstable) [`build-std` feature](../../cargo/reference/unstable.html#build-std) of `cargo`.
<div class="warning">
The target JSON properties are not stable and subject to change.
Always pin your compiler version when using custom targets!
</div>
## JSON Schema
`rustc` provides a JSON schema for the custom target JSON specification.
Because the schema is subject to change, you should always use the schema from the version of rustc which you are passing the target to.
It can be found in `etc/target-spec-json-schema.json` in the sysroot (`rustc --print sysroot`) or printed with `rustc +nightly -Zunstable-options --print target-spec-json-schema`.
The existence and name of this schema is, just like the properties of the JSON specification, not stable and subject to change.
## Custom Target Lookup Path
When `rustc` is given an option `--target=TARGET` (where `TARGET` is any string), it uses the following logic:

View File

@@ -267,6 +267,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"digest",
"displaydoc",
"dissimilar",
"dyn-clone",
"either",
"elsa",
"ena",
@@ -346,6 +347,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"rand_xorshift", // dependency for doc-tests in rustc_thread_pool
"rand_xoshiro",
"redox_syscall",
"ref-cast",
"ref-cast-impl",
"regex",
"regex-automata",
"regex-syntax",
@@ -357,11 +360,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"rustix",
"ruzstd", // via object in thorin-dwp
"ryu",
"schemars",
"schemars_derive",
"scoped-tls",
"scopeguard",
"self_cell",
"serde",
"serde_derive",
"serde_derive_internals",
"serde_json",
"serde_path_to_error",
"sha1",

View File

@@ -2,6 +2,6 @@
error: unknown print request: `xxx`
|
- = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models`
+ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
+ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information

View File

@@ -1,5 +1,5 @@
error: unknown print request: `xxx`
|
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information

View File

@@ -43,7 +43,7 @@ Options:
--print <INFO>[=<FILE>]
Compiler information to print on stdout (or to a file)
INFO may be one of
<all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>.
<all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.
-g Equivalent to -C debuginfo=2
-O Equivalent to -C opt-level=3
-o <FILENAME> Write output to FILENAME

View File

@@ -43,7 +43,7 @@ Options:
--print <INFO>[=<FILE>]
Compiler information to print on stdout (or to a file)
INFO may be one of
<all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>.
<all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.
-g Equivalent to -C debuginfo=2
-O Equivalent to -C opt-level=3
-o <FILENAME> Write output to FILENAME

View File

@@ -3,5 +3,5 @@ error: Argument to option 'print' missing
--print <INFO>[=<FILE>]
Compiler information to print on stdout (or to a file)
INFO may be one of
<all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>.
<all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.

View File

@@ -1,5 +1,5 @@
error: unknown print request: `yyyy`
|
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information

View File

@@ -1,6 +1,6 @@
error: unknown print request: `lints`
|
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: use `-Whelp` to print a list of lints
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information