| #!/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 = { | 
 |   "codemirror-minified": "codemirror-minified", | 
 |   "es6-promise": "es6-promise", | 
 |   "fetch": "fetch", | 
 |   "font-roboto": "polymer", | 
 |   "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-icon": "polymer", | 
 |   "iron-iconset-svg": "polymer", | 
 |   "iron-input": "polymer", | 
 |   "iron-menu-behavior": "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", | 
 |   "paper-button": "polymer", | 
 |   "paper-input": "polymer", | 
 |   "paper-item": "polymer", | 
 |   "paper-listbox": "polymer", | 
 |   "paper-styles": "polymer", | 
 |   "polymer": "polymer", | 
 |   "polymer-resin": "polymer", | 
 |   "promise-polyfill": "promise-polyfill", | 
 |   "web-animations-js": "Apache2.0", | 
 |   "webcomponentsjs": "polymer", | 
 |   "paper-material": "polymer", | 
 |   "paper-styles": "polymer", | 
 |   "paper-behaviors": "polymer", | 
 |   "paper-ripple": "polymer", | 
 |   "iron-checked-element-behavior": "polymer", | 
 |   "font-roboto": "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: | 
 |     path = os.path.join("bazel-out/*-fastbuild/bin", v.lstrip("/").replace(":", "/")) | 
 |     fs = glob.glob(path) | 
 |     assert len(fs) == 1, '%s: file not found or multiple files found: %s' % (path, fs) | 
 |     with open(fs[0]) 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 | 
 |     pkg["normalized-name"] = pkg["_originalSource"] | 
 |     data.append(pkg) | 
 |  | 
 |   dump_workspace(data, seeds, ws_out) | 
 |   dump_build(data, seeds, build_out) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   main(sys.argv[1:]) |