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
5ANDROID_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
22def 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
33def _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
120def _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
130def CheckChangeOnUpload(input_api, output_api):
131  results = []
132  results.extend(_CheckThirdPartyReadmesUpdated(input_api, output_api))
133  return results
134