aboutsummaryrefslogtreecommitdiff
path: root/tools/checker_demo.py
blob: 6cdf07fb2e97377512bd309b1824d3dedaeadfa4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env python3
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Proof of concept license checker.

This is only a demonstration. It will be replaced with other tools.
"""

import argparse
import codecs
import json

# Conditions allowed for all applications
_ALWAYS_ALLOWED_CONDITIONS = frozenset(['notice', 'permissive', 'unencumbered'])


def _load_license_data(licenses_info):
  with codecs.open(licenses_info, encoding='utf-8') as licenses_file:
    return json.loads(licenses_file.read())


def unique_licenses(licenses):
  for target in licenses:
    for lic in target.get('licenses') or []:
      yield lic

def _do_report(out, licenses):
  """Produce a report showing the set of licenses being used.

  Args:
    out: file object to write to
    licenses: list of LicenseInfo objects

  Returns:
    0 for no restricted licenses.
  """

  for target in unique_licenses(licenses):
    for lic in target.get('licenses') or []:
      print("lic:", lic)
      rule = lic['rule']
      for kind in lic['license_kinds']:
        out.write('= %s\n  kind: %s\n' % (rule, kind['target']))
        out.write('  conditions: %s\n' % kind['conditions'])


def _check_conditions(out, licenses, allowed_conditions):
  """Check that the application does not use any disallowed licenses.

  Args:
    out: file object to write to
    licenses: list of LicenseInfo objects
    allowed_conditions: list of allowed condition names

  Returns:
    0 for no licenses from outside allowed_conditions.
  """
  err = 0
  for lic in licenses:  # using strange name lic because license is built-in
    rule = lic['rule']
    for kind in lic['license_kinds']:
      disallowed = []
      for condition in kind['conditions']:
        if condition not in allowed_conditions:
          disallowed.append(condition)
      if disallowed:
        out.write('ERROR: %s\n' % rule)
        out.write('   kind: %s\n' % kind['target'])
        out.write('   conditions: %s\n' % kind['conditions'])
        out.write('   disallowed condition: %s\n' % ','.join(disallowed))
        err += 1
  return err


def _do_copyright_notices(out, licenses):
  for l in licenses:
    name = l.get('package_name') or '<unknown>'
    if l.get('package_version'):
      name =  name + "/" + l['package_version']
    # IGNORE_COPYRIGHT: Not a copyright notice. It is a variable holding one.
    out.write('package(%s), copyright(%s)\n' % (name, l['copyright_notice']))


def _do_licenses(out, licenses):
  for lic in unique_licenses(licenses):
    path = lic['license_text']
    with codecs.open(path, encoding='utf-8') as license_file:
      out.write('= %s\n' % path)
      out.write(license_file.read())


def main():
  parser = argparse.ArgumentParser(
      description='Demonstraton license compliance checker')

  parser.add_argument('--licenses_info',
                      help='path to JSON file containing all license data')
  parser.add_argument('--report', default='report', help='Summary report')
  parser.add_argument('--copyright_notices', 
                      help='output file of all copyright notices')
  parser.add_argument('--license_texts', help='output file of all license files')
  parser.add_argument('--check_conditions', action='store_true',
                      help='check that the dep only includes allowed license conditions')
  args = parser.parse_args()

  license_data = _load_license_data(args.licenses_info)
  target = license_data[0]  # we assume only one target for the demo

  top_level_target = target['top_level_target']
  dependencies = target['dependencies']
  licenses = target['licenses']

  err = 0
  with codecs.open(args.report, mode='w', encoding='utf-8') as rpt:
    _do_report(rpt, licenses)
    if args.check_conditions:
      # TODO(aiuto): Read conditions from a file of allowed conditions for
      # a specified application deployment environment.
      err = _check_conditions(rpt, licenses, _ALWAYS_ALLOWED_CONDITIONS)
  if args.copyright_notices:
    with codecs.open(
        args.copyright_notices, mode='w', encoding='utf-8') as out:
      _do_copyright_notices(out, licenses)
  if args.license_texts:
    with codecs.open(args.license_texts, mode='w', encoding='utf-8') as out:
      _do_licenses(out, licenses)
  return err


if __name__ == '__main__':
  main()