Skip to content

Commit 9dfc682

Browse files
generate-copyright: Now generates a library file too.
We only run reuse once, so the output has to be filtered to find only the files that are relevant to the library tree. Outputs build/COPYRIGHT.html and build/COPYRIGHT-library.html.
1 parent e6c1e14 commit 9dfc682

File tree

4 files changed

+180
-28
lines changed

4 files changed

+180
-28
lines changed

src/bootstrap/src/core/build_steps/run.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,13 @@ impl Step for GenerateCopyright {
211211
fn run(self, builder: &Builder<'_>) -> Self::Output {
212212
let license_metadata = builder.ensure(CollectLicenseMetadata);
213213

214-
// Temporary location, it will be moved to the proper one once it's accurate.
215214
let dest = builder.out.join("COPYRIGHT.html");
215+
let dest_libstd = builder.out.join("COPYRIGHT-library.html");
216216

217217
let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
218218
cmd.env("LICENSE_METADATA", &license_metadata);
219219
cmd.env("DEST", &dest);
220+
cmd.env("DEST_LIBSTD", &dest_libstd);
220221
cmd.env("OUT_DIR", &builder.out);
221222
cmd.env("CARGO", &builder.initial_cargo);
222223
cmd.run(builder);

src/tools/generate-copyright/src/cargo_metadata.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,13 @@ pub struct PackageMetadata {
5252
/// assume `reuse` has covered it already.
5353
pub fn get_metadata_and_notices(
5454
cargo: &Path,
55-
dest: &Path,
55+
vendor_path: &Path,
5656
root_path: &Path,
5757
manifest_paths: &[&Path],
5858
) -> Result<BTreeMap<Package, PackageMetadata>, Error> {
5959
let mut output = get_metadata(cargo, root_path, manifest_paths)?;
6060

6161
// Now do a cargo-vendor and grab everything
62-
let vendor_path = dest.join("vendor");
6362
println!("Vendoring deps into {}...", vendor_path.display());
6463
run_cargo_vendor(cargo, &vendor_path, manifest_paths)?;
6564

src/tools/generate-copyright/src/main.rs

+124-25
Original file line numberDiff line numberDiff line change
@@ -6,63 +6,96 @@ use rinja::Template;
66

77
mod cargo_metadata;
88

9-
#[derive(Template)]
10-
#[template(path = "COPYRIGHT.html")]
11-
struct CopyrightTemplate {
12-
in_tree: Node,
13-
dependencies: BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>,
14-
}
15-
169
/// The entry point to the binary.
1710
///
1811
/// You should probably let `bootstrap` execute this program instead of running it directly.
1912
///
2013
/// Run `x.py run generate-copyright`
2114
fn main() -> Result<(), Error> {
2215
let dest_file = env_path("DEST")?;
16+
let libstd_dest_file = env_path("DEST_LIBSTD")?;
2317
let out_dir = env_path("OUT_DIR")?;
2418
let cargo = env_path("CARGO")?;
2519
let license_metadata = env_path("LICENSE_METADATA")?;
2620

27-
let collected_tree_metadata: Metadata =
28-
serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
29-
3021
let root_path = std::path::absolute(".")?;
31-
let workspace_paths = [
32-
Path::new("./Cargo.toml"),
33-
Path::new("./src/tools/cargo/Cargo.toml"),
34-
Path::new("./library/Cargo.toml"),
35-
];
36-
let mut collected_cargo_metadata =
37-
cargo_metadata::get_metadata_and_notices(&cargo, &out_dir, &root_path, &workspace_paths)?;
3822

39-
let stdlib_set =
40-
cargo_metadata::get_metadata(&cargo, &root_path, &[Path::new("./library/std/Cargo.toml")])?;
23+
// Scan Cargo dependencies
24+
let mut collected_cargo_metadata =
25+
cargo_metadata::get_metadata_and_notices(&cargo, &out_dir.join("vendor"), &root_path, &[
26+
Path::new("./Cargo.toml"),
27+
Path::new("./src/tools/cargo/Cargo.toml"),
28+
Path::new("./library/Cargo.toml"),
29+
])?;
30+
31+
let library_collected_cargo_metadata = cargo_metadata::get_metadata_and_notices(
32+
&cargo,
33+
&out_dir.join("library-vendor"),
34+
&root_path,
35+
&[Path::new("./library/Cargo.toml")],
36+
)?;
4137

4238
for (key, value) in collected_cargo_metadata.iter_mut() {
43-
value.is_in_libstd = Some(stdlib_set.contains_key(key));
39+
value.is_in_libstd = Some(library_collected_cargo_metadata.contains_key(key));
4440
}
4541

42+
// Load JSON output by reuse
43+
let collected_tree_metadata: Metadata =
44+
serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
45+
46+
// Find libstd sub-set
47+
let library_collected_tree_metadata = Metadata {
48+
files: collected_tree_metadata
49+
.files
50+
.trim_clone(&Path::new("./library"), &Path::new("."))
51+
.unwrap(),
52+
};
53+
54+
// Output main file
4655
let template = CopyrightTemplate {
4756
in_tree: collected_tree_metadata.files,
4857
dependencies: collected_cargo_metadata,
4958
};
50-
5159
let output = template.render()?;
52-
5360
std::fs::write(&dest_file, output)?;
5461

62+
// Output libstd subset file
63+
let template = LibraryCopyrightTemplate {
64+
in_tree: library_collected_tree_metadata.files,
65+
dependencies: library_collected_cargo_metadata,
66+
};
67+
let output = template.render()?;
68+
std::fs::write(&libstd_dest_file, output)?;
69+
5570
Ok(())
5671
}
5772

73+
/// The HTML template for the toolchain copyright file
74+
#[derive(Template)]
75+
#[template(path = "COPYRIGHT.html")]
76+
struct CopyrightTemplate {
77+
in_tree: Node,
78+
dependencies: BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>,
79+
}
80+
81+
/// The HTML template for the library copyright file
82+
#[derive(Template)]
83+
#[template(path = "COPYRIGHT-library.html")]
84+
struct LibraryCopyrightTemplate {
85+
in_tree: Node,
86+
dependencies: BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>,
87+
}
88+
5889
/// Describes a tree of metadata for our filesystem tree
59-
#[derive(serde::Deserialize)]
90+
///
91+
/// Must match the JSON emitted by the `CollectLicenseMetadata` bootstrap tool.
92+
#[derive(serde::Deserialize, Clone, Debug, PartialEq, Eq)]
6093
struct Metadata {
6194
files: Node,
6295
}
6396

6497
/// Describes one node in our metadata tree
65-
#[derive(serde::Deserialize, rinja::Template)]
98+
#[derive(serde::Deserialize, rinja::Template, Clone, Debug, PartialEq, Eq)]
6699
#[serde(rename_all = "kebab-case", tag = "type")]
67100
#[template(path = "Node.html")]
68101
pub(crate) enum Node {
@@ -72,8 +105,74 @@ pub(crate) enum Node {
72105
Group { files: Vec<String>, directories: Vec<String>, license: License },
73106
}
74107

108+
impl Node {
109+
/// Clone, this node, but only if the path to the item is within the match path
110+
fn trim_clone(&self, match_path: &Path, parent_path: &Path) -> Option<Node> {
111+
match self {
112+
Node::Root { children } => {
113+
let mut filtered_children = Vec::new();
114+
for node in children {
115+
if let Some(child_node) = node.trim_clone(match_path, parent_path) {
116+
filtered_children.push(child_node);
117+
}
118+
}
119+
if filtered_children.is_empty() {
120+
None
121+
} else {
122+
Some(Node::Root { children: filtered_children })
123+
}
124+
}
125+
Node::Directory { name, children, license } => {
126+
let child_name = parent_path.join(name);
127+
if !(child_name.starts_with(match_path) || match_path.starts_with(&child_name)) {
128+
return None;
129+
}
130+
let mut filtered_children = Vec::new();
131+
for node in children {
132+
if let Some(child_node) = node.trim_clone(match_path, &child_name) {
133+
filtered_children.push(child_node);
134+
}
135+
}
136+
Some(Node::Directory {
137+
name: name.clone(),
138+
children: filtered_children,
139+
license: license.clone(),
140+
})
141+
}
142+
Node::File { name, license } => {
143+
let child_name = parent_path.join(name);
144+
if !(child_name.starts_with(match_path) || match_path.starts_with(&child_name)) {
145+
return None;
146+
}
147+
Some(Node::File { name: name.clone(), license: license.clone() })
148+
}
149+
Node::Group { files, directories, license } => {
150+
let mut filtered_child_files = Vec::new();
151+
for child in files {
152+
let child_name = parent_path.join(child);
153+
if child_name.starts_with(match_path) || match_path.starts_with(&child_name) {
154+
filtered_child_files.push(child.clone());
155+
}
156+
}
157+
let mut filtered_child_dirs = Vec::new();
158+
for child in directories {
159+
let child_name = parent_path.join(child);
160+
if child_name.starts_with(match_path) || match_path.starts_with(&child_name) {
161+
filtered_child_dirs.push(child.clone());
162+
}
163+
}
164+
Some(Node::Group {
165+
files: filtered_child_files,
166+
directories: filtered_child_dirs,
167+
license: license.clone(),
168+
})
169+
}
170+
}
171+
}
172+
}
173+
75174
/// A License has an SPDX license name and a list of copyright holders.
76-
#[derive(serde::Deserialize)]
175+
#[derive(serde::Deserialize, Clone, Debug, PartialEq, Eq)]
77176
struct License {
78177
spdx: String,
79178
copyright: Vec<String>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Copyright notices for The Rust Standard Library</title>
6+
</head>
7+
<body>
8+
9+
<h1>Copyright notices for The Rust Standard Library</h1>
10+
11+
<p>This file describes the copyright and licensing information for the Rust
12+
Standard Library source code within The Rust Project git tree, and the
13+
third-party dependencies used when building the Rust Standard Library.</p>
14+
15+
<h2>Table of Contents</h2>
16+
<ul>
17+
<li><a href="#in-tree-files">In-tree files</a></li>
18+
<li><a href="#out-of-tree-dependencies">Out-of-tree dependencies</a></li>
19+
</ul>
20+
21+
<h2 id="in-tree-files">In-tree files</h2>
22+
23+
<p>The following licenses cover the in-tree source files that were used in this
24+
release:</p>
25+
26+
{{ in_tree|safe }}
27+
28+
<h2 id="out-of-tree-dependencies">Out-of-tree dependencies</h2>
29+
30+
<p>The following licenses cover the out-of-tree crates that were used in the
31+
Rust Standard Library in this release:</p>
32+
33+
{% for (key, value) in dependencies %}
34+
<h3>📦 {{key.name}}-{{key.version}}</h3>
35+
<p><b>URL:</b> <a href="https://crates.io/crates/{{ key.name }}/{{ key.version }}">https://crates.io/crates/{{ key.name }}/{{ key.version }}</a></p>
36+
<p><b>Authors:</b> {{ value.authors|join(", ") }}</p>
37+
<p><b>License:</b> {{ value.license }}</p>
38+
{% let len = value.notices.len() %}
39+
{% if len > 0 %}
40+
<p><b>Notices:</b>
41+
{% for (notice_name, notice_text) in value.notices %}
42+
<details>
43+
<summary><code>{{ notice_name }}</code></summary>
44+
<pre>
45+
{{ notice_text }}
46+
</pre>
47+
</details>
48+
{% endfor %}
49+
</p>
50+
{% endif %}
51+
{% endfor %}
52+
</body>
53+
</html>

0 commit comments

Comments
 (0)