blob: dee1a5324ff73e3ddf495622e1c7aa9f57b1ae86 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Build the project's dependencies."""
import html
import json
import logging
import os
import re
import sys
import nassh
import libdot
LICENSE_FILE = os.path.join(nassh.DIR, 'html', 'licenses.html')
LICENSE_TEMPLATE = os.path.join(nassh.DIR, 'html', 'licenses.html.in')
# Make sure we keep a handle on what licenses we ship. This is not the list of
# licenses that are actually used, just all the ones that we're OK with using.
ALLOWED_LICENSES = {
'Apache-2.0',
'BSD',
'BSD-2-Clause',
'BSD-3-Clause',
'CC0',
'GPL-2',
'GPL-2.0',
'GPL-3',
'GPL-3.0',
'ISC',
'LGPL',
'LGPL-2.1',
'LGPL-3',
'MIT',
'Unlicense',
'zlib',
}
# Licenses that we explicitly never want to use. Do *not* remove any of these.
BANNED_LICENSES = {
'AGPL',
'AGPL-3',
'AGPL-3+',
'CC-BY-NC',
'CC-BY-NC-ND',
'CC-BY-NC-SA',
'CPAL',
'EUPL',
'SISSL',
'SSPL',
'WTFPL',
}
assert not BANNED_LICENSES & ALLOWED_LICENSES
def mkdeps(rollup=True):
"""Build the dependencies."""
libdot.concat.concat(os.path.join(nassh.DIR, 'concat', 'nassh_deps.concat'),
os.path.join(nassh.DIR, 'js', 'nassh_deps.concat.js'))
if rollup:
libdot.node.run(['rollup', '-c'], cwd=nassh.DIR)
def concat_third_party_dir(third_party_dir):
"""Concatenate all licenses of |third_party_dir| bundles."""
ret = {}
for package in os.listdir(third_party_dir):
entry = {}
version = None
package_dir = os.path.join(third_party_dir, package)
metadata_file = os.path.join(package_dir, 'METADATA')
with open(metadata_file, 'r', encoding='utf-8') as fp:
lines = fp.readlines()
for i, line in enumerate(lines):
if 'HOMEPAGE' in line:
m = re.match(r'.*"(.*)"', lines[i + 1])
entry['repository'] = m.group(1)
elif 'version' in line:
m = re.match(r'.*"(.*)"', line)
version = m.group(1)
for path in ('LICENSE', 'LICENSE.md'):
license_file = os.path.join(package_dir, path)
if not os.path.exists(license_file):
continue
logging.debug('%s: loading %s', package, license_file)
with open(license_file, 'r', encoding='utf-8') as fp:
entry['data'] = fp.read().strip()
break
else:
raise ValueError('Unable to locate LICENSE for %s' % (package,))
ret['%s@%s' % (package, version)] = entry
return ret
def concat_local_deps():
"""Concatenate all licenses of third_party/ bundles."""
ret = {}
ret.update(concat_third_party_dir(os.path.join(nassh.DIR, 'third_party')))
ret.update(concat_third_party_dir(
os.path.join(libdot.LIBAPPS_DIR, 'ssh_client', 'third_party')))
return ret
def concat_licenses():
"""Concatenate all licenses of npm dependencies."""
ret = libdot.node.run([
'license-checker', '--search', nassh.DIR, '--onlyunknown',
'--production', '--csv',
], capture_output=True, cwd=nassh.DIR)
# 'Found error' in stderr indicates that no packages with unspecified
# licenses were found.
if b'Found error' in ret.stderr:
ret = libdot.node.run([
'license-checker', '--search', nassh.DIR, '--unknown',
'--production', '--json', '--onlyAllow',
';'.join(sorted(ALLOWED_LICENSES)),
], capture_output=True, cwd=nassh.DIR)
res = json.loads(ret.stdout.decode('utf8'))
for entry in res.values():
with open(entry['licenseFile'], 'r', encoding='utf8') as fp:
entry['data'] = fp.read().strip()
res.update(concat_local_deps())
generate_html(res)
else:
logging.error('The following packages did not specify their licenses:')
logging.error(ret.stdout.decode('utf8'))
# Template for every package/license entry.
LICENSE_ENTRY_TEMPLATE = """
<h2 class='package' id='package-%(package)s'>
<a href='%(repository)s'>%(package)s</a>
<a class='license'
id='show-%(package)s'
i18n='{"aria-label": "$id", "_": "LICENSES_EXPAND_LINK"}'
href='#'></a>
</h2>
<pre class='license' id='license-%(package)s'>
%(data)s
</pre>
"""
def generate_html(licenses):
"""Write the collected |licenses| to an HTML file."""
with open(LICENSE_TEMPLATE, 'r', encoding='utf8') as fp:
template = fp.read()
output = ''
for package in sorted(licenses):
entry = licenses[package]
output += LICENSE_ENTRY_TEMPLATE % {
'repository': entry['repository'],
'package': html.escape(package),
'data': html.escape(entry['data']),
}
output = template.replace('%%LICENSES%%', output)
with open(LICENSE_FILE, 'w', encoding='utf8') as fp:
fp.write(output)
def get_parser():
"""Get a command line parser."""
parser = libdot.ArgumentParser(description=__doc__)
parser.add_argument('--skip-rollup', dest='rollup',
action='store_false', default=True,
help='Skip (re)building of rollup dependencies.')
return parser
def main(argv):
"""The main func!"""
parser = get_parser()
opts = parser.parse_args(argv)
libdot.node_and_npm_setup()
nassh.fonts.fonts_update()
mkdeps(rollup=opts.rollup)
nassh.generate_changelog.generate_changelog()
if opts.rollup:
logging.info('Concatenating licenses...')
# We use nassh's package.json, but reuse libapps' node_modules.
libdot.symlink(
os.path.join(libdot.LIBAPPS_DIR, 'node_modules'),
os.path.join(nassh.DIR, 'node_modules'))
try:
concat_licenses()
finally:
libdot.unlink(os.path.join(nassh.DIR, 'node_modules'))
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))