11#!/usr/bin/env python3
22"""
3- Copyright (C) 2025-2026 Arm Limited
3+ Copyright 2025-2026 Arm Limited
44
5- This script updates copyright headers in source files to reflect the current year.
6- It supports headers in the format:
7- Copyright (C) <original year> Arm Limited
8- Copyright (C) <original year>-<current year> Arm Limited
9- If the header does not reflect the current year, it will be updated to <original year>-<current year>.
10- SPDX-FileCopyrightText headers are also updated similarly.
5+ Licensed under the Apache License, Version 2.0 (the "License");
6+ you may not use this file except in compliance with the License.
7+ You may obtain a copy of the License at
118
12- The script is generated using ChatGPT
9+ http://www.apache.org/licenses/LICENSE-2.0
10+
11+ Unless required by applicable law or agreed to in writing, software
12+ distributed under the License is distributed on an "AS IS" BASIS,
13+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ See the License for the specific language governing permissions and
15+ limitations under the License.
16+
17+ generated with AI
1318"""
1419
1520import re , sys , pathlib
1823CURRENT_YEAR = datetime .datetime .now ().year # Use system year
1924
2025COPYRIGHT_PATTERN = re .compile (
21- r'(?P<prefix>Copyright(?:\s *\(c\))?[^0-9\n]{0,40}) '
26+ r'\bCopyright(?:[ \t] *\(c\))?[ \t]* '
2227 r'(?P<y1>\d{4})'
23- r'(?:\s*-\s*(?P<y2>\d{4}))?' ,
24- flags = re .IGNORECASE
25- )
26- SPDX_PATTERN = re .compile (
27- r'(?P<prefix>SPDX-FileCopyrightText:\s*)'
28- r'(?P<y1>\d{4})'
29- r'(?:\s*-\s*(?P<y2>\d{4}))?' ,
28+ r'(?:[ \t]*-[ \t]*(?P<y2>\d{4}))?'
29+ r'(?:[ \t]+Arm[ \t]+Limited\b)*' ,
3030 flags = re .IGNORECASE
3131)
3232
3333def update_text (t : str ):
3434 changed = False
35+
36+ def canonical_copyright (y1 : int , y2 : int | None ) -> str :
37+ if y2 is None :
38+ return f"Copyright { y1 } Arm Limited"
39+ return f"Copyright { y1 } -{ y2 } Arm Limited"
40+
3541 def repl (m ):
3642 nonlocal changed
3743 y1 = int (m .group ('y1' ))
3844 y2 = m .group ('y2' )
3945 if y2 :
4046 y2 = int (y2 )
41- if y2 >= CURRENT_YEAR : return m .group (0 )
42- changed = True
43- return f"{ m .group ('prefix' )} { y1 } -{ CURRENT_YEAR } "
47+ target_y2 = y2 if y2 >= CURRENT_YEAR else CURRENT_YEAR
48+ new_text = canonical_copyright (y1 , target_y2 )
4449 else :
45- if y1 == CURRENT_YEAR : return m .group (0 )
46- changed = True
47- return f"{ m .group ('prefix' )} { y1 } -{ CURRENT_YEAR } "
50+ new_text = (
51+ canonical_copyright (y1 , None )
52+ if y1 == CURRENT_YEAR
53+ else canonical_copyright (y1 , CURRENT_YEAR )
54+ )
55+
56+ if new_text == m .group (0 ):
57+ return m .group (0 )
58+
59+ changed = True
60+ return new_text
61+
4862 t2 = COPYRIGHT_PATTERN .sub (repl , t )
49- t2 = SPDX_PATTERN .sub (repl , t2 )
5063 return t2 , changed
5164
65+
66+ def collect_paths () -> list [str ]:
67+ # Prefer explicit CLI paths; otherwise read piped stdin.
68+ if len (sys .argv ) > 1 :
69+ return [p .strip () for p in sys .argv [1 :] if p .strip ()]
70+ if not sys .stdin .isatty ():
71+ return [p .strip () for p in sys .stdin if p .strip ()]
72+ return []
73+
5274def main ():
53- paths = [p .strip () for p in sys .stdin if p .strip ()]
75+ paths = collect_paths ()
76+ if not paths :
77+ print (
78+ "Usage: python update_copyright_years.py <file1> <file2> ...\n "
79+ " or: <command that outputs file paths> | python update_copyright_years.py"
80+ )
81+ return 2
82+
5483 updated = 0
5584 for p in paths :
5685 fp = pathlib .Path (p )
@@ -69,5 +98,7 @@ def main():
6998 print (f"[OK ] { p } " )
7099 updated += 1
71100 print (f"Updated { updated } files to year { CURRENT_YEAR } " )
101+ return 0
102+
72103if __name__ == "__main__" :
73- main ()
104+ raise SystemExit ( main () )
0 commit comments