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