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