Skip to content

Commit

Permalink
Add support for import_prefix to protobuf rule
Browse files Browse the repository at this point in the history
  • Loading branch information
Yannic committed Jun 13, 2019
1 parent 9e82c78 commit bbeaff0
Show file tree
Hide file tree
Showing 15 changed files with 297 additions and 101 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 2 additions & 13 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions closure/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand All @@ -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
8 changes: 8 additions & 0 deletions closure/protobuf/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,11 @@ closure_js_library(
"//closure/library/string",
],
)

closure_js_library(
name = "runtime",
exports = [
":jspb",
"//closure/library/array",
],
)
126 changes: 45 additions & 81 deletions closure/protobuf/closure_proto_library.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path/to/file>.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",
Expand All @@ -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([
Expand Down
89 changes: 87 additions & 2 deletions closure/protobuf/test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -51,7 +135,8 @@ closure_js_test(
],
deps = [
":foo",
"//closure/library:testing",
":import_all_closure_proto",
"//closure/library/testing:testsuite",
],
)

Expand Down Expand Up @@ -94,6 +179,6 @@ closure_js_test(
deps = [
":baz",
":foo",
"//closure/library:testing",
"//closure/library/testing:testsuite",
],
)
21 changes: 21 additions & 0 deletions closure/protobuf/test/add_prefix.proto
Original file line number Diff line number Diff line change
@@ -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;
}
1 change: 0 additions & 1 deletion closure/protobuf/test/baz_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class BazTest {

/** @return {void} */
testFooField() {
// This test verifies that we
const foo = new Foo('value');
assertEquals('value', foo.field());
}
Expand Down
11 changes: 7 additions & 4 deletions closure/protobuf/test/foo_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Loading

0 comments on commit bbeaff0

Please sign in to comment.