diff options
Diffstat (limited to 'gcc/m2/tools-src/boilerplate.py')
-rw-r--r-- | gcc/m2/tools-src/boilerplate.py | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/gcc/m2/tools-src/boilerplate.py b/gcc/m2/tools-src/boilerplate.py new file mode 100644 index 0000000..9959652 --- /dev/null +++ b/gcc/m2/tools-src/boilerplate.py @@ -0,0 +1,548 @@ +#!/usr/bin/env python3 +# +# boilerplate.py utility to rewrite the boilerplate with new dates. +# +# Copyright (C) 2018-2022 Free Software Foundation, Inc. +# Contributed by Gaius Mulley <gaius@glam.ac.uk>. +# +# This file is part of GNU Modula-2. +# +# GNU Modula-2 is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Modula-2 is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Modula-2; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. +# + +import argparse +import datetime +import os +import sys + + +error_count = 0 +seen_files = [] +output_name = None + +ISO_COPYRIGHT = 'Copyright ISO/IEC' +COPYRIGHT = 'Copyright (C)' +GNU_PUBLIC_LICENSE = 'GNU General Public License' +GNU_LESSER_GENERAL = 'GNU Lesser General' +GCC_RUNTIME_LIB_EXC = 'GCC Runtime Library Exception' +VERSION_2_1 = 'version 2.1' +VERSION_2 = 'version 2' +VERSION_3 = 'version 3' +Licenses = {VERSION_2_1: 'v2.1', VERSION_2: 'v2', VERSION_3: 'v3'} +CONTRIBUTED_BY = 'ontributed by' + + +def printf(fmt, *args): + # printf - keeps C programmers happy :-) + print(str(fmt) % args, end=' ') + + +def error(fmt, *args): + # error - issue an error message. + global error_count + + print(str(fmt) % args, end=' ') + error_count += 1 + + +def halt_on_error(): + if error_count > 0: + os.sys.exit(1) + + +def basename(f): + b = f.split('/') + return b[-1] + + +def analyse_comment(text, f): + # analyse_comment determine the license from the top comment. + start_date, end_date = None, None + contribution, summary, lic = None, None, None + if text.find(ISO_COPYRIGHT) > 0: + lic = 'BSISO' + now = datetime.datetime.now() + for d in range(1984, now.year+1): + if text.find(str(d)) > 0: + if start_date is None: + start_date = str(d) + end_date = str(d) + return start_date, end_date, '', '', lic + elif text.find(COPYRIGHT) > 0: + if text.find(GNU_PUBLIC_LICENSE) > 0: + lic = 'GPL' + elif text.find(GNU_LESSER_GENERAL) > 0: + lic = 'LGPL' + for license_ in Licenses.keys(): + if text.find(license_) > 0: + lic += Licenses[license_] + if text.find(GCC_RUNTIME_LIB_EXC) > 0: + lic += 'x' + now = datetime.datetime.now() + for d in range(1984, now.year+1): + if text.find(str(d)) > 0: + if start_date is None: + start_date = str(d) + end_date = str(d) + if text.find(CONTRIBUTED_BY) > 0: + i = text.find(CONTRIBUTED_BY) + i += len(CONTRIBUTED_BY) + j = text.index('. ', i) + contribution = text[i:j] + if text.find(basename(f)) > 0: + i = text.find(basename(f)) + j = text.find('. ', i) + if j < 0: + error("summary of the file does not finish with a '.'") + summary = text[i:] + else: + summary = text[i:j] + return start_date, end_date, contribution, summary, lic + + +def analyse_header_without_terminator(f, start): + text = '' + for count, l in enumerate(open(f).readlines()): + parts = l.split(start) + if len(parts) > 1: + line = start.join(parts[1:]) + line = line.strip() + text += ' ' + text += line + elif (l.rstrip() != '') and (len(parts[0]) > 0): + return analyse_comment(text, f), count + return [None, None, None, None, None], 0 + + +def analyse_header_with_terminator(f, start, end): + inComment = False + text = '' + for count, line in enumerate(open(f).readlines()): + while line != '': + line = line.strip() + if inComment: + text += ' ' + pos = line.find(end) + if pos >= 0: + text += line[:pos] + line = line[pos:] + inComment = False + else: + text += line + line = '' + else: + pos = line.find(start) + if (pos >= 0) and (len(line) > len(start)): + before = line[:pos].strip() + if before != '': + return analyse_comment(text, f), count + line = line[pos + len(start):] + inComment = True + elif (line != '') and (line == end): + line = '' + else: + return analyse_comment(text, f), count + return [None, None, None, None, None], 0 + + +def analyse_header(f, start, end): + # analyse_header - + if end is None: + return analyse_header_without_terminator(f, start) + else: + return analyse_header_with_terminator(f, start, end) + + +def add_stop(sentence): + # add_stop - add a full stop to a sentance. + if sentence is None: + return None + sentence = sentence.rstrip() + if (len(sentence) > 0) and (sentence[-1] != '.'): + return sentence + '.' + return sentence + + +GPLv3 = """ +%s + +Copyright (C) %s Free Software Foundation, Inc. +Contributed by %s + +This file is part of GNU Modula-2. + +GNU Modula-2 is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Modula-2 is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Modula-2; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. +""" + +GPLv3x = """ +%s + +Copyright (C) %s Free Software Foundation, Inc. +Contributed by %s + +This file is part of GNU Modula-2. + +GNU Modula-2 is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Modula-2 is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. +""" + +LGPLv3 = """ +%s + +Copyright (C) %s Free Software Foundation, Inc. +Contributed by %s + +This file is part of GNU Modula-2. + +GNU Modula-2 is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +GNU Modula-2 is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with GNU Modula-2. If not, see <https://www.gnu.org/licenses/>. +""" + +BSISO = """ +Library module defined by the International Standard + Information technology - programming languages + BS ISO/IEC 10514-1:1996E Part 1: Modula-2, Base Language. + + Copyright ISO/IEC (International Organization for Standardization + and International Electrotechnical Commission) %s. + + It may be freely copied for the purpose of implementation (see page + 707 of the Information technology - Programming languages Part 1: + Modula-2, Base Language. BS ISO/IEC 10514-1:1996). +""" + +templates = {} +templates['GPLv3'] = GPLv3 +templates['GPLv3x'] = GPLv3x +templates['LGPLv3'] = LGPLv3 +templates['LGPLv2.1'] = LGPLv3 +templates['BSISO'] = BSISO + + +def write_template(fo, magic, start, end, dates, contribution, summary, lic): + if lic in templates: + if lic == 'BSISO': + # non gpl but freely distributed for the implementation of a + # compiler + text = templates[lic] % (dates) + text = text.rstrip() + else: + summary = summary.lstrip() + contribution = contribution.lstrip() + summary = add_stop(summary) + contribution = add_stop(contribution) + if magic is not None: + fo.write(magic) + fo.write('\n') + text = templates[lic] % (summary, dates, contribution) + text = text.rstrip() + if end is None: + text = text.split('\n') + for line in text: + fo.write(start) + fo.write(' ') + fo.write(line) + fo.write('\n') + else: + text = text.lstrip() + fo.write(start) + fo.write(' ') + fo.write(text) + fo.write(' ') + fo.write(end) + fo.write('\n') + # add a blank comment line for a script for eye candy. + if start == '#' and end is None: + fo.write(start) + fo.write('\n') + else: + error('no template found for: %s\n', lic) + os.sys.exit(1) + return fo + + +def write_boiler_plate(fo, magic, start, end, + start_date, end_date, contribution, summary, gpl): + if start_date == end_date: + dates = start_date + else: + dates = '%s-%s' % (start_date, end_date) + return write_template(fo, magic, start, end, + dates, contribution, summary, gpl) + + +def rewrite_file(f, magic, start, end, start_date, end_date, + contribution, summary, gpl, lines): + text = ''.join(open(f).readlines()[lines:]) + if output_name == '-': + fo = sys.stdout + else: + fo = open(f, 'w') + fo = write_boiler_plate(fo, magic, start, end, + start_date, end_date, contribution, summary, gpl) + fo.write(text) + fo.flush() + if output_name != '-': + fo.close() + + +def handle_header(f, magic, start, end): + # handle_header keep reading lines of file, f, looking for start, end + # sequences and comments inside. The comments are checked for: + # date, contribution, summary + global error_count + + error_count = 0 + [start_date, end_date, + contribution, summary, lic], lines = analyse_header(f, start, end) + if lic is None: + error('%s:1:no GPL found at the top of the file\n', f) + else: + if args.verbose: + printf('copyright: %s\n', lic) + if (start_date is not None) and (end_date is not None): + if start_date == end_date: + printf('dates = %s\n', start_date) + else: + printf('dates = %s-%s\n', start_date, end_date) + if summary is not None: + printf('summary: %s\n', summary) + if contribution is not None: + printf('contribution: %s\n', contribution) + if start_date is None: + error('%s:1:no date found in the GPL at the top of the file\n', f) + if args.contribution is None: + if contribution == '': + error('%s:1:no contribution found in the ' + + 'GPL at the top of the file\n', f) + else: + contribution = args.contribution + if summary is None: + if args.summary == '': + error('%s:1:no single line summary found in the ' + + 'GPL at the top of the file\n', f) + else: + summary = args.summary + if error_count == 0: + now = datetime.datetime.now() + if args.no: + print(f, 'suppressing change as requested: %s-%s %s' + % (start_date, end_date, lic)) + else: + if lic == 'BSISO': + # don't change the BS ISO license! + pass + elif args.extensions: + lic = 'GPLv3x' + elif args.gpl3: + lic = 'GPLv3' + rewrite_file(f, magic, start, end, start_date, + str(now.year), contribution, summary, lic, lines) + else: + printf('too many errors, no modifications will occur\n') + + +def bash_tidy(f): + # bash_tidy tidy up dates using '#' comment + handle_header(f, '#!/bin/bash', '#', None) + + +def python_tidy(f): + # python_tidy tidy up dates using '#' comment + handle_header(f, '#!/usr/bin/env python3', '#', None) + + +def bnf_tidy(f): + # bnf_tidy tidy up dates using '--' comment + handle_header(f, None, '--', None) + + +def c_tidy(f): + # c_tidy tidy up dates using '/* */' comments + handle_header(f, None, '/*', '*/') + + +def m2_tidy(f): + # m2_tidy tidy up dates using '(* *)' comments + handle_header(f, None, '(*', '*)') + + +def in_tidy(f): + # in_tidy tidy up dates using '#' as a comment and check + # the first line for magic number. + first = open(f).readlines()[0] + if (len(first) > 0) and (first[:2] == '#!'): + # magic number found, use this + handle_header(f, first, '#', None) + else: + handle_header(f, None, '#', None) + + +def do_visit(args, dirname, names): + # do_visit helper function to call func on every extension file. + global output_name + func, extension = args + for f in names: + if len(f) > len(extension) and f[-len(extension):] == extension: + output_name = f + func(os.path.join(dirname, f)) + + +def visit_dir(startDir, ext, func): + # visit_dir call func for each file in startDir which has ext. + global output_name, seen_files + for dirName, subdirList, fileList in os.walk(startDir): + for fname in fileList: + if (len(fname) > len(ext)) and (fname[-len(ext):] == ext): + fullpath = os.path.join(dirName, fname) + output_name = fullpath + if not (fullpath in seen_files): + seen_files += [fullpath] + func(fullpath) + # Remove the first entry in the list of sub-directories + # if there are any sub-directories present + if len(subdirList) > 0: + del subdirList[0] + + +def find_files(): + # find_files for each file extension call the appropriate tidy routine. + visit_dir(args.recursive, '.h.in', c_tidy) + visit_dir(args.recursive, '.in', in_tidy) + visit_dir(args.recursive, '.sh', in_tidy) + visit_dir(args.recursive, '.py', python_tidy) + visit_dir(args.recursive, '.c', c_tidy) + visit_dir(args.recursive, '.h', c_tidy) + visit_dir(args.recursive, '.cc', c_tidy) + visit_dir(args.recursive, '.def', m2_tidy) + visit_dir(args.recursive, '.mod', m2_tidy) + visit_dir(args.recursive, '.bnf', bnf_tidy) + + +def handle_arguments(): + # handle_arguments create and return the args object. + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--contribution', + help='set the contribution string ' + + 'at the top of the file.', + default='', action='store') + parser.add_argument('-d', '--debug', help='turn on internal debugging.', + default=False, action='store_true') + parser.add_argument('-f', '--force', + help='force a check to insist that the ' + + 'contribution, summary and GPL exist.', + default=False, action='store_true') + parser.add_argument('-g', '--gplv3', help='change to GPLv3', + default=False, action='store_true') + parser.add_argument('-o', '--outputfile', help='set the output file', + default='-', action='store') + parser.add_argument('-r', '--recursive', + help='recusively scan directory for known file ' + + 'extensions (.def, .mod, .c, .h, .py, .in, .sh).', + default='.', action='store') + parser.add_argument('-s', '--summary', + help='set the summary line for the file.', + default=None, action='store') + parser.add_argument('-u', '--update', help='update all dates.', + default=False, action='store_true') + parser.add_argument('-v', '--verbose', + help='display copyright, ' + + 'date and contribution messages', + action='store_true') + parser.add_argument('-x', '--extensions', + help='change to GPLv3 with GCC runtime extensions.', + default=False, action='store_true') + parser.add_argument('-N', '--no', + help='do not modify any file.', + action='store_true') + args = parser.parse_args() + return args + + +def has_ext(name, ext): + # has_ext return True if, name, ends with, ext. + if len(name) > len(ext): + return name[-len(ext):] == ext + return False + + +def single_file(name): + # single_file scan the single file for a GPL boilerplate which + # has a GPL, contribution field and a summary heading. + if has_ext(name, '.def') or has_ext(name, '.mod'): + m2_tidy(name) + elif has_ext(name, '.h') or has_ext(name, '.c') or has_ext(name, '.cc'): + c_tidy(name) + elif has_ext(name, '.in'): + in_tidy(name) + elif has_ext(name, '.sh'): + in_tidy(name) # uses magic number for actual sh/bash + elif has_ext(name, '.py'): + python_tidy(name) + + +def main(): + # main - handle_arguments and then find source files. + global args, output_name + args = handle_arguments() + output_name = args.outputfile + if args.recursive: + find_files() + elif args.inputfile is None: + print('an input file must be specified on the command line') + else: + single_file(args.inputfile) + halt_on_error() + + +main() |