@@ -233,6 +233,27 @@ fn run_host_tests(test_opt: &TestOpt) -> Result<()> {
233
233
fn run_fmt_project ( fmt_opt : & FmtOpt ) -> Result < ( ) > {
234
234
let mut any_errors = false ;
235
235
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
+
236
257
// fmt rust
237
258
{
238
259
eprintln ! ( "Formatting: rust" ) ;
@@ -323,6 +344,67 @@ fn run_fmt_project(fmt_opt: &FmtOpt) -> Result<()> {
323
344
Ok ( ( ) )
324
345
}
325
346
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
+
326
408
fn has_cmd ( target_cmd : & str ) -> bool {
327
409
#[ cfg( target_os = "windows" ) ]
328
410
let mut cmd = Command :: new ( "where" ) ;
0 commit comments