diff --git a/Cargo.lock b/Cargo.lock
index f328054..6a640be 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,37 +2,6 @@
# It is not intended for manual editing.
version = 3
-[[package]]
-name = "crossbeam-deque"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
-dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
-
-[[package]]
-name = "either"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
-
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -107,26 +76,6 @@ dependencies = [
"proc-macro2",
]
-[[package]]
-name = "rayon"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
-dependencies = [
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
-dependencies = [
- "crossbeam-deque",
- "crossbeam-utils",
-]
-
[[package]]
name = "ryu"
version = "1.0.16"
@@ -153,6 +102,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "serde_json"
+version = "1.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
[[package]]
name = "serde_yaml"
version = "0.9.31"
@@ -183,8 +143,8 @@ version = "0.1.0"
dependencies = [
"eyre",
"glob-match",
- "rayon",
"serde",
+ "serde_json",
"serde_yaml",
]
diff --git a/Cargo.toml b/Cargo.toml
index 3228e64..7874171 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,12 +2,13 @@
name = "target-docs"
version = "0.1.0"
edition = "2021"
+license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
eyre = "0.6.12"
glob-match = "0.2.1"
-rayon = "1.8.1"
-serde = { version = "1.0.196", features = ["derive"] }
+serde = { version = "1.0.185", features = ["derive"] }
+serde_json = "1.0.114"
serde_yaml = "0.9.31"
diff --git a/README.md b/README.md
index 79220b8..6059944 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,57 @@
-# target tier docs experiment
+# target-docs
-Experiment with automatically generating target tier docs.
+This tool generates target documentation for all targets in the rustc book.
-**View the deployment on **
+To achieve this, it uses a list of input markdown files provided in `src/doc/rustc/target_infos`. These files follow a strict format.
+Every file covers a glob pattern of targets according to its file name.
-## Problems
+For every rustc target, we iterate through all the target infos and find matching globs.
+When a glob matches, it extracts the h2 markdown sections and saves them for the target.
-Currenly, the [target tier docs](https://doc.rust-lang.org/rustc/platform-support.html) are hard to navigate.
-If you want to find information about a specific target, you first need to do some glob-search yourself and then also hope
-that the target actually exists. This is super annoying (`:(`). Additionally, some targets are completely missing and there
-is no reason to believe that the documentation won't suddenly start being out of date.
-Pages are also inconsistent about which sections exist and which ones don't.
+In the end, a page is generated for every target using these sections.
+Sections that are not provided are stubbed out. Currently, the sections are
-## Solution
+- Overview
+- Requirements
+- Testing
+- Building the target
+- Cross compilation
+- Building Rust programs
-Enter: adding yet another preprocessing step.
+In addition to the markdown sections, we also have extra data about the targets.
+This is achieved through YAML frontmatter.
-By adding yet another preprocessing step, we can solve all these problems.
-- Have a *dedicated* page for *every single* target including information about maintainers etc.
- This makes it super easy to find things when there are problems.
-- Ensure that no target is completely undocumented, at least having a stub page pointing out the undocumentedness
-- Error when there is documentation that is not needed anymore, for example a removed target
-- Still keep the nice and easy-to-organize glob structure in the source
-- Use a unified structure for all the pages
-- This also allows us to put more dynamic values into the docs. For example, I put `--print cfg` there, isn't that pretty!?
+The frontmatter follows the following format:
+
+```yaml
+tier: "1"
+maintainers: ["@someone"]
+metadata:
+ - target: "i686-pc-windows-gnu"
+ notes: "32-bit MinGW (Windows 7+)"
+ std: true
+ host: true
+ footnotes:
+ - name: "x86_32-floats-return-ABI"
+ content: |
+ Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant:
+ floating-point return values are passed via an x87 register, so NaN payload bits can be lost.
+ See [issue #114479][https://github.com/rust-lang/rust/issues/114479].
+ - name: "windows-support"
+ content: "Only Windows 10 currently undergoes automated testing. Earlier versions of Windows rely on testing and support from the community."
+```
+
+The top level keys are:
+
+- `tier` (optional): `1`, `2` or `3`
+- `maintainers` (optional): list of strings
+
+There is also `metadata`, which is specific to every single target and not just a target "group" (the glob).
+
+`metadata` has the following properties:
+
+- `target`: the target name
+- `notes`: a string containing a short description of the target for the table
+- `std`: `true`, `false`, `unknown`, whether the target has `std`
+- `host`: `true`, `false`, `unknown`, whether the target has host tools
+- `footnotes` (optional): a list of footnotes, where every footnote has a `name` and `content`. These are used in the table.
diff --git a/src/main.rs b/src/main.rs
index a931dc4..e03d039 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,33 +2,27 @@ mod parse;
mod render;
use std::{
- io,
path::{Path, PathBuf},
process::Command,
};
use eyre::{bail, Context, OptionExt, Result};
use parse::{Footnote, ParsedTargetInfoFile, Tier, TriStateBool};
-use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
+use serde::Deserialize;
-/// Information about a target obtained from `target_info.toml``.
+/// Information about a target obtained from the target_info markdown file.
struct TargetDocs {
name: String,
maintainers: Vec,
sections: Vec<(String, String)>,
- tier: Option,
- // TODO: Make this mandatory.
- metadata: Option,
-}
-
-/// Metadata for the table
-struct TargetMetadata {
- notes: String,
- std: TriStateBool,
- host: TriStateBool,
- footnotes: Vec,
+ footnotes: Vec,
}
+/// All the sections that we want every doc page to have.
+/// It may make sense to relax this into two kinds of sections, "required" sections
+/// and "optional" sections, where required sections will get stubbed out when not found
+/// while optional sections will just not exist when not found.
+// IMPORTANT: This is also documented in the README, keep it in sync.
const SECTIONS: &[&str] = &[
"Overview",
"Requirements",
@@ -38,51 +32,34 @@ const SECTIONS: &[&str] = &[
"Building Rust programs",
];
-fn is_in_rust_lang_rust() -> bool {
- std::env::var("RUST_LANG_RUST") == Ok("1".to_owned())
-}
-
fn main() -> Result<()> {
let args = std::env::args().collect::>();
let input_dir = args
.get(1)
- .ok_or_eyre("first argument must be path to directory containing source md files")?;
+ .ok_or_eyre("first argument must be path to target_infos directory containing target source md files (src/doc/rustc/target_infos/)")?;
let output_src = args
.get(2)
- .ok_or_eyre("second argument must be path to `src` output directory")?;
+ .ok_or_eyre("second argument must be path to `src` output directory (src/doc/rustc/src)")?;
let rustc =
PathBuf::from(std::env::var("RUSTC").expect("must pass RUSTC env var pointing to rustc"));
+ let check_only = std::env::var("TARGET_CHECK_ONLY") == Ok("1".to_owned());
let targets = rustc_stdout(&rustc, &["--print", "target-list"]);
let targets = targets.lines().collect::>();
- if !is_in_rust_lang_rust() {
- match std::fs::create_dir("targets/src") {
- Ok(()) => {}
- Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
- e @ _ => e.wrap_err("failed creating src dir")?,
- }
- }
-
let mut info_patterns = parse::load_target_infos(Path::new(input_dir))
.wrap_err("failed loading target_info")?
.into_iter()
.map(|info| {
let metadata_used = vec![false; info.metadata.len()];
- TargetPatternEntry {
- info,
- used: false,
- metadata_used,
- }
+ TargetPatternEntry { info, used: false, footnotes_used: metadata_used }
})
.collect::>();
eprintln!("Collecting rustc information");
- let rustc_infos = targets
- .par_iter()
- .map(|target| rustc_target_info(&rustc, target))
- .collect::>();
+ let rustc_infos =
+ targets.iter().map(|target| rustc_target_info(&rustc, target)).collect::>();
let targets = targets
.into_iter()
@@ -90,42 +67,39 @@ fn main() -> Result<()> {
.zip(rustc_infos)
.collect::>();
- eprintln!("Rendering targets");
+ eprintln!("Rendering targets check_only={check_only}");
+ let targets_dir = Path::new(output_src).join("platform-support").join("targets");
+ if !check_only {
+ std::fs::create_dir_all(&targets_dir).wrap_err("creating platform-support/targets dir")?;
+ }
for (info, rustc_info) in &targets {
let doc = render::render_target_md(info, rustc_info);
- std::fs::write(
- Path::new(output_src)
- .join("platform-support")
- .join("targets")
- .join(format!("{}.md", info.name)),
- doc,
- )
- .wrap_err("writing target file")?;
+ if !check_only {
+ std::fs::write(targets_dir.join(format!("{}.md", info.name)), doc)
+ .wrap_err("writing target file")?;
+ }
}
for target_pattern in info_patterns {
if !target_pattern.used {
- bail!(
- "target pattern `{}` was never used",
- target_pattern.info.pattern
- );
+ bail!("target pattern `{}` was never used", target_pattern.info.pattern);
}
for (used, meta) in
- std::iter::zip(target_pattern.metadata_used, target_pattern.info.metadata)
+ std::iter::zip(target_pattern.footnotes_used, target_pattern.info.metadata)
{
if !used {
bail!(
- "in target pattern `{}`, the metadata pattern `{}` was never used",
+ "in target pattern `{}`, the footnotes for target `{}` were never used",
target_pattern.info.pattern,
- meta.pattern
+ meta.target
);
}
}
}
- render::render_static(Path::new(output_src), &targets)?;
+ render::render_static(check_only, Path::new(output_src), &targets)?;
eprintln!("Finished generating target docs");
Ok(())
@@ -134,7 +108,7 @@ fn main() -> Result<()> {
struct TargetPatternEntry {
info: ParsedTargetInfoFile,
used: bool,
- metadata_used: Vec,
+ footnotes_used: Vec,
}
fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetDocs {
@@ -143,6 +117,7 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta
let mut sections = Vec::new();
let mut metadata = None;
+ let mut footnotes = Vec::new();
for target_pattern_entry in info_patterns {
if glob_match::glob_match(&target_pattern_entry.info.pattern, target) {
@@ -153,21 +128,34 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta
if let Some(pattern_value) = &target_pattern.tier {
if tier.is_some() {
- panic!("target {target} inherits a tier from multiple patterns, create a more specific pattern and add it there");
+ panic!(
+ "target {target} inherits a tier from multiple patterns, create a more specific pattern and add it there"
+ );
}
tier = Some(pattern_value.clone());
}
for (section_name, content) in &target_pattern.sections {
if sections.iter().any(|(name, _)| name == section_name) {
- panic!("target {target} inherits the section {section_name} from multiple patterns, create a more specific pattern and add it there");
+ panic!(
+ "target {target} inherits the section {section_name} from multiple patterns, create a more specific pattern and add it there"
+ );
}
sections.push((section_name.clone(), content.clone()));
}
+ if let Some(target_footnotes) = target_pattern.footnotes.get(target) {
+ target_pattern_entry.footnotes_used[i] = true;
+
+ if !footnotes.is_empty() {
+ panic!("target {target} is assigned metadata from more than one pattern");
+ }
+ footnotes = target_footnotes.clone();
+ }
+
for (i, metadata_pattern) in target_pattern.metadata.iter().enumerate() {
- if glob_match::glob_match(&metadata_pattern.pattern, target) {
- target_pattern_entry.metadata_used[i] = true;
+ if metadata_pattern.target == target {
+ target_pattern_entry.footnotes_used[i] = true;
if metadata.is_some() {
panic!("target {target} is assigned metadata from more than one pattern");
}
@@ -182,18 +170,21 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta
}
}
- TargetDocs {
- name: target.to_owned(),
- maintainers,
- tier,
- sections,
- metadata,
- }
+ TargetDocs { name: target.to_owned(), maintainers, sections, footnotes }
}
/// Information about a target obtained from rustc.
struct RustcTargetInfo {
target_cfgs: Vec<(String, String)>,
+ metadata: RustcTargetMetadata,
+}
+
+#[derive(Deserialize)]
+struct RustcTargetMetadata {
+ description: Option,
+ tier: Option,
+ host_tools: Option,
+ std: Option,
}
/// Get information about a target from rustc.
@@ -213,7 +204,19 @@ fn rustc_target_info(rustc: &Path, target: &str) -> RustcTargetInfo {
}
})
.collect();
- RustcTargetInfo { target_cfgs }
+
+ #[derive(Deserialize)]
+ struct TargetJson {
+ metadata: RustcTargetMetadata,
+ }
+
+ let json_spec = rustc_stdout(
+ rustc,
+ &["-Zunstable-options", "--print", "target-spec-json", "--target", target],
+ );
+ let spec = serde_json::from_str::(&json_spec);
+
+ RustcTargetInfo { target_cfgs, metadata: spec.metadata }
}
fn rustc_stdout(rustc: &Path, args: &[&str]) -> String {
diff --git a/src/parse.rs b/src/parse.rs
index e1be2bc..00fc64a 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -2,7 +2,7 @@
use eyre::{bail, OptionExt, Result, WrapErr};
use serde::Deserialize;
-use std::{fs::DirEntry, path::Path};
+use std::{collections::HashMap, fs::DirEntry, path::Path};
#[derive(Debug, PartialEq, Clone, Deserialize)]
pub enum Tier {
@@ -20,9 +20,10 @@ pub struct ParsedTargetInfoFile {
pub tier: Option,
pub maintainers: Vec,
pub sections: Vec<(String, String)>,
- pub metadata: Vec,
+ pub footnotes: HashMap>,
}
+// IMPORTANT: This is also documented in the README, keep it in sync.
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Frontmatter {
@@ -30,27 +31,19 @@ struct Frontmatter {
#[serde(default)]
maintainers: Vec,
#[serde(default)]
- metadata: Vec,
+ footnotes: HashMap>,
}
+// IMPORTANT: This is also documented in the README, keep it in sync.
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
-pub struct ParsedTargetMetadata {
- pub pattern: String,
- pub notes: String,
- pub std: TriStateBool,
- pub host: TriStateBool,
+pub struct TargetFootnotes {
+ pub target: String,
#[serde(default)]
- pub footnotes: Vec,
-}
-
-#[derive(Debug, Clone, Deserialize)]
-#[serde(deny_unknown_fields)]
-pub struct Footnote {
- pub name: String,
- pub content: String,
+ pub footnotes: Vec,
}
+// IMPORTANT: This is also documented in the README, keep it in sync.
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TriStateBool {
@@ -89,9 +82,7 @@ fn load_single_target_info(entry: &DirEntry) -> Result {
fn parse_file(name: &str, content: &str) -> Result {
let mut frontmatter_splitter = content.split("---\n");
- let frontmatter = frontmatter_splitter
- .nth(1)
- .ok_or_eyre("missing frontmatter")?;
+ let frontmatter = frontmatter_splitter.nth(1).ok_or_eyre("missing frontmatter")?;
let frontmatter_line_count = frontmatter.lines().count() + 2; // 2 from ---
@@ -149,99 +140,16 @@ fn parse_file(name: &str, content: &str) -> Result {
}
}
- sections
- .iter_mut()
- .for_each(|section| section.1 = section.1.trim().to_owned());
+ sections.iter_mut().for_each(|section| section.1 = section.1.trim().to_owned());
Ok(ParsedTargetInfoFile {
pattern: name.to_owned(),
maintainers: frontmatter.maintainers,
tier: frontmatter.tier,
sections,
- metadata: frontmatter.metadata,
+ footnotes: frontmatter.footnotes,
})
}
#[cfg(test)]
-mod tests {
- use crate::parse::Tier;
-
- #[test]
- fn no_frontmatter() {
- let name = "archlinux-unknown-linux-gnu.md"; // arch linux is an arch, right?
- let content = "";
- assert!(super::parse_file(name, content).is_err());
- }
-
- #[test]
- fn invalid_section() {
- let name = "6502-nintendo-nes.md";
- let content = "
----
----
-
-## Not A Real Section
-";
-
- assert!(super::parse_file(name, content).is_err());
- }
-
- #[test]
- fn wrong_header() {
- let name = "x86_64-known-linux-gnu.md";
- let content = "
----
----
-
-# x86_64-known-linux-gnu
-";
-
- assert!(super::parse_file(name, content).is_err());
- }
-
- #[test]
- fn parse_correctly() {
- let name = "cat-unknown-linux-gnu.md";
- let content = r#"
----
-tier: "1" # first-class cats
-maintainers: ["who maintains the cat?"]
----
-## Requirements
-
-This target mostly just meows and doesn't do much.
-
-## Testing
-
-You can pet the cat and it might respond positively.
-
-## Cross compilation
-
-If you're on a dog system, there might be conflicts with the cat, be careful.
-But it should be possible.
- "#;
-
- let info = super::parse_file(name, content).unwrap();
-
- assert_eq!(info.maintainers, vec!["who maintains the cat?"]);
- assert_eq!(info.pattern, name);
- assert_eq!(info.tier, Some(Tier::One));
- assert_eq!(
- info.sections,
- vec![
- (
- "Requirements".to_owned(),
- "This target mostly just meows and doesn't do much.".to_owned(),
- ),
- (
- "Testing".to_owned(),
- "You can pet the cat and it might respond positively.".to_owned(),
- ),
- (
- "Cross compilation".to_owned(),
- "If you're on a dog system, there might be conflicts with the cat, be careful.\nBut it should be possible.".to_owned(),
- ),
- ]
- );
- }
-}
+mod tests;
diff --git a/src/parse/tests.rs b/src/parse/tests.rs
new file mode 100644
index 0000000..cdaaa1f
--- /dev/null
+++ b/src/parse/tests.rs
@@ -0,0 +1,80 @@
+use crate::parse::Tier;
+
+#[test]
+fn no_frontmatter() {
+ let name = "archlinux-unknown-linux-gnu.md"; // arch linux is an arch, right?
+ let content = "";
+ assert!(super::parse_file(name, content).is_err());
+}
+
+#[test]
+fn invalid_section() {
+ let name = "6502-nintendo-nes.md";
+ let content = "
+---
+---
+
+## Not A Real Section
+";
+
+ assert!(super::parse_file(name, content).is_err());
+}
+
+#[test]
+fn wrong_header() {
+ let name = "x86_64-known-linux-gnu.md";
+ let content = "
+---
+---
+
+# x86_64-known-linux-gnu
+";
+
+ assert!(super::parse_file(name, content).is_err());
+}
+
+#[test]
+fn parse_correctly() {
+ let name = "cat-unknown-linux-gnu.md";
+ let content = r#"
+---
+tier: "1" # first-class cats
+maintainers: ["who maintains the cat?"]
+---
+## Requirements
+
+This target mostly just meows and doesn't do much.
+
+## Testing
+
+You can pet the cat and it might respond positively.
+
+## Cross compilation
+
+If you're on a dog system, there might be conflicts with the cat, be careful.
+But it should be possible.
+ "#;
+
+ let info = super::parse_file(name, content).unwrap();
+
+ assert_eq!(info.maintainers, vec!["who maintains the cat?"]);
+ assert_eq!(info.pattern, name);
+ assert_eq!(info.tier, Some(Tier::One));
+ assert_eq!(
+ info.sections,
+ vec![
+ (
+ "Requirements".to_owned(),
+ "This target mostly just meows and doesn't do much.".to_owned(),
+ ),
+ (
+ "Testing".to_owned(),
+ "You can pet the cat and it might respond positively.".to_owned(),
+ ),
+ (
+ "Cross compilation".to_owned(),
+ "If you're on a dog system, there might be conflicts with the cat, be careful.\nBut it should be possible.".to_owned(),
+ ),
+ ]
+ );
+}
diff --git a/src/render.rs b/src/render.rs
index 9bdcd72..c90a0f5 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -2,16 +2,13 @@ use eyre::{Context, OptionExt, Result};
use std::{fs, path::Path};
use crate::{
- is_in_rust_lang_rust,
parse::{Footnote, Tier, TriStateBool},
RustcTargetInfo, TargetDocs,
};
impl TargetDocs {
fn has_host_tools(&self) -> bool {
- self.metadata
- .as_ref()
- .map_or(false, |meta| meta.host == TriStateBool::True)
+ self.metadata.as_ref().map_or(false, |meta| meta.host == TriStateBool::True)
}
}
@@ -64,10 +61,7 @@ pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> St
section("Maintainers", &maintainers_content);
for section_name in crate::SECTIONS {
- let value = target
- .sections
- .iter()
- .find(|(name, _)| name == section_name);
+ let value = target.sections.iter().find(|(name, _)| name == section_name);
let section_content = match value {
Some((_, value)) => value.clone(),
@@ -112,7 +106,11 @@ fn replace_section(prev_content: &str, section_name: &str, replacement: &str) ->
}
/// Renders the non-target files like `SUMMARY.md` that depend on the target.
-pub fn render_static(src_output: &Path, targets: &[(TargetDocs, RustcTargetInfo)]) -> Result<()> {
+pub fn render_static(
+ check_only: bool,
+ src_output: &Path,
+ targets: &[(TargetDocs, RustcTargetInfo)],
+) -> Result<()> {
let targets_file = src_output.join("platform-support").join("targets.md");
let old_targets = fs::read_to_string(&targets_file).wrap_err("reading summary file")?;
@@ -125,26 +123,8 @@ pub fn render_static(src_output: &Path, targets: &[(TargetDocs, RustcTargetInfo)
let new_targets =
replace_section(&old_targets, "TARGET", &target_list).wrap_err("replacing targets.md")?;
- fs::write(targets_file, new_targets).wrap_err("writing targets.md")?;
-
- if !is_in_rust_lang_rust() {
- fs::write(
- "targets/src/information.md",
- "\
- # platform support generated
-
- This is an experiment of what generated target tier documentation could look like.
-
- See for the source.
- The README of the repo contains more information about the motivation and benefits.
-
- Targets of interest with information filled out are any tvos targets like [aarch64-apple-tvos](./aarch64-apple-tvos.md)
- and [powerpc64-ibm-aix](./powerpc64-ibm-aix.md).
-
- But as you might notice, all targets are actually present with a stub :3.
- ",
- )
- .wrap_err("writing front page information about experiment")?;
+ if !check_only {
+ fs::write(targets_file, new_targets).wrap_err("writing targets.md")?;
}
let platform_support_main = src_output.join("platform-support.md");
@@ -152,8 +132,11 @@ pub fn render_static(src_output: &Path, targets: &[(TargetDocs, RustcTargetInfo)
fs::read_to_string(&platform_support_main).wrap_err("reading platform-support.md")?;
let platform_support_main_new =
render_platform_support_tables(&platform_support_main_old, targets)?;
- fs::write(platform_support_main, platform_support_main_new)
- .wrap_err("writing platform-support.md")?;
+
+ if !check_only {
+ fs::write(platform_support_main, platform_support_main_new)
+ .wrap_err("writing platform-support.md")?;
+ }
Ok(())
}
@@ -227,47 +210,35 @@ struct TierTable {
include_host: bool,
}
-fn render_table<'a>(targets: &[(TargetDocs, RustcTargetInfo)], table: TierTable) -> Result {
+fn render_table(targets: &[(TargetDocs, RustcTargetInfo)], table: TierTable) -> Result {
let mut rows = Vec::new();
let mut all_footnotes = Vec::new();
- let targets = targets
- .into_iter()
- .filter(|target| (table.filter)(&target.0));
+ let targets = targets.into_iter().filter(|target| (table.filter)(&target.0));
for (target, _) in targets {
let meta = target.metadata.as_ref();
- let mut notes = meta
- .map(|meta| meta.notes.as_str())
- .unwrap_or("unknown")
- .to_owned();
+ let mut notes = meta.map(|meta| meta.notes.as_str()).unwrap_or("unknown").to_owned();
if meta.map_or(false, |meta| !meta.footnotes.is_empty()) {
let footnotes = &meta.unwrap().footnotes;
all_footnotes.extend(footnotes);
- let footnotes_str = footnotes
- .iter()
- .map(|footnote| footnote.reference())
- .collect::>()
- .join(" ");
+ let footnotes_str =
+ footnotes.iter().map(|footnote| footnote.reference()).collect::>().join(" ");
notes = format!("{notes} {footnotes_str}");
}
let std = if table.include_std {
- let std = meta
- .map(|meta| render_table_tri_state_bool(meta.std))
- .unwrap_or("?");
+ let std = meta.map(|meta| render_table_tri_state_bool(meta.std)).unwrap_or("?");
format!(" | {std}")
} else {
String::new()
};
let host = if table.include_host {
- let host = meta
- .map(|meta| render_table_tri_state_bool(meta.host))
- .unwrap_or("?");
+ let host = meta.map(|meta| render_table_tri_state_bool(meta.host)).unwrap_or("?");
format!(" | {host}")
} else {
String::new()