Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ef433a3

Browse files
authoredNov 23, 2024
Rollup merge of #133208 - ferrocene:split-copyright-html, r=Kobzol
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 COPYRIGHT.html and COPYRIGHT-library.html. The license-metadata.json file is also now in the tree. We need a CI tool to check that it's correct. r? kobzol Remaining steps: * [ ] Teach CI to double-check the license-metadata.json file is correct * [ ] Add the COPYRIGHT.html and COPYRIGHT-license.html to the releases
2 parents bb92131 + 2932833 commit ef433a3

File tree

4 files changed

+181
-29
lines changed

4 files changed

+181
-29
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

+2-3
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,20 @@ pub struct PackageMetadata {
4545

4646
/// Use `cargo metadata` and `cargo vendor` to get a list of dependencies and their license data.
4747
///
48-
/// This will involve running `cargo vendor` into `${BUILD}/vendor` so we can
48+
/// This will involve running `cargo vendor` into `vendor_path` so we can
4949
/// grab the license files.
5050
///
5151
/// Any dependency with a path beginning with `root_path` is ignored, as we
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)
Failed to load comments.