Skip to content

Commit 12a32fe

Browse files
generate-copyright: Marks which deps are in the stdlib
1 parent 1cb8150 commit 12a32fe

File tree

2 files changed

+83
-72
lines changed

2 files changed

+83
-72
lines changed

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

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Gets metadata about a workspace from Cargo
22
3-
use std::collections::{BTreeMap, BTreeSet};
3+
use std::collections::BTreeMap;
44
use std::ffi::{OsStr, OsString};
55
use std::path::Path;
66

@@ -23,13 +23,18 @@ pub enum Error {
2323
RunningVendor,
2424
}
2525

26-
/// Describes one of our dependencies
26+
/// Uniquely describes a package on crates.io
2727
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
28-
pub struct Dependency {
28+
pub struct Package {
2929
/// The name of the package
3030
pub name: String,
3131
/// The version number
3232
pub version: String,
33+
}
34+
35+
/// Extra data about a package
36+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
37+
pub struct PackageMetadata {
3338
/// The license it is under
3439
pub license: String,
3540
/// The list of authors from the package metadata
@@ -40,20 +45,44 @@ pub struct Dependency {
4045
pub notices: BTreeMap<OsString, String>,
4146
}
4247

43-
/// Use `cargo` to get a list of dependencies and their license data.
48+
/// Use `cargo metadata` and `cargo vendor` to get a list of dependencies and their license data.
4449
///
4550
/// This will involve running `cargo vendor` into `${BUILD}/vendor` so we can
4651
/// grab the license files.
4752
///
4853
/// Any dependency with a path beginning with `root_path` is ignored, as we
4954
/// assume `reuse` has covered it already.
50-
pub fn get(
55+
pub fn get_metadata_and_notices(
5156
cargo: &Path,
5257
dest: &Path,
5358
root_path: &Path,
5459
manifest_paths: &[&Path],
55-
) -> Result<BTreeSet<Dependency>, Error> {
56-
let mut temp_set = BTreeSet::new();
60+
) -> Result<BTreeMap<Package, PackageMetadata>, Error> {
61+
let mut output = get_metadata(cargo, root_path, manifest_paths)?;
62+
63+
// Now do a cargo-vendor and grab everything
64+
let vendor_path = dest.join("vendor");
65+
println!("Vendoring deps into {}...", vendor_path.display());
66+
run_cargo_vendor(cargo, &vendor_path, manifest_paths)?;
67+
68+
// Now for each dependency we found, go and grab any important looking files
69+
for (package, metadata) in output.iter_mut() {
70+
load_important_files(package, metadata, &vendor_path)?;
71+
}
72+
73+
Ok(output)
74+
}
75+
76+
/// Use `cargo metadata` to get a list of dependencies and their license data.
77+
///
78+
/// Any dependency with a path beginning with `root_path` is ignored, as we
79+
/// assume `reuse` has covered it already.
80+
pub fn get_metadata(
81+
cargo: &Path,
82+
root_path: &Path,
83+
manifest_paths: &[&Path],
84+
) -> Result<BTreeMap<Package, PackageMetadata>, Error> {
85+
let mut output = BTreeMap::new();
5786
// Look at the metadata for each manifest
5887
for manifest_path in manifest_paths {
5988
if manifest_path.file_name() != Some(OsStr::new("Cargo.toml")) {
@@ -71,7 +100,7 @@ pub fn get(
71100
.and_then(|v| v.as_str())
72101
.map(Path::new)
73102
.ok_or_else(|| Error::MissingJsonElement("package.manifest_path"))?;
74-
if manifest_path.starts_with(&root_path) {
103+
if manifest_path.starts_with(root_path) {
75104
// it's an in-tree dependency and reuse covers it
76105
continue;
77106
}
@@ -93,28 +122,14 @@ pub fn get(
93122
.ok_or_else(|| Error::MissingJsonElement("package.authors"))?;
94123
let authors: Vec<String> =
95124
authors_list.iter().filter_map(|v| v.as_str()).map(|s| s.to_owned()).collect();
96-
temp_set.insert(Dependency {
97-
name: name.to_owned(),
98-
version: version.to_owned(),
99-
license: license.to_owned(),
100-
authors,
101-
notices: BTreeMap::new(),
102-
});
125+
let package = Package { name: name.to_owned(), version: version.to_owned() };
126+
output.insert(
127+
package.clone(),
128+
PackageMetadata { license: license.to_owned(), authors, notices: BTreeMap::new() },
129+
);
103130
}
104131
}
105132

106-
// Now do a cargo-vendor and grab everything
107-
let vendor_path = dest.join("vendor");
108-
println!("Vendoring deps into {}...", vendor_path.display());
109-
run_cargo_vendor(cargo, &vendor_path, manifest_paths)?;
110-
111-
// Now for each dependency we found, go and grab any important looking files
112-
let mut output = BTreeSet::new();
113-
for mut dep in temp_set {
114-
load_important_files(&mut dep, &vendor_path)?;
115-
output.insert(dep);
116-
}
117-
118133
Ok(output)
119134
}
120135

@@ -128,7 +143,7 @@ fn get_metadata_json(cargo: &Path, manifest_path: &Path) -> Result<serde_json::V
128143
.arg(manifest_path)
129144
.env("RUSTC_BOOTSTRAP", "1")
130145
.output()
131-
.map_err(|e| Error::LaunchingMetadata(e))?;
146+
.map_err(Error::LaunchingMetadata)?;
132147
if !metadata_output.status.success() {
133148
return Err(Error::GettingMetadata(
134149
String::from_utf8(metadata_output.stderr).expect("UTF-8 output from cargo"),
@@ -151,7 +166,7 @@ fn run_cargo_vendor(cargo: &Path, dest: &Path, manifest_paths: &[&Path]) -> Resu
151166
}
152167
vendor_command.arg(dest);
153168

154-
let vendor_status = vendor_command.status().map_err(|e| Error::LaunchingVendor(e))?;
169+
let vendor_status = vendor_command.status().map_err(Error::LaunchingVendor)?;
155170

156171
if !vendor_status.success() {
157172
return Err(Error::RunningVendor);
@@ -164,8 +179,12 @@ fn run_cargo_vendor(cargo: &Path, dest: &Path, manifest_paths: &[&Path]) -> Resu
164179
///
165180
/// Maybe one-day Cargo.toml will contain enough information that we don't need
166181
/// to do this manual scraping.
167-
fn load_important_files(dep: &mut Dependency, vendor_root: &Path) -> Result<(), Error> {
168-
let name_version = format!("{}-{}", dep.name, dep.version);
182+
fn load_important_files(
183+
package: &Package,
184+
dep: &mut PackageMetadata,
185+
vendor_root: &Path,
186+
) -> Result<(), Error> {
187+
let name_version = format!("{}-{}", package.name, package.version);
169188
println!("Scraping notices for {}...", name_version);
170189
let dep_vendor_path = vendor_root.join(name_version);
171190
for entry in std::fs::read_dir(dep_vendor_path)? {

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

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use anyhow::Error;
2-
use std::collections::BTreeSet;
2+
use std::collections::{BTreeMap, BTreeSet};
33
use std::io::Write;
44
use std::path::{Path, PathBuf};
55

66
mod cargo_metadata;
77

8-
static TOP_BOILERPLATE: &'static str = r##"
8+
static TOP_BOILERPLATE: &str = r##"
99
<!DOCTYPE html>
1010
<html>
1111
<head>
@@ -27,7 +27,7 @@ when building the Rust toolchain (including the Rust Standard Library).</p>
2727
</ul>
2828
"##;
2929

30-
static BOTTOM_BOILERPLATE: &'static str = r#"
30+
static BOTTOM_BOILERPLATE: &str = r#"
3131
</body>
3232
</html>
3333
"#;
@@ -53,9 +53,10 @@ fn main() -> Result<(), Error> {
5353
Path::new("./library/std/Cargo.toml"),
5454
];
5555
let collected_cargo_metadata =
56-
cargo_metadata::get(&cargo, &out_dir, &root_path, &workspace_paths)?;
56+
cargo_metadata::get_metadata_and_notices(&cargo, &out_dir, &root_path, &workspace_paths)?;
5757

58-
let mut license_set = BTreeSet::new();
58+
let stdlib_set =
59+
cargo_metadata::get_metadata(&cargo, &root_path, &[Path::new("./library/std/Cargo.toml")])?;
5960

6061
let mut buffer = Vec::new();
6162

@@ -65,13 +66,13 @@ fn main() -> Result<(), Error> {
6566
buffer,
6667
r#"<h2 id="in-tree-files">In-tree files</h2><p>The following licenses cover the in-tree source files that were used in this release:</p>"#
6768
)?;
68-
render_tree_recursive(&collected_tree_metadata.files, &mut buffer, 0, &mut license_set)?;
69+
render_tree_recursive(&collected_tree_metadata.files, &mut buffer)?;
6970

7071
writeln!(
7172
buffer,
7273
r#"<h2 id="out-of-tree-dependencies">Out-of-tree dependencies</h2><p>The following licenses cover the out-of-tree crates that were used in this release:</p>"#
7374
)?;
74-
render_deps(collected_cargo_metadata.iter(), &mut buffer, &mut license_set)?;
75+
render_deps(&collected_cargo_metadata, &stdlib_set, &mut buffer)?;
7576

7677
writeln!(buffer, "{}", BOTTOM_BOILERPLATE)?;
7778

@@ -82,46 +83,35 @@ fn main() -> Result<(), Error> {
8283

8384
/// Recursively draw the tree of files/folders we found on disk and their licenses, as
8485
/// markdown, into the given Vec.
85-
fn render_tree_recursive(
86-
node: &Node,
87-
buffer: &mut Vec<u8>,
88-
depth: usize,
89-
license_set: &mut BTreeSet<String>,
90-
) -> Result<(), Error> {
86+
fn render_tree_recursive(node: &Node, buffer: &mut Vec<u8>) -> Result<(), Error> {
9187
writeln!(buffer, r#"<div style="border:1px solid black; padding: 5px;">"#)?;
9288
match node {
9389
Node::Root { children } => {
9490
for child in children {
95-
render_tree_recursive(child, buffer, depth, license_set)?;
91+
render_tree_recursive(child, buffer)?;
9692
}
9793
}
9894
Node::Directory { name, children, license } => {
99-
render_tree_license(std::iter::once(name), license.iter(), buffer, license_set)?;
95+
render_tree_license(std::iter::once(name), license.iter(), buffer)?;
10096
if !children.is_empty() {
10197
writeln!(buffer, "<p><b>Exceptions:</b></p>")?;
10298
for child in children {
103-
render_tree_recursive(child, buffer, depth + 1, license_set)?;
99+
render_tree_recursive(child, buffer)?;
104100
}
105101
}
106102
}
107103
Node::CondensedDirectory { name, licenses } => {
108-
render_tree_license(std::iter::once(name), licenses.iter(), buffer, license_set)?;
104+
render_tree_license(std::iter::once(name), licenses.iter(), buffer)?;
109105
}
110106
Node::Group { files, directories, license } => {
111107
render_tree_license(
112108
directories.iter().chain(files.iter()),
113109
std::iter::once(license),
114110
buffer,
115-
license_set,
116111
)?;
117112
}
118113
Node::File { name, license } => {
119-
render_tree_license(
120-
std::iter::once(name),
121-
std::iter::once(license),
122-
buffer,
123-
license_set,
124-
)?;
114+
render_tree_license(std::iter::once(name), std::iter::once(license), buffer)?;
125115
}
126116
}
127117
writeln!(buffer, "</div>")?;
@@ -134,14 +124,12 @@ fn render_tree_license<'a>(
134124
names: impl Iterator<Item = &'a String>,
135125
licenses: impl Iterator<Item = &'a License>,
136126
buffer: &mut Vec<u8>,
137-
license_set: &mut BTreeSet<String>,
138127
) -> Result<(), Error> {
139128
// de-duplicate and sort SPDX and Copyright strings
140129
let mut spdxs = BTreeSet::new();
141130
let mut copyrights = BTreeSet::new();
142131
for license in licenses {
143132
spdxs.insert(&license.spdx);
144-
license_set.insert(license.spdx.clone());
145133
for copyright in &license.copyright {
146134
copyrights.insert(copyright);
147135
}
@@ -168,34 +156,38 @@ fn render_tree_license<'a>(
168156
}
169157

170158
/// Render a list of out-of-tree dependencies as markdown into the given Vec.
171-
fn render_deps<'a, 'b>(
172-
deps: impl Iterator<Item = &'a cargo_metadata::Dependency>,
173-
buffer: &'b mut Vec<u8>,
174-
license_set: &mut BTreeSet<String>,
159+
fn render_deps(
160+
all_deps: &BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>,
161+
stdlib_set: &BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>,
162+
buffer: &mut Vec<u8>,
175163
) -> Result<(), Error> {
176-
for dep in deps {
177-
let authors_list = if dep.authors.is_empty() {
164+
for (package, metadata) in all_deps {
165+
let authors_list = if metadata.authors.is_empty() {
178166
"None Specified".to_owned()
179167
} else {
180-
dep.authors.join(", ")
168+
metadata.authors.join(", ")
181169
};
182-
let url = format!("https://crates.io/crates/{}/{}", dep.name, dep.version);
170+
let url = format!("https://crates.io/crates/{}/{}", package.name, package.version);
183171
writeln!(buffer)?;
184172
writeln!(
185173
buffer,
186174
r#"<h3>📦 {name}-{version}</h3>"#,
187-
name = dep.name,
188-
version = dep.version,
175+
name = package.name,
176+
version = package.version,
189177
)?;
190178
writeln!(buffer, r#"<p><b>URL:</b> <a href="{url}">{url}</a></p>"#,)?;
179+
writeln!(
180+
buffer,
181+
"<p><b>In libstd:</b> {}</p>",
182+
if stdlib_set.contains_key(package) { "Yes" } else { "No" }
183+
)?;
191184
writeln!(buffer, "<p><b>Authors:</b> {}</p>", escape_html(&authors_list))?;
192-
writeln!(buffer, "<p><b>License:</b> {}</p>", escape_html(&dep.license))?;
193-
license_set.insert(dep.license.clone());
185+
writeln!(buffer, "<p><b>License:</b> {}</p>", escape_html(&metadata.license))?;
194186
writeln!(buffer, "<p><b>Notices:</b> ")?;
195-
if dep.notices.is_empty() {
187+
if metadata.notices.is_empty() {
196188
writeln!(buffer, "None")?;
197189
} else {
198-
for (name, contents) in &dep.notices {
190+
for (name, contents) in &metadata.notices {
199191
writeln!(
200192
buffer,
201193
"<details><summary><code>{}</code></summary>",
@@ -244,7 +236,7 @@ fn env_path(var: &str) -> Result<PathBuf, Error> {
244236

245237
/// Escapes any invalid HTML characters
246238
fn escape_html(input: &str) -> String {
247-
static MAPPING: [(char, &'static str); 3] = [('&', "&amp;"), ('<', "&lt;"), ('>', "&gt;")];
239+
static MAPPING: [(char, &str); 3] = [('&', "&amp;"), ('<', "&lt;"), ('>', "&gt;")];
248240
let mut output = input.to_owned();
249241
for (ch, s) in &MAPPING {
250242
output = output.replace(*ch, s);

0 commit comments

Comments
 (0)