def main(argv=sys.argv[1:]): extensions = [ 'c', 'cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'hxx', 'cmake', 'py', ] parser = argparse.ArgumentParser( description='Check code files for copyright and license information.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( 'paths', nargs='*', default=[os.curdir], help='The files or directories to check. For directories files ending ' "in %s will be considered (except directories starting with '.' " "or '_' and 'setup.py' files beside 'package.xml' files)." % ', '.join(["'.%s'" % e for e in extensions])) parser.add_argument('--exclude', metavar='filename', nargs='*', dest='excludes', help='The filenames to exclude.') group = parser.add_mutually_exclusive_group() group.add_argument( '--add-missing', nargs=2, metavar=('COPYRIGHT_NAME', 'LICENSE'), help=('Add missing copyright notice and license information using the ' 'passed copyright holder and license')) group.add_argument( '--add-copyright-year', nargs='*', type=int, help='Add the current year to existing copyright notices') group.add_argument('--list-copyright-names', action='store_true', help='List names of known copyright holders') group.add_argument('--list-licenses', action='store_true', help='List names of known licenses') parser.add_argument( '--verbose', action='store_true', help= 'Show all files instead of only the ones with errors / modifications') # not using a file handle directly # in order to prevent leaving an empty file when something fails early group.add_argument('--xunit-file', help='Generate a xunit compliant XML file') args = parser.parse_args(argv) names = get_copyright_names() if args.list_copyright_names: for key in sorted(names.keys()): print('%s: %s' % (key, names[key])) return 0 licenses = get_licenses() if args.list_licenses: for key in sorted(licenses.keys()): print('%s: %s' % (key, licenses[key].name)) return 0 if args.xunit_file: start_time = time.time() filenames = get_files(args.paths, extensions) if args.excludes: filenames = [ f for f in filenames if os.path.basename(f) not in args.excludes ] if not filenames: print('No repository roots and files found', file=sys.stderr) return 0 file_descriptors = {} for filename in sorted(filenames): file_descriptors[filename] = parse_file(filename) if args.add_missing: name = names.get(args.add_missing[0], args.add_missing[0]) if args.add_missing[1] not in licenses: parser.error( "'LICENSE' argument must be a known license name. " "Use the '--list-licenses' options to see alist of valid license names." ) license = licenses[args.add_missing[1]] add_missing_header(file_descriptors, name, license, args.verbose) return 0 if args.add_copyright_year is not None: if not args.add_copyright_year: args.add_copyright_year.append(time.strftime('%Y')) args.add_copyright_year = [ int(year) for year in args.add_copyright_year ] add_copyright_year(file_descriptors, args.add_copyright_year, args.verbose) return 0 report = [] # check each directory for CONTRIBUTING.md and LICENSE files for path in sorted(file_descriptors.keys()): file_descriptor = file_descriptors[path] message = None has_error = False if file_descriptor.filetype == SOURCE_FILETYPE: if not file_descriptor.exists: message = 'file not found' has_error = True elif not file_descriptor.content: message = 'file empty' elif not file_descriptor.copyright_identifiers: message = 'could not find copyright notice' has_error = True else: message = 'copyright=%s, license=%s' % \ (', '.join([str(c) for c in file_descriptor.copyrights]), file_descriptor.license_identifier) has_error = file_descriptor.license_identifier == UNKNOWN_IDENTIFIER elif file_descriptor.filetype in [ CONTRIBUTING_FILETYPE, LICENSE_FILETYPE ]: if not file_descriptor.exists: message = 'file not found' has_error = True elif not file_descriptor.content: message = 'file empty' has_error = True elif file_descriptor.license_identifier: message = file_descriptor.license_identifier has_error = file_descriptor.license_identifier == UNKNOWN_IDENTIFIER else: assert False, file_descriptor else: assert False, 'Unknown filetype: ' + file_descriptor.filetype if args.verbose or has_error: print('%s: %s' % (file_descriptor.path, message), file=sys.stderr if has_error else sys.stdout) report.append((file_descriptor.path, not has_error, message)) # output summary error_count = len([r for r in report if not r[1]]) if not error_count: print('No errors, checked %d files' % len(report)) rc = 0 else: print('%d errors, checked %d files' % (error_count, len(report)), file=sys.stderr) rc = 1 # generate xunit file if args.xunit_file: folder_name = os.path.basename(os.path.dirname(args.xunit_file)) file_name = os.path.basename(args.xunit_file) suffix = '.xml' if file_name.endswith(suffix): file_name = file_name[0:-len(suffix)] suffix = '.xunit' if file_name.endswith(suffix): file_name = file_name[0:-len(suffix)] testname = '%s.%s' % (folder_name, file_name) xml = get_xunit_content(report, testname, time.time() - start_time) path = os.path.dirname(os.path.abspath(args.xunit_file)) if not os.path.exists(path): os.makedirs(path) with open(args.xunit_file, 'w') as f: f.write(xml) return rc
def main(argv=sys.argv[1:]): extensions = [ 'c', 'cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'hxx', 'cmake', 'py', ] parser = argparse.ArgumentParser( description='Check code files for copyright and license information.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( 'paths', nargs='*', default=[os.curdir], help='The files or directories to check. For directories files ending ' "in %s will be considered (except directories starting with '.' " "or '_' and 'setup.py' files beside 'package.xml' files)." % ', '.join(["'.%s'" % e for e in extensions])) parser.add_argument( '--exclude', metavar='filename', nargs='*', dest='excludes', help='The filenames to exclude.') group = parser.add_mutually_exclusive_group() group.add_argument( '--add-missing', nargs=2, metavar=('COPYRIGHT_NAME', 'LICENSE'), help=( 'Add missing copyright notice and license information using the ' 'passed copyright holder and license')) group.add_argument( '--add-copyright-year', nargs='*', type=int, help='Add the current year to existing copyright notices') group.add_argument( '--list-copyright-names', action='store_true', help='List names of known copyright holders') group.add_argument( '--list-licenses', action='store_true', help='List names of known licenses') parser.add_argument( '--verbose', action='store_true', help='Show all files instead of only the ones with errors / modifications') # not using a file handle directly # in order to prevent leaving an empty file when something fails early group.add_argument( '--xunit-file', help='Generate a xunit compliant XML file') args = parser.parse_args(argv) names = get_copyright_names() if args.list_copyright_names: for key in sorted(names.keys()): print('%s: %s' % (key, names[key])) return 0 licenses = get_licenses() if args.list_licenses: for key in sorted(licenses.keys()): print('%s: %s' % (key, licenses[key].name)) return 0 if args.xunit_file: start_time = time.time() filenames = get_files(args.paths, extensions) if args.excludes: filenames = [f for f in filenames if os.path.basename(f) not in args.excludes] if not filenames: print('No repository roots and files found', file=sys.stderr) return 0 file_descriptors = {} for filename in sorted(filenames): file_descriptors[filename] = parse_file(filename) if args.add_missing: name = names.get(args.add_missing[0], args.add_missing[0]) if args.add_missing[1] not in licenses: parser.error( "'LICENSE' argument must be a known license name. " "Use the '--list-licenses' options to see alist of valid license names.") license = licenses[args.add_missing[1]] add_missing_header(file_descriptors, name, license, args.verbose) return 0 if args.add_copyright_year is not None: if not args.add_copyright_year: args.add_copyright_year.append(time.strftime('%Y')) args.add_copyright_year = [int(year) for year in args.add_copyright_year] add_copyright_year(file_descriptors, args.add_copyright_year, args.verbose) return 0 report = [] # check each directory for CONTRIBUTING.md and LICENSE files for path in sorted(file_descriptors.keys()): file_descriptor = file_descriptors[path] message = None has_error = False if file_descriptor.filetype == SOURCE_FILETYPE: if not file_descriptor.exists: message = 'file not found' has_error = True elif not file_descriptor.content: message = 'file empty' elif not file_descriptor.copyright_identifiers: message = 'could not find copyright notice' has_error = True else: message = 'copyright=%s, license=%s' % \ (', '.join([str(c) for c in file_descriptor.copyrights]), file_descriptor.license_identifier) has_error = file_descriptor.license_identifier == UNKNOWN_IDENTIFIER elif file_descriptor.filetype in [CONTRIBUTING_FILETYPE, LICENSE_FILETYPE]: if not file_descriptor.exists: message = 'file not found' has_error = True elif not file_descriptor.content: message = 'file empty' has_error = True elif file_descriptor.license_identifier: message = file_descriptor.license_identifier has_error = file_descriptor.license_identifier == UNKNOWN_IDENTIFIER else: assert False, file_descriptor else: assert False, 'Unknown filetype: ' + file_descriptor.filetype if args.verbose or has_error: print('%s: %s' % (file_descriptor.path, message), file=sys.stderr if has_error else sys.stdout) report.append((file_descriptor.path, not has_error, message)) # output summary error_count = len([r for r in report if not r[1]]) if not error_count: print('No errors, checked %d files' % len(report)) rc = 0 else: print('%d errors, checked %d files' % (error_count, len(report)), file=sys.stderr) rc = 1 # generate xunit file if args.xunit_file: folder_name = os.path.basename(os.path.dirname(args.xunit_file)) file_name = os.path.basename(args.xunit_file) suffix = '.xml' if file_name.endswith(suffix): file_name = file_name[0:-len(suffix)] suffix = '.xunit' if file_name.endswith(suffix): file_name = file_name[0:-len(suffix)] testname = '%s.%s' % (folder_name, file_name) xml = get_xunit_content(report, testname, time.time() - start_time) path = os.path.dirname(os.path.abspath(args.xunit_file)) if not os.path.exists(path): os.makedirs(path) with open(args.xunit_file, 'w') as f: f.write(xml) return rc