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