Home | History | Annotate | Download | only in third_party
      1 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 ANDROID_WHITELISTED_LICENSES = [
      6   'A(pple )?PSL 2(\.0)?',
      7   'Apache( Version)? 2(\.0)?',
      8   '(New )?([23]-Clause )?BSD( [23]-Clause)?( with advertising clause)?',
      9   'L?GPL ?v?2(\.[01])?( or later)?',
     10   'MIT(/X11)?(-like)?',
     11   'MPL 1\.1 ?/ ?GPL 2(\.0)? ?/ ?LGPL 2\.1',
     12   'MPL 2(\.0)?',
     13   'Microsoft Limited Public License',
     14   'Microsoft Permissive License',
     15   'Public Domain',
     16   'Python',
     17   'SGI Free Software License B',
     18   'University of Illinois\/NCSA Open Source',
     19   'X11',
     20 ]
     21 
     22 def LicenseIsCompatibleWithAndroid(input_api, license):
     23   regex = '^(%s)$' % '|'.join(ANDROID_WHITELISTED_LICENSES)
     24   tokens = \
     25     [x.strip() for x in input_api.re.split(' and |,', license) if len(x) > 0]
     26   has_compatible_license = False
     27   for token in tokens:
     28     if input_api.re.match(regex, token, input_api.re.IGNORECASE):
     29       has_compatible_license = True
     30       break
     31   return has_compatible_license
     32 
     33 def _CheckThirdPartyReadmesUpdated(input_api, output_api):
     34   """
     35   Checks to make sure that README.chromium files are properly updated
     36   when dependancies in third_party are modified.
     37   """
     38   readmes = []
     39   files = []
     40   errors = []
     41   for f in input_api.AffectedFiles():
     42     local_path = f.LocalPath()
     43     if input_api.os_path.dirname(local_path) == 'third_party':
     44       continue
     45     if local_path.startswith('third_party' + input_api.os_path.sep):
     46       files.append(f)
     47       if local_path.endswith("README.chromium"):
     48         readmes.append(f)
     49   if files and not readmes:
     50     errors.append(output_api.PresubmitPromptWarning(
     51        'When updating or adding third party code the appropriate\n'
     52        '\'README.chromium\' file should also be updated with the correct\n'
     53        'version and package information.', files))
     54   if not readmes:
     55     return errors
     56 
     57   name_pattern = input_api.re.compile(
     58     r'^Name: [a-zA-Z0-9_\-\. \(\)]+\r?$',
     59     input_api.re.IGNORECASE | input_api.re.MULTILINE)
     60   shortname_pattern = input_api.re.compile(
     61     r'^Short Name: [a-zA-Z0-9_\-\.]+\r?$',
     62     input_api.re.IGNORECASE | input_api.re.MULTILINE)
     63   version_pattern = input_api.re.compile(
     64     r'^Version: [a-zA-Z0-9_\-\.:]+\r?$',
     65     input_api.re.IGNORECASE | input_api.re.MULTILINE)
     66   release_pattern = input_api.re.compile(
     67     r'^Security Critical: (yes|no)\r?$',
     68     input_api.re.IGNORECASE | input_api.re.MULTILINE)
     69   license_pattern = input_api.re.compile(
     70     r'^License: (.+)\r?$',
     71     input_api.re.IGNORECASE | input_api.re.MULTILINE)
     72   license_android_compatible_pattern = input_api.re.compile(
     73     r'^License Android Compatible: (yes|no)\r?$',
     74     input_api.re.IGNORECASE | input_api.re.MULTILINE)
     75 
     76   for f in readmes:
     77     if 'D' in f.Action():
     78       _IgnoreIfDeleting(input_api, output_api, f, errors)
     79       continue
     80 
     81     contents = input_api.ReadFile(f)
     82     if (not shortname_pattern.search(contents)
     83         and not name_pattern.search(contents)):
     84       errors.append(output_api.PresubmitError(
     85         'Third party README files should contain either a \'Short Name\' or\n'
     86         'a \'Name\' which is the name under which the package is\n'
     87         'distributed. Check README.chromium.template for details.',
     88         [f]))
     89     if not version_pattern.search(contents):
     90       errors.append(output_api.PresubmitError(
     91         'Third party README files should contain a \'Version\' field.\n'
     92         'If the package is not versioned or the version is not known\n'
     93         'list the version as \'unknown\'.\n'
     94         'Check README.chromium.template for details.',
     95         [f]))
     96     if not release_pattern.search(contents):
     97       errors.append(output_api.PresubmitError(
     98         'Third party README files should contain a \'Security Critical\'\n'
     99         'field. This field specifies whether the package is built with\n'
    100         'Chromium. Check README.chromium.template for details.',
    101         [f]))
    102     license_match = license_pattern.search(contents)
    103     if not license_match:
    104       errors.append(output_api.PresubmitError(
    105         'Third party README files should contain a \'License\' field.\n'
    106         'This field specifies the license used by the package. Check\n'
    107         'README.chromium.template for details.',
    108         [f]))
    109     elif not LicenseIsCompatibleWithAndroid(input_api, license_match.group(1)) \
    110          and not license_android_compatible_pattern.search(contents):
    111       errors.append(output_api.PresubmitPromptWarning(
    112         'Cannot determine whether specified license is compatible with\n' +
    113         'the Android licensing requirements. Please check that the license\n' +
    114         'name is spelled according to third_party/PRESUBMIT.py. Please see\n' +
    115         'README.chromium.template for details.',
    116         [f]))
    117   return errors
    118 
    119 
    120 def _IgnoreIfDeleting(input_api, output_api, affected_file, errors):
    121   third_party_dir = input_api.os_path.dirname(affected_file.LocalPath())
    122   for f in input_api.AffectedFiles():
    123     if f.LocalPath().startswith(third_party_dir):
    124       if 'D' not in f.Action():
    125         errors.append(output_api.PresubmitError(
    126           'Third party README should only be removed when the whole\n'
    127           'directory is being removed.\n', [f, affected_file]))
    128 
    129 
    130 def CheckChangeOnUpload(input_api, output_api):
    131   results = []
    132   results.extend(_CheckThirdPartyReadmesUpdated(input_api, output_api))
    133   return results
    134