Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1623f03

Browse files
committedFeb 13, 2024
Introduce infrastructure for generating target docs
1 parent eaff1af commit 1623f03

File tree

17 files changed

+1134
-45
lines changed

17 files changed

+1134
-45
lines changed
 

‎Cargo.lock

Lines changed: 77 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ dependencies = [
257257
"proc-macro2",
258258
"quote",
259259
"serde",
260-
"syn 2.0.32",
260+
"syn 2.0.48",
261261
]
262262

263263
[[package]]
@@ -569,7 +569,7 @@ dependencies = [
569569
"heck",
570570
"proc-macro2",
571571
"quote",
572-
"syn 2.0.32",
572+
"syn 2.0.48",
573573
]
574574

575575
[[package]]
@@ -596,7 +596,7 @@ dependencies = [
596596
"regex",
597597
"rustc_tools_util",
598598
"serde",
599-
"syn 2.0.32",
599+
"syn 2.0.48",
600600
"tempfile",
601601
"termize",
602602
"tester",
@@ -994,7 +994,7 @@ dependencies = [
994994
"proc-macro2",
995995
"quote",
996996
"strsim",
997-
"syn 2.0.32",
997+
"syn 2.0.48",
998998
]
999999

10001000
[[package]]
@@ -1016,7 +1016,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
10161016
dependencies = [
10171017
"darling_core 0.20.3",
10181018
"quote",
1019-
"syn 2.0.32",
1019+
"syn 2.0.48",
10201020
]
10211021

10221022
[[package]]
@@ -1031,7 +1031,7 @@ version = "0.1.78"
10311031
dependencies = [
10321032
"itertools",
10331033
"quote",
1034-
"syn 2.0.32",
1034+
"syn 2.0.48",
10351035
]
10361036

10371037
[[package]]
@@ -1098,7 +1098,7 @@ dependencies = [
10981098
"darling 0.20.3",
10991099
"proc-macro2",
11001100
"quote",
1101-
"syn 2.0.32",
1101+
"syn 2.0.48",
11021102
]
11031103

11041104
[[package]]
@@ -1187,7 +1187,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
11871187
dependencies = [
11881188
"proc-macro2",
11891189
"quote",
1190-
"syn 2.0.32",
1190+
"syn 2.0.48",
11911191
]
11921192

11931193
[[package]]
@@ -1337,9 +1337,9 @@ dependencies = [
13371337

13381338
[[package]]
13391339
name = "eyre"
1340-
version = "0.6.8"
1340+
version = "0.6.12"
13411341
source = "registry+https://github.com/rust-lang/crates.io-index"
1342-
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
1342+
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
13431343
dependencies = [
13441344
"indenter",
13451345
"once_cell",
@@ -1541,7 +1541,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
15411541
dependencies = [
15421542
"proc-macro2",
15431543
"quote",
1544-
"syn 2.0.32",
1544+
"syn 2.0.48",
15451545
]
15461546

15471547
[[package]]
@@ -1642,6 +1642,12 @@ version = "0.3.1"
16421642
source = "registry+https://github.com/rust-lang/crates.io-index"
16431643
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
16441644

1645+
[[package]]
1646+
name = "glob-match"
1647+
version = "0.2.1"
1648+
source = "registry+https://github.com/rust-lang/crates.io-index"
1649+
checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d"
1650+
16451651
[[package]]
16461652
name = "globset"
16471653
version = "0.4.10"
@@ -1964,7 +1970,7 @@ checksum = "2060258edfcfe32ca7058849bf0f146cb5c59aadbedf480333c0d0002f97bc99"
19641970
dependencies = [
19651971
"proc-macro2",
19661972
"quote",
1967-
"syn 2.0.32",
1973+
"syn 2.0.48",
19681974
]
19691975

19701976
[[package]]
@@ -2014,9 +2020,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
20142020

20152021
[[package]]
20162022
name = "indexmap"
2017-
version = "2.0.0"
2023+
version = "2.2.3"
20182024
source = "registry+https://github.com/rust-lang/crates.io-index"
2019-
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
2025+
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
20202026
dependencies = [
20212027
"equivalent",
20222028
"hashbrown",
@@ -2706,7 +2712,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
27062712
dependencies = [
27072713
"proc-macro2",
27082714
"quote",
2709-
"syn 2.0.32",
2715+
"syn 2.0.48",
27102716
]
27112717

27122718
[[package]]
@@ -2898,7 +2904,7 @@ dependencies = [
28982904
"pest_meta",
28992905
"proc-macro2",
29002906
"quote",
2901-
"syn 2.0.32",
2907+
"syn 2.0.48",
29022908
]
29032909

29042910
[[package]]
@@ -3015,9 +3021,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
30153021

30163022
[[package]]
30173023
name = "proc-macro2"
3018-
version = "1.0.63"
3024+
version = "1.0.78"
30193025
source = "registry+https://github.com/rust-lang/crates.io-index"
3020-
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
3026+
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
30213027
dependencies = [
30223028
"unicode-ident",
30233029
]
@@ -3091,9 +3097,9 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
30913097

30923098
[[package]]
30933099
name = "quote"
3094-
version = "1.0.29"
3100+
version = "1.0.35"
30953101
source = "registry+https://github.com/rust-lang/crates.io-index"
3096-
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
3102+
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
30973103
dependencies = [
30983104
"proc-macro2",
30993105
]
@@ -3899,7 +3905,7 @@ dependencies = [
38993905
"fluent-syntax",
39003906
"proc-macro2",
39013907
"quote",
3902-
"syn 2.0.32",
3908+
"syn 2.0.48",
39033909
"unic-langid",
39043910
]
39053911

@@ -4033,7 +4039,7 @@ version = "0.0.0"
40334039
dependencies = [
40344040
"proc-macro2",
40354041
"quote",
4036-
"syn 2.0.32",
4042+
"syn 2.0.48",
40374043
"synstructure",
40384044
]
40394045

@@ -4179,7 +4185,7 @@ version = "0.0.0"
41794185
dependencies = [
41804186
"proc-macro2",
41814187
"quote",
4182-
"syn 2.0.32",
4188+
"syn 2.0.48",
41834189
"synstructure",
41844190
]
41854191

@@ -4798,7 +4804,7 @@ dependencies = [
47984804
"proc-macro2",
47994805
"quote",
48004806
"serde",
4801-
"syn 2.0.32",
4807+
"syn 2.0.48",
48024808
]
48034809

48044810
[[package]]
@@ -4946,22 +4952,22 @@ dependencies = [
49464952

49474953
[[package]]
49484954
name = "serde"
4949-
version = "1.0.185"
4955+
version = "1.0.196"
49504956
source = "registry+https://github.com/rust-lang/crates.io-index"
4951-
checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31"
4957+
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
49524958
dependencies = [
49534959
"serde_derive",
49544960
]
49554961

49564962
[[package]]
49574963
name = "serde_derive"
4958-
version = "1.0.185"
4964+
version = "1.0.196"
49594965
source = "registry+https://github.com/rust-lang/crates.io-index"
4960-
checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec"
4966+
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
49614967
dependencies = [
49624968
"proc-macro2",
49634969
"quote",
4964-
"syn 2.0.32",
4970+
"syn 2.0.48",
49654971
]
49664972

49674973
[[package]]
@@ -4997,6 +5003,19 @@ dependencies = [
49975003
"serde",
49985004
]
49995005

5006+
[[package]]
5007+
name = "serde_yaml"
5008+
version = "0.9.31"
5009+
source = "registry+https://github.com/rust-lang/crates.io-index"
5010+
checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e"
5011+
dependencies = [
5012+
"indexmap",
5013+
"itoa",
5014+
"ryu",
5015+
"serde",
5016+
"unsafe-libyaml",
5017+
]
5018+
50005019
[[package]]
50015020
name = "sha1"
50025021
version = "0.10.5"
@@ -5252,9 +5271,9 @@ dependencies = [
52525271

52535272
[[package]]
52545273
name = "syn"
5255-
version = "2.0.32"
5274+
version = "2.0.48"
52565275
source = "registry+https://github.com/rust-lang/crates.io-index"
5257-
checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
5276+
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
52585277
dependencies = [
52595278
"proc-macro2",
52605279
"quote",
@@ -5269,7 +5288,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
52695288
dependencies = [
52705289
"proc-macro2",
52715290
"quote",
5272-
"syn 2.0.32",
5291+
"syn 2.0.48",
52735292
"unicode-xid",
52745293
]
52755294

@@ -5317,6 +5336,16 @@ dependencies = [
53175336
"xattr",
53185337
]
53195338

5339+
[[package]]
5340+
name = "target-docs"
5341+
version = "0.1.0"
5342+
dependencies = [
5343+
"eyre",
5344+
"glob-match",
5345+
"serde",
5346+
"serde_yaml",
5347+
]
5348+
53205349
[[package]]
53215350
name = "tempfile"
53225351
version = "3.8.0"
@@ -5429,7 +5458,7 @@ checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
54295458
dependencies = [
54305459
"proc-macro2",
54315460
"quote",
5432-
"syn 2.0.32",
5461+
"syn 2.0.48",
54335462
]
54345463

54355464
[[package]]
@@ -5650,7 +5679,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
56505679
dependencies = [
56515680
"proc-macro2",
56525681
"quote",
5653-
"syn 2.0.32",
5682+
"syn 2.0.48",
56545683
]
56555684

56565685
[[package]]
@@ -5830,7 +5859,7 @@ checksum = "fea2a4c80deb4fb3ca51f66b5e2dd91e3642bbce52234bcf22e41668281208e4"
58305859
dependencies = [
58315860
"proc-macro-hack",
58325861
"quote",
5833-
"syn 2.0.32",
5862+
"syn 2.0.48",
58345863
"unic-langid-impl",
58355864
]
58365865

@@ -5925,6 +5954,12 @@ dependencies = [
59255954
"diff",
59265955
]
59275956

5957+
[[package]]
5958+
name = "unsafe-libyaml"
5959+
version = "0.2.10"
5960+
source = "registry+https://github.com/rust-lang/crates.io-index"
5961+
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
5962+
59285963
[[package]]
59295964
name = "unstable-book-gen"
59305965
version = "0.1.0"
@@ -6056,7 +6091,7 @@ dependencies = [
60566091
"once_cell",
60576092
"proc-macro2",
60586093
"quote",
6059-
"syn 2.0.32",
6094+
"syn 2.0.48",
60606095
"wasm-bindgen-shared",
60616096
]
60626097

@@ -6090,7 +6125,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
60906125
dependencies = [
60916126
"proc-macro2",
60926127
"quote",
6093-
"syn 2.0.32",
6128+
"syn 2.0.48",
60946129
"wasm-bindgen-backend",
60956130
"wasm-bindgen-shared",
60966131
]
@@ -6159,7 +6194,7 @@ checksum = "970efb0b6849eb8a87a898f586af7cc167567b070014c7434514c0bde0ca341c"
61596194
dependencies = [
61606195
"proc-macro2",
61616196
"rayon",
6162-
"syn 2.0.32",
6197+
"syn 2.0.48",
61636198
"windows-metadata",
61646199
]
61656200

@@ -6467,7 +6502,7 @@ checksum = "d5e19fb6ed40002bab5403ffa37e53e0e56f914a4450c8765f533018db1db35f"
64676502
dependencies = [
64686503
"proc-macro2",
64696504
"quote",
6470-
"syn 2.0.32",
6505+
"syn 2.0.48",
64716506
"synstructure",
64726507
]
64736508

@@ -6488,7 +6523,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
64886523
dependencies = [
64896524
"proc-macro2",
64906525
"quote",
6491-
"syn 2.0.32",
6526+
"syn 2.0.48",
64926527
]
64936528

64946529
[[package]]
@@ -6508,7 +6543,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3"
65086543
dependencies = [
65096544
"proc-macro2",
65106545
"quote",
6511-
"syn 2.0.32",
6546+
"syn 2.0.48",
65126547
"synstructure",
65136548
]
65146549

@@ -6531,7 +6566,7 @@ checksum = "acabf549809064225ff8878baedc4ce3732ac3b07e7c7ce6e5c2ccdbc485c324"
65316566
dependencies = [
65326567
"proc-macro2",
65336568
"quote",
6534-
"syn 2.0.32",
6569+
"syn 2.0.48",
65356570
]
65366571

65376572
[[package]]

‎Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ members = [
4444
"src/tools/rustdoc-gui-test",
4545
"src/tools/opt-dist",
4646
"src/tools/coverage-dump",
47+
"src/tools/target-docs",
4748
]
4849

4950
exclude = [

‎src/bootstrap/src/core/build_steps/doc.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,13 +1139,14 @@ impl Step for RustcBook {
11391139

11401140
/// Builds the rustc book.
11411141
///
1142-
/// The lints are auto-generated by a tool, and then merged into the book
1142+
/// The lints and target docs are auto-generated by a tool, and then merged into the book
11431143
/// in the "md-doc" directory in the build output directory. Then
11441144
/// "rustbook" is used to convert it to HTML.
11451145
fn run(self, builder: &Builder<'_>) {
11461146
let out_base = builder.md_doc_out(self.target).join("rustc");
11471147
t!(fs::create_dir_all(&out_base));
1148-
let out_listing = out_base.join("src/lints");
1148+
let out_lints_listing = out_base.join("src/lints");
1149+
let out_src_listing = out_base.join("src");
11491150
builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base);
11501151
builder.info(&format!("Generating lint docs ({})", self.target));
11511152

@@ -1157,7 +1158,7 @@ impl Step for RustcBook {
11571158
cmd.arg("--src");
11581159
cmd.arg(builder.src.join("compiler"));
11591160
cmd.arg("--out");
1160-
cmd.arg(&out_listing);
1161+
cmd.arg(&out_lints_listing);
11611162
cmd.arg("--rustc");
11621163
cmd.arg(&rustc);
11631164
cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg());
@@ -1186,6 +1187,25 @@ impl Step for RustcBook {
11861187
builder.run(&mut cmd);
11871188
drop(doc_generator_guard);
11881189

1190+
// Run target-docs generator
1191+
let mut cmd = builder.tool_cmd(Tool::TargetDocs);
1192+
cmd.arg(builder.src.join("src/doc/rustc/target_infos"));
1193+
cmd.arg(&out_src_listing);
1194+
cmd.env("RUSTC", &rustc);
1195+
// For now, we just check that the files are correct but do not generate output.
1196+
// See #120745 for more info.
1197+
cmd.env("TARGET_CHECK_ONLY", "1");
1198+
1199+
let doc_generator_guard = builder.msg(
1200+
Kind::Run,
1201+
self.compiler.stage,
1202+
"target-docs",
1203+
self.compiler.host,
1204+
self.target,
1205+
);
1206+
builder.run(&mut cmd);
1207+
drop(doc_generator_guard);
1208+
11891209
// Run rustbook/mdbook to generate the HTML pages.
11901210
builder.ensure(RustbookSrc {
11911211
target: self.target,

‎src/bootstrap/src/core/build_steps/tool.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ bootstrap_tool!(
301301
RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes";
302302
ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors";
303303
LintDocs, "src/tools/lint-docs", "lint-docs";
304+
TargetDocs, "src/tools/target-docs", "target-docs";
304305
JsonDocCk, "src/tools/jsondocck", "jsondocck";
305306
JsonDocLint, "src/tools/jsondoclint", "jsondoclint";
306307
HtmlChecker, "src/tools/html-checker", "html-checker";

‎src/doc/rustc/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- [Platform Support](platform-support.md)
1616
- [Target Tier Policy](target-tier-policy.md)
1717
- [Template for Target-specific Documentation](platform-support/TEMPLATE.md)
18+
- [List of Targets](platform-support/targets.md)
1819
- [arm64e-apple-ios.md](platform-support/arm64e-apple-ios.md)
1920
- [arm64e-apple-darwin.md](platform-support/arm64e-apple-darwin.md)
2021
- [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md)

‎src/doc/rustc/src/platform-support.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ All tier 1 targets with host tools support the full standard library.
3232

3333
target | notes
3434
-------|-------
35+
<!-- TIER1HOST SECTION START -->
36+
<!-- See `src/tools/target-docs` -->
37+
<!-- TIER1HOST SECTION END -->
3538
`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) [^missing-stack-probes]
3639
`i686-pc-windows-gnu` | 32-bit MinGW (Windows 7+) [^windows-support] [^x86_32-floats-return-ABI]
3740
`i686-pc-windows-msvc` | 32-bit MSVC (Windows 7+) [^windows-support] [^x86_32-floats-return-ABI]
@@ -92,6 +95,8 @@ so Rustup may install the documentation for a similar tier 1 target instead.
9295

9396
target | notes
9497
-------|-------
98+
<!-- TIER2HOST SECTION START -->
99+
<!-- TIER2HOST SECTION END -->
95100
`aarch64-apple-darwin` | ARM64 macOS (11.0+, Big Sur+)
96101
`aarch64-pc-windows-msvc` | ARM64 Windows MSVC
97102
`aarch64-unknown-linux-musl` | ARM64 Linux with MUSL
@@ -138,6 +143,8 @@ so Rustup may install the documentation for a similar tier 1 target instead.
138143

139144
target | std | notes
140145
-------|:---:|-------
146+
<!-- TIER2 SECTION START -->
147+
<!-- TIER2 SECTION END -->
141148
`aarch64-apple-ios` | ✓ | ARM64 iOS
142149
[`aarch64-apple-ios-sim`](platform-support/aarch64-apple-ios-sim.md) | ✓ | Apple iOS Simulator on ARM64
143150
`aarch64-fuchsia` | ✓ | Alias for `aarch64-unknown-fuchsia`
@@ -234,6 +241,8 @@ host tools.
234241

235242
target | std | host | notes
236243
-------|:---:|:----:|-------
244+
<!-- TIER3 SECTION START -->
245+
<!-- TIER3 SECTION END -->
237246
[`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS
238247
[`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin
239248
`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# List of all targets
2+
3+
An alphabetical list of all targets.
4+
5+
<!-- TARGET SECTION START -->
6+
<!-- See `src/tools/target-docs` -->
7+
<!-- TARGET SECTION END -->
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
tier: "2"
3+
maintainers: ["@thomcc"]
4+
metadata:
5+
- target: "aarch64-apple-tvos"
6+
notes: "ARM64 tvOS"
7+
std: "unknown"
8+
host: false
9+
---
10+
11+
## Overview
12+
13+
Apple tvOS targets:
14+
- Apple tvOS on aarch64
15+
- Apple tvOS Simulator on x86_64
16+
17+
## Requirements
18+
19+
These targets are cross-compiled. You will need appropriate versions of Xcode
20+
and the SDKs for tvOS (`AppleTVOS.sdk`) and/or the tvOS Simulator
21+
(`AppleTVSimulator.sdk`) to build a toolchain and target these platforms.
22+
23+
The targets support most (see below) of the standard library including the
24+
allocator to the best of my knowledge, however they are very new, not yet
25+
well-tested, and it is possible that there are various bugs.
26+
27+
In theory we support back to tvOS version 7.0, although the actual minimum
28+
version you can target may be newer than this, for example due to the versions
29+
of Xcode and your SDKs.
30+
31+
As with the other Apple targets, `rustc` respects the common environment
32+
variables used by Xcode to configure this, in this case
33+
`TVOS_DEPLOYMENT_TARGET`.
34+
35+
As mentioned, "most" of the standard library is supported, which means that some portions
36+
are known to be unsupported. The following APIs are currently known to have
37+
missing or incomplete support:
38+
39+
- `std::process::Command`'s API will return an error if it is configured in a
40+
manner which cannot be performed using `posix_spawn` -- this is because the
41+
more flexible `fork`/`exec`-based approach is prohibited on these platforms in
42+
favor of `posix_spawn{,p}` (which still probably will get you rejected from
43+
app stores, so is likely sideloading-only). A concrete set of cases where this
44+
will occur is difficult to enumerate (and would quickly become stale), but in
45+
some cases it may be worked around by tweaking the manner in which `Command`
46+
is invoked.
47+
48+
## Building the target
49+
50+
The targets can be built by enabling them for a `rustc` build in `config.toml`, by adding, for example:
51+
52+
```toml
53+
[build]
54+
build-stage = 1
55+
target = ["aarch64-apple-tvos", "x86_64-apple-tvos", "aarch64-apple-tvos-sim"]
56+
```
57+
58+
It's possible that cargo under `-Zbuild-std` may also be used to target them.
59+
60+
## Building Rust programs
61+
62+
*Note: Building for this target requires the corresponding TVOS SDK, as provided by Xcode.*
63+
64+
Rust programs can be built for these targets
65+
66+
```text
67+
$ rustc --target aarch64-apple-tvos your-code.rs
68+
...
69+
$ rustc --target x86_64-apple-tvos your-code.rs
70+
...
71+
$ rustc --target aarch64-apple-tvos-sim your-code.rs
72+
```
73+
74+
## Testing
75+
76+
There is no support for running the Rust or standard library testsuite on tvOS or the simulators at the moment. Testing has mostly been done manually with builds of static libraries called from Xcode or a simulator.
77+
78+
It hopefully will be possible to improve this in the future.
79+
80+
## Cross compilation
81+
82+
This target can be cross-compiled from x86_64 or aarch64 macOS hosts.
83+
84+
Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
tier: "1"
3+
metadata:
4+
- target: "i686-pc-windows-gnu"
5+
notes: "32-bit MinGW (Windows 7+)"
6+
std: true
7+
host: true
8+
footnotes:
9+
- name: "x86_32-floats-return-ABI"
10+
content: |
11+
Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant:
12+
floating-point return values are passed via an x87 register, so NaN payload bits can be lost.
13+
See [issue #114479][https://github.com/rust-lang/rust/issues/114479].
14+
- name: "windows-support"
15+
content: "Only Windows 10 currently undergoes automated testing. Earlier versions of Windows rely on testing and support from the community."
16+
---
17+
18+
## Overview
19+
20+
32-bit Windows using MinGW.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
tier: "2"
3+
maintainers:
4+
- "[WANG Rui](https://github.com/heiher) `wangrui@loongson.cn`"
5+
- "[ZHAI Xiang](https://github.com/xiangzhai) `zhaixiang@loongson.cn`"
6+
- "[ZHAI Xiaojuan](https://github.com/zhaixiaojuan) `zhaixiaojuan@loongson.cn`"
7+
- "[WANG Xuerui](https://github.com/xen0n) `git@xen0n.name`"
8+
metadata:
9+
- target: "loongarch64-unknown-linux-gnu"
10+
notes: "LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36)"
11+
std: true
12+
host: true
13+
---
14+
15+
## Overview
16+
17+
[LoongArch] is a new RISC ISA developed by Loongson Technology Corporation Limited.
18+
19+
[LoongArch]: https://loongson.github.io/LoongArch-Documentation/README-EN.html
20+
21+
The target name follow this format: `<machine>-<vendor>-<os><fabi_suffix>`, where `<machine>` specifies the CPU family/model, `<vendor>` specifies the vendor and `<os>` the operating system name.
22+
While the integer base ABI is implied by the machine field, the floating point base ABI type is encoded into the os field of the specifier using the string suffix `<fabi-suffix>`.
23+
24+
| `<fabi-suffix>` | `Description` |
25+
|------------------------|--------------------------------------------------------------------|
26+
| f64 | The base ABI use 64-bits FPRs for parameter passing. (lp64d)|
27+
| f32 | The base ABI uses 32-bit FPRs for parameter passing. (lp64f)|
28+
| sf | The base ABI uses no FPR for parameter passing. (lp64s) |
29+
30+
<br>
31+
32+
|`ABI type(Base ABI/ABI extension)`| `C library` | `kernel` | `target tuple` |
33+
|----------------------------------|-------------|----------|----------------------------------|
34+
| lp64d/base | glibc | linux | loongarch64-unknown-linux-gnu |
35+
| lp64f/base | glibc | linux | loongarch64-unknown-linux-gnuf32 |
36+
| lp64s/base | glibc | linux | loongarch64-unknown-linux-gnusf |
37+
| lp64d/base | musl libc | linux | loongarch64-unknown-linux-musl|
38+
| lp64f/base | musl libc | linux | loongarch64-unknown-linux-muslf32|
39+
| lp64s/base | musl libc | linux | loongarch64-unknown-linux-muslsf |
40+
41+
## Requirements
42+
43+
This target is cross-compiled.
44+
A GNU toolchain for LoongArch target is required. It can be downloaded from https://github.com/loongson/build-tools/releases, or built from the source code of GCC (12.1.0 or later) and Binutils (2.40 or later).
45+
46+
## Building the target
47+
48+
The target can be built by enabling it for a `rustc` build.
49+
50+
```toml
51+
[build]
52+
target = ["loongarch64-unknown-linux-gnu"]
53+
```
54+
55+
Make sure `loongarch64-unknown-linux-gnu-gcc` can be searched from the directories specified in`$PATH`. Alternatively, you can use GNU LoongArch Toolchain by adding the following to `config.toml`:
56+
57+
```toml
58+
[target.loongarch64-unknown-linux-gnu]
59+
# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN
60+
cc = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc"
61+
cxx = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-g++"
62+
ar = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-ar"
63+
ranlib = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-ranlib"
64+
linker = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc"
65+
```
66+
67+
## Cross compilation
68+
69+
This target can be cross-compiled on a `x86_64-unknown-linux-gnu` host. Cross-compilation on other hosts may work but is not tested.
70+
71+
## Testing
72+
To test a cross-compiled binary on your build system, install the qemu binary that supports the LoongArch architecture and execute the following commands.
73+
```text
74+
CC_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc \
75+
CXX_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-g++ \
76+
AR_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc-ar \
77+
CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNUN_LINKER=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc \
78+
# SET TARGET SYSTEM LIBRARY PATH
79+
CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNUN_RUNNER="qemu-loongarch64 -L /TOOLCHAIN_PATH/TARGET_LIBRARY_PATH" \
80+
cargo run --target loongarch64-unknown-linux-gnu --release
81+
```
82+
Tested on x86 architecture, other architectures not tested.
83+
84+
## Building Rust programs
85+
86+
Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you will either need to build Rust with the target enabled (see "Building the target" above), or build your own copy of `std` by using `build-std` or similar.
87+
88+
If `rustc` has support for that target and the library artifacts are available, then Rust static libraries can be built for that target:
89+
90+
```shell
91+
$ rustc --target loongarch64-unknown-linux-gnu your-code.rs --crate-type staticlib
92+
$ ls libyour_code.a
93+
```
94+
95+
On Rust Nightly it's possible to build without the target artifacts available:
96+
97+
```text
98+
cargo build -Z build-std --target loongarch64-unknown-linux-gnu
99+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
tier: "3"
3+
maintainers: [
4+
"QIU Chaofan `qiucofan@cn.ibm.com`, https://github.com/ecnelises",
5+
"Kai LUO, `lkail@cn.ibm.com`, https://github.com/bzEq",
6+
]
7+
metadata:
8+
- target: powerpc64-ibm-aix
9+
notes: "64-bit AIX (7.2 and newer)"
10+
std: "unknown"
11+
host: "unknown"
12+
---
13+
14+
## Requirements
15+
16+
This target supports host tools, std and alloc. This target cannot be cross-compiled as for now, mainly because of the unavailability of system linker on other platforms.
17+
18+
Binary built for this target is expected to run on Power7 or newer CPU, and AIX 7.2 or newer version.
19+
20+
Binary format of this platform is XCOFF. Archive file format is 'AIX big format'.
21+
22+
## Testing
23+
24+
This target supports running test suites natively, but it's not available to cross-compile and execute in emulator.

‎src/tools/target-docs/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "target-docs"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "MIT OR Apache-2.0"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
eyre = "0.6.12"
11+
glob-match = "0.2.1"
12+
serde = { version = "1.0.185", features = ["derive"] }
13+
serde_yaml = "0.9.31"

‎src/tools/target-docs/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# target-docs
2+
3+
This tool generates target documentation for all targets in the rustc book.
4+
5+
To achieve this, it uses a list of input markdown files provided in `src/doc/rustc/target_infos`. These files follow a strict format.
6+
Every file covers a glob pattern of targets according to its file name.
7+
8+
For every rustc target, we iterate through all the target infos and find matching globs.
9+
When a glob matches, it extracts the h2 markdown sections and saves them for the target.
10+
11+
In the end, a page is generated for every target using these sections.
12+
Sections that are not provided are stubbed out. Currently, the sections are
13+
14+
- Overview
15+
- Requirements
16+
- Testing
17+
- Building the target
18+
- Cross compilation
19+
- Building Rust programs
20+
21+
In addition to the markdown sections, we also have extra data about the targets.
22+
This is achieved through YAML frontmatter.
23+
24+
The frontmatter follows the following format:
25+
26+
```yaml
27+
tier: "1"
28+
maintainers: ["@someone"]
29+
metadata:
30+
- target: "i686-pc-windows-gnu"
31+
notes: "32-bit MinGW (Windows 7+)"
32+
std: true
33+
host: true
34+
footnotes:
35+
- name: "x86_32-floats-return-ABI"
36+
content: |
37+
Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant:
38+
floating-point return values are passed via an x87 register, so NaN payload bits can be lost.
39+
See [issue #114479][https://github.com/rust-lang/rust/issues/114479].
40+
- name: "windows-support"
41+
content: "Only Windows 10 currently undergoes automated testing. Earlier versions of Windows rely on testing and support from the community."
42+
```
43+
44+
The top level keys are:
45+
46+
- `tier` (optional): `1`, `2` or `3`
47+
- `maintainers` (optional): list of strings
48+
49+
There is also `metadata`, which is specific to every single target and not just a target "group" (the glob).
50+
51+
`metadata` has the following properties:
52+
53+
- `target`: the target name
54+
- `notes`: a string containing a short description of the target for the table
55+
- `std`: `true`, `false`, `unknown`, whether the target has `std`
56+
- `host`: `true`, `false`, `unknown`, whether the target has host tools
57+
- `footnotes` (optional): a list of footnotes, where every footnote has a `name` and `content`. These are used in the table.

‎src/tools/target-docs/src/main.rs

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
mod parse;
2+
mod render;
3+
4+
use std::{
5+
path::{Path, PathBuf},
6+
process::Command,
7+
};
8+
9+
use eyre::{bail, Context, OptionExt, Result};
10+
use parse::{Footnote, ParsedTargetInfoFile, Tier, TriStateBool};
11+
12+
/// Information about a target obtained from `target_info.toml``.
13+
struct TargetDocs {
14+
name: String,
15+
maintainers: Vec<String>,
16+
sections: Vec<(String, String)>,
17+
tier: Option<Tier>,
18+
metadata: Option<TargetMetadata>,
19+
}
20+
21+
/// Metadata for the table
22+
struct TargetMetadata {
23+
notes: String,
24+
std: TriStateBool,
25+
host: TriStateBool,
26+
footnotes: Vec<Footnote>,
27+
}
28+
29+
/// All the sections that we want every doc page to have.
30+
/// It may make sense to relax this into two kinds of sections, "required" sections
31+
/// and "optional" sections, where required sections will get stubbed out when not found
32+
/// while optional sections will just not exist when not found.
33+
// IMPORTANT: This is also documented in the README, keep it in sync.
34+
const SECTIONS: &[&str] = &[
35+
"Overview",
36+
"Requirements",
37+
"Testing",
38+
"Building the target",
39+
"Cross compilation",
40+
"Building Rust programs",
41+
];
42+
43+
fn main() -> Result<()> {
44+
let args = std::env::args().collect::<Vec<_>>();
45+
let input_dir = args
46+
.get(1)
47+
.ok_or_eyre("first argument must be path to target_infos directory containing target source md files (src/doc/rustc/target_infos/)")?;
48+
let output_src = args
49+
.get(2)
50+
.ok_or_eyre("second argument must be path to `src` output directory (src/doc/rustc/src)")?;
51+
52+
let rustc =
53+
PathBuf::from(std::env::var("RUSTC").expect("must pass RUSTC env var pointing to rustc"));
54+
let check_only = std::env::var("TARGET_CHECK_ONLY") == Ok("1".to_owned());
55+
56+
let targets = rustc_stdout(&rustc, &["--print", "target-list"]);
57+
let targets = targets.lines().collect::<Vec<_>>();
58+
59+
let mut info_patterns = parse::load_target_infos(Path::new(input_dir))
60+
.wrap_err("failed loading target_info")?
61+
.into_iter()
62+
.map(|info| {
63+
let metadata_used = vec![false; info.metadata.len()];
64+
TargetPatternEntry { info, used: false, metadata_used }
65+
})
66+
.collect::<Vec<_>>();
67+
68+
eprintln!("Collecting rustc information");
69+
let rustc_infos =
70+
targets.iter().map(|target| rustc_target_info(&rustc, target)).collect::<Vec<_>>();
71+
72+
let targets = targets
73+
.into_iter()
74+
.map(|target| target_doc_info(&mut info_patterns, target))
75+
.zip(rustc_infos)
76+
.collect::<Vec<_>>();
77+
78+
eprintln!("Rendering targets check_only={check_only}");
79+
let targets_dir = Path::new(output_src).join("platform-support").join("targets");
80+
if !check_only {
81+
std::fs::create_dir_all(&targets_dir).wrap_err("creating platform-support/targets dir")?;
82+
}
83+
for (info, rustc_info) in &targets {
84+
let doc = render::render_target_md(info, rustc_info);
85+
86+
if !check_only {
87+
std::fs::write(targets_dir.join(format!("{}.md", info.name)), doc)
88+
.wrap_err("writing target file")?;
89+
}
90+
}
91+
92+
for target_pattern in info_patterns {
93+
if !target_pattern.used {
94+
bail!("target pattern `{}` was never used", target_pattern.info.pattern);
95+
}
96+
97+
for (used, meta) in
98+
std::iter::zip(target_pattern.metadata_used, target_pattern.info.metadata)
99+
{
100+
if !used {
101+
bail!(
102+
"in target pattern `{}`, the metadata for target `{}` was never used",
103+
target_pattern.info.pattern,
104+
meta.target
105+
);
106+
}
107+
}
108+
}
109+
110+
render::render_static(check_only, Path::new(output_src), &targets)?;
111+
112+
eprintln!("Finished generating target docs");
113+
Ok(())
114+
}
115+
116+
struct TargetPatternEntry {
117+
info: ParsedTargetInfoFile,
118+
used: bool,
119+
metadata_used: Vec<bool>,
120+
}
121+
122+
fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetDocs {
123+
let mut tier = None;
124+
let mut maintainers = Vec::new();
125+
let mut sections = Vec::new();
126+
127+
let mut metadata = None;
128+
129+
for target_pattern_entry in info_patterns {
130+
if glob_match::glob_match(&target_pattern_entry.info.pattern, target) {
131+
target_pattern_entry.used = true;
132+
let target_pattern = &target_pattern_entry.info;
133+
134+
maintainers.extend_from_slice(&target_pattern.maintainers);
135+
136+
if let Some(pattern_value) = &target_pattern.tier {
137+
if tier.is_some() {
138+
panic!(
139+
"target {target} inherits a tier from multiple patterns, create a more specific pattern and add it there"
140+
);
141+
}
142+
tier = Some(pattern_value.clone());
143+
}
144+
145+
for (section_name, content) in &target_pattern.sections {
146+
if sections.iter().any(|(name, _)| name == section_name) {
147+
panic!(
148+
"target {target} inherits the section {section_name} from multiple patterns, create a more specific pattern and add it there"
149+
);
150+
}
151+
sections.push((section_name.clone(), content.clone()));
152+
}
153+
154+
for (i, metadata_pattern) in target_pattern.metadata.iter().enumerate() {
155+
if metadata_pattern.target == target {
156+
target_pattern_entry.metadata_used[i] = true;
157+
if metadata.is_some() {
158+
panic!("target {target} is assigned metadata from more than one pattern");
159+
}
160+
metadata = Some(TargetMetadata {
161+
notes: metadata_pattern.notes.clone(),
162+
host: metadata_pattern.host.clone(),
163+
std: metadata_pattern.std.clone(),
164+
footnotes: metadata_pattern.footnotes.clone(),
165+
});
166+
}
167+
}
168+
}
169+
}
170+
171+
TargetDocs { name: target.to_owned(), maintainers, tier, sections, metadata }
172+
}
173+
174+
/// Information about a target obtained from rustc.
175+
struct RustcTargetInfo {
176+
target_cfgs: Vec<(String, String)>,
177+
}
178+
179+
/// Get information about a target from rustc.
180+
fn rustc_target_info(rustc: &Path, target: &str) -> RustcTargetInfo {
181+
let cfgs = rustc_stdout(rustc, &["--print", "cfg", "--target", target]);
182+
let target_cfgs = cfgs
183+
.lines()
184+
.filter_map(|line| {
185+
if line.starts_with("target_") {
186+
let Some((key, value)) = line.split_once("=") else {
187+
// For example `unix`
188+
return None;
189+
};
190+
Some((key.to_owned(), value.to_owned()))
191+
} else {
192+
None
193+
}
194+
})
195+
.collect();
196+
RustcTargetInfo { target_cfgs }
197+
}
198+
199+
fn rustc_stdout(rustc: &Path, args: &[&str]) -> String {
200+
let output = Command::new(rustc).args(args).output().unwrap();
201+
if !output.status.success() {
202+
panic!(
203+
"rustc failed: {}, {}",
204+
output.status,
205+
String::from_utf8(output.stderr).unwrap_or_default()
206+
)
207+
}
208+
String::from_utf8(output.stdout).unwrap()
209+
}

‎src/tools/target-docs/src/parse.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//! Suboptimal half-markdown parser that's just good-enough for this.
2+
3+
use eyre::{bail, OptionExt, Result, WrapErr};
4+
use serde::Deserialize;
5+
use std::{fs::DirEntry, path::Path};
6+
7+
#[derive(Debug, PartialEq, Clone, Deserialize)]
8+
pub enum Tier {
9+
#[serde(rename = "1")]
10+
One,
11+
#[serde(rename = "2")]
12+
Two,
13+
#[serde(rename = "3")]
14+
Three,
15+
}
16+
17+
#[derive(Debug)]
18+
pub struct ParsedTargetInfoFile {
19+
pub pattern: String,
20+
pub tier: Option<Tier>,
21+
pub maintainers: Vec<String>,
22+
pub sections: Vec<(String, String)>,
23+
pub metadata: Vec<ParsedTargetMetadata>,
24+
}
25+
26+
// IMPORTANT: This is also documented in the README, keep it in sync.
27+
#[derive(Deserialize)]
28+
#[serde(deny_unknown_fields)]
29+
struct Frontmatter {
30+
tier: Option<Tier>,
31+
#[serde(default)]
32+
maintainers: Vec<String>,
33+
#[serde(default)]
34+
metadata: Vec<ParsedTargetMetadata>,
35+
}
36+
37+
// IMPORTANT: This is also documented in the README, keep it in sync.
38+
#[derive(Debug, Clone, Deserialize)]
39+
#[serde(deny_unknown_fields)]
40+
pub struct ParsedTargetMetadata {
41+
pub target: String,
42+
pub notes: String,
43+
pub std: TriStateBool,
44+
pub host: TriStateBool,
45+
#[serde(default)]
46+
pub footnotes: Vec<Footnote>,
47+
}
48+
49+
// IMPORTANT: This is also documented in the README, keep it in sync.
50+
#[derive(Debug, Clone, Deserialize)]
51+
#[serde(deny_unknown_fields)]
52+
pub struct Footnote {
53+
pub name: String,
54+
pub content: String,
55+
}
56+
57+
// IMPORTANT: This is also documented in the README, keep it in sync.
58+
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
59+
#[serde(rename_all = "snake_case")]
60+
pub enum TriStateBool {
61+
True,
62+
False,
63+
Unknown,
64+
}
65+
66+
pub fn load_target_infos(directory: &Path) -> Result<Vec<ParsedTargetInfoFile>> {
67+
let dir = std::fs::read_dir(directory).unwrap();
68+
let mut infos = Vec::new();
69+
70+
for entry in dir {
71+
let entry = entry?;
72+
infos.push(
73+
load_single_target_info(&entry)
74+
.wrap_err_with(|| format!("loading {}", entry.path().display()))?,
75+
)
76+
}
77+
78+
Ok(infos)
79+
}
80+
81+
fn load_single_target_info(entry: &DirEntry) -> Result<ParsedTargetInfoFile> {
82+
let pattern = entry.file_name();
83+
let name = pattern
84+
.to_str()
85+
.ok_or_eyre("file name is invalid utf8")?
86+
.strip_suffix(".md")
87+
.ok_or_eyre("target_info files must end with .md")?;
88+
let content: String = std::fs::read_to_string(entry.path()).wrap_err("reading content")?;
89+
90+
parse_file(name, &content)
91+
}
92+
93+
fn parse_file(name: &str, content: &str) -> Result<ParsedTargetInfoFile> {
94+
let mut frontmatter_splitter = content.split("---\n");
95+
96+
let frontmatter = frontmatter_splitter.nth(1).ok_or_eyre("missing frontmatter")?;
97+
98+
let frontmatter_line_count = frontmatter.lines().count() + 2; // 2 from ---
99+
100+
let mut frontmatter =
101+
serde_yaml::from_str::<Frontmatter>(frontmatter).wrap_err("invalid frontmatter")?;
102+
103+
frontmatter.metadata.iter_mut().for_each(|meta| {
104+
meta.footnotes.iter_mut().for_each(|footnote| {
105+
footnote.content = footnote.content.replace("\r\n", " ").replace("\n", " ")
106+
})
107+
});
108+
let frontmatter = frontmatter;
109+
110+
let body = frontmatter_splitter.next().ok_or_eyre("no body")?;
111+
112+
let mut sections = Vec::<(String, String)>::new();
113+
let mut in_codeblock = false;
114+
115+
for (idx, line) in body.lines().enumerate() {
116+
let number = frontmatter_line_count + idx + 1; // 1 because "line numbers" are off by 1
117+
if line.starts_with("```") {
118+
in_codeblock ^= true; // toggle
119+
} else if line.starts_with("#") {
120+
if in_codeblock {
121+
match sections.last_mut() {
122+
Some((_, content)) => {
123+
content.push_str(line);
124+
content.push('\n');
125+
}
126+
None if line.trim().is_empty() => {}
127+
None => {
128+
bail!("line {number} with content not allowed before the first heading")
129+
}
130+
}
131+
} else if let Some(header) = line.strip_prefix("## ") {
132+
if !crate::SECTIONS.contains(&header) {
133+
bail!(
134+
"on line {number}, `{header}` is not an allowed section name, must be one of {:?}",
135+
super::SECTIONS
136+
);
137+
}
138+
sections.push((header.to_owned(), String::new()));
139+
} else {
140+
bail!("on line {number}, the only allowed headings are `## `: `{line}`");
141+
}
142+
} else {
143+
match sections.last_mut() {
144+
Some((_, content)) => {
145+
content.push_str(line);
146+
content.push('\n');
147+
}
148+
None if line.trim().is_empty() => {}
149+
None => bail!("line with content not allowed before the first heading"),
150+
}
151+
}
152+
}
153+
154+
sections.iter_mut().for_each(|section| section.1 = section.1.trim().to_owned());
155+
156+
Ok(ParsedTargetInfoFile {
157+
pattern: name.to_owned(),
158+
maintainers: frontmatter.maintainers,
159+
tier: frontmatter.tier,
160+
sections,
161+
metadata: frontmatter.metadata,
162+
})
163+
}
164+
165+
#[cfg(test)]
166+
mod tests;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use crate::parse::Tier;
2+
3+
#[test]
4+
fn no_frontmatter() {
5+
let name = "archlinux-unknown-linux-gnu.md"; // arch linux is an arch, right?
6+
let content = "";
7+
assert!(super::parse_file(name, content).is_err());
8+
}
9+
10+
#[test]
11+
fn invalid_section() {
12+
let name = "6502-nintendo-nes.md";
13+
let content = "
14+
---
15+
---
16+
17+
## Not A Real Section
18+
";
19+
20+
assert!(super::parse_file(name, content).is_err());
21+
}
22+
23+
#[test]
24+
fn wrong_header() {
25+
let name = "x86_64-known-linux-gnu.md";
26+
let content = "
27+
---
28+
---
29+
30+
# x86_64-known-linux-gnu
31+
";
32+
33+
assert!(super::parse_file(name, content).is_err());
34+
}
35+
36+
#[test]
37+
fn parse_correctly() {
38+
let name = "cat-unknown-linux-gnu.md";
39+
let content = r#"
40+
---
41+
tier: "1" # first-class cats
42+
maintainers: ["who maintains the cat?"]
43+
---
44+
## Requirements
45+
46+
This target mostly just meows and doesn't do much.
47+
48+
## Testing
49+
50+
You can pet the cat and it might respond positively.
51+
52+
## Cross compilation
53+
54+
If you're on a dog system, there might be conflicts with the cat, be careful.
55+
But it should be possible.
56+
"#;
57+
58+
let info = super::parse_file(name, content).unwrap();
59+
60+
assert_eq!(info.maintainers, vec!["who maintains the cat?"]);
61+
assert_eq!(info.pattern, name);
62+
assert_eq!(info.tier, Some(Tier::One));
63+
assert_eq!(
64+
info.sections,
65+
vec![
66+
(
67+
"Requirements".to_owned(),
68+
"This target mostly just meows and doesn't do much.".to_owned(),
69+
),
70+
(
71+
"Testing".to_owned(),
72+
"You can pet the cat and it might respond positively.".to_owned(),
73+
),
74+
(
75+
"Cross compilation".to_owned(),
76+
"If you're on a dog system, there might be conflicts with the cat, be careful.\nBut it should be possible.".to_owned(),
77+
),
78+
]
79+
);
80+
}

‎src/tools/target-docs/src/render.rs

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
use eyre::{Context, OptionExt, Result};
2+
use std::{fs, path::Path};
3+
4+
use crate::{
5+
parse::{Footnote, Tier, TriStateBool},
6+
RustcTargetInfo, TargetDocs,
7+
};
8+
9+
impl TargetDocs {
10+
fn has_host_tools(&self) -> bool {
11+
self.metadata.as_ref().map_or(false, |meta| meta.host == TriStateBool::True)
12+
}
13+
}
14+
15+
/// Renders a single target markdown file from the information obtained.
16+
pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> String {
17+
let mut doc = format!(
18+
"# {}\n\n**Tier: {}**\n\n",
19+
target.name,
20+
match target.tier {
21+
Some(Tier::One) => "1",
22+
Some(Tier::Two) => "2",
23+
Some(Tier::Three) => "3",
24+
None => "UNKNOWN",
25+
}
26+
);
27+
28+
let mut section = |name: &str, content: &str| {
29+
doc.push_str("## ");
30+
doc.push_str(name.trim());
31+
doc.push('\n');
32+
doc.push_str(content.trim());
33+
doc.push_str("\n\n");
34+
};
35+
36+
let maintainers_content = if target.maintainers.is_empty() {
37+
"This target does not have any maintainers!".to_owned()
38+
} else {
39+
format!(
40+
"This target is maintained by:\n{}",
41+
target
42+
.maintainers
43+
.iter()
44+
.map(|maintainer| {
45+
let maintainer = if maintainer.starts_with('@') && !maintainer.contains(" ") {
46+
format!(
47+
"[@{0}](https://github.com/{0})",
48+
maintainer.strip_prefix("@").unwrap()
49+
)
50+
} else {
51+
maintainer.to_owned()
52+
};
53+
54+
format!("- {maintainer}")
55+
})
56+
.collect::<Vec<_>>()
57+
.join("\n")
58+
)
59+
};
60+
61+
section("Maintainers", &maintainers_content);
62+
63+
for section_name in crate::SECTIONS {
64+
let value = target.sections.iter().find(|(name, _)| name == section_name);
65+
66+
let section_content = match value {
67+
Some((_, value)) => value.clone(),
68+
None => "Unknown.".to_owned(),
69+
};
70+
section(&section_name, &section_content);
71+
}
72+
73+
let cfg_text = rustc_info
74+
.target_cfgs
75+
.iter()
76+
.map(|(key, value)| format!("- `{key}` = `{value}`"))
77+
.collect::<Vec<_>>()
78+
.join("\n");
79+
let cfg_content =
80+
format!("This target defines the following target-specific cfg values:\n{cfg_text}\n");
81+
82+
section("cfg", &cfg_content);
83+
84+
doc
85+
}
86+
87+
/// Replaces inner part of the form
88+
/// `<!-- {section_name} SECTION START --><!-- {section_name} SECTION END -->`
89+
/// with replacement`.
90+
fn replace_section(prev_content: &str, section_name: &str, replacement: &str) -> Result<String> {
91+
let magic_summary_start = format!("<!-- {section_name} SECTION START -->");
92+
let magic_summary_end = format!("<!-- {section_name} SECTION END -->");
93+
94+
let (pre_target, target_and_after) = prev_content
95+
.split_once(&magic_summary_start)
96+
.ok_or_eyre("<!-- TARGET SECTION START --> not found")?;
97+
98+
let (_, post_target) = target_and_after
99+
.split_once(&magic_summary_end)
100+
.ok_or_eyre("<!-- TARGET SECTION START --> not found")?;
101+
102+
let new = format!(
103+
"{pre_target}{magic_summary_start}\n{replacement}\n{magic_summary_end}{post_target}"
104+
);
105+
Ok(new)
106+
}
107+
108+
/// Renders the non-target files like `SUMMARY.md` that depend on the target.
109+
pub fn render_static(
110+
check_only: bool,
111+
src_output: &Path,
112+
targets: &[(TargetDocs, RustcTargetInfo)],
113+
) -> Result<()> {
114+
let targets_file = src_output.join("platform-support").join("targets.md");
115+
let old_targets = fs::read_to_string(&targets_file).wrap_err("reading summary file")?;
116+
117+
let target_list = targets
118+
.iter()
119+
.map(|(target, _)| format!("- [{0}](targets/{0}.md)", target.name))
120+
.collect::<Vec<_>>()
121+
.join("\n");
122+
123+
let new_targets =
124+
replace_section(&old_targets, "TARGET", &target_list).wrap_err("replacing targets.md")?;
125+
126+
if !check_only {
127+
fs::write(targets_file, new_targets).wrap_err("writing targets.md")?;
128+
}
129+
130+
let platform_support_main = src_output.join("platform-support.md");
131+
let platform_support_main_old =
132+
fs::read_to_string(&platform_support_main).wrap_err("reading platform-support.md")?;
133+
let platform_support_main_new =
134+
render_platform_support_tables(&platform_support_main_old, targets)?;
135+
136+
if !check_only {
137+
fs::write(platform_support_main, platform_support_main_new)
138+
.wrap_err("writing platform-support.md")?;
139+
}
140+
141+
Ok(())
142+
}
143+
144+
impl Footnote {
145+
fn reference(&self) -> String {
146+
format!("[^{}]", self.name)
147+
}
148+
}
149+
150+
fn render_platform_support_tables(
151+
content: &str,
152+
targets: &[(TargetDocs, RustcTargetInfo)],
153+
) -> Result<String> {
154+
let replace_table = |content, name, tier_table| -> Result<String> {
155+
let section_string = render_table(targets, tier_table)?;
156+
replace_section(content, name, &section_string).wrap_err("replacing platform support.md")
157+
};
158+
159+
let content = replace_table(
160+
content,
161+
"TIER1HOST",
162+
TierTable {
163+
filter: |target| target.tier == Some(Tier::One),
164+
include_host: false,
165+
include_std: false,
166+
},
167+
)?;
168+
let content = replace_table(
169+
&content,
170+
"TIER2HOST",
171+
TierTable {
172+
filter: |target| target.tier == Some(Tier::Two) && target.has_host_tools(),
173+
include_host: false,
174+
include_std: false,
175+
},
176+
)?;
177+
let content = replace_table(
178+
&content,
179+
"TIER2",
180+
TierTable {
181+
filter: |target| target.tier == Some(Tier::Two) && !target.has_host_tools(),
182+
include_host: false,
183+
include_std: true,
184+
},
185+
)?;
186+
let content = replace_table(
187+
&content,
188+
"TIER3",
189+
TierTable {
190+
filter: |target| target.tier == Some(Tier::Three),
191+
include_host: true,
192+
include_std: true,
193+
},
194+
)?;
195+
196+
Ok(content)
197+
}
198+
199+
fn render_table_tri_state_bool(bool: TriStateBool) -> &'static str {
200+
match bool {
201+
TriStateBool::True => "✓",
202+
TriStateBool::False => " ",
203+
TriStateBool::Unknown => "?",
204+
}
205+
}
206+
207+
struct TierTable {
208+
filter: fn(&TargetDocs) -> bool,
209+
include_std: bool,
210+
include_host: bool,
211+
}
212+
213+
fn render_table(targets: &[(TargetDocs, RustcTargetInfo)], table: TierTable) -> Result<String> {
214+
let mut rows = Vec::new();
215+
let mut all_footnotes = Vec::new();
216+
217+
let targets = targets.into_iter().filter(|target| (table.filter)(&target.0));
218+
219+
for (target, _) in targets {
220+
let meta = target.metadata.as_ref();
221+
222+
let mut notes = meta.map(|meta| meta.notes.as_str()).unwrap_or("unknown").to_owned();
223+
224+
if meta.map_or(false, |meta| !meta.footnotes.is_empty()) {
225+
let footnotes = &meta.unwrap().footnotes;
226+
all_footnotes.extend(footnotes);
227+
let footnotes_str =
228+
footnotes.iter().map(|footnote| footnote.reference()).collect::<Vec<_>>().join(" ");
229+
230+
notes = format!("{notes} {footnotes_str}");
231+
}
232+
233+
let std = if table.include_std {
234+
let std = meta.map(|meta| render_table_tri_state_bool(meta.std)).unwrap_or("?");
235+
format!(" | {std}")
236+
} else {
237+
String::new()
238+
};
239+
240+
let host = if table.include_host {
241+
let host = meta.map(|meta| render_table_tri_state_bool(meta.host)).unwrap_or("?");
242+
format!(" | {host}")
243+
} else {
244+
String::new()
245+
};
246+
247+
rows.push(format!(
248+
"[`{0}`](platform-support/targets/{0}.md){std}{host} | {notes}",
249+
target.name
250+
));
251+
}
252+
253+
let mut result = rows.join("\n");
254+
255+
for footnote in all_footnotes {
256+
result.push_str("\n\n");
257+
result.push_str(&footnote.reference());
258+
result.push_str(": ");
259+
result.push_str(&footnote.content);
260+
}
261+
262+
Ok(result)
263+
}

0 commit comments

Comments
 (0)
Please sign in to comment.