@@ -17,24 +17,46 @@ pub fn check_copyright(path: &Path, fix: bool) -> anyhow::Result<()> {
17
17
. and_then ( |e| e. to_str ( ) )
18
18
. unwrap_or_default ( ) ;
19
19
20
- if !matches ! ( ext, "rs" | "c" | "proto" | "toml" | "ts" | "js" ) {
20
+ if !matches ! (
21
+ ext,
22
+ "rs" | "c" | "proto" | "toml" | "ts" | "js" | "py" | "ps1" | "config"
23
+ ) {
21
24
return Ok ( ( ) ) ;
22
25
}
23
26
24
27
let f = BufReader :: new ( File :: open ( path) ?) ;
25
28
let mut lines = f. lines ( ) ;
26
- let first_line = lines. next ( ) . unwrap_or ( Ok ( String :: new ( ) ) ) ?;
27
- let second_line = lines. next ( ) . unwrap_or ( Ok ( String :: new ( ) ) ) ?;
28
- let third_line = lines. next ( ) . unwrap_or ( Ok ( String :: new ( ) ) ) ?;
29
+ let ( script_interpreter_line, blank_after_script_interpreter_line, first_content_line) = {
30
+ let line = lines. next ( ) . unwrap_or ( Ok ( String :: new ( ) ) ) ?;
31
+ // Besides the "py", "ps1, "toml", and "config" files, only for Rust,
32
+ // `#!` is in the first set of the grammar. That's why we need to check
33
+ // the extension for not being "rs".
34
+ // Someone may decide to put a script interpreter line (aka "shebang")
35
+ // in a .config or a .toml file, and mark the file as executable. While
36
+ // that's not common, we choose not to constrain creativity.
37
+ if line. starts_with ( "#!" ) && ext != "rs" {
38
+ let script_interpreter_line = line;
39
+ let after_script_interpreter_line = lines. next ( ) . unwrap_or ( Ok ( String :: new ( ) ) ) ?;
40
+ (
41
+ Some ( script_interpreter_line) ,
42
+ Some ( after_script_interpreter_line. is_empty ( ) ) ,
43
+ lines. next ( ) . unwrap_or ( Ok ( String :: new ( ) ) ) ?,
44
+ )
45
+ } else {
46
+ ( None , None , line)
47
+ }
48
+ } ;
49
+ let second_content_line = lines. next ( ) . unwrap_or ( Ok ( String :: new ( ) ) ) ?;
50
+ let third_content_line = lines. next ( ) . unwrap_or ( Ok ( String :: new ( ) ) ) ?;
29
51
30
52
// Preserve any files which are copyright, but not by Microsoft.
31
- if first_line . contains ( "Copyright" ) && !first_line . contains ( "Microsoft" ) {
53
+ if first_content_line . contains ( "Copyright" ) && !first_content_line . contains ( "Microsoft" ) {
32
54
return Ok ( ( ) ) ;
33
55
}
34
56
35
- let mut missing_banner =
36
- !first_line . contains ( HEADER_MIT_FIRST ) || !second_line . contains ( HEADER_MIT_SECOND ) ;
37
- let mut missing_blank_line = !third_line . is_empty ( ) ;
57
+ let mut missing_banner = !first_content_line . contains ( HEADER_MIT_FIRST )
58
+ || !second_content_line . contains ( HEADER_MIT_SECOND ) ;
59
+ let mut missing_blank_line = !third_content_line . is_empty ( ) ;
38
60
39
61
// TEMP: until we have more robust infrastructure for distinct
40
62
// microsoft-internal checks, include this "escape hatch" for preserving
@@ -44,8 +66,9 @@ pub fn check_copyright(path: &Path, fix: bool) -> anyhow::Result<()> {
44
66
let is_msft_internal = std:: env:: var ( "XTASK_FMT_COPYRIGHT_ALLOW_MISSING_MIT" ) . is_ok ( ) ;
45
67
if is_msft_internal {
46
68
// support both new and existing copyright banner styles
47
- missing_banner = !( first_line. contains ( "Copyright" ) && first_line. contains ( "Microsoft" ) ) ;
48
- missing_blank_line = !second_line. is_empty ( ) ;
69
+ missing_banner =
70
+ !( first_content_line. contains ( "Copyright" ) && first_content_line. contains ( "Microsoft" ) ) ;
71
+ missing_blank_line = !second_content_line. is_empty ( ) ;
49
72
}
50
73
51
74
if fix {
@@ -64,10 +87,19 @@ pub fn check_copyright(path: &Path, fix: bool) -> anyhow::Result<()> {
64
87
let mut f = BufReader :: new ( File :: open ( path) ?) ;
65
88
let mut f_fixed = File :: create ( path_fix) ?;
66
89
90
+ if let Some ( script_interpreter_line) = script_interpreter_line {
91
+ writeln ! ( f_fixed, "{script_interpreter_line}" ) ?;
92
+ }
93
+ if let Some ( blank_after_script_interpreter_line) = blank_after_script_interpreter_line {
94
+ if !blank_after_script_interpreter_line {
95
+ writeln ! ( f_fixed) ?;
96
+ }
97
+ }
98
+
67
99
if missing_banner {
68
100
let prefix = match ext {
69
101
"rs" | "c" | "proto" | "ts" | "js" => "//" ,
70
- "toml" => "#" ,
102
+ "toml" | "py" | "ps1" | "config" => "#" ,
71
103
_ => unreachable ! ( ) ,
72
104
} ;
73
105
@@ -101,17 +133,33 @@ pub fn check_copyright(path: &Path, fix: bool) -> anyhow::Result<()> {
101
133
}
102
134
}
103
135
104
- let msg = match ( missing_banner, missing_blank_line) {
105
- ( true , true ) => "missing copyright & license header + subsequent blank line" ,
106
- ( true , false ) => "missing copyright & license header" ,
107
- ( false , true ) => "missing blank line after copyright & license header" ,
108
- ( false , false ) => return Ok ( ( ) ) ,
109
- } ;
136
+ // Consider using an enum if there more than three,
137
+ // or the errors need to be compared.
138
+ let mut missing = vec ! [ ] ;
139
+ if missing_banner {
140
+ missing. push ( "the copyright & license header" ) ;
141
+ }
142
+ if missing_blank_line {
143
+ missing. push ( "a blank line after the copyright & license header" ) ;
144
+ }
145
+ if let Some ( blank_after_script_interpreter_line) = blank_after_script_interpreter_line {
146
+ if !blank_after_script_interpreter_line {
147
+ missing. push ( "a blank line after the script interpreter line" ) ;
148
+ }
149
+ }
150
+
151
+ if missing. is_empty ( ) {
152
+ return Ok ( ( ) ) ;
153
+ }
110
154
111
155
if fix {
112
- log:: info!( "fixed {} in {}" , msg, path. display( ) ) ;
156
+ log:: info!(
157
+ "applied fixes for missing {:?} in {}" ,
158
+ missing,
159
+ path. display( )
160
+ ) ;
113
161
Ok ( ( ) )
114
162
} else {
115
- Err ( anyhow ! ( "{ } in {}" , msg , path. display( ) ) )
163
+ Err ( anyhow ! ( "missing {:? } in {}" , missing , path. display( ) ) )
116
164
}
117
165
}
0 commit comments