Merge branch 'stable-2.15' into stable-2.16
* stable-2.15:
download_file.py: Synchronize curl options with gerrit core
Change-Id: Ic3af6e838ff726a317f390b416a88f6af8756a27
diff --git a/gerrit_api.bzl b/gerrit_api.bzl
index 3536a50..3c62f95 100644
--- a/gerrit_api.bzl
+++ b/gerrit_api.bzl
@@ -5,7 +5,7 @@
gerrit_api is rule for fetching Gerrit plugin API using Bazel.
"""
-VER = "2.15.14"
+VER = "2.16.9"
def gerrit_api():
bouncycastle_repos()
@@ -13,18 +13,18 @@
maven_jar(
name = "gerrit_plugin_api",
artifact = "com.google.gerrit:gerrit-plugin-api:" + VER,
- sha1 = "72b062a380b23c9b8186d4d69c5c75cd9495de9c",
+ sha1 = "f650c16c8fdc4a7d76663f0bd720fe3055c0cbe1",
)
maven_jar(
name = "gerrit_plugin_gwtui",
artifact = "com.google.gerrit:gerrit-plugin-gwtui:" + VER,
- sha1 = "799ad200d4482b78ebabd815de5be62e111cd8be",
+ sha1 = "33516d850b4906e069046add77037a96e27e26ae",
exclude = ["com/google/gwt/*"],
)
maven_jar(
name = "gerrit_acceptance_framework",
artifact = "com.google.gerrit:gerrit-acceptance-framework:" + VER,
- sha1 = "bb8c4bc73ab4e0d539cdf0e7fb11707621797cdb",
+ sha1 = "cd48eb229a72b4e8af4e975366af570ff0c8fc5a",
)
native.bind(
name = "gerrit-plugin-api",
diff --git a/gerrit_api_maven_local.bzl b/gerrit_api_maven_local.bzl
index 3769cbb..8c4640d 100644
--- a/gerrit_api_maven_local.bzl
+++ b/gerrit_api_maven_local.bzl
@@ -5,7 +5,7 @@
gerrit_api is rule for fetching Gerrit plugin API using Bazel.
"""
-VER = "2.15.2-SNAPSHOT"
+VER = "2.16.5-SNAPSHOT"
def gerrit_api_maven_local():
bouncycastle_repos()
diff --git a/gerrit_polymer.bzl b/gerrit_polymer.bzl
new file mode 100644
index 0000000..aaf1d4a
--- /dev/null
+++ b/gerrit_polymer.bzl
@@ -0,0 +1,18 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
+
+def gerrit_polymer():
+ http_archive(
+ name = "io_bazel_rules_closure",
+ sha256 = "75c58680af5f7b938ce9fe2abe8ecd9d24c698d160c0b71a945bd100fa77632b",
+ strip_prefix = "rules_closure-10cb1a78bd6cc8927eb39c2644c0369934f4aed6",
+ urls = ["https://github.com/bazelbuild/rules_closure/archive/10cb1a78bd6cc8927eb39c2644c0369934f4aed6.tar.gz"],
+ )
+
+ # File is specific to Polymer and copied from the Closure Github -- should be
+ # synced any time there are major changes to Polymer.
+ # https://github.com/google/closure-compiler/blob/master/contrib/externs/polymer-1.0.js
+ http_file(
+ name = "polymer_closure",
+ sha256 = "5a589bdba674e1fec7188e9251c8624ebf2d4d969beb6635f9148f420d1e08b1",
+ urls = ["https://raw.githubusercontent.com/google/closure-compiler/775609aad61e14aef289ebec4bfc09ad88877f9e/contrib/externs/polymer-1.0.js"],
+ )
diff --git a/lib/BUILD b/lib/BUILD
new file mode 100644
index 0000000..00301d3
--- /dev/null
+++ b/lib/BUILD
@@ -0,0 +1 @@
+# Empty marker file, indicating this directory is a Bazel package.
diff --git a/lib/js/BUILD b/lib/js/BUILD
new file mode 100644
index 0000000..00301d3
--- /dev/null
+++ b/lib/js/BUILD
@@ -0,0 +1 @@
+# Empty marker file, indicating this directory is a Bazel package.
diff --git a/lib/js/externs/BUILD b/lib/js/externs/BUILD
new file mode 100644
index 0000000..fab3954
--- /dev/null
+++ b/lib/js/externs/BUILD
@@ -0,0 +1,25 @@
+# Copyright (C) 2018 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.
+
+package(
+ default_visibility = ["//visibility:public"],
+)
+
+load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library")
+
+closure_js_library(
+ name = "plugin",
+ srcs = ["plugin.js"],
+ no_closure_library = True,
+)
diff --git a/lib/js/externs/plugin.js b/lib/js/externs/plugin.js
new file mode 100644
index 0000000..c88c724
--- /dev/null
+++ b/lib/js/externs/plugin.js
@@ -0,0 +1,30 @@
+/**
+ * @license
+ * Copyright (C) 2018 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.
+ */
+
+/**
+ * @fileoverview Closure compiler externs for the Gerrit UI plugins.
+ * @externs
+ */
+
+/* eslint-disable no-var */
+
+var Gerrit = {};
+
+/**
+ * @param {!Function} callback
+ */
+Gerrit.install = function(callback) {};
diff --git a/lib/js/npm.bzl b/lib/js/npm.bzl
new file mode 100644
index 0000000..92f44bd
--- /dev/null
+++ b/lib/js/npm.bzl
@@ -0,0 +1,11 @@
+NPM_VERSIONS = {
+ "bower": "1.8.2",
+ "crisper": "2.0.2",
+ "polymer-bundler": "4.0.2",
+}
+
+NPM_SHA1S = {
+ "bower": "adf53529c8d4af02ef24fb8d5341c1419d33e2f7",
+ "crisper": "7183c58cea33632fb036c91cefd1b43e390d22a2",
+ "polymer-bundler": "6b296b6099ab5a0e93ca914cbe93e753f2395910",
+}
diff --git a/lib/polymer_externs/BUILD b/lib/polymer_externs/BUILD
new file mode 100644
index 0000000..9a45508
--- /dev/null
+++ b/lib/polymer_externs/BUILD
@@ -0,0 +1,32 @@
+# Copyright (C) 2017 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.
+
+package(
+ default_visibility = ["//visibility:public"],
+)
+
+load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library")
+
+genrule(
+ name = "polymer_closure_renamed",
+ srcs = ["@polymer_closure//file"],
+ outs = ["polymer_closure_renamed.js"],
+ cmd = "cp $< $@",
+)
+
+closure_js_library(
+ name = "polymer_closure",
+ srcs = [":polymer_closure_renamed"],
+ no_closure_library = True,
+)
diff --git a/tools/javadoc.bzl b/tools/javadoc.bzl
index 204f569..2503594 100644
--- a/tools/javadoc.bzl
+++ b/tools/javadoc.bzl
@@ -31,7 +31,7 @@
"rm -rf %s" % dir,
"mkdir %s" % dir,
" ".join([
- ctx.file._javadoc.path,
+ "%s/bin/javadoc" % ctx.attr._jdk[java_common.JavaRuntimeInfo].java_home,
"-Xdoclint:-missing",
"-protected",
"-encoding UTF-8",
@@ -62,13 +62,10 @@
"libs": attr.label_list(allow_files = False),
"pkgs": attr.string_list(),
"title": attr.string(),
- "_javadoc": attr.label(
- default = Label("@local_jdk//:bin/javadoc"),
- allow_single_file = True,
- ),
"_jdk": attr.label(
- default = Label("@local_jdk//:jdk-default"),
+ default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
allow_files = True,
+ providers = [java_common.JavaRuntimeInfo],
),
},
outputs = {"zip": "%{name}.zip"},
diff --git a/tools/js.bzl b/tools/js.bzl
new file mode 100644
index 0000000..bbae345
--- /dev/null
+++ b/tools/js.bzl
@@ -0,0 +1,521 @@
+load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_binary", "closure_js_library")
+load("//lib/js:npm.bzl", "NPM_SHA1S", "NPM_VERSIONS")
+
+NPMJS = "NPMJS"
+
+GERRIT = "GERRIT:"
+
+def _npm_tarball(name):
+ return "%s@%s.npm_binary.tgz" % (name, NPM_VERSIONS[name])
+
+def _npm_binary_impl(ctx):
+ """rule to download a NPM archive."""
+ name = ctx.name
+ version = NPM_VERSIONS[name]
+ sha1 = NPM_SHA1S[name]
+
+ dir = "%s-%s" % (name, version)
+ filename = "%s.tgz" % dir
+ base = "%s@%s.npm_binary.tgz" % (name, version)
+ dest = ctx.path(base)
+ repository = ctx.attr.repository
+ if repository == GERRIT:
+ url = "http://gerrit-maven.storage.googleapis.com/npm-packages/%s" % filename
+ elif repository == NPMJS:
+ url = "http://registry.npmjs.org/%s/-/%s" % (name, filename)
+ else:
+ fail("repository %s not in {%s,%s}" % (repository, GERRIT, NPMJS))
+
+ python = ctx.which("python")
+ script = ctx.path(ctx.attr._download_script)
+
+ args = [python, script, "-o", dest, "-u", url, "-v", sha1]
+ out = ctx.execute(args)
+ if out.return_code:
+ fail("failed %s: %s" % (args, out.stderr))
+ ctx.file("BUILD", "package(default_visibility=['//visibility:public'])\nfilegroup(name='tarball', srcs=['%s'])" % base, False)
+
+npm_binary = repository_rule(
+ attrs = {
+ "repository": attr.string(default = NPMJS),
+ # Label resolves within repo of the .bzl file.
+ "_download_script": attr.label(default = Label("//tools:download_file.py")),
+ },
+ local = True,
+ implementation = _npm_binary_impl,
+)
+
+ComponentInfo = provider()
+
+# for use in repo rules.
+def _run_npm_binary_str(ctx, tarball, args):
+ python_bin = ctx.which("python")
+ return " ".join([
+ python_bin,
+ ctx.path(ctx.attr._run_npm),
+ ctx.path(tarball),
+ ] + args)
+
+def _bower_archive(ctx):
+ """Download a bower package."""
+ download_name = "%s__download_bower.zip" % ctx.name
+ renamed_name = "%s__renamed.zip" % ctx.name
+ version_name = "%s__version.json" % ctx.name
+
+ cmd = [
+ ctx.which("python"),
+ ctx.path(ctx.attr._download_bower),
+ "-b",
+ "%s" % _run_npm_binary_str(ctx, ctx.attr._bower_archive, []),
+ "-n",
+ ctx.name,
+ "-p",
+ ctx.attr.package,
+ "-v",
+ ctx.attr.version,
+ "-s",
+ ctx.attr.sha1,
+ "-o",
+ download_name,
+ ]
+
+ out = ctx.execute(cmd)
+ if out.return_code:
+ fail("failed %s: %s" % (" ".join(cmd), out.stderr))
+
+ _bash(ctx, " && ".join([
+ "TMP=$(mktemp -d || mktemp -d -t bazel-tmp)",
+ "TZ=UTC",
+ "export UTC",
+ "cd $TMP",
+ "mkdir bower_components",
+ "cd bower_components",
+ "unzip %s" % ctx.path(download_name),
+ "cd ..",
+ "find . -exec touch -t 198001010000 '{}' ';'",
+ "zip -Xr %s bower_components" % renamed_name,
+ "cd ..",
+ "rm -rf ${TMP}",
+ ]))
+
+ dep_version = ctx.attr.semver if ctx.attr.semver else ctx.attr.version
+ ctx.file(
+ version_name,
+ '"%s":"%s#%s"' % (ctx.name, ctx.attr.package, dep_version),
+ )
+ ctx.file(
+ "BUILD",
+ "\n".join([
+ "package(default_visibility=['//visibility:public'])",
+ "filegroup(name = 'zipfile', srcs = ['%s'], )" % download_name,
+ "filegroup(name = 'version_json', srcs = ['%s'], visibility=['//visibility:public'])" % version_name,
+ ]),
+ False,
+ )
+
+def _bash(ctx, cmd):
+ cmd_list = ["bash", "-c", cmd]
+ out = ctx.execute(cmd_list)
+ if out.return_code:
+ fail("failed %s: %s" % (" ".join(cmd_list), out.stderr))
+
+bower_archive = repository_rule(
+ _bower_archive,
+ attrs = {
+ "package": attr.string(mandatory = True),
+ "semver": attr.string(),
+ "sha1": attr.string(mandatory = True),
+ "version": attr.string(mandatory = True),
+ "_bower_archive": attr.label(default = Label("@bower//:%s" % _npm_tarball("bower"))),
+ "_download_bower": attr.label(default = Label("//tools/js:download_bower.py")),
+ "_run_npm": attr.label(default = Label("//tools/js:run_npm_binary.py")),
+ },
+)
+
+def _bower_component_impl(ctx):
+ transitive_zipfiles = depset(
+ direct = [ctx.file.zipfile],
+ transitive = [d[ComponentInfo].transitive_zipfiles for d in ctx.attr.deps],
+ )
+
+ transitive_licenses = depset(
+ direct = [ctx.file.license],
+ transitive = [d[ComponentInfo].transitive_licenses for d in ctx.attr.deps],
+ )
+
+ transitive_versions = depset(
+ direct = ctx.files.version_json,
+ transitive = [d[ComponentInfo].transitive_versions for d in ctx.attr.deps],
+ )
+
+ return [
+ ComponentInfo(
+ transitive_licenses = transitive_licenses,
+ transitive_versions = transitive_versions,
+ transitive_zipfiles = transitive_zipfiles,
+ ),
+ ]
+
+_common_attrs = {
+ "deps": attr.label_list(providers = [ComponentInfo]),
+}
+
+def _js_component(ctx):
+ dir = ctx.outputs.zip.path + ".dir"
+ name = ctx.outputs.zip.basename
+ if name.endswith(".zip"):
+ name = name[:-4]
+ dest = "%s/%s" % (dir, name)
+ cmd = " && ".join([
+ "TZ=UTC",
+ "export TZ",
+ "mkdir -p %s" % dest,
+ "cp %s %s/" % (" ".join([s.path for s in ctx.files.srcs]), dest),
+ "cd %s" % dir,
+ "find . -exec touch -t 198001010000 '{}' ';'",
+ "zip -Xqr ../%s *" % ctx.outputs.zip.basename,
+ ])
+
+ ctx.actions.run_shell(
+ inputs = ctx.files.srcs,
+ outputs = [ctx.outputs.zip],
+ command = cmd,
+ mnemonic = "GenBowerZip",
+ )
+
+ licenses = []
+ if ctx.file.license:
+ licenses.append(ctx.file.license)
+
+ return [
+ ComponentInfo(
+ transitive_licenses = depset(licenses),
+ transitive_versions = depset(),
+ transitive_zipfiles = list([ctx.outputs.zip]),
+ ),
+ ]
+
+js_component = rule(
+ _js_component,
+ attrs = dict(_common_attrs.items() + {
+ "srcs": attr.label_list(allow_files = [".js"]),
+ "license": attr.label(allow_single_file = True),
+ }.items()),
+ outputs = {
+ "zip": "%{name}.zip",
+ },
+)
+
+_bower_component = rule(
+ _bower_component_impl,
+ attrs = dict(_common_attrs.items() + {
+ "license": attr.label(allow_single_file = True),
+
+ # If set, define by hand, and don't regenerate this entry in bower2bazel.
+ "seed": attr.bool(default = False),
+ "version_json": attr.label(allow_files = [".json"]),
+ "zipfile": attr.label(allow_single_file = [".zip"]),
+ }.items()),
+)
+
+# TODO(hanwen): make license mandatory.
+def bower_component(name, license = None, **kwargs):
+ prefix = "//lib:LICENSE-"
+ if license and not license.startswith(prefix):
+ license = prefix + license
+ _bower_component(
+ name = name,
+ license = license,
+ zipfile = "@%s//:zipfile" % name,
+ version_json = "@%s//:version_json" % name,
+ **kwargs
+ )
+
+def _bower_component_bundle_impl(ctx):
+ """A bunch of bower components zipped up."""
+ zips = depset()
+ for d in ctx.attr.deps:
+ files = d[ComponentInfo].transitive_zipfiles
+
+ # TODO(davido): Make sure the field always contains a depset
+ if type(files) == "list":
+ files = depset(files)
+ zips = depset(transitive = [zips, files])
+
+ versions = depset(transitive = [d[ComponentInfo].transitive_versions for d in ctx.attr.deps])
+
+ licenses = depset(transitive = [d[ComponentInfo].transitive_versions for d in ctx.attr.deps])
+
+ out_zip = ctx.outputs.zip
+ out_versions = ctx.outputs.version_json
+
+ ctx.actions.run_shell(
+ inputs = zips.to_list(),
+ outputs = [out_zip],
+ command = " && ".join([
+ "p=$PWD",
+ "TZ=UTC",
+ "export TZ",
+ "rm -rf %s.dir" % out_zip.path,
+ "mkdir -p %s.dir/bower_components" % out_zip.path,
+ "cd %s.dir/bower_components" % out_zip.path,
+ "for z in %s; do unzip -q $p/$z ; done" % " ".join(sorted([z.path for z in zips.to_list()])),
+ "cd ..",
+ "find . -exec touch -t 198001010000 '{}' ';'",
+ "zip -Xqr $p/%s bower_components/*" % out_zip.path,
+ ]),
+ mnemonic = "BowerCombine",
+ )
+
+ ctx.actions.run_shell(
+ inputs = versions.to_list(),
+ outputs = [out_versions],
+ mnemonic = "BowerVersions",
+ command = "(echo '{' ; for j in %s ; do cat $j; echo ',' ; done ; echo \\\"\\\":\\\"\\\"; echo '}') > %s" % (" ".join([v.path for v in versions.to_list()]), out_versions.path),
+ )
+
+ return [
+ ComponentInfo(
+ transitive_licenses = licenses,
+ transitive_versions = versions,
+ transitive_zipfiles = zips,
+ ),
+ ]
+
+bower_component_bundle = rule(
+ _bower_component_bundle_impl,
+ attrs = _common_attrs,
+ outputs = {
+ "version_json": "%{name}-versions.json",
+ "zip": "%{name}.zip",
+ },
+)
+
+def _bundle_impl(ctx):
+ """Groups a set of .html and .js together in a zip file.
+
+ Outputs:
+ NAME-versions.json:
+ a JSON file containing a PKG-NAME => PKG-NAME#VERSION mapping for the
+ transitive dependencies.
+ NAME.zip:
+ a zip file containing the transitive dependencies for this bundle.
+ """
+
+ # intermediate artifact if split is wanted.
+ if ctx.attr.split:
+ bundled = ctx.actions.declare_file(ctx.outputs.html.path + ".bundled.html")
+ else:
+ bundled = ctx.outputs.html
+ destdir = ctx.outputs.html.path + ".dir"
+ zips = [z for d in ctx.attr.deps for z in d[ComponentInfo].transitive_zipfiles.to_list()]
+
+ hermetic_npm_binary = " ".join([
+ "python",
+ "$p/" + ctx.file._run_npm.path,
+ "$p/" + ctx.file._bundler_archive.path,
+ "--inline-scripts",
+ "--inline-css",
+ "--strip-comments",
+ "--out-file",
+ "$p/" + bundled.path,
+ ctx.file.app.path,
+ ])
+
+ pkg_dir = ctx.attr.pkg.lstrip("/")
+ cmd = " && ".join([
+ # unpack dependencies.
+ "export PATH",
+ "p=$PWD",
+ "rm -rf %s" % destdir,
+ "mkdir -p %s/%s/bower_components" % (destdir, pkg_dir),
+ "for z in %s; do unzip -qd %s/%s/bower_components/ $z; done" % (
+ " ".join([z.path for z in zips]),
+ destdir,
+ pkg_dir,
+ ),
+ "tar -cf - %s | tar -C %s -xf -" % (" ".join([s.path for s in ctx.files.srcs]), destdir),
+ "cd %s" % destdir,
+ hermetic_npm_binary,
+ ])
+
+ # Node/NPM is not (yet) hermeticized, so we have to get the binary
+ # from the environment, and it may be under $HOME, so we can't run
+ # in the sandbox.
+ node_tweaks = dict(
+ execution_requirements = {"local": "1"},
+ use_default_shell_env = True,
+ )
+ ctx.actions.run_shell(
+ mnemonic = "Bundle",
+ inputs = [
+ ctx.file._run_npm,
+ ctx.file.app,
+ ctx.file._bundler_archive,
+ ] + list(zips) + ctx.files.srcs,
+ outputs = [bundled],
+ command = cmd,
+ **node_tweaks
+ )
+
+ if ctx.attr.split:
+ hermetic_npm_command = "export PATH && " + " ".join([
+ "python",
+ ctx.file._run_npm.path,
+ ctx.file._crisper_archive.path,
+ "--always-write-script",
+ "--source",
+ bundled.path,
+ "--html",
+ ctx.outputs.html.path,
+ "--js",
+ ctx.outputs.js.path,
+ ])
+
+ ctx.actions.run_shell(
+ mnemonic = "Crisper",
+ inputs = [
+ ctx.file._run_npm,
+ ctx.file.app,
+ ctx.file._crisper_archive,
+ bundled,
+ ],
+ outputs = [ctx.outputs.js, ctx.outputs.html],
+ command = hermetic_npm_command,
+ **node_tweaks
+ )
+
+def _bundle_output_func(name, split):
+ _ignore = [name] # unused.
+ out = {"html": "%{name}.html"}
+ if split:
+ out["js"] = "%{name}.js"
+ return out
+
+_bundle_rule = rule(
+ _bundle_impl,
+ attrs = {
+ "srcs": attr.label_list(allow_files = [
+ ".js",
+ ".html",
+ ".txt",
+ ".css",
+ ".ico",
+ ]),
+ "app": attr.label(
+ mandatory = True,
+ allow_single_file = True,
+ ),
+ "pkg": attr.string(mandatory = True),
+ "split": attr.bool(default = True),
+ "deps": attr.label_list(providers = [ComponentInfo]),
+ "_bundler_archive": attr.label(
+ default = Label("@polymer-bundler//:%s" % _npm_tarball("polymer-bundler")),
+ allow_single_file = True,
+ ),
+ "_crisper_archive": attr.label(
+ default = Label("@crisper//:%s" % _npm_tarball("crisper")),
+ allow_single_file = True,
+ ),
+ "_run_npm": attr.label(
+ default = Label("//tools/js:run_npm_binary.py"),
+ allow_single_file = True,
+ ),
+ },
+ outputs = _bundle_output_func,
+)
+
+def bundle_assets(*args, **kwargs):
+ """Combine html, js, css files and optionally split into js and html bundles."""
+ _bundle_rule(pkg = native.package_name(), *args, **kwargs)
+
+def polygerrit_plugin(name, app, srcs = [], assets = None, **kwargs):
+ """Bundles plugin dependencies for deployment.
+
+ This rule bundles all Polymer elements and JS dependencies into .html and .js files.
+ Run-time dependencies (e.g. JS libraries loaded after plugin starts) should be provided using "assets" property.
+ Output of this rule is a FileSet with "${name}_fs", with deploy artifacts in "plugins/${name}/static".
+
+ Args:
+ name: String, plugin name.
+ app: String, the main or root source file.
+ assets: Fileset, additional files to be used by plugin in runtime, exported to "plugins/${name}/static".
+ srcs: Source files required for combining.
+ """
+
+ # Combines all .js and .html files into foo_combined.js and foo_combined.html
+ _bundle_rule(
+ name = name + "_combined",
+ app = app,
+ srcs = srcs if app in srcs else srcs + [app],
+ pkg = native.package_name(),
+ **kwargs
+ )
+
+ closure_js_binary(
+ name = name + "_bin",
+ compilation_level = "WHITESPACE_ONLY",
+ defs = [
+ "--polymer_version=1",
+ "--language_out=ECMASCRIPT6",
+ "--rewrite_polyfills=false",
+ ],
+ deps = [
+ name + "_closure_lib",
+ ],
+ )
+
+ closure_js_library(
+ name = name + "_closure_lib",
+ srcs = [name + "_combined.js"],
+ convention = "GOOGLE",
+ no_closure_library = True,
+ deps = [
+ "@com_googlesource_gerrit_bazlets//lib/polymer_externs:polymer_closure",
+ "@com_googlesource_gerrit_bazlets//lib/js/externs:plugin",
+ ],
+ )
+
+ native.genrule(
+ name = name + "_rename_html",
+ srcs = [name + "_combined.html"],
+ outs = [name + ".html"],
+ cmd = "sed 's/<script src=\"" + name + "_combined.js\"/<script src=\"" + name + ".js\"/g' $(SRCS) > $(OUTS)",
+ output_to_bindir = True,
+ )
+
+ native.genrule(
+ name = name + "_rename_js",
+ srcs = [name + "_bin.js"],
+ outs = [name + ".js"],
+ cmd = "cp $< $@",
+ output_to_bindir = True,
+ )
+
+ static_files = [
+ name + ".js",
+ name + ".html",
+ ]
+
+ if assets:
+ nested, direct = [], []
+ for x in assets:
+ target = nested if "/" in x else direct
+ target.append(x)
+
+ static_files += direct
+
+ if nested:
+ native.genrule(
+ name = name + "_copy_assets",
+ srcs = assets,
+ outs = [f.split("/")[-1] for f in nested],
+ cmd = "cp $(SRCS) $(@D)",
+ output_to_bindir = True,
+ )
+ static_files += [":" + name + "_copy_assets"]
+
+ native.filegroup(
+ name = name,
+ srcs = static_files,
+ )
diff --git a/tools/js/BUILD b/tools/js/BUILD
new file mode 100644
index 0000000..fedaf7f
--- /dev/null
+++ b/tools/js/BUILD
@@ -0,0 +1 @@
+exports_files(["run_npm_binary.py"])
diff --git a/tools/js/run_npm_binary.py b/tools/js/run_npm_binary.py
new file mode 100644
index 0000000..bdee5ab
--- /dev/null
+++ b/tools/js/run_npm_binary.py
@@ -0,0 +1,102 @@
+#!/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.
+
+from __future__ import print_function
+
+import atexit
+from distutils import spawn
+import hashlib
+import os
+import shutil
+import subprocess
+import sys
+import tarfile
+import tempfile
+
+
+def extract(path, outdir, bin):
+ if os.path.exists(os.path.join(outdir, bin)):
+ return # Another process finished extracting, ignore.
+
+ # Use a temp directory adjacent to outdir so shutil.move can use the same
+ # device atomically.
+ tmpdir = tempfile.mkdtemp(dir=os.path.dirname(outdir))
+
+ def cleanup():
+ try:
+ shutil.rmtree(tmpdir)
+ except OSError:
+ pass # Too late now
+ atexit.register(cleanup)
+
+ def extract_one(mem):
+ dest = os.path.join(outdir, mem.name)
+ tar.extract(mem, path=tmpdir)
+ try:
+ os.makedirs(os.path.dirname(dest))
+ except OSError:
+ pass # Either exists, or will fail on the next line.
+ shutil.move(os.path.join(tmpdir, mem.name), dest)
+
+ with tarfile.open(path, 'r:gz') as tar:
+ for mem in tar.getmembers():
+ if mem.name != bin:
+ extract_one(mem)
+ # Extract bin last so other processes only short circuit when
+ # extraction is finished.
+ if bin in tar.getnames():
+ extract_one(tar.getmember(bin))
+
+
+def main(args):
+ path = args[0]
+ suffix = '.npm_binary.tgz'
+ tgz = os.path.basename(path)
+
+ parts = tgz[:-len(suffix)].split('@')
+
+ if not tgz.endswith(suffix) or len(parts) != 2:
+ print('usage: %s <path/to/npm_binary>' % sys.argv[0], file=sys.stderr)
+ return 1
+
+ name, _ = parts
+
+ # Avoid importing from gerrit because we don't want to depend on the right
+ # working directory
+ sha1 = hashlib.sha1(open(path, 'rb').read()).hexdigest()
+ outdir = '%s-%s' % (path[:-len(suffix)], sha1)
+ rel_bin = os.path.join('package', 'bin', name)
+ rel_lib_bin = os.path.join('package', 'lib', 'bin', name + '.js')
+ bin = os.path.join(outdir, rel_bin)
+ libbin = os.path.join(outdir, rel_lib_bin)
+ if not os.path.isfile(bin):
+ extract(path, outdir, rel_bin)
+
+ nodejs = spawn.find_executable('nodejs')
+ if nodejs:
+ # Debian installs Node.js as 'nodejs', due to a conflict with another
+ # package.
+ if not os.path.isfile(bin) and os.path.isfile(libbin):
+ subprocess.check_call([nodejs, libbin] + args[1:])
+ else:
+ subprocess.check_call([nodejs, bin] + args[1:])
+ elif not os.path.isfile(bin) and os.path.isfile(libbin):
+ subprocess.check_call([libbin] + args[1:])
+ else:
+ subprocess.check_call([bin] + args[1:])
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))