• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2020 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# https://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Proof of concept license checker.
17
18This is only a demonstration. It will be replaced with other tools.
19"""
20
21import argparse
22import codecs
23import json
24
25# Conditions allowed for all applications
26_ALWAYS_ALLOWED_CONDITIONS = frozenset(['notice', 'permissive', 'unencumbered'])
27
28
29def _load_license_data(licenses_info):
30  with codecs.open(licenses_info, encoding='utf-8') as licenses_file:
31    return json.loads(licenses_file.read())
32
33
34def _do_report(out, licenses):
35  """Produce a report showing the set of licenses being used.
36
37  Args:
38    out: file object to write to
39    licenses: list of LicenseInfo objects
40
41  Returns:
42    0 for no restricted licenses.
43  """
44
45  for lic in licenses:
46    print("lic:", lic)
47    rule = lic['rule']
48    for kind in lic['license_kinds']:
49      out.write('= %s\n  kind: %s\n' % (rule, kind['target']))
50      out.write('  conditions: %s\n' % kind['conditions'])
51
52
53def _check_conditions(out, licenses, allowed_conditions):
54  """Check that the application does not use any disallowed licenses.
55
56  Args:
57    out: file object to write to
58    licenses: list of LicenseInfo objects
59    allowed_conditions: list of allowed condition names
60
61  Returns:
62    0 for no licenses from outside allowed_conditions.
63  """
64  err = 0
65  for lic in licenses:  # using strange name lic because license is built-in
66    rule = lic['rule']
67    for kind in lic['license_kinds']:
68      disallowed = []
69      for condition in kind['conditions']:
70        if condition not in allowed_conditions:
71          disallowed.append(condition)
72      if disallowed:
73        out.write('ERROR: %s\n' % rule)
74        out.write('   kind: %s\n' % kind['target'])
75        out.write('   conditions: %s\n' % kind['conditions'])
76        out.write('   disallowed condition: %s\n' % ','.join(disallowed))
77        err += 1
78  return err
79
80
81def _do_copyright_notices(out, licenses):
82  for l in licenses:
83    name = l.get('package_name') or '<unknown>'
84    if l.get('package_version'):
85      name =  name + "/" + l['package_version']
86    # IGNORE_COPYRIGHT: Not a copyright notice. It is a variable holding one.
87    out.write('package(%s), copyright(%s)\n' % (name, l['copyright_notice']))
88
89
90def _do_licenses(out, licenses):
91  for lic in licenses:
92    path = lic['license_text']
93    with codecs.open(path, encoding='utf-8') as license_file:
94      out.write('= %s\n' % path)
95      out.write(license_file.read())
96
97
98def main():
99  parser = argparse.ArgumentParser(
100      description='Demonstraton license compliance checker')
101
102  parser.add_argument('--licenses_info',
103                      help='path to JSON file containing all license data')
104  parser.add_argument('--report', default='report', help='Summary report')
105  parser.add_argument('--copyright_notices',
106                      help='output file of all copyright notices')
107  parser.add_argument('--license_texts', help='output file of all license files')
108  parser.add_argument('--check_conditions', action='store_true',
109                      help='check that the dep only includes allowed license conditions')
110  args = parser.parse_args()
111
112  license_data = _load_license_data(args.licenses_info)
113  target = license_data[0]  # we assume only one target for the demo
114
115  top_level_target = target['top_level_target']
116  dependencies = target['dependencies']
117  licenses = target['licenses']
118
119  err = 0
120  with codecs.open(args.report, mode='w', encoding='utf-8') as rpt:
121    _do_report(rpt, licenses)
122    if args.check_conditions:
123      # TODO(aiuto): Read conditions from a file of allowed conditions for
124      # a specified application deployment environment.
125      err = _check_conditions(rpt, licenses, _ALWAYS_ALLOWED_CONDITIONS)
126  if args.copyright_notices:
127    with codecs.open(
128        args.copyright_notices, mode='w', encoding='utf-8') as out:
129      _do_copyright_notices(out, licenses)
130  if args.license_texts:
131    with codecs.open(args.license_texts, mode='w', encoding='utf-8') as out:
132      _do_licenses(out, licenses)
133  return err
134
135
136if __name__ == '__main__':
137  main()
138