diff --git a/README.md b/README.md index 15da46fa14..dc2082af06 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,10 @@ http_archive( load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories") closure_repositories() + +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_register_toolchains") + +closure_register_toolchains() ``` You are not required to install the Closure Tools, PhantomJS, or anything else diff --git a/WORKSPACE b/WORKSPACE index cb9eee519f..a0a1fb04f9 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -6,20 +6,9 @@ load("//closure:repositories.bzl", "closure_repositories") closure_repositories() -http_archive( - name = "zlib", - build_file = "//:third_party/zlib.BUILD", - sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1", - strip_prefix = "zlib-1.2.11", - urls = ["https://zlib.net/zlib-1.2.11.tar.gz"], -) +load("//closure:toolchains.bzl", "closure_register_toolchains") -http_archive( - name = "bazel_skylib", - sha256 = "bbccf674aa441c266df9894182d80de104cabd19be98be002f6d478aaa31574d", - strip_prefix = "bazel-skylib-2169ae1c374aab4a09aa90e65efe1a3aad4e279b", - urls = ["https://github.com/bazelbuild/bazel-skylib/archive/2169ae1c374aab4a09aa90e65efe1a3aad4e279b.tar.gz"], -) +closure_register_toolchains() java_import_external( name = "com_google_guava_testlib", diff --git a/closure/defs.bzl b/closure/defs.bzl index 0fd1d54afb..045a5aae85 100644 --- a/closure/defs.bzl +++ b/closure/defs.bzl @@ -29,6 +29,7 @@ load("//closure/testing:closure_js_test.bzl", _closure_js_test = "closure_js_tes load("//closure/testing:phantomjs_test.bzl", _phantomjs_test = "phantomjs_test") load("//closure:filegroup_external.bzl", _filegroup_external = "filegroup_external") load("//closure:repositories.bzl", _closure_repositories = "closure_repositories") +load("//closure:toolchains.bzl", _closure_register_toolchains = "closure_register_toolchains") load("//closure:webfiles/web_library.bzl", _web_library = "web_library") load("//closure:webfiles/web_library_external.bzl", _web_library_external = "web_library_external") @@ -50,5 +51,6 @@ closure_js_test = _closure_js_test phantomjs_test = _phantomjs_test filegroup_external = _filegroup_external closure_repositories = _closure_repositories +closure_register_toolchains = _closure_register_toolchains web_library = _web_library web_library_external = _web_library_external diff --git a/closure/protobuf/BUILD b/closure/protobuf/BUILD index 91f951a592..4a8124bff1 100644 --- a/closure/protobuf/BUILD +++ b/closure/protobuf/BUILD @@ -36,3 +36,11 @@ closure_js_library( "//closure/library/string", ], ) + +closure_js_library( + name = "runtime", + exports = [ + ":jspb", + "//closure/library/array", + ], +) diff --git a/closure/protobuf/closure_proto_library.bzl b/closure/protobuf/closure_proto_library.bzl index f3ee6fb2b6..0f5f13a106 100644 --- a/closure/protobuf/closure_proto_library.bzl +++ b/closure/protobuf/closure_proto_library.bzl @@ -17,79 +17,50 @@ load("//closure/compiler:closure_js_library.bzl", "create_closure_js_library") load("//closure/private:defs.bzl", "CLOSURE_JS_TOOLCHAIN_ATTRS", "unfurl") +load("@build_bazel_rules_proto//proto:proto_common.bzl", "proto_common") -# This was borrowed from Rules Go, licensed under Apache 2. -# https://github.com/bazelbuild/rules_go/blob/67f44035d84a352cffb9465159e199066ecb814c/proto/compiler.bzl#L72 -def _proto_path(proto): - path = proto.path - root = proto.root.path - ws = proto.owner.workspace_root - if path.startswith(root): - path = path[len(root):] - if path.startswith("/"): - path = path[1:] - if path.startswith(ws): - path = path[len(ws):] - if path.startswith("/"): - path = path[1:] - return path - -def _proto_include_path(proto): - path = proto.path[:-len(_proto_path(proto))] - if not path: - return "." - if path.endswith("/"): - path = path[:-1] - return path - -def _proto_include_paths(protos): - return depset([_proto_include_path(proto) for proto in protos.to_list()]) - -def _generate_closure_js_progress_message(name): - # TODO(yannic): Add a better message? - return "Generating JavaScript Protocol Buffer %s" % name - -def _generate_closure_js(target, ctx): - # Support only `import_style=closure`, and always add - # |goog.require()| for enums. - js_out_options = [ - "import_style=closure", - "library=%s" % ctx.label.name, - "add_require_for_enums", - ] - if getattr(ctx.rule.attr, "testonly", False): - js_out_options.append("testonly") - js = ctx.actions.declare_file("%s.js" % ctx.label.name) - - # Add include paths for all proto files, - # to avoid copying/linking the files for every target. - protos = target[ProtoInfo].transitive_imports - args = ["-I%s" % p for p in _proto_include_paths(protos).to_list()] - - out_options = ",".join(js_out_options) - out_path = "/".join(js.path.split("/")[:-1]) - args += ["--js_out=%s:%s" % (out_options, out_path)] - - # Add paths of protos we generate files for. - args += [file.path for file in target[ProtoInfo].direct_sources] - - ctx.actions.run( - inputs = protos, - outputs = [js], - executable = ctx.executable._protoc, - arguments = args, - progress_message = - _generate_closure_js_progress_message(ctx.rule.attr.name), - ) +def _closure_proto_output_name(src): + # src is of format .proto + return ["{}.js".format(src[:-6])] + +def _closure_proto_options(params): + options = [] + + # The framework in rules_protobuf requires that we generate one output + # file for every input file. + options.append("one_output_file_per_input_file") + + # Only `import_style=closure` is supported for now. + # See https://github.com/grpc/grpc-web/pull/260#issuecomment-415443448 + options.append("import_style=closure") + + # Enums are allways |goog.require()|'d in the generated files to avoid + # namespace-not-provided if no library |goog.require()|'s them. + options.append("add_require_for_enums") - return js + # If the proto_library is marked as testonly, tell protoc to add + # |goog.setTestOnly()| to the generated code. + if params.testonly: + options.append("testonly") + + return ",".join(options) def _closure_proto_aspect_impl(target, ctx): - js = _generate_closure_js(target, ctx) + srcs = proto_common.lang_proto_aspect_impl( + actions = ctx.actions, + toolchain = ctx.toolchains[proto_common.TOOLCHAIN_TYPE], + flavor = "Closure", + get_generator_options = _closure_proto_options, + get_generator_options_params = struct( + testonly = getattr(ctx.rule.attr, "testonly", False), + ), + get_output_files = _closure_proto_output_name, + language = "js", + target = target, + ) - srcs = depset([js]) deps = unfurl(ctx.rule.attr.deps, provider = "closure_js_library") - deps += [ctx.attr._closure_library, ctx.attr._closure_protobuf_jspb] + deps += [ctx.attr._closure_protobuf_runtime] suppress = [ "missingProperties", @@ -100,27 +71,20 @@ def _closure_proto_aspect_impl(target, ctx): return struct( exports = library.exports, closure_js_library = library.closure_js_library, - # The usual suspects are exported as runfiles, in addition to raw source. - runfiles = ctx.runfiles(files = [js]), ) -closure_proto_aspect = aspect( - attr_aspects = ["deps"], +closure_proto_aspect = proto_common.lang_proto_aspect( attrs = dict({ # internal only - "_protoc": attr.label( - default = Label("@com_google_protobuf//:protoc"), - executable = True, - cfg = "host", - ), - "_closure_library": attr.label( - default = Label("//closure/library/array"), - ), - "_closure_protobuf_jspb": attr.label( - default = Label("//closure/protobuf:jspb"), + "_closure_protobuf_runtime": attr.label( + default = Label("//closure/protobuf:runtime"), ), }, **CLOSURE_JS_TOOLCHAIN_ATTRS), implementation = _closure_proto_aspect_impl, + provides = [ + "closure_js_library", + "exports", + ], ) _error_multiple_deps = "".join([ diff --git a/closure/protobuf/test/BUILD b/closure/protobuf/test/BUILD index addf5387e9..777bd78aa6 100644 --- a/closure/protobuf/test/BUILD +++ b/closure/protobuf/test/BUILD @@ -19,6 +19,90 @@ licenses(["notice"]) # Apache 2.0 load("//closure/compiler:closure_js_library.bzl", "closure_js_library") load("//closure/protobuf:closure_proto_library.bzl", "closure_proto_library") load("//closure/testing:closure_js_test.bzl", "closure_js_test") +load("@build_bazel_rules_proto//proto:proto_library.bzl", "proto_library") + +# TODO(yannic): Add test for attr `proto_source_root`. + +proto_library( + name = "add_prefix_proto", + srcs = [ + "add_prefix.proto", + ], + import_prefix = "foo", +) + +closure_proto_library( + name = "add_prefix_closure_proto", + deps = [ + ":add_prefix_proto", + ], +) + +proto_library( + name = "add_and_strip_prefix_proto", + srcs = [ + "import_prefix/add_and_strip_prefix.proto", + ], + import_prefix = "bar", + strip_import_prefix = "import_prefix", +) + +closure_proto_library( + name = "add_and_strip_prefix_closure_proto", + deps = [ + ":add_and_strip_prefix_proto", + ], +) + +proto_library( + name = "no_prefix_proto", + srcs = [ + "no_prefix.proto", + ], +) + +closure_proto_library( + name = "no_prefix_closure_proto", + deps = [ + ":no_prefix_proto", + ], +) + +proto_library( + name = "strip_prefix_proto", + srcs = [ + "import_prefix/strip_prefix.proto", + ], + strip_import_prefix = "import_prefix", +) + +closure_proto_library( + name = "strip_prefix_closure_proto", + deps = [ + ":strip_prefix_proto", + ], +) + +proto_library( + name = "import_all_proto", + srcs = [ + "import_all.proto", + ], + deps = [ + ":add_and_strip_prefix_proto", + ":add_prefix_proto", + ":no_prefix_proto", + ":strip_prefix_proto", + ], +) + +closure_proto_library( + name = "import_all_closure_proto", + tags = ["manual"], + deps = [ + ":import_all_proto", + ], +) proto_library( name = "foo_proto", @@ -51,7 +135,8 @@ closure_js_test( ], deps = [ ":foo", - "//closure/library:testing", + ":import_all_closure_proto", + "//closure/library/testing:testsuite", ], ) @@ -94,6 +179,6 @@ closure_js_test( deps = [ ":baz", ":foo", - "//closure/library:testing", + "//closure/library/testing:testsuite", ], ) diff --git a/closure/protobuf/test/add_prefix.proto b/closure/protobuf/test/add_prefix.proto new file mode 100644 index 0000000000..1e9176f798 --- /dev/null +++ b/closure/protobuf/test/add_prefix.proto @@ -0,0 +1,21 @@ +// Copyright 2018 The Closure Rules Authors. All rights reserved. +// +// 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. + +syntax = "proto3"; + +package io.bazel.rules.closure.protobuf.foo; + +message AddPrefix { + string foo = 1; +} diff --git a/closure/protobuf/test/baz_test.js b/closure/protobuf/test/baz_test.js index 4da6da34da..2b8cdbcfeb 100644 --- a/closure/protobuf/test/baz_test.js +++ b/closure/protobuf/test/baz_test.js @@ -31,7 +31,6 @@ class BazTest { /** @return {void} */ testFooField() { - // This test verifies that we const foo = new Foo('value'); assertEquals('value', foo.field()); } diff --git a/closure/protobuf/test/foo_test.js b/closure/protobuf/test/foo_test.js index e842cff690..356994e0ee 100644 --- a/closure/protobuf/test/foo_test.js +++ b/closure/protobuf/test/foo_test.js @@ -16,18 +16,21 @@ goog.module('io.bazel.rules.closure.protobuf.FooTest'); goog.setTestOnly('io.bazel.rules.closure.protobuf.FooTest'); const Foo = goog.require('io.bazel.rules.closure.protobuf.Foo'); +const ImportAll = goog.require('proto.io.bazel.rules.closure.protobuf.ImportAll'); const testSuite = goog.require('goog.testing.testSuite'); class FooTest { - /** - * @return {void} - * @suppress {visibility} - */ + /** @return {void} */ testField() { const foo = new Foo('value'); assertEquals('value', foo.field()); } + + /** @return {void} */ + testImportAll() { + assertNotNull(new ImportAll()); + } } testSuite(new FooTest()); diff --git a/closure/protobuf/test/import_all.proto b/closure/protobuf/test/import_all.proto new file mode 100644 index 0000000000..516ac97b0f --- /dev/null +++ b/closure/protobuf/test/import_all.proto @@ -0,0 +1,29 @@ +// Copyright 2018 The Closure Rules Authors. All rights reserved. +// +// 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. + +syntax = "proto3"; + +package io.bazel.rules.closure.protobuf; + +import "bar/add_and_strip_prefix.proto"; +import "closure/protobuf/test/no_prefix.proto"; +import "foo/closure/protobuf/test/add_prefix.proto"; +import "strip_prefix.proto"; + +message ImportAll { + NoPrefix no_prefix = 1; + StripPrefix strip_prefix = 2; + bar.AddAndStripPrefix add_and_strip_prefix = 3; + foo.AddPrefix add_prefix = 4; +} diff --git a/closure/protobuf/test/import_prefix/add_and_strip_prefix.proto b/closure/protobuf/test/import_prefix/add_and_strip_prefix.proto new file mode 100644 index 0000000000..0ac2659526 --- /dev/null +++ b/closure/protobuf/test/import_prefix/add_and_strip_prefix.proto @@ -0,0 +1,21 @@ +// Copyright 2018 The Closure Rules Authors. All rights reserved. +// +// 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. + +syntax = "proto3"; + +package bar; + +message AddAndStripPrefix { + string foo = 1; +} diff --git a/closure/protobuf/test/import_prefix/strip_prefix.proto b/closure/protobuf/test/import_prefix/strip_prefix.proto new file mode 100644 index 0000000000..ffb141534f --- /dev/null +++ b/closure/protobuf/test/import_prefix/strip_prefix.proto @@ -0,0 +1,19 @@ +// Copyright 2018 The Closure Rules Authors. All rights reserved. +// +// 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. + +syntax = "proto3"; + +message StripPrefix { + string foo = 1; +} diff --git a/closure/protobuf/test/no_prefix.proto b/closure/protobuf/test/no_prefix.proto new file mode 100644 index 0000000000..87ed2ec6fc --- /dev/null +++ b/closure/protobuf/test/no_prefix.proto @@ -0,0 +1,21 @@ +// Copyright 2018 The Closure Rules Authors. All rights reserved. +// +// 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. + +syntax = "proto3"; + +package io.bazel.rules.closure.protobuf; + +message NoPrefix { + string foo = 1; +} diff --git a/closure/repositories.bzl b/closure/repositories.bzl index 3e459520f3..df985a47ee 100644 --- a/closure/repositories.bzl +++ b/closure/repositories.bzl @@ -22,6 +22,7 @@ def closure_repositories( omit_aopalliance = False, omit_args4j = False, omit_bazel_skylib = False, + omit_build_bazel_rules_proto = False, omit_clang = False, omit_com_google_auto_common = False, omit_com_google_auto_factory = False, @@ -79,6 +80,8 @@ def closure_repositories( args4j() if not omit_bazel_skylib: bazel_skylib() + if not omit_build_bazel_rules_proto: + build_bazel_rules_proto() if not omit_clang: clang() if not omit_com_google_auto_common: @@ -207,6 +210,14 @@ def bazel_skylib(): urls = ["https://github.com/bazelbuild/bazel-skylib/archive/2169ae1c374aab4a09aa90e65efe1a3aad4e279b.tar.gz"], ) +def build_bazel_rules_proto(): + http_archive( + name = "build_bazel_rules_proto", + sha256 = "26ef80422c978cd0590734c103bc66dae6932989542845efb365fcc48b6d810f", + strip_prefix = "rules_proto-6b247dbf28aa3b6f485047c938731ec8c2453a83", + urls = ["https://github.com/Yannic/rules_proto/archive/6b247dbf28aa3b6f485047c938731ec8c2453a83.tar.gz"], + ) + def clang(): platform_http_file( name = "clang", diff --git a/closure/toolchains.bzl b/closure/toolchains.bzl new file mode 100644 index 0000000000..b03c9c7d68 --- /dev/null +++ b/closure/toolchains.bzl @@ -0,0 +1,20 @@ +# Copyright 2019 The Closure Rules Authors. All rights reserved. +# +# 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. + +"""Toolchains for Closure Rules.""" + +load("@build_bazel_rules_proto//proto:workspace.bzl", "proto_register_toolchains") + +def closure_register_toolchains(): + proto_register_toolchains()