Skip to content

Commit 7e5b1b4

Browse files
xtask: Add file header check to xtask fmt
This will be used to verify that source files (just *.rs for now) all have an SPDX license header. In non-check mode, the license header will be added. For now, in check mode an error is shown but it's not treated as fatal. Once all files have been fixed, the error will be made fatal.
1 parent 5dd3ef6 commit 7e5b1b4

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

xtask/src/main.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,27 @@ fn run_host_tests(test_opt: &TestOpt) -> Result<()> {
233233
fn run_fmt_project(fmt_opt: &FmtOpt) -> Result<()> {
234234
let mut any_errors = false;
235235

236+
{
237+
eprintln!("Formatting: file headers");
238+
239+
match format_file_headers(fmt_opt) {
240+
Ok(_) => {
241+
eprintln!("✅ expected file headers present")
242+
}
243+
Err(e) => {
244+
if fmt_opt.check {
245+
eprintln!("❌ {e:#?}");
246+
// TODO: Make this a hard error after all the headers have
247+
// been added.
248+
eprintln!("...ignoring for now");
249+
} else {
250+
eprintln!("❌ rust formatter failed: {e:#?}");
251+
any_errors = true;
252+
}
253+
}
254+
}
255+
}
256+
236257
// fmt rust
237258
{
238259
eprintln!("Formatting: rust");
@@ -323,6 +344,67 @@ fn run_fmt_project(fmt_opt: &FmtOpt) -> Result<()> {
323344
Ok(())
324345
}
325346

347+
/// Check that SPDX file headers are present.
348+
///
349+
/// If not in check mode, add any missing headers.
350+
fn format_file_headers(fmt_opt: &FmtOpt) -> Result<()> {
351+
const EXPECTED_HEADER: &str = "// SPDX-License-Identifier: MIT OR Apache-2.0\n\n";
352+
353+
// Path prefixes that should not be checked/formatted.
354+
const EXCLUDE_PATH_PREFIXES: &[&str] = &[
355+
// A user copying the template or uefi-std-example does not need to use
356+
// our license.
357+
"template/src/main.rs",
358+
"uefi-std-example/src/main.rs",
359+
// This directory contains short code snippets used in `trybuild` tests,
360+
// no license needed.
361+
"uefi-macros/tests/ui/",
362+
];
363+
364+
// Recursively get Rust files
365+
let mut cmd = Command::new("git");
366+
cmd.args(["ls-files", "*.rs"]);
367+
let output = cmd.output()?;
368+
if !output.status.success() {
369+
bail!("command failed: {}", output.status);
370+
}
371+
let mut paths: Vec<&str> = std::str::from_utf8(&output.stdout)?.lines().collect();
372+
373+
// Filter out excluded paths.
374+
paths.retain(|path| {
375+
!EXCLUDE_PATH_PREFIXES
376+
.iter()
377+
.any(|prefix| path.starts_with(prefix))
378+
});
379+
380+
// Paths that are missing the file header (only used in check mode).
381+
let mut missing = Vec::new();
382+
383+
// Format or check each path.
384+
for path in paths {
385+
let text = fs_err::read_to_string(path)?;
386+
if text.starts_with(EXPECTED_HEADER) {
387+
// File header is present, nothing to do.
388+
continue;
389+
}
390+
391+
if fmt_opt.check {
392+
// Add to the list of paths missing file headers.
393+
missing.push(path);
394+
} else {
395+
// Rewrite the file to prepend the header.
396+
let text = EXPECTED_HEADER.to_owned() + &text;
397+
fs_err::write(path, text)?;
398+
}
399+
}
400+
401+
if fmt_opt.check && !missing.is_empty() {
402+
bail!("expected header missing from {}", missing.join(", "));
403+
}
404+
405+
Ok(())
406+
}
407+
326408
fn has_cmd(target_cmd: &str) -> bool {
327409
#[cfg(target_os = "windows")]
328410
let mut cmd = Command::new("where");

0 commit comments

Comments
 (0)