Skip to content

Commit da3bb97

Browse files
authored
Rollup merge of rust-lang#144498 - Noratrieb:rustc-json-schema, r=jieyouxu
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 5bc7837 + 1dfa421 commit da3bb97

File tree

19 files changed

+484
-730
lines changed

19 files changed

+484
-730
lines changed

Cargo.lock

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

1206+
[[package]]
1207+
name = "dyn-clone"
1208+
version = "1.0.19"
1209+
source = "registry+https://github.com/rust-lang/crates.io-index"
1210+
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
1211+
12061212
[[package]]
12071213
name = "either"
12081214
version = "1.15.0"
@@ -3196,6 +3202,26 @@ dependencies = [
31963202
"thiserror 2.0.12",
31973203
]
31983204

3205+
[[package]]
3206+
name = "ref-cast"
3207+
version = "1.0.24"
3208+
source = "registry+https://github.com/rust-lang/crates.io-index"
3209+
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
3210+
dependencies = [
3211+
"ref-cast-impl",
3212+
]
3213+
3214+
[[package]]
3215+
name = "ref-cast-impl"
3216+
version = "1.0.24"
3217+
source = "registry+https://github.com/rust-lang/crates.io-index"
3218+
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
3219+
dependencies = [
3220+
"proc-macro2",
3221+
"quote",
3222+
"syn 2.0.104",
3223+
]
3224+
31993225
[[package]]
32003226
name = "regex"
32013227
version = "1.11.1"
@@ -4650,6 +4676,7 @@ dependencies = [
46504676
"rustc_macros",
46514677
"rustc_serialize",
46524678
"rustc_span",
4679+
"schemars",
46534680
"serde",
46544681
"serde_derive",
46554682
"serde_json",
@@ -4964,6 +4991,31 @@ dependencies = [
49644991
"windows-sys 0.59.0",
49654992
]
49664993

4994+
[[package]]
4995+
name = "schemars"
4996+
version = "1.0.4"
4997+
source = "registry+https://github.com/rust-lang/crates.io-index"
4998+
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
4999+
dependencies = [
5000+
"dyn-clone",
5001+
"ref-cast",
5002+
"schemars_derive",
5003+
"serde",
5004+
"serde_json",
5005+
]
5006+
5007+
[[package]]
5008+
name = "schemars_derive"
5009+
version = "1.0.4"
5010+
source = "registry+https://github.com/rust-lang/crates.io-index"
5011+
checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80"
5012+
dependencies = [
5013+
"proc-macro2",
5014+
"quote",
5015+
"serde_derive_internals",
5016+
"syn 2.0.104",
5017+
]
5018+
49675019
[[package]]
49685020
name = "scoped-tls"
49695021
version = "1.0.1"
@@ -5038,6 +5090,17 @@ dependencies = [
50385090
"syn 2.0.104",
50395091
]
50405092

5093+
[[package]]
5094+
name = "serde_derive_internals"
5095+
version = "0.29.1"
5096+
source = "registry+https://github.com/rust-lang/crates.io-index"
5097+
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
5098+
dependencies = [
5099+
"proc-macro2",
5100+
"quote",
5101+
"syn 2.0.104",
5102+
]
5103+
50415104
[[package]]
50425105
name = "serde_json"
50435106
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
@@ -667,6 +667,10 @@ fn print_crate_info(
667667
TargetSpecJson => {
668668
println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
669669
}
670+
TargetSpecJsonSchema => {
671+
let schema = rustc_target::spec::json_schema();
672+
println_info!("{}", serde_json::to_string_pretty(&schema).unwrap());
673+
}
670674
AllTargetSpecsJson => {
671675
let mut targets = BTreeMap::new();
672676
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
@@ -69,6 +69,7 @@ pub const PRINT_KINDS: &[(&str, PrintKind)] = &[
6969
("target-libdir", PrintKind::TargetLibdir),
7070
("target-list", PrintKind::TargetList),
7171
("target-spec-json", PrintKind::TargetSpecJson),
72+
("target-spec-json-schema", PrintKind::TargetSpecJsonSchema),
7273
("tls-models", PrintKind::TlsModels),
7374
// tidy-alphabetical-end
7475
];
@@ -1045,6 +1046,7 @@ pub enum PrintKind {
10451046
TargetLibdir,
10461047
TargetList,
10471048
TargetSpecJson,
1049+
TargetSpecJsonSchema,
10481050
TlsModels,
10491051
// tidy-alphabetical-end
10501052
}
@@ -2307,7 +2309,8 @@ fn is_print_request_stable(print_kind: PrintKind) -> bool {
23072309
| PrintKind::CheckCfg
23082310
| PrintKind::CrateRootLintLevels
23092311
| PrintKind::SupportedCrateTypes
2310-
| PrintKind::TargetSpecJson => false,
2312+
| PrintKind::TargetSpecJson
2313+
| PrintKind::TargetSpecJsonSchema => false,
23112314
_ => true,
23122315
}
23132316
}

compiler/rustc_target/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ rustc_fs_util = { path = "../rustc_fs_util" }
1212
rustc_macros = { path = "../rustc_macros" }
1313
rustc_serialize = { path = "../rustc_serialize" }
1414
rustc_span = { path = "../rustc_span" }
15+
schemars = "1.0.4"
1516
serde = "1.0.219"
1617
serde_derive = "1.0.219"
1718
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
@@ -413,12 +413,12 @@ impl ToJson for Target {
413413
}
414414
}
415415

416-
#[derive(serde_derive::Deserialize)]
416+
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
417417
struct LinkSelfContainedComponentsWrapper {
418418
components: Vec<LinkSelfContainedComponents>,
419419
}
420420

421-
#[derive(serde_derive::Deserialize)]
421+
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
422422
#[serde(untagged)]
423423
enum TargetFamiliesJson {
424424
Array(StaticCow<[StaticCow<str>]>),
@@ -434,6 +434,18 @@ impl FromStr for EndianWrapper {
434434
}
435435
}
436436
crate::json::serde_deserialize_from_str!(EndianWrapper);
437+
impl schemars::JsonSchema for EndianWrapper {
438+
fn schema_name() -> std::borrow::Cow<'static, str> {
439+
"Endian".into()
440+
}
441+
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
442+
schemars::json_schema! ({
443+
"type": "string",
444+
"enum": ["big", "little"]
445+
})
446+
.into()
447+
}
448+
}
437449

438450
/// `ExternAbi` is in `rustc_abi`, which doesn't have access to the macro and serde.
439451
struct ExternAbiWrapper(rustc_abi::ExternAbi);
@@ -446,16 +458,30 @@ impl FromStr for ExternAbiWrapper {
446458
}
447459
}
448460
crate::json::serde_deserialize_from_str!(ExternAbiWrapper);
461+
impl schemars::JsonSchema for ExternAbiWrapper {
462+
fn schema_name() -> std::borrow::Cow<'static, str> {
463+
"ExternAbi".into()
464+
}
465+
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
466+
let all =
467+
rustc_abi::ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect::<Vec<_>>();
468+
schemars::json_schema! ({
469+
"type": "string",
470+
"enum": all,
471+
})
472+
.into()
473+
}
474+
}
449475

450-
#[derive(serde_derive::Deserialize)]
476+
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
451477
struct TargetSpecJsonMetadata {
452478
description: Option<StaticCow<str>>,
453479
tier: Option<u64>,
454480
host_tools: Option<bool>,
455481
std: Option<bool>,
456482
}
457483

458-
#[derive(serde_derive::Deserialize)]
484+
#[derive(serde_derive::Deserialize, schemars::JsonSchema)]
459485
#[serde(rename_all = "kebab-case")]
460486
// Ensure that all unexpected fields get turned into errors.
461487
// This helps users stay up to date when the schema changes instead of silently
@@ -598,3 +624,7 @@ struct TargetSpecJson {
598624
supports_xray: Option<bool>,
599625
entry_abi: Option<ExternAbiWrapper>,
600626
}
627+
628+
pub fn json_schema() -> schemars::Schema {
629+
schemars::schema_for!(TargetSpecJson)
630+
}

0 commit comments

Comments
 (0)