Skip to content

Commit cf04752

Browse files
generate-copyright: Produce HTML, not Markdown
This format works better with large amounts of structured data.
1 parent 6320c3d commit cf04752

File tree

2 files changed

+69
-66
lines changed
  • src
    • bootstrap/src/core/build_steps
    • tools/generate-copyright/src

2 files changed

+69
-66
lines changed

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,10 @@ impl Step for GenerateCopyright {
209209
}
210210

211211
fn run(self, builder: &Builder<'_>) -> Self::Output {
212-
// let license_metadata = builder.ensure(CollectLicenseMetadata);
213-
let license_metadata = builder.out.join("license-metadata.json");
212+
let license_metadata = builder.ensure(CollectLicenseMetadata);
214213

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

218217
let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
219218
cmd.env("LICENSE_METADATA", &license_metadata);

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

Lines changed: 67 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@ use std::path::{Path, PathBuf};
55

66
mod cargo_metadata;
77

8+
static TOP_BOILERPLATE: &'static str = r##"
9+
<!DOCTYPE html>
10+
<html>
11+
<head>
12+
<meta charset="UTF-8">
13+
<title>Copyright notices for The Rust Toolchain</title>
14+
</head>
15+
<body>
16+
17+
<h1>Copyright notices for The Rust Toolchain</h1>
18+
19+
<p>This file describes the copyright and licensing information for the source
20+
code within The Rust Project git tree, and the third-party dependencies used
21+
when building the Rust toolchain (including the Rust Standard Library).</p>
22+
23+
<h2>Table of Contents</h2>
24+
<ul>
25+
<li><a href="#in-tree-files">In-tree files</a></li>
26+
<li><a href="#out-of-tree-dependencies">Out-of-tree dependencies</a></li>
27+
</ul>
28+
"##;
29+
30+
static BOTTOM_BOILERPLATE: &'static str = r#"
31+
</body>
32+
</html>
33+
"#;
34+
835
/// The entry point to the binary.
936
///
1037
/// You should probably let `bootstrap` execute this program instead of running it directly.
@@ -32,40 +59,22 @@ fn main() -> Result<(), Error> {
3259

3360
let mut buffer = Vec::new();
3461

35-
writeln!(buffer, "# COPYRIGHT for Rust")?;
36-
writeln!(buffer)?;
37-
writeln!(
38-
buffer,
39-
"This file describes the copyright and licensing information for the source code within The Rust Project git tree, and the third-party dependencies used when building the Rust toolchain (including the Rust Standard Library)"
40-
)?;
41-
writeln!(buffer)?;
42-
writeln!(buffer, "## Table of Contents")?;
43-
writeln!(buffer)?;
44-
writeln!(buffer, "* [In-tree files](#in-tree-files)")?;
45-
writeln!(buffer, "* [Out-of-tree files](#out-of-tree-files)")?;
46-
// writeln!(buffer, "* [License Texts](#license-texts)")?;
47-
writeln!(buffer)?;
48-
49-
writeln!(buffer, "## In-tree files")?;
50-
writeln!(buffer)?;
62+
writeln!(buffer, "{}", TOP_BOILERPLATE)?;
63+
5164
writeln!(
5265
buffer,
53-
"The following licenses cover the in-tree source files that were used in this release:"
66+
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>"#
5467
)?;
55-
writeln!(buffer)?;
5668
render_tree_recursive(&collected_tree_metadata.files, &mut buffer, 0, &mut license_set)?;
5769

58-
writeln!(buffer)?;
59-
60-
writeln!(buffer, "## Out-of-tree files")?;
61-
writeln!(buffer)?;
6270
writeln!(
6371
buffer,
64-
"The following licenses cover the out-of-tree crates that were used in this release:"
72+
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>"#
6573
)?;
66-
writeln!(buffer)?;
6774
render_deps(collected_cargo_metadata.iter(), &mut buffer, &mut license_set)?;
6875

76+
writeln!(buffer, "{}", BOTTOM_BOILERPLATE)?;
77+
6978
std::fs::write(&dest_file, &buffer)?;
7079

7180
Ok(())
@@ -79,43 +88,27 @@ fn render_tree_recursive(
7988
depth: usize,
8089
license_set: &mut BTreeSet<String>,
8190
) -> Result<(), Error> {
82-
let prefix = std::iter::repeat("> ").take(depth + 1).collect::<String>();
83-
91+
writeln!(buffer, r#"<div style="border:1px solid black; padding: 5px;">"#)?;
8492
match node {
8593
Node::Root { children } => {
8694
for child in children {
8795
render_tree_recursive(child, buffer, depth, license_set)?;
8896
}
8997
}
9098
Node::Directory { name, children, license } => {
91-
render_tree_license(
92-
&prefix,
93-
std::iter::once(name),
94-
license.iter(),
95-
buffer,
96-
license_set,
97-
)?;
99+
render_tree_license(std::iter::once(name), license.iter(), buffer, license_set)?;
98100
if !children.is_empty() {
99-
writeln!(buffer, "{prefix}")?;
100-
writeln!(buffer, "{prefix}*Exceptions:*")?;
101+
writeln!(buffer, "<p><b>Exceptions:</b></p>")?;
101102
for child in children {
102-
writeln!(buffer, "{prefix}")?;
103103
render_tree_recursive(child, buffer, depth + 1, license_set)?;
104104
}
105105
}
106106
}
107107
Node::CondensedDirectory { name, licenses } => {
108-
render_tree_license(
109-
&prefix,
110-
std::iter::once(name),
111-
licenses.iter(),
112-
buffer,
113-
license_set,
114-
)?;
108+
render_tree_license(std::iter::once(name), licenses.iter(), buffer, license_set)?;
115109
}
116110
Node::Group { files, directories, license } => {
117111
render_tree_license(
118-
&prefix,
119112
directories.iter().chain(files.iter()),
120113
std::iter::once(license),
121114
buffer,
@@ -124,26 +117,26 @@ fn render_tree_recursive(
124117
}
125118
Node::File { name, license } => {
126119
render_tree_license(
127-
&prefix,
128120
std::iter::once(name),
129121
std::iter::once(license),
130122
buffer,
131123
license_set,
132124
)?;
133125
}
134126
}
127+
writeln!(buffer, "</div>")?;
135128

136129
Ok(())
137130
}
138131

139132
/// Draw a series of sibling files/folders, as markdown, into the given Vec.
140133
fn render_tree_license<'a>(
141-
prefix: &str,
142134
names: impl Iterator<Item = &'a String>,
143135
licenses: impl Iterator<Item = &'a License>,
144136
buffer: &mut Vec<u8>,
145137
license_set: &mut BTreeSet<String>,
146138
) -> Result<(), Error> {
139+
// de-duplicate and sort SPDX and Copyright strings
147140
let mut spdxs = BTreeSet::new();
148141
let mut copyrights = BTreeSet::new();
149142
for license in licenses {
@@ -154,15 +147,21 @@ fn render_tree_license<'a>(
154147
}
155148
}
156149

150+
writeln!(buffer, "<p><b>File/Directory:</b> ")?;
157151
for name in names {
158-
writeln!(buffer, "{prefix}**`{name}`** ")?;
152+
writeln!(buffer, "<code>{name}</code>")?;
159153
}
160-
for spdx in spdxs.iter() {
161-
writeln!(buffer, "{prefix}License: `{spdx}` ")?;
154+
writeln!(buffer, "</p>")?;
155+
156+
writeln!(buffer, "<p><b>License:</b> ")?;
157+
for (i, spdx) in spdxs.iter().enumerate() {
158+
let suffix = if i == spdxs.len() - 1 { "" } else { ", " };
159+
writeln!(buffer, "{spdx}{suffix}")?;
162160
}
163-
for (i, copyright) in copyrights.iter().enumerate() {
164-
let suffix = if i == copyrights.len() - 1 { "" } else { " " };
165-
writeln!(buffer, "{prefix}Copyright: {copyright}{suffix}")?;
161+
writeln!(buffer, "</p>")?;
162+
163+
for copyright in copyrights.iter() {
164+
writeln!(buffer, "<p><b>Copyright:</b> {copyright}</p>")?;
166165
}
167166

168167
Ok(())
@@ -175,30 +174,25 @@ fn render_deps<'a, 'b>(
175174
license_set: &mut BTreeSet<String>,
176175
) -> Result<(), Error> {
177176
for dep in deps {
178-
let authors_list = dep.authors.join(", ").replace("<", "\\<").replace(">", "\\>");
177+
let authors_list = dep.authors.join(", ");
179178
let url = format!("https://crates.io/crates/{}/{}", dep.name, dep.version);
180179
writeln!(buffer)?;
181180
writeln!(
182181
buffer,
183-
"### [{name} {version}]({url})",
182+
r#"<h3><a href="{url}">{name} {version}</a></h3>"#,
184183
name = dep.name,
185184
version = dep.version,
186185
url = url,
187186
)?;
188-
writeln!(buffer)?;
189-
writeln!(buffer, "* Authors: {}", authors_list)?;
190-
writeln!(buffer, "* License: {}", dep.license)?;
187+
writeln!(buffer, "<h4>Authors</h4><p>{}</p>", escape_html(&authors_list))?;
188+
writeln!(buffer, "<h4>License</h4><p>{}</p>", escape_html(&dep.license))?;
191189
license_set.insert(dep.license.clone());
192190
for (name, contents) in &dep.notices {
193191
writeln!(buffer)?;
194-
writeln!(buffer, "#### {}", name.to_string_lossy())?;
192+
writeln!(buffer, "<h4>{}</h3>", name.to_string_lossy())?;
195193
writeln!(buffer)?;
196194
writeln!(buffer, "<details><summary>Click to expand</summary>")?;
197-
writeln!(buffer)?;
198-
writeln!(buffer, "```")?;
199-
writeln!(buffer, "{}", contents)?;
200-
writeln!(buffer, "```")?;
201-
writeln!(buffer)?;
195+
writeln!(buffer, "<pre>\n{}\n</pre>", contents)?;
202196
writeln!(buffer, "</details>")?;
203197
}
204198
}
@@ -236,3 +230,13 @@ fn env_path(var: &str) -> Result<PathBuf, Error> {
236230
anyhow::bail!("missing environment variable {var}")
237231
}
238232
}
233+
234+
/// Escapes any invalid HTML characters
235+
fn escape_html(input: &str) -> String {
236+
static MAPPING: [(char, &'static str); 3] = [('&', "&amp;"), ('<', "&lt;"), ('>', "&gt;")];
237+
let mut output = input.to_owned();
238+
for (ch, s) in &MAPPING {
239+
output = output.replace(*ch, s);
240+
}
241+
output
242+
}

0 commit comments

Comments
 (0)