| #!/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()) |