# Copyright (c) 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import os import re import sys import time import checklicenses from tracing import tracing_project def _FormatError(msg, files): return ('%s in these files:\n' % msg + '\n'.join(' ' + x for x in files)) def _ReportErrorFileAndLine(filename, line_num, dummy_line): """Default error formatter for _FindNewViolationsOfRule.""" return '%s:%s' % (filename, line_num) def _FindNewViolationsOfRule(callable_rule, input_api, error_formatter=_ReportErrorFileAndLine): """Find all newly introduced violations of a per-line rule (a callable). Arguments: callable_rule: a callable taking a file extension and line of input and returning True if the rule is satisfied and False if there was a problem. input_api: object to enumerate the affected files. source_file_filter: a filter to be passed to the input api. error_formatter: a callable taking (filename, line_number, line) and returning a formatted error string. Returns: A list of the newly-introduced violations reported by the rule. """ errors = [] for f in input_api.AffectedFiles(include_deletes=False): # For speed, we do two passes, checking first the full file. Shelling out # to the SCM to determine the changed region can be quite expensive on # Win32. Assuming that most files will be kept problem-free, we can # skip the SCM operations most of the time. extension = str(f.LocalPath()).rsplit('.', 1)[-1] if all(callable_rule(extension, line) for line in f.NewContents()): continue # No violation found in full text: can skip considering diff. if tracing_project.TracingProject.IsIgnoredFile(f): continue for line_num, line in f.ChangedContents(): if not callable_rule(extension, line): errors.append(error_formatter(f.LocalPath(), line_num, line)) return errors def CheckCopyright(input_api): results = [] results += _CheckCopyrightThirdParty(input_api) results += _CheckCopyrightNonThirdParty(input_api) return results def _CheckCopyrightThirdParty(input_api): results = [] has_third_party_change = any( tracing_project.TracingProject.IsThirdParty(f) for f in input_api.AffectedFiles(include_deletes=False)) if has_third_party_change: tracing_root = os.path.abspath( os.path.join(os.path.dirname(__file__), '..')) tracing_third_party = os.path.join(tracing_root, 'tracing', 'third_party') has_invalid_license = checklicenses.check_licenses( tracing_root, tracing_third_party) if has_invalid_license: results.append( 'License check encountered invalid licenses in tracing/third_party/.') return results def _CheckCopyrightNonThirdParty(input_api): project_name = 'Chromium' current_year = int(time.strftime('%Y')) allow_old_years=True if allow_old_years: allowed_years = (str(s) for s in reversed(xrange(2006, current_year + 1))) else: allowed_years = [str(current_year)] years_re = '(' + '|'.join(allowed_years) + ')' # The (c) is deprecated, but tolerate it until it's removed from all files. non_html_license_header = ( r'.*? Copyright (\(c\) )?%(year)s The %(project)s Authors\. ' r'All rights reserved\.\n' r'.*? Use of this source code is governed by a BSD-style license that ' r'can be\n' r'.*? found in the LICENSE file\.(?: \*/)?\n' ) % { 'year': years_re, 'project': project_name, } non_html_license_re = re.compile(non_html_license_header, re.MULTILINE) html_license_header = ( r'^Copyright (\(c\) )?%(year)s The %(project)s Authors\. ' r'All rights reserved\.\n' r'Use of this source code is governed by a BSD-style license that ' r'can be\n' r'found in the LICENSE file\.(?: \*/)?\n' ) % { 'year': years_re, 'project': project_name, } html_license_re = re.compile(html_license_header, re.MULTILINE) sources = list(s for s in input_api.AffectedFiles(include_deletes=False) if not tracing_project.TracingProject.IsThirdParty(s)) html_sources = [f for f in sources if os.path.splitext(f.LocalPath())[1] == '.html'] non_html_sources = [f for f in sources if os.path.splitext(f.LocalPath())[1] != '.html'] results = [] results += _Check(input_api, html_license_re, html_sources) results += _Check(input_api, non_html_license_re, non_html_sources) return results def _Check(input_api, license_re, sources): bad_files = [] for f in sources: if tracing_project.TracingProject.IsIgnoredFile(f): continue contents = '\n'.join(f.NewContents()) if not license_re.search(contents): bad_files.append(f.LocalPath()) if bad_files: return [_FormatError( 'License must match:\n%s\n' % license_re.pattern + 'Found a bad license header', bad_files)] return [] def CheckLongLines(input_api, maxlen=80): """Checks the line length in all text files to be submitted.""" maxlens = { '': maxlen, } # Language specific exceptions to max line length. # '.h' is considered an obj-c file extension, since OBJC_EXCEPTIONS are a # superset of CPP_EXCEPTIONS. CPP_FILE_EXTS = ('c', 'cc') CPP_EXCEPTIONS = ('#define', '#endif', '#if', '#include', '#pragma') JAVA_FILE_EXTS = ('java',) JAVA_EXCEPTIONS = ('import ', 'package ') OBJC_FILE_EXTS = ('h', 'm', 'mm') OBJC_EXCEPTIONS = ('#define', '#endif', '#if', '#import', '#include', '#pragma') LANGUAGE_EXCEPTIONS = [ (CPP_FILE_EXTS, CPP_EXCEPTIONS), (JAVA_FILE_EXTS, JAVA_EXCEPTIONS), (OBJC_FILE_EXTS, OBJC_EXCEPTIONS), ] def no_long_lines(file_extension, line): # Check for language specific exceptions. if any(file_extension in exts and line.startswith(exceptions) for exts, exceptions in LANGUAGE_EXCEPTIONS): return True file_maxlen = maxlens.get(file_extension, maxlens['']) # Stupidly long symbols that needs to be worked around if takes 66% of line. long_symbol = file_maxlen * 2 / 3 # Hard line length limit at 50% more. extra_maxlen = file_maxlen * 3 / 2 line_len = len(line) if line_len <= file_maxlen: return True if '@suppress longLineCheck' in line: return True if line_len > extra_maxlen: return False if any((url in line) for url in ('file://', 'http://', 'https://')): return True if 'url(' in line and file_extension == 'css': return True if '