initial prototype of the tool to generate copyright notices

This commit is contained in:
Pietro Albini 2022-11-15 11:27:19 +01:00
parent 13efb20846
commit 4af7de13d2
No known key found for this signature in database
GPG key ID: CD76B35F7734769E
7 changed files with 147 additions and 0 deletions

View file

@ -1498,6 +1498,15 @@ dependencies = [
"termcolor",
]
[[package]]
name = "generate-copyright"
version = "0.1.0"
dependencies = [
"anyhow",
"serde",
"serde_json",
]
[[package]]
name = "generic-array"
version = "0.14.4"

View file

@ -40,6 +40,7 @@ members = [
"src/tools/replace-version-placeholder",
"src/tools/lld-wrapper",
"src/tools/collect-license-metadata",
"src/tools/generate-copyright",
]
exclude = [

View file

@ -754,6 +754,7 @@ macro_rules! describe {
run::ReplaceVersionPlaceholder,
run::Miri,
run::CollectLicenseMetadata,
run::GenerateCopyright,
),
// These commands either don't use paths, or they're special-cased in Build::build()
Kind::Clean | Kind::Format | Kind::Setup => vec![],

View file

@ -222,3 +222,33 @@ fn run(self, builder: &Builder<'_>) -> Self::Output {
dest
}
}
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct GenerateCopyright;
impl Step for GenerateCopyright {
type Output = PathBuf;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/generate-copyright")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(GenerateCopyright);
}
fn run(self, builder: &Builder<'_>) -> Self::Output {
let license_metadata = builder.ensure(CollectLicenseMetadata);
// Temporary location, it will be moved to the proper one once it's accurate.
let dest = builder.out.join("COPYRIGHT.md");
let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
cmd.env("LICENSE_METADATA", &license_metadata);
cmd.env("DEST", &dest);
builder.run(&mut cmd);
dest
}
}

View file

@ -381,6 +381,7 @@ fn run(self, builder: &Builder<'_>) -> PathBuf {
BumpStage0, "src/tools/bump-stage0", "bump-stage0";
ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
);
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]

View file

@ -0,0 +1,11 @@
[package]
name = "generate-copyright"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.65"
serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.85"

View file

@ -0,0 +1,94 @@
use anyhow::Error;
use std::io::Write;
use std::path::PathBuf;
fn main() -> Result<(), Error> {
let dest = env_path("DEST")?;
let license_metadata = env_path("LICENSE_METADATA")?;
let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
let mut buffer = Vec::new();
render_recursive(&metadata.files, &mut buffer, 0)?;
std::fs::write(&dest, &buffer)?;
Ok(())
}
fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(), Error> {
let prefix = std::iter::repeat("> ").take(depth + 1).collect::<String>();
match node {
Node::Root { childs } => {
for child in childs {
render_recursive(child, buffer, depth)?;
}
}
Node::Directory { name, childs, license } => {
render_license(&prefix, std::iter::once(name), license, buffer)?;
if !childs.is_empty() {
writeln!(buffer, "{prefix}")?;
writeln!(buffer, "{prefix}*Exceptions:*")?;
for child in childs {
writeln!(buffer, "{prefix}")?;
render_recursive(child, buffer, depth + 1)?;
}
}
}
Node::FileGroup { names, license } => {
render_license(&prefix, names.iter(), license, buffer)?;
}
Node::File { name, license } => {
render_license(&prefix, std::iter::once(name), license, buffer)?;
}
}
Ok(())
}
fn render_license<'a>(
prefix: &str,
names: impl Iterator<Item = &'a String>,
license: &License,
buffer: &mut Vec<u8>,
) -> Result<(), Error> {
for name in names {
writeln!(buffer, "{prefix}**`{name}`** ")?;
}
writeln!(buffer, "{prefix}License: `{}` ", license.spdx)?;
for (i, copyright) in license.copyright.iter().enumerate() {
let suffix = if i == license.copyright.len() - 1 { "" } else { " " };
writeln!(buffer, "{prefix}Copyright: {copyright}{suffix}")?;
}
Ok(())
}
#[derive(serde::Deserialize)]
struct Metadata {
files: Node,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "kebab-case", tag = "type")]
pub(crate) enum Node {
Root { childs: Vec<Node> },
Directory { name: String, childs: Vec<Node>, license: License },
File { name: String, license: License },
FileGroup { names: Vec<String>, license: License },
}
#[derive(serde::Deserialize)]
struct License {
spdx: String,
copyright: Vec<String>,
}
fn env_path(var: &str) -> Result<PathBuf, Error> {
if let Some(var) = std::env::var_os(var) {
Ok(var.into())
} else {
anyhow::bail!("missing environment variable {var}")
}
}