blob: e728cc302569f171a748ba6845a33bbacb71f551 [file] [log] [blame]
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +02001#!/usr/bin/env python
2# Copyright (C) 2015 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Chad Horohoe69142322018-05-17 10:19:22 -070016"""
17Suggested call sequence:
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +020018
Chad Horohoe69142322018-05-17 10:19:22 -070019python tools/js/bower2bazel.py -w lib/js/bower_archives.bzl \
20 -b lib/js/bower_components.bzl
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +020021"""
22
23from __future__ import print_function
24
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +020025import collections
26import json
27import hashlib
28import optparse
29import os
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +020030import subprocess
31import sys
32import tempfile
33import glob
34import bowerutil
35
Chad Horohoe69142322018-05-17 10:19:22 -070036# list of licenses for packages that don't specify one in their bower.json file
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +020037package_licenses = {
Chad Horohoedd224702018-05-16 22:33:06 -040038 "codemirror-minified": "codemirror-minified",
39 "es6-promise": "es6-promise",
40 "fetch": "fetch",
41 "font-roboto": "polymer",
42 "iron-a11y-announcer": "polymer",
43 "iron-a11y-keys-behavior": "polymer",
44 "iron-autogrow-textarea": "polymer",
45 "iron-behaviors": "polymer",
46 "iron-dropdown": "polymer",
47 "iron-fit-behavior": "polymer",
48 "iron-flex-layout": "polymer",
49 "iron-form-element-behavior": "polymer",
50 "iron-icon": "polymer",
51 "iron-iconset-svg": "polymer",
52 "iron-input": "polymer",
53 "iron-menu-behavior": "polymer",
54 "iron-meta": "polymer",
55 "iron-overlay-behavior": "polymer",
56 "iron-resizable-behavior": "polymer",
57 "iron-selector": "polymer",
58 "iron-validatable-behavior": "polymer",
59 "moment": "moment",
60 "neon-animation": "polymer",
61 "page": "page.js",
62 "paper-button": "polymer",
63 "paper-icon-button": "polymer",
64 "paper-input": "polymer",
65 "paper-item": "polymer",
66 "paper-listbox": "polymer",
67 "paper-toggle-button": "polymer",
68 "paper-styles": "polymer",
69 "paper-tabs": "polymer",
70 "polymer": "polymer",
71 "polymer-resin": "polymer",
72 "promise-polyfill": "promise-polyfill",
73 "web-animations-js": "Apache2.0",
74 "webcomponentsjs": "polymer",
75 "paper-material": "polymer",
76 "paper-styles": "polymer",
77 "paper-behaviors": "polymer",
78 "paper-ripple": "polymer",
79 "iron-checked-element-behavior": "polymer",
80 "font-roboto": "polymer",
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +020081}
82
83
Han-Wen Nienhuys3dede162017-02-01 13:33:19 +010084def build_bower_json(version_targets, seeds):
Chad Horohoedd224702018-05-16 22:33:06 -040085 """Generate bower JSON file, return its path.
Han-Wen Nienhuys3dede162017-02-01 13:33:19 +010086
Chad Horohoedd224702018-05-16 22:33:06 -040087 Args:
88 version_targets: bazel target names of the versions.json file.
89 seeds: an iterable of bower package names of the seed packages, ie.
90 the packages whose versions we control manually.
91 """
92 bower_json = collections.OrderedDict()
93 bower_json['name'] = 'bower2bazel-output'
94 bower_json['version'] = '0.0.0'
Chad Horohoe69142322018-05-17 10:19:22 -070095 bower_json['description'] = 'Auto-generated bower.json for dependency ' + \
96 'management'
Chad Horohoedd224702018-05-16 22:33:06 -040097 bower_json['private'] = True
98 bower_json['dependencies'] = {}
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +020099
Chad Horohoedd224702018-05-16 22:33:06 -0400100 seeds = set(seeds)
101 for v in version_targets:
Chad Horohoe69142322018-05-17 10:19:22 -0700102 path = os.path.join("bazel-out/*-fastbuild/bin",
103 v.lstrip("/").replace(":", "/"))
Chad Horohoedd224702018-05-16 22:33:06 -0400104 fs = glob.glob(path)
Chad Horohoe69142322018-05-17 10:19:22 -0700105 err_msg = '%s: file not found or multiple files found: %s' % (path, fs)
106 assert len(fs) == 1, err_msg
Chad Horohoedd224702018-05-16 22:33:06 -0400107 with open(fs[0]) as f:
108 j = json.load(f)
109 if "" in j:
110 # drop dummy entries.
111 del j[""]
Han-Wen Nienhuys3dede162017-02-01 13:33:19 +0100112
Chad Horohoedd224702018-05-16 22:33:06 -0400113 trimmed = {}
114 for k, v in j.items():
115 if k in seeds:
116 trimmed[k] = v
Han-Wen Nienhuys3dede162017-02-01 13:33:19 +0100117
Chad Horohoedd224702018-05-16 22:33:06 -0400118 bower_json['dependencies'].update(trimmed)
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200119
Chad Horohoedd224702018-05-16 22:33:06 -0400120 tmpdir = tempfile.mkdtemp()
121 ret = os.path.join(tmpdir, 'bower.json')
122 with open(ret, 'w') as f:
123 json.dump(bower_json, f, indent=2)
124 return ret
125
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200126
Paladox none0fb28c12018-03-28 13:34:51 +0000127def decode(input):
Chad Horohoedd224702018-05-16 22:33:06 -0400128 try:
129 return input.decode("utf-8")
130 except TypeError:
131 return input
132
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200133
134def bower_command(args):
Chad Horohoedd224702018-05-16 22:33:06 -0400135 base = subprocess.check_output(["bazel", "info", "output_base"]).strip()
136 exp = os.path.join(decode(base), "external", "bower", "*npm_binary.tgz")
137 fs = sorted(glob.glob(exp))
Chad Horohoe69142322018-05-17 10:19:22 -0700138 err_msg = "bower tarball not found or have multiple versions %s" % fs
139 assert len(fs) == 1, err_msg
140 return ["python",
141 os.getcwd() + "/tools/js/run_npm_binary.py", sorted(fs)[0]] + args
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200142
143
144def main(args):
Chad Horohoedd224702018-05-16 22:33:06 -0400145 opts = optparse.OptionParser()
146 opts.add_option('-w', help='.bzl output for WORKSPACE')
147 opts.add_option('-b', help='.bzl output for //lib:BUILD')
148 opts, args = opts.parse_args()
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200149
Chad Horohoedd224702018-05-16 22:33:06 -0400150 target_str = subprocess.check_output([
151 "bazel", "query", "kind(bower_component_bundle, //polygerrit-ui/...)"])
Chad Horohoe69142322018-05-17 10:19:22 -0700152 seed_str = subprocess.check_output(
153 ["bazel", "query",
154 "attr(seed, 1, kind(bower_component, deps(//polygerrit-ui/...)))"])
Chad Horohoedd224702018-05-16 22:33:06 -0400155 targets = [s for s in decode(target_str).split('\n') if s]
156 seeds = [s for s in decode(seed_str).split('\n') if s]
157 prefix = "//lib/js:"
158 non_seeds = [s for s in seeds if not s.startswith(prefix)]
159 assert not non_seeds, non_seeds
160 seeds = set([s[len(prefix):] for s in seeds])
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200161
Chad Horohoedd224702018-05-16 22:33:06 -0400162 version_targets = [t + "-versions.json" for t in targets]
163 subprocess.check_call(['bazel', 'build'] + version_targets)
164 bower_json_path = build_bower_json(version_targets, seeds)
165 dir = os.path.dirname(bower_json_path)
166 cmd = bower_command(["install"])
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200167
Chad Horohoedd224702018-05-16 22:33:06 -0400168 build_out = sys.stdout
169 if opts.b:
170 build_out = open(opts.b + ".tmp", 'w')
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200171
Chad Horohoedd224702018-05-16 22:33:06 -0400172 ws_out = sys.stdout
173 if opts.b:
174 ws_out = open(opts.w + ".tmp", 'w')
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200175
Chad Horohoedd224702018-05-16 22:33:06 -0400176 header = """# DO NOT EDIT
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200177# generated with the following command:
178#
179# %s
180#
181
182""" % ' '.join(sys.argv)
183
Chad Horohoedd224702018-05-16 22:33:06 -0400184 ws_out.write(header)
185 build_out.write(header)
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200186
Chad Horohoedd224702018-05-16 22:33:06 -0400187 oldwd = os.getcwd()
188 os.chdir(dir)
189 subprocess.check_call(cmd)
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200190
Chad Horohoedd224702018-05-16 22:33:06 -0400191 interpret_bower_json(seeds, ws_out, build_out)
192 ws_out.close()
193 build_out.close()
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200194
Chad Horohoedd224702018-05-16 22:33:06 -0400195 os.chdir(oldwd)
196 os.rename(opts.w + ".tmp", opts.w)
197 os.rename(opts.b + ".tmp", opts.b)
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200198
199
200def dump_workspace(data, seeds, out):
Chad Horohoedd224702018-05-16 22:33:06 -0400201 out.write('load("//tools/bzl:js.bzl", "bower_archive")\n\n')
202 out.write('def load_bower_archives():\n')
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200203
Chad Horohoedd224702018-05-16 22:33:06 -0400204 for d in data:
205 if d["name"] in seeds:
206 continue
Paladox noned2bc63c2019-04-07 21:47:26 +0000207 out.write(""" bower_archive(
208 name = "%(name)s",
209 package = "%(normalized-name)s",
210 version = "%(version)s",
211 sha1 = "%(bazel-sha1)s",
212 )
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200213""" % d)
214
215
216def dump_build(data, seeds, out):
Chad Horohoedd224702018-05-16 22:33:06 -0400217 out.write('load("//tools/bzl:js.bzl", "bower_component")\n\n')
218 out.write('def define_bower_components():\n')
219 for d in data:
Paladox noned2bc63c2019-04-07 21:47:26 +0000220 out.write(" bower_component(\n")
221 out.write(" name = \"%s\",\n" % d["name"])
222 out.write(" license = \"//lib:LICENSE-%s\",\n" % d["bazel-license"])
Chad Horohoedd224702018-05-16 22:33:06 -0400223 deps = sorted(d.get("dependencies", {}).keys())
224 if deps:
225 if len(deps) == 1:
Paladox noned2bc63c2019-04-07 21:47:26 +0000226 out.write(" deps = [\":%s\"],\n" % deps[0])
Chad Horohoedd224702018-05-16 22:33:06 -0400227 else:
Paladox noned2bc63c2019-04-07 21:47:26 +0000228 out.write(" deps = [\n")
Chad Horohoedd224702018-05-16 22:33:06 -0400229 for dep in deps:
Paladox noned2bc63c2019-04-07 21:47:26 +0000230 out.write(" \":%s\",\n" % dep)
231 out.write(" ],\n")
Chad Horohoedd224702018-05-16 22:33:06 -0400232 if d["name"] in seeds:
Paladox noned2bc63c2019-04-07 21:47:26 +0000233 out.write(" seed = True,\n")
234 out.write(" )\n")
Chad Horohoedd224702018-05-16 22:33:06 -0400235 # done
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200236
237
238def interpret_bower_json(seeds, ws_out, build_out):
Chad Horohoe69142322018-05-17 10:19:22 -0700239 out = subprocess.check_output(["find", "bower_components/", "-name",
240 ".bower.json"])
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200241
Chad Horohoedd224702018-05-16 22:33:06 -0400242 data = []
243 for f in sorted(decode(out).split('\n')):
244 if not f:
245 continue
246 pkg = json.load(open(f))
247 pkg_name = pkg["name"]
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200248
Chad Horohoedd224702018-05-16 22:33:06 -0400249 pkg["bazel-sha1"] = bowerutil.hash_bower_component(
250 hashlib.sha1(), os.path.dirname(f)).hexdigest()
251 license = package_licenses.get(pkg_name, "DO_NOT_DISTRIBUTE")
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200252
Chad Horohoedd224702018-05-16 22:33:06 -0400253 pkg["bazel-license"] = license
254 pkg["normalized-name"] = pkg["_originalSource"]
255 data.append(pkg)
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200256
Chad Horohoedd224702018-05-16 22:33:06 -0400257 dump_workspace(data, seeds, ws_out)
258 dump_build(data, seeds, build_out)
Han-Wen Nienhuys28e7a6d2016-09-21 15:03:54 +0200259
260
261if __name__ == '__main__':
Chad Horohoedd224702018-05-16 22:33:06 -0400262 main(sys.argv[1:])