This commit is contained in:
nora 2024-03-16 11:24:29 +01:00
parent 720b23bd8e
commit a00ca6ae97
3 changed files with 87 additions and 92 deletions

View file

@ -2,12 +2,13 @@ mod parse;
mod render; mod render;
use std::{ use std::{
collections::HashMap,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
}; };
use eyre::{bail, Context, OptionExt, Result}; use eyre::{bail, Context, OptionExt, Result};
use parse::{Footnote, ParsedTargetInfoFile, Tier, TriStateBool}; use parse::ParsedTargetInfoFile;
use serde::Deserialize; use serde::Deserialize;
/// Information about a target obtained from the target_info markdown file. /// Information about a target obtained from the target_info markdown file.
@ -52,14 +53,24 @@ fn main() -> Result<()> {
.wrap_err("failed loading target_info")? .wrap_err("failed loading target_info")?
.into_iter() .into_iter()
.map(|info| { .map(|info| {
let metadata_used = vec![false; info.metadata.len()]; let footnotes_used = info
TargetPatternEntry { info, used: false, footnotes_used: metadata_used } .footnotes
.iter()
.map(|(target, _)| (target.clone(), false))
.collect();
TargetPatternEntry {
info,
used: false,
footnotes_used,
}
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
eprintln!("Collecting rustc information"); eprintln!("Collecting rustc information");
let rustc_infos = let rustc_infos = targets
targets.iter().map(|target| rustc_target_info(&rustc, target)).collect::<Vec<_>>(); .iter()
.map(|target| rustc_target_info(&rustc, target))
.collect::<Vec<_>>();
let targets = targets let targets = targets
.into_iter() .into_iter()
@ -68,7 +79,9 @@ fn main() -> Result<()> {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
eprintln!("Rendering targets check_only={check_only}"); eprintln!("Rendering targets check_only={check_only}");
let targets_dir = Path::new(output_src).join("platform-support").join("targets"); let targets_dir = Path::new(output_src)
.join("platform-support")
.join("targets");
if !check_only { if !check_only {
std::fs::create_dir_all(&targets_dir).wrap_err("creating platform-support/targets dir")?; std::fs::create_dir_all(&targets_dir).wrap_err("creating platform-support/targets dir")?;
} }
@ -83,17 +96,19 @@ fn main() -> Result<()> {
for target_pattern in info_patterns { for target_pattern in info_patterns {
if !target_pattern.used { 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 for footnote_target in target_pattern.info.footnotes.keys() {
std::iter::zip(target_pattern.footnotes_used, target_pattern.info.metadata) let used = target_pattern.footnotes_used[footnote_target];
{
if !used { if !used {
bail!( bail!(
"in target pattern `{}`, the footnotes for target `{}` were never used", "in target pattern `{}`, the footnotes for target `{}` were never used",
target_pattern.info.pattern, target_pattern.info.pattern,
meta.target footnote_target,
); );
} }
} }
@ -108,7 +123,7 @@ fn main() -> Result<()> {
struct TargetPatternEntry { struct TargetPatternEntry {
info: ParsedTargetInfoFile, info: ParsedTargetInfoFile,
used: bool, used: bool,
footnotes_used: Vec<bool>, footnotes_used: HashMap<String, bool>,
} }
fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetDocs { fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetDocs {
@ -116,7 +131,6 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta
let mut maintainers = Vec::new(); let mut maintainers = Vec::new();
let mut sections = Vec::new(); let mut sections = Vec::new();
let mut metadata = None;
let mut footnotes = Vec::new(); let mut footnotes = Vec::new();
for target_pattern_entry in info_patterns { for target_pattern_entry in info_patterns {
@ -145,32 +159,24 @@ fn target_doc_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> Ta
} }
if let Some(target_footnotes) = target_pattern.footnotes.get(target) { if let Some(target_footnotes) = target_pattern.footnotes.get(target) {
target_pattern_entry.footnotes_used[i] = true; target_pattern_entry
.footnotes_used
.insert(target.to_owned(), true);
if !footnotes.is_empty() { if !footnotes.is_empty() {
panic!("target {target} is assigned metadata from more than one pattern"); panic!("target {target} is assigned metadata from more than one pattern");
} }
footnotes = target_footnotes.clone(); footnotes = target_footnotes.clone();
} }
for (i, metadata_pattern) in target_pattern.metadata.iter().enumerate() {
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");
}
metadata = Some(TargetMetadata {
notes: metadata_pattern.notes.clone(),
host: metadata_pattern.host.clone(),
std: metadata_pattern.std.clone(),
footnotes: metadata_pattern.footnotes.clone(),
});
}
}
} }
} }
TargetDocs { name: target.to_owned(), maintainers, sections, footnotes } TargetDocs {
name: target.to_owned(),
maintainers,
sections,
footnotes,
}
} }
/// Information about a target obtained from rustc. /// Information about a target obtained from rustc.
@ -212,11 +218,21 @@ fn rustc_target_info(rustc: &Path, target: &str) -> RustcTargetInfo {
let json_spec = rustc_stdout( let json_spec = rustc_stdout(
rustc, rustc,
&["-Zunstable-options", "--print", "target-spec-json", "--target", target], &[
"-Zunstable-options",
"--print",
"target-spec-json",
"--target",
target,
],
); );
let spec = serde_json::from_str::<TargetJson>(&json_spec); let spec = serde_json::from_str::<TargetJson>(&json_spec)
.expect("parsing --print target-spec-json for metadata");
RustcTargetInfo { target_cfgs, metadata: spec.metadata } RustcTargetInfo {
target_cfgs,
metadata: spec.metadata,
}
} }
fn rustc_stdout(rustc: &Path, args: &[&str]) -> String { fn rustc_stdout(rustc: &Path, args: &[&str]) -> String {

View file

@ -86,16 +86,9 @@ fn parse_file(name: &str, content: &str) -> Result<ParsedTargetInfoFile> {
let frontmatter_line_count = frontmatter.lines().count() + 2; // 2 from --- let frontmatter_line_count = frontmatter.lines().count() + 2; // 2 from ---
let mut frontmatter = let frontmatter =
serde_yaml::from_str::<Frontmatter>(frontmatter).wrap_err("invalid frontmatter")?; serde_yaml::from_str::<Frontmatter>(frontmatter).wrap_err("invalid frontmatter")?;
frontmatter.metadata.iter_mut().for_each(|meta| {
meta.footnotes.iter_mut().for_each(|footnote| {
footnote.content = footnote.content.replace("\r\n", " ").replace("\n", " ")
})
});
let frontmatter = frontmatter;
let body = frontmatter_splitter.next().ok_or_eyre("no body")?; let body = frontmatter_splitter.next().ok_or_eyre("no body")?;
let mut sections = Vec::<(String, String)>::new(); let mut sections = Vec::<(String, String)>::new();

View file

@ -1,27 +1,18 @@
use eyre::{Context, OptionExt, Result}; use eyre::{Context, OptionExt, Result};
use std::{fs, path::Path}; use std::{fs, path::Path};
use crate::{ use crate::{RustcTargetInfo, TargetDocs};
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)
}
}
/// Renders a single target markdown file from the information obtained. /// Renders a single target markdown file from the information obtained.
pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> String { pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> String {
let mut doc = format!( let mut doc = format!(
"# {}\n\n**Tier: {}**\n\n", "# {}\n\n**Tier: {}**\n\n",
target.name, target.name,
match target.tier { match rustc_info.metadata.tier {
Some(Tier::One) => "1", Some(1) => "1",
Some(Tier::Two) => "2", Some(2) => "2",
Some(Tier::Three) => "3", Some(3) => "3",
None => "UNKNOWN", _ => "UNKNOWN",
} }
); );
@ -61,7 +52,10 @@ pub fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> St
section("Maintainers", &maintainers_content); section("Maintainers", &maintainers_content);
for section_name in crate::SECTIONS { 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 { let section_content = match value {
Some((_, value)) => value.clone(), Some((_, value)) => value.clone(),
@ -141,12 +135,6 @@ pub fn render_static(
Ok(()) Ok(())
} }
impl Footnote {
fn reference(&self) -> String {
format!("[^{}]", self.name)
}
}
fn render_platform_support_tables( fn render_platform_support_tables(
content: &str, content: &str,
targets: &[(TargetDocs, RustcTargetInfo)], targets: &[(TargetDocs, RustcTargetInfo)],
@ -160,7 +148,7 @@ fn render_platform_support_tables(
content, content,
"TIER1HOST", "TIER1HOST",
TierTable { TierTable {
filter: |target| target.tier == Some(Tier::One), filter: |target| target.1.metadata.tier == Some(1),
include_host: false, include_host: false,
include_std: false, include_std: false,
}, },
@ -169,7 +157,9 @@ fn render_platform_support_tables(
&content, &content,
"TIER2HOST", "TIER2HOST",
TierTable { TierTable {
filter: |target| target.tier == Some(Tier::Two) && target.has_host_tools(), filter: |target| {
target.1.metadata.tier == Some(2) && target.1.metadata.host_tools.unwrap_or(false)
},
include_host: false, include_host: false,
include_std: false, include_std: false,
}, },
@ -178,7 +168,9 @@ fn render_platform_support_tables(
&content, &content,
"TIER2", "TIER2",
TierTable { TierTable {
filter: |target| target.tier == Some(Tier::Two) && !target.has_host_tools(), filter: |target| {
target.1.metadata.tier == Some(2) && !target.1.metadata.host_tools.unwrap_or(false)
},
include_host: false, include_host: false,
include_std: true, include_std: true,
}, },
@ -187,7 +179,7 @@ fn render_platform_support_tables(
&content, &content,
"TIER3", "TIER3",
TierTable { TierTable {
filter: |target| target.tier == Some(Tier::Three), filter: |target| target.1.metadata.tier == Some(3),
include_host: true, include_host: true,
include_std: true, include_std: true,
}, },
@ -196,49 +188,50 @@ fn render_platform_support_tables(
Ok(content) Ok(content)
} }
fn render_table_tri_state_bool(bool: TriStateBool) -> &'static str { fn render_table_option_bool(bool: Option<bool>) -> &'static str {
match bool { match bool {
TriStateBool::True => "", Some(true) => "",
TriStateBool::False => " ", Some(false) => " ",
TriStateBool::Unknown => "?", None => "?",
} }
} }
struct TierTable { struct TierTable {
filter: fn(&TargetDocs) -> bool, filter: fn(&(TargetDocs, RustcTargetInfo)) -> bool,
include_std: bool, include_std: bool,
include_host: bool, include_host: bool,
} }
fn render_table(targets: &[(TargetDocs, RustcTargetInfo)], table: TierTable) -> Result<String> { fn render_table(targets: &[(TargetDocs, RustcTargetInfo)], table: TierTable) -> Result<String> {
let mut rows = Vec::new(); 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));
for (target, _) in targets { for (target, rustc_info) in targets {
let meta = target.metadata.as_ref(); let meta = &rustc_info.metadata;
let mut notes = meta.map(|meta| meta.notes.as_str()).unwrap_or("unknown").to_owned(); let mut notes = meta.description.as_deref().unwrap_or("unknown").to_owned();
if meta.map_or(false, |meta| !meta.footnotes.is_empty()) { if !target.footnotes.is_empty() {
let footnotes = &meta.unwrap().footnotes; let footnotes_str = target
all_footnotes.extend(footnotes); .footnotes
let footnotes_str = .iter()
footnotes.iter().map(|footnote| footnote.reference()).collect::<Vec<_>>().join(" "); .map(|footnote| format!("[^{}]", footnote))
.collect::<Vec<_>>()
.join(" ");
notes = format!("{notes} {footnotes_str}"); notes = format!("{notes} {footnotes_str}");
} }
let std = if table.include_std { let std = if table.include_std {
let std = meta.map(|meta| render_table_tri_state_bool(meta.std)).unwrap_or("?"); let std = render_table_option_bool(meta.std);
format!(" | {std}") format!(" | {std}")
} else { } else {
String::new() String::new()
}; };
let host = if table.include_host { let host = if table.include_host {
let host = meta.map(|meta| render_table_tri_state_bool(meta.host)).unwrap_or("?"); let host = render_table_option_bool(meta.host_tools);
format!(" | {host}") format!(" | {host}")
} else { } else {
String::new() String::new()
@ -250,14 +243,7 @@ fn render_table(targets: &[(TargetDocs, RustcTargetInfo)], table: TierTable) ->
)); ));
} }
let mut result = rows.join("\n"); let result = rows.join("\n");
for footnote in all_footnotes {
result.push_str("\n\n");
result.push_str(&footnote.reference());
result.push_str(": ");
result.push_str(&footnote.content);
}
Ok(result) Ok(result)
} }