Files
gerrit/tools/js/bower2bazel.py
Han-Wen Nienhuys 3dede1653a bower2bazel: don't specify versions for non-seed packages.
The 'seed' packages are the ones whose versions are set by us in
WORKSPACE. We should not set the versions for the rest of the packages
in the bower input JSON, so bower can suggest the right versions to
use.

Change-Id: I9b75f16655d049e2064726862980a339c91dd534
2017-02-01 13:33:55 +01:00

232 lines
6.9 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright (C) 2015 The Android Open Source Project
#
# 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
#
# http://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.
"""Suggested call sequence:
python tools/js/bower2bazel.py -w lib/js/bower_archives.bzl -b lib/js/bower_components.bzl
"""
from __future__ import print_function
import collections
import json
import hashlib
import optparse
import os
import subprocess
import sys
import tempfile
import glob
import bowerutil
# list of licenses for packages that don't specify one in their bower.json file.
package_licenses = {
"es6-promise": "es6-promise",
"fetch": "fetch",
"iron-a11y-announcer": "polymer",
"iron-a11y-keys-behavior": "polymer",
"iron-autogrow-textarea": "polymer",
"iron-behaviors": "polymer",
"iron-dropdown": "polymer",
"iron-fit-behavior": "polymer",
"iron-flex-layout": "polymer",
"iron-form-element-behavior": "polymer",
"iron-input": "polymer",
"iron-meta": "polymer",
"iron-overlay-behavior": "polymer",
"iron-resizable-behavior": "polymer",
"iron-selector": "polymer",
"iron-validatable-behavior": "polymer",
"moment": "moment",
"neon-animation": "polymer",
"page": "page.js",
"polymer": "polymer",
"promise-polyfill": "promise-polyfill",
"web-animations-js": "Apache2.0",
"webcomponentsjs": "polymer",
}
def build_bower_json(version_targets, seeds):
"""Generate bower JSON file, return its path.
Args:
version_targets: bazel target names of the versions.json file.
seeds: an iterable of bower package names of the seed packages, ie.
the packages whose versions we control manually.
"""
bower_json = collections.OrderedDict()
bower_json['name'] = 'bower2bazel-output'
bower_json['version'] = '0.0.0'
bower_json['description'] = 'Auto-generated bower.json for dependency management'
bower_json['private'] = True
bower_json['dependencies'] = {}
seeds = set(seeds)
for v in version_targets:
fn = os.path.join("bazel-out/local-fastbuild/bin", v.lstrip("/").replace(":", "/"))
with open(fn) as f:
j = json.load(f)
if "" in j:
# drop dummy entries.
del j[""]
trimmed = {}
for k, v in j.items():
if k in seeds:
trimmed[k] = v
bower_json['dependencies'].update(trimmed)
tmpdir = tempfile.mkdtemp()
ret = os.path.join(tmpdir, 'bower.json')
with open(ret, 'w') as f:
json.dump(bower_json, f, indent=2)
return ret
def bower_command(args):
base = subprocess.check_output(["bazel", "info", "output_base"]).strip()
exp = os.path.join(base, "external", "bower", "*npm_binary.tgz")
fs = sorted(glob.glob(exp))
assert len(fs) == 1, "bower tarball not found or have multiple versions %s" % fs
return ["python", os.getcwd() + "/tools/js/run_npm_binary.py", sorted(fs)[0]] + args
def main(args):
opts = optparse.OptionParser()
opts.add_option('-w', help='.bzl output for WORKSPACE')
opts.add_option('-b', help='.bzl output for //lib:BUILD')
opts, args = opts.parse_args()
target_str = subprocess.check_output([
"bazel", "query", "kind(bower_component_bundle, //polygerrit-ui/...)"])
seed_str = subprocess.check_output([
"bazel", "query", "attr(seed, 1, kind(bower_component, deps(//polygerrit-ui/...)))"])
targets = [s for s in target_str.split('\n') if s]
seeds = [s for s in seed_str.split('\n') if s]
prefix = "//lib/js:"
non_seeds = [s for s in seeds if not s.startswith(prefix)]
assert not non_seeds, non_seeds
seeds = set([s[len(prefix):] for s in seeds])
version_targets = [t + "-versions.json" for t in targets]
subprocess.check_call(['bazel', 'build'] + version_targets)
bower_json_path = build_bower_json(version_targets, seeds)
dir = os.path.dirname(bower_json_path)
cmd = bower_command(["install"])
build_out = sys.stdout
if opts.b:
build_out = open(opts.b + ".tmp", 'w')
ws_out = sys.stdout
if opts.b:
ws_out = open(opts.w + ".tmp", 'w')
header = """# DO NOT EDIT
# generated with the following command:
#
# %s
#
""" % ' '.join(sys.argv)
ws_out.write(header)
build_out.write(header)
oldwd = os.getcwd()
os.chdir(dir)
subprocess.check_call(cmd)
interpret_bower_json(seeds, ws_out, build_out)
ws_out.close()
build_out.close()
os.chdir(oldwd)
os.rename(opts.w + ".tmp", opts.w)
os.rename(opts.b + ".tmp", opts.b)
def dump_workspace(data, seeds, out):
out.write('load("//tools/bzl:js.bzl", "bower_archive")\n\n')
out.write('def load_bower_archives():\n')
for d in data:
if d["name"] in seeds:
continue
out.write(""" bower_archive(
name = "%(name)s",
package = "%(normalized-name)s",
version = "%(version)s",
sha1 = "%(bazel-sha1)s")
""" % d)
def dump_build(data, seeds, out):
out.write('load("//tools/bzl:js.bzl", "bower_component")\n\n')
out.write('def define_bower_components():\n')
for d in data:
out.write(" bower_component(\n")
out.write(" name = \"%s\",\n" % d["name"])
out.write(" license = \"//lib:LICENSE-%s\",\n" % d["bazel-license"])
deps = sorted(d.get("dependencies", {}).keys())
if deps:
if len(deps) == 1:
out.write(" deps = [ \":%s\" ],\n" % deps[0])
else:
out.write(" deps = [\n")
for dep in deps:
out.write(" \":%s\",\n" % dep)
out.write(" ],\n")
if d["name"] in seeds:
out.write(" seed = True,\n")
out.write(" )\n")
# done
def interpret_bower_json(seeds, ws_out, build_out):
out = subprocess.check_output(["find", "bower_components/", "-name", ".bower.json"])
data = []
for f in sorted(out.split('\n')):
if not f:
continue
pkg = json.load(open(f))
pkg_name = pkg["name"]
pkg["bazel-sha1"] = bowerutil.hash_bower_component(
hashlib.sha1(), os.path.dirname(f)).hexdigest()
license = package_licenses.get(pkg_name, "DO_NOT_DISTRIBUTE")
pkg["bazel-license"] = license
# TODO(hanwen): bower packages can also have 'fully qualified'
# names, ("PolymerElements/iron-ajax") as well as short names
# ("iron-ajax"). It is possible for bower.json files to refer to
# long names as their dependencies. If any package does this, we
# will have to either 1) strip off the prefix (typically github
# user?), or 2) build a map of short name <=> fully qualified
# name. For now, we just ignore the problem.
pkg["normalized-name"] = pkg["name"]
data.append(pkg)
dump_workspace(data, seeds, ws_out)
dump_build(data, seeds, build_out)
if __name__ == '__main__':
main(sys.argv[1:])