| #!/usr/bin/env python3 | 
 |  | 
 | # Copyright 2020 The IREE Authors | 
 | # | 
 | # Licensed under the Apache License v2.0 with LLVM Exceptions. | 
 | # See https://llvm.org/LICENSE.txt for license information. | 
 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | """Prepends a license header to files that don't already have one. | 
 |  | 
 | By default, only operates on known filetypes but behavior can be overridden with | 
 | flags. Ignores files already containing a license as determined by the presence | 
 | of a block that looks like "Copyright SOME_YEAR" | 
 | """ | 
 |  | 
 | import argparse | 
 | import datetime | 
 | import os | 
 | import re | 
 | import sys | 
 |  | 
 | COPYRIGHT_PATTERN = re.compile(r"Copyright\s+\d+") | 
 |  | 
 | LICENSE_HEADER_FORMATTER = """{shebang}{start_comment} Copyright {year} {holder} | 
 | {middle_comment} Licensed under the Apache License v2.0 with LLVM Exceptions. | 
 | {middle_comment} See https://llvm.org/LICENSE.txt for license information. | 
 | {middle_comment} SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception{end_comment} | 
 |  | 
 | """ | 
 |  | 
 |  | 
 | class CommentSyntax(object): | 
 |     def __init__(self, start_comment, middle_comment=None, end_comment=""): | 
 |         self.start_comment = start_comment | 
 |         self.middle_comment = middle_comment if middle_comment else start_comment | 
 |         self.end_comment = end_comment | 
 |  | 
 |  | 
 | def comment_arg_parser(v): | 
 |     """Can be used to parse a comment syntax triple.""" | 
 |     if v is None: | 
 |         return None | 
 |     if not isinstance(v, str): | 
 |         raise argparse.ArgumentTypeError("String expected") | 
 |     return CommentSyntax(*v.split(",")) | 
 |  | 
 |  | 
 | def create_multikey(d): | 
 |     # pylint: disable=g-complex-comprehension | 
 |     return {k: v for keys, v in d.items() for k in keys} | 
 |  | 
 |  | 
 | filename_to_comment = create_multikey( | 
 |     { | 
 |         ("BUILD", "CMakeLists.txt"): CommentSyntax("#"), | 
 |     } | 
 | ) | 
 |  | 
 | ext_to_comment = create_multikey( | 
 |     { | 
 |         (".bzl", ".cfg", ".cmake", ".overlay", ".py", ".sh", ".yml"): CommentSyntax( | 
 |             "#" | 
 |         ), | 
 |         (".cc", ".cpp", ".comp", ".fbs", ".h", ".hpp", ".inc", ".td"): CommentSyntax( | 
 |             "//" | 
 |         ), | 
 |         (".def",): CommentSyntax(";;"), | 
 |     } | 
 | ) | 
 |  | 
 |  | 
 | def get_comment_syntax(args): | 
 |     """Deterime the comment syntax to use.""" | 
 |     if args.comment: | 
 |         return args.comment | 
 |     basename = os.path.basename(args.filename) | 
 |     from_filename = filename_to_comment.get(basename) | 
 |     if from_filename: | 
 |         return from_filename | 
 |     _, ext = os.path.splitext(args.filename) | 
 |     return ext_to_comment.get(ext, args.default_comment) | 
 |  | 
 |  | 
 | def parse_arguments(): | 
 |     """Parses command line arguments.""" | 
 |     current_year = datetime.date.today().year | 
 |     parser = argparse.ArgumentParser() | 
 |     input_group = parser.add_mutually_exclusive_group() | 
 |     input_group.add_argument( | 
 |         "infile", | 
 |         nargs="?", | 
 |         type=argparse.FileType("r", encoding="UTF-8"), | 
 |         help="Input file to format. Default: stdin", | 
 |         default=sys.stdin, | 
 |     ) | 
 |     parser.add_argument( | 
 |         "--filename", | 
 |         "--assume-filename", | 
 |         type=str, | 
 |         default=None, | 
 |         help=( | 
 |             "Filename to use for determining comment syntax. Default: actual name" | 
 |             "of input file." | 
 |         ), | 
 |     ) | 
 |     parser.add_argument( | 
 |         "--year", | 
 |         "-y", | 
 |         help="Year to add copyright. Default: the current year ({})".format( | 
 |             current_year | 
 |         ), | 
 |         default=current_year, | 
 |     ) | 
 |     parser.add_argument( | 
 |         "--holder", | 
 |         help="Copyright holder. Default: The IREE Authors", | 
 |         default="The IREE Authors", | 
 |     ) | 
 |     parser.add_argument( | 
 |         "--quiet", | 
 |         help=( | 
 |             "Don't raise a runtime error on encountering an unhandled filetype." | 
 |             "Useful for running across many files at once. Default: False" | 
 |         ), | 
 |         action="store_true", | 
 |         default=False, | 
 |     ) | 
 |     output_group = parser.add_mutually_exclusive_group() | 
 |     output_group.add_argument( | 
 |         "-o", | 
 |         "--outfile", | 
 |         "--output", | 
 |         help="File to send output. Default: stdout", | 
 |         type=argparse.FileType("w", encoding="UTF-8"), | 
 |         default=sys.stdout, | 
 |     ) | 
 |     output_group.add_argument( | 
 |         "--in_place", | 
 |         "-i", | 
 |         action="store_true", | 
 |         help="Run formatting in place. Default: False", | 
 |         default=False, | 
 |     ) | 
 |     comment_group = parser.add_mutually_exclusive_group() | 
 |     comment_group.add_argument( | 
 |         "--comment", | 
 |         "-c", | 
 |         type=comment_arg_parser, | 
 |         help="Override comment syntax.", | 
 |         default=None, | 
 |     ) | 
 |     comment_group.add_argument( | 
 |         "--default_comment", | 
 |         type=comment_arg_parser, | 
 |         help="Fallback comment syntax if filename is unknown. Default: None", | 
 |         default=None, | 
 |     ) | 
 |     args = parser.parse_args() | 
 |  | 
 |     if args.in_place and args.infile == sys.stdin: | 
 |         raise parser.error("Cannot format stdin in place") | 
 |  | 
 |     if not args.filename and args.infile != sys.stdin: | 
 |         args.filename = args.infile.name | 
 |  | 
 |     return args | 
 |  | 
 |  | 
 | def main(args): | 
 |     first_line = args.infile.readline() | 
 |     already_has_license = False | 
 |     shebang = "" | 
 |     content_lines = [] | 
 |     if first_line.startswith("#!"): | 
 |         shebang = first_line | 
 |     else: | 
 |         content_lines = [first_line] | 
 |     content_lines.extend(args.infile.readlines()) | 
 |     for line in content_lines: | 
 |         if COPYRIGHT_PATTERN.search(line): | 
 |             already_has_license = True | 
 |             break | 
 |     if already_has_license: | 
 |         header = shebang | 
 |     else: | 
 |         comment_syntax = get_comment_syntax(args) | 
 |         if not comment_syntax: | 
 |             if args.quiet: | 
 |                 header = shebang | 
 |             else: | 
 |                 raise ValueError( | 
 |                     "Could not determine comment syntax for " + args.filename | 
 |                 ) | 
 |         else: | 
 |             header = LICENSE_HEADER_FORMATTER.format( | 
 |                 # Add a blank line between shebang and license. | 
 |                 shebang=(shebang + "\n" if shebang else ""), | 
 |                 start_comment=comment_syntax.start_comment, | 
 |                 middle_comment=comment_syntax.middle_comment, | 
 |                 # Add a blank line before the end comment. | 
 |                 end_comment=( | 
 |                     "\n" + comment_syntax.end_comment | 
 |                     if comment_syntax.end_comment | 
 |                     else "" | 
 |                 ), | 
 |                 year=args.year, | 
 |                 holder=args.holder, | 
 |             ) | 
 |  | 
 |     # Have to open for write after we're done reading. | 
 |     if args.in_place: | 
 |         args.outfile = open(args.filename, "w", encoding="UTF-8") | 
 |     args.outfile.write(header) | 
 |     args.outfile.writelines(content_lines) | 
 |  | 
 |  | 
 | if __name__ == "__main__": | 
 |     main(parse_arguments()) |