Skip to content

Commit 5446444

Browse files
authored
Rollup merge of #144498 - Noratrieb:rustc-json-schema, r=jieyouxu,davidtwco
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. I was motivated to do this because I saw someone write their own version of this schema by hand, so demand for this clearly exists. It's not a lot of effort to implement, so I thought it would make sense. MCP: rust-lang/compiler-team#905 I think it would also be useful to put this in the sysroot in `etc` so people can link it directly in their editors. I would have loved to add a test that validates the JSON schema against the spec JSON of every builtin target, but I don't want to do it as the JSON schema validation crates have incredible amounts of dependencies because JSON schema supports a ton of random features. I don't want to add that, even as a dev dependency.
2 parents 9642c0e + f157ce9 commit 5446444

File tree

19 files changed

+473
-731
lines changed

19 files changed

+473
-731
lines changed

Cargo.lock

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,12 @@ version = "1.0.10"
11911191
source = "registry+https://github.com/rust-lang/crates.io-index"
11921192
checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
11931193

1194+
[[package]]
1195+
name = "dyn-clone"
1196+
version = "1.0.19"
1197+
source = "registry+https://github.com/rust-lang/crates.io-index"
1198+
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
1199+
11941200
[[package]]
11951201
name = "either"
11961202
version = "1.15.0"
@@ -3122,6 +3128,26 @@ dependencies = [
31223128
"thiserror 2.0.15",
31233129
]
31243130

3131+
[[package]]
3132+
name = "ref-cast"
3133+
version = "1.0.24"
3134+
source = "registry+https://github.com/rust-lang/crates.io-index"
3135+
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
3136+
dependencies = [
3137+
"ref-cast-impl",
3138+
]
3139+
3140+
[[package]]
3141+
name = "ref-cast-impl"
3142+
version = "1.0.24"
3143+
source = "registry+https://github.com/rust-lang/crates.io-index"
3144+
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
3145+
dependencies = [
3146+
"proc-macro2",
3147+
"quote",
3148+
"syn 2.0.106",
3149+
]
3150+
31253151
[[package]]
31263152
name = "regex"
31273153
version = "1.11.1"
@@ -4577,6 +4603,7 @@ dependencies = [
45774603
"rustc_macros",
45784604
"rustc_serialize",
45794605
"rustc_span",
4606+
"schemars",
45804607
"serde",
45814608
"serde_derive",
45824609
"serde_json",
@@ -4900,6 +4927,31 @@ dependencies = [
49004927
"windows-sys 0.59.0",
49014928
]
49024929

4930+
[[package]]
4931+
name = "schemars"
4932+
version = "1.0.4"
4933+
source = "registry+https://github.com/rust-lang/crates.io-index"
4934+
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
4935+
dependencies = [
4936+
"dyn-clone",
4937+
"ref-cast",
4938+
"schemars_derive",
4939+
"serde",
4940+
"serde_json",
4941+
]
4942+
4943+
[[package]]
4944+
name = "schemars_derive"
4945+
version = "1.0.4"
4946+
source = "registry+https://github.com/rust-lang/crates.io-index"
4947+
checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80"
4948+
dependencies = [
4949+
"proc-macro2",
4950+
"quote",
4951+
"serde_derive_internals",
4952+
"syn 2.0.106",
4953+
]
4954+
49034955
[[package]]
49044956
name = "scoped-tls"
49054957
version = "1.0.1"
@@ -4974,6 +5026,17 @@ dependencies = [
49745026
"syn 2.0.106",
49755027
]
49765028

5029+
[[package]]
5030+
name = "serde_derive_internals"
5031+
version = "0.29.1"
5032+
source = "registry+https://github.com/rust-lang/crates.io-index"
5033+
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
5034+
dependencies = [
5035+
"proc-macro2",
5036+
"quote",
5037+
"syn 2.0.106",
5038+
]
5039+
49775040
[[package]]
49785041
name = "serde_json"
49795042
version = "1.0.142"

compiler/rustc_codegen_ssa/src/back/command.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ impl Command {
109109
}
110110
Program::Lld(ref p, flavor) => {
111111
let mut c = process::Command::new(p);
112-
c.arg("-flavor").arg(flavor.as_str());
112+
c.arg("-flavor").arg(flavor.desc());
113113
c
114114
}
115115
};

compiler/rustc_driver_impl/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,10 @@ fn print_crate_info(
668668
TargetSpecJson => {
669669
println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
670670
}
671+
TargetSpecJsonSchema => {
672+
let schema = rustc_target::spec::json_schema();
673+
println_info!("{}", serde_json::to_string_pretty(&schema).unwrap());
674+
}
671675
AllTargetSpecsJson => {
672676
let mut targets = BTreeMap::new();
673677
for name in rustc_target::spec::TARGETS {

compiler/rustc_session/src/config.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub const PRINT_KINDS: &[(&str, PrintKind)] = &[
7070
("target-libdir", PrintKind::TargetLibdir),
7171
("target-list", PrintKind::TargetList),
7272
("target-spec-json", PrintKind::TargetSpecJson),
73+
("target-spec-json-schema", PrintKind::TargetSpecJsonSchema),
7374
("tls-models", PrintKind::TlsModels),
7475
// tidy-alphabetical-end
7576
];
@@ -1043,6 +1044,7 @@ pub enum PrintKind {
10431044
TargetLibdir,
10441045
TargetList,
10451046
TargetSpecJson,
1047+
TargetSpecJsonSchema,
10461048
TlsModels,
10471049
// tidy-alphabetical-end
10481050
}
@@ -2323,7 +2325,8 @@ fn is_print_request_stable(print_kind: PrintKind) -> bool {
23232325
| PrintKind::CheckCfg
23242326
| PrintKind::CrateRootLintLevels
23252327
| PrintKind::SupportedCrateTypes
2326-
| PrintKind::TargetSpecJson => false,
2328+
| PrintKind::TargetSpecJson
2329+
| PrintKind::TargetSpecJsonSchema => false,
23272330
_ => true,
23282331
}
23292332
}

compiler/rustc_target/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ rustc_fs_util = { path = "../rustc_fs_util" }
1414
rustc_macros = { path = "../rustc_macros" }
1515
rustc_serialize = { path = "../rustc_serialize" }
1616
rustc_span = { path = "../rustc_span" }
17+
schemars = "1.0.4"
1718
serde = "1.0.219"
1819
serde_derive = "1.0.219"
1920
serde_json = "1.0.59"

compiler/rustc_target/src/lib.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,65 @@ fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> {
7272
Some(libdir) => libdir.into(),
7373
}
7474
}
75+
76+
macro_rules! target_spec_enum {
77+
(
78+
$( #[$attr:meta] )*
79+
pub enum $name:ident {
80+
$(
81+
$( #[$variant_attr:meta] )*
82+
$variant:ident = $string:literal,
83+
)*
84+
}
85+
parse_error_type = $parse_error_type:literal;
86+
) => {
87+
$( #[$attr] )*
88+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
89+
#[derive(schemars::JsonSchema)]
90+
pub enum $name {
91+
$(
92+
$( #[$variant_attr] )*
93+
#[serde(rename = $string)] // for JSON schema generation only
94+
$variant,
95+
)*
96+
}
97+
98+
impl FromStr for $name {
99+
type Err = String;
100+
101+
fn from_str(s: &str) -> Result<Self, Self::Err> {
102+
Ok(match s {
103+
$( $string => Self::$variant, )*
104+
_ => {
105+
let all = [$( concat!("'", $string, "'") ),*].join(", ");
106+
return Err(format!("invalid {}: '{s}'. allowed values: {all}", $parse_error_type));
107+
}
108+
})
109+
}
110+
}
111+
112+
impl $name {
113+
pub fn desc(&self) -> &'static str {
114+
match self {
115+
$( Self::$variant => $string, )*
116+
}
117+
}
118+
}
119+
120+
impl crate::json::ToJson for $name {
121+
fn to_json(&self) -> crate::json::Json {
122+
self.desc().to_json()
123+
}
124+
}
125+
126+
crate::json::serde_deserialize_from_str!($name);
127+
128+
129+
impl std::fmt::Display for $name {
130+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131+
f.write_str(self.desc())
132+
}
133+
}
134+
};
135+
}
136+
use target_spec_enum;

compiler/rustc_target/src/spec/json.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -408,12 +408,12 @@ impl ToJson for Target {
408408
}
409409
}
410410

411-
#[derive(serde_derive::Deserialize)]
411+
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
412412
struct LinkSelfContainedComponentsWrapper {
413413
components: Vec<LinkSelfContainedComponents>,
414414
}
415415

416-
#[derive(serde_derive::Deserialize)]
416+
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
417417
#[serde(untagged)]
418418
enum TargetFamiliesJson {
419419
Array(StaticCow<[StaticCow<str>]>),
@@ -429,6 +429,18 @@ impl FromStr for EndianWrapper {
429429
}
430430
}
431431
crate::json::serde_deserialize_from_str!(EndianWrapper);
432+
impl schemars::JsonSchema for EndianWrapper {
433+
fn schema_name() -> std::borrow::Cow<'static, str> {
434+
"Endian".into()
435+
}
436+
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
437+
schemars::json_schema! ({
438+
"type": "string",
439+
"enum": ["big", "little"]
440+
})
441+
.into()
442+
}
443+
}
432444

433445
/// `ExternAbi` is in `rustc_abi`, which doesn't have access to the macro and serde.
434446
struct ExternAbiWrapper(rustc_abi::ExternAbi);
@@ -441,16 +453,30 @@ impl FromStr for ExternAbiWrapper {
441453
}
442454
}
443455
crate::json::serde_deserialize_from_str!(ExternAbiWrapper);
456+
impl schemars::JsonSchema for ExternAbiWrapper {
457+
fn schema_name() -> std::borrow::Cow<'static, str> {
458+
"ExternAbi".into()
459+
}
460+
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
461+
let all =
462+
rustc_abi::ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect::<Vec<_>>();
463+
schemars::json_schema! ({
464+
"type": "string",
465+
"enum": all,
466+
})
467+
.into()
468+
}
469+
}
444470

445-
#[derive(serde_derive::Deserialize)]
471+
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
446472
struct TargetSpecJsonMetadata {
447473
description: Option<StaticCow<str>>,
448474
tier: Option<u64>,
449475
host_tools: Option<bool>,
450476
std: Option<bool>,
451477
}
452478

453-
#[derive(serde_derive::Deserialize)]
479+
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
454480
#[serde(rename_all = "kebab-case")]
455481
// Ensure that all unexpected fields get turned into errors.
456482
// This helps users stay up to date when the schema changes instead of silently
@@ -593,3 +619,7 @@ struct TargetSpecJson {
593619
supports_xray: Option<bool>,
594620
entry_abi: Option<ExternAbiWrapper>,
595621
}
622+
623+
pub fn json_schema() -> schemars::Schema {
624+
schemars::schema_for!(TargetSpecJson)
625+
}

0 commit comments

Comments
 (0)