mirror of
https://github.com/Noratrieb/target-tier-docs-experiment.git
synced 2026-01-14 16:35:09 +01:00
things
This commit is contained in:
parent
33748193aa
commit
a58ec204c5
5 changed files with 195 additions and 41 deletions
52
Cargo.lock
generated
52
Cargo.lock
generated
|
|
@ -2,6 +2,37 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
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]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
@ -54,6 +85,26 @@ dependencies = [
|
||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.196"
|
version = "1.0.196"
|
||||||
|
|
@ -99,6 +150,7 @@ name = "target-docs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glob-match",
|
"glob-match",
|
||||||
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,6 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glob-match = "0.2.1"
|
glob-match = "0.2.1"
|
||||||
|
rayon = "1.8.1"
|
||||||
serde = { version = "1.0.196", features = ["derive"] }
|
serde = { version = "1.0.196", features = ["derive"] }
|
||||||
toml = "0.8.10"
|
toml = "0.8.10"
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,4 @@ By adding yet another preprocessing step, we can solve all these problems.
|
||||||
- Error when there is documentation that is not needed anymore, for example a removed target
|
- 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
|
- Still keep the nice and easy-to-organize glob structure in the source
|
||||||
- Use a unified structure for all the pages
|
- 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!?
|
||||||
|
|
|
||||||
178
src/main.rs
178
src/main.rs
|
|
@ -1,4 +1,10 @@
|
||||||
use std::io;
|
use std::{
|
||||||
|
io,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
|
|
||||||
struct TargetDocs {
|
struct TargetDocs {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -11,7 +17,50 @@ struct TargetDocs {
|
||||||
tier: u8,
|
tier: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_target_md(target: &TargetDocs) -> String {
|
fn main() {
|
||||||
|
let rustc =
|
||||||
|
PathBuf::from(std::env::var("RUSTC").expect("must pass RUSTC env var pointing to rustc"));
|
||||||
|
|
||||||
|
let targets = rustc_stdout(&rustc, &["--print", "target-list"]);
|
||||||
|
let targets = targets.lines().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
match std::fs::create_dir("targets/src") {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
|
||||||
|
e @ _ => e.unwrap(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut info_patterns = load_target_info_patterns();
|
||||||
|
|
||||||
|
eprintln!("Collecting rustc information");
|
||||||
|
let rustc_infos = targets
|
||||||
|
.par_iter()
|
||||||
|
.map(|target| rustc_target_info(&rustc, target))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
eprintln!("Rendering targets");
|
||||||
|
for (target, rustc_info) in std::iter::zip(&targets, rustc_infos) {
|
||||||
|
let info = target_info(&mut info_patterns, target);
|
||||||
|
let doc = render_target_md(&info, &rustc_info);
|
||||||
|
|
||||||
|
std::fs::write(format!("targets/src/{target}.md"), doc).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for target_pattern in info_patterns {
|
||||||
|
if !target_pattern.used {
|
||||||
|
panic!(
|
||||||
|
"target pattern `{}` was never used",
|
||||||
|
target_pattern.info.pattern
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render_static(&targets);
|
||||||
|
|
||||||
|
eprintln!("Finished generating target docs");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_target_md(target: &TargetDocs, rustc_info: &RustcTargetInfo) -> String {
|
||||||
let mut doc = format!("# {}\n**Tier: {}**", target.name, target.tier);
|
let mut doc = format!("# {}\n**Tier: {}**", target.name, target.tier);
|
||||||
|
|
||||||
let maintainers_str = if target.maintainers.is_empty() {
|
let maintainers_str = if target.maintainers.is_empty() {
|
||||||
|
|
@ -55,24 +104,20 @@ fn render_target_md(target: &TargetDocs) -> String {
|
||||||
section(&target.cross_compilation, "Cross Compilation");
|
section(&target.cross_compilation, "Cross Compilation");
|
||||||
section(&target.building_rust_programs, "Building Rust Programs");
|
section(&target.building_rust_programs, "Building Rust Programs");
|
||||||
|
|
||||||
|
let cfg_text = rustc_info
|
||||||
|
.target_cfgs
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| format!("- `{key}` = `{value}`"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
let cfg_text =
|
||||||
|
format!("This target defines the following target-specific cfg values:\n{cfg_text}\n");
|
||||||
|
section(&Some(cfg_text), "cfg");
|
||||||
|
|
||||||
doc
|
doc
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn render_static(targets: &[&str]) {
|
||||||
let targets = include_str!("../targets.txt").lines().collect::<Vec<_>>();
|
|
||||||
|
|
||||||
match std::fs::create_dir("targets/src") {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
|
|
||||||
e @ _ => e.unwrap(),
|
|
||||||
}
|
|
||||||
|
|
||||||
for target in &targets {
|
|
||||||
let doc = render_target_md(&target_info(target));
|
|
||||||
|
|
||||||
std::fs::write(format!("targets/src/{target}.md"), doc).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::fs::write(
|
std::fs::write(
|
||||||
format!("targets/src/SUMMARY.md"),
|
format!("targets/src/SUMMARY.md"),
|
||||||
format!(
|
format!(
|
||||||
|
|
@ -89,39 +134,56 @@ fn main() {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
std::fs::write("targets/src/information.md", "\
|
std::fs::write(
|
||||||
|
"targets/src/information.md",
|
||||||
|
"\
|
||||||
# platform support generated
|
# platform support generated
|
||||||
|
|
||||||
This is an experiment of what target tier documentation could look like.
|
This is an experiment of what target tier documentation could look like.
|
||||||
|
|
||||||
See https://github.com/Nilstrieb/target-tier-docs-experiment for the source.
|
See https://github.com/Nilstrieb/target-tier-docs-experiment for the source.
|
||||||
").unwrap();
|
",
|
||||||
println!("generated some target docs :3");
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct TargetMaintainerTable {
|
struct TargetInfoTable {
|
||||||
target: Vec<TargetMaintainerEntry>,
|
target: Vec<TargetInfoPattern>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct TargetMaintainerEntry {
|
struct TargetInfoPattern {
|
||||||
pattern: String,
|
pattern: String,
|
||||||
|
#[serde(default)]
|
||||||
|
maintainers: Vec<String>,
|
||||||
tier: Option<u8>,
|
tier: Option<u8>,
|
||||||
requirements: Option<String>,
|
requirements: Option<String>,
|
||||||
testing: Option<String>,
|
testing: Option<String>,
|
||||||
building_the_target: Option<String>,
|
building_the_target: Option<String>,
|
||||||
cross_compilation: Option<String>,
|
cross_compilation: Option<String>,
|
||||||
building_rust_programs: Option<String>,
|
building_rust_programs: Option<String>,
|
||||||
maintainers: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_info(target: &str) -> TargetDocs {
|
struct TargetPatternEntry {
|
||||||
let file = include_str!("../target_info.toml");
|
info: TargetInfoPattern,
|
||||||
let table = toml::from_str::<TargetMaintainerTable>(file).unwrap();
|
used: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_target_info_patterns() -> Vec<TargetPatternEntry> {
|
||||||
|
let file = include_str!("../target_info.toml");
|
||||||
|
let table = toml::from_str::<TargetInfoTable>(file).unwrap();
|
||||||
|
|
||||||
|
table
|
||||||
|
.target
|
||||||
|
.into_iter()
|
||||||
|
.map(|info| TargetPatternEntry { info, used: false })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_info(info_patterns: &mut [TargetPatternEntry], target: &str) -> TargetDocs {
|
||||||
let mut tier = None;
|
let mut tier = None;
|
||||||
let mut maintainers = Vec::new();
|
let mut maintainers = Vec::new();
|
||||||
let mut requirements = None;
|
let mut requirements = None;
|
||||||
|
|
@ -130,13 +192,16 @@ fn target_info(target: &str) -> TargetDocs {
|
||||||
let mut cross_compilation = None;
|
let mut cross_compilation = None;
|
||||||
let mut building_rust_programs = None;
|
let mut building_rust_programs = None;
|
||||||
|
|
||||||
for target_pattern in table.target {
|
for target_pattern in info_patterns {
|
||||||
if glob_match::glob_match(&target_pattern.pattern, target) {
|
if glob_match::glob_match(&target_pattern.info.pattern, target) {
|
||||||
|
target_pattern.used = true;
|
||||||
|
let target_pattern = &target_pattern.info;
|
||||||
|
|
||||||
maintainers.extend_from_slice(&target_pattern.maintainers);
|
maintainers.extend_from_slice(&target_pattern.maintainers);
|
||||||
|
|
||||||
fn set_once<T>(
|
fn set_once<T: Clone>(
|
||||||
target: &str,
|
target: &str,
|
||||||
pattern_value: Option<T>,
|
pattern_value: &Option<T>,
|
||||||
to_insert: &mut Option<T>,
|
to_insert: &mut Option<T>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) {
|
) {
|
||||||
|
|
@ -144,23 +209,21 @@ fn target_info(target: &str) -> TargetDocs {
|
||||||
if to_insert.is_some() {
|
if to_insert.is_some() {
|
||||||
panic!("target {target} inherits a {name} from multiple patterns, create a more specific pattern and add it there");
|
panic!("target {target} inherits a {name} from multiple patterns, create a more specific pattern and add it there");
|
||||||
}
|
}
|
||||||
*to_insert = Some(pattern_value);
|
*to_insert = Some(pattern_value.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
{
|
{
|
||||||
set_once(target, target_pattern.tier, &mut tier, "tier");
|
set_once(target, &target_pattern.tier, &mut tier, "tier");
|
||||||
set_once(target, target_pattern.requirements, &mut requirements, "requirements");
|
set_once(target, &target_pattern.requirements, &mut requirements, "requirements");
|
||||||
set_once(target, target_pattern.testing, &mut testing, "testing");
|
set_once(target, &target_pattern.testing, &mut testing, "testing");
|
||||||
set_once(target, target_pattern.building_the_target, &mut building_the_target, "building_the_target");
|
set_once(target, &target_pattern.building_the_target, &mut building_the_target, "building_the_target");
|
||||||
set_once(target, target_pattern.cross_compilation, &mut cross_compilation, "cross_compilation");
|
set_once(target, &target_pattern.cross_compilation, &mut cross_compilation, "cross_compilation");
|
||||||
set_once(target, target_pattern.building_rust_programs, &mut building_rust_programs, "building_rust_programs");
|
set_once(target, &target_pattern.building_rust_programs, &mut building_rust_programs, "building_rust_programs");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we should give errors for unused patterns.
|
|
||||||
|
|
||||||
TargetDocs {
|
TargetDocs {
|
||||||
name: target.to_owned(),
|
name: target.to_owned(),
|
||||||
maintainers,
|
maintainers,
|
||||||
|
|
@ -173,3 +236,38 @@ fn target_info(target: &str) -> TargetDocs {
|
||||||
tier: tier.unwrap_or(0),
|
tier: tier.unwrap_or(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RustcTargetInfo {
|
||||||
|
target_cfgs: Vec<(String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rustc_target_info(rustc: &Path, target: &str) -> RustcTargetInfo {
|
||||||
|
let cfgs = rustc_stdout(rustc, &["--print", "cfg", "--target", target]);
|
||||||
|
let target_cfgs = cfgs
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
|
if line.starts_with("target_") {
|
||||||
|
let Some((key, value)) = line.split_once("=") else {
|
||||||
|
// For example `unix`
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some((key.to_owned(), value.to_owned()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
RustcTargetInfo { target_cfgs }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rustc_stdout(rustc: &Path, args: &[&str]) -> String {
|
||||||
|
let output = Command::new(rustc).args(args).output().unwrap();
|
||||||
|
if !output.status.success() {
|
||||||
|
panic!(
|
||||||
|
"rustc failed: {}, {}",
|
||||||
|
output.status,
|
||||||
|
String::from_utf8(output.stderr).unwrap_or_default()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
String::from_utf8(output.stdout).unwrap()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
# An actual implementation would probably split this over several files, however it is most conventient.
|
||||||
|
|
||||||
[[target]]
|
[[target]]
|
||||||
pattern = "*-apple-tvos"
|
pattern = "*-apple-tvos"
|
||||||
tier = 2
|
tier = 2
|
||||||
|
|
@ -46,4 +48,4 @@ Binary format of this platform is XCOFF. Archive file format is 'AIX big format'
|
||||||
"""
|
"""
|
||||||
testing = """
|
testing = """
|
||||||
This target supports running test suites natively, but it's not available to cross-compile and execute in emulator.
|
This target supports running test suites natively, but it's not available to cross-compile and execute in emulator.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue