Skip to content

Commit 4af7de1

Browse files
committed
initial prototype of the tool to generate copyright notices
1 parent 13efb20 commit 4af7de1

File tree

7 files changed

+147
-0
lines changed

7 files changed

+147
-0
lines changed

Cargo.lock

+9
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,15 @@ dependencies = [
14981498
"termcolor",
14991499
]
15001500

1501+
[[package]]
1502+
name = "generate-copyright"
1503+
version = "0.1.0"
1504+
dependencies = [
1505+
"anyhow",
1506+
"serde",
1507+
"serde_json",
1508+
]
1509+
15011510
[[package]]
15021511
name = "generic-array"
15031512
version = "0.14.4"

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ members = [
4040
"src/tools/replace-version-placeholder",
4141
"src/tools/lld-wrapper",
4242
"src/tools/collect-license-metadata",
43+
"src/tools/generate-copyright",
4344
]
4445

4546
exclude = [

src/bootstrap/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ impl<'a> Builder<'a> {
754754
run::ReplaceVersionPlaceholder,
755755
run::Miri,
756756
run::CollectLicenseMetadata,
757+
run::GenerateCopyright,
757758
),
758759
// These commands either don't use paths, or they're special-cased in Build::build()
759760
Kind::Clean | Kind::Format | Kind::Setup => vec![],

src/bootstrap/run.rs

+30
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,33 @@ impl Step for CollectLicenseMetadata {
222222
dest
223223
}
224224
}
225+
226+
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
227+
pub struct GenerateCopyright;
228+
229+
impl Step for GenerateCopyright {
230+
type Output = PathBuf;
231+
const ONLY_HOSTS: bool = true;
232+
233+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
234+
run.path("src/tools/generate-copyright")
235+
}
236+
237+
fn make_run(run: RunConfig<'_>) {
238+
run.builder.ensure(GenerateCopyright);
239+
}
240+
241+
fn run(self, builder: &Builder<'_>) -> Self::Output {
242+
let license_metadata = builder.ensure(CollectLicenseMetadata);
243+
244+
// Temporary location, it will be moved to the proper one once it's accurate.
245+
let dest = builder.out.join("COPYRIGHT.md");
246+
247+
let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
248+
cmd.env("LICENSE_METADATA", &license_metadata);
249+
cmd.env("DEST", &dest);
250+
builder.run(&mut cmd);
251+
252+
dest
253+
}
254+
}

src/bootstrap/tool.rs

+1
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ bootstrap_tool!(
381381
BumpStage0, "src/tools/bump-stage0", "bump-stage0";
382382
ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
383383
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
384+
GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
384385
);
385386

386387
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "generate-copyright"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
anyhow = "1.0.65"
10+
serde = { version = "1.0.147", features = ["derive"] }
11+
serde_json = "1.0.85"
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use anyhow::Error;
2+
use std::io::Write;
3+
use std::path::PathBuf;
4+
5+
fn main() -> Result<(), Error> {
6+
let dest = env_path("DEST")?;
7+
let license_metadata = env_path("LICENSE_METADATA")?;
8+
9+
let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
10+
11+
let mut buffer = Vec::new();
12+
render_recursive(&metadata.files, &mut buffer, 0)?;
13+
14+
std::fs::write(&dest, &buffer)?;
15+
16+
Ok(())
17+
}
18+
19+
fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(), Error> {
20+
let prefix = std::iter::repeat("> ").take(depth + 1).collect::<String>();
21+
22+
match node {
23+
Node::Root { childs } => {
24+
for child in childs {
25+
render_recursive(child, buffer, depth)?;
26+
}
27+
}
28+
Node::Directory { name, childs, license } => {
29+
render_license(&prefix, std::iter::once(name), license, buffer)?;
30+
if !childs.is_empty() {
31+
writeln!(buffer, "{prefix}")?;
32+
writeln!(buffer, "{prefix}*Exceptions:*")?;
33+
for child in childs {
34+
writeln!(buffer, "{prefix}")?;
35+
render_recursive(child, buffer, depth + 1)?;
36+
}
37+
}
38+
}
39+
Node::FileGroup { names, license } => {
40+
render_license(&prefix, names.iter(), license, buffer)?;
41+
}
42+
Node::File { name, license } => {
43+
render_license(&prefix, std::iter::once(name), license, buffer)?;
44+
}
45+
}
46+
47+
Ok(())
48+
}
49+
50+
fn render_license<'a>(
51+
prefix: &str,
52+
names: impl Iterator<Item = &'a String>,
53+
license: &License,
54+
buffer: &mut Vec<u8>,
55+
) -> Result<(), Error> {
56+
for name in names {
57+
writeln!(buffer, "{prefix}**`{name}`** ")?;
58+
}
59+
writeln!(buffer, "{prefix}License: `{}` ", license.spdx)?;
60+
for (i, copyright) in license.copyright.iter().enumerate() {
61+
let suffix = if i == license.copyright.len() - 1 { "" } else { " " };
62+
writeln!(buffer, "{prefix}Copyright: {copyright}{suffix}")?;
63+
}
64+
65+
Ok(())
66+
}
67+
68+
#[derive(serde::Deserialize)]
69+
struct Metadata {
70+
files: Node,
71+
}
72+
73+
#[derive(serde::Deserialize)]
74+
#[serde(rename_all = "kebab-case", tag = "type")]
75+
pub(crate) enum Node {
76+
Root { childs: Vec<Node> },
77+
Directory { name: String, childs: Vec<Node>, license: License },
78+
File { name: String, license: License },
79+
FileGroup { names: Vec<String>, license: License },
80+
}
81+
82+
#[derive(serde::Deserialize)]
83+
struct License {
84+
spdx: String,
85+
copyright: Vec<String>,
86+
}
87+
88+
fn env_path(var: &str) -> Result<PathBuf, Error> {
89+
if let Some(var) = std::env::var_os(var) {
90+
Ok(var.into())
91+
} else {
92+
anyhow::bail!("missing environment variable {var}")
93+
}
94+
}

0 commit comments

Comments
 (0)