diff --git a/WORKSPACE b/WORKSPACE index a6d8ff90e..d582ff154 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,12 +1,13 @@ workspace(name = "com_github_grpc_grpc_web") +# TODO(yannic): Update to use official repository. +# See https://github.com/bazelbuild/rules_closure/pull/278 http_archive( name = "io_bazel_rules_closure", - sha256 = "a80acb69c63d5f6437b099c111480a4493bad4592015af2127a2f49fb7512d8d", - strip_prefix = "rules_closure-0.7.0", + sha256 = "248a7a98eb3962d9f0013e543ea79c5063a83bac7af349ebf412d844e6ab3035", + strip_prefix = "rules_closure-53f2cab21fa6c608f32f114387d88ffd7868c5fc", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/0.7.0.tar.gz", - "https://github.com/bazelbuild/rules_closure/archive/0.7.0.tar.gz", + "https://github.com/Yannic/rules_closure/archive/53f2cab21fa6c608f32f114387d88ffd7868c5fc.zip", ], ) diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/bazel/closure_grpc_web_library.bzl b/bazel/closure_grpc_web_library.bzl new file mode 100644 index 000000000..8537686a7 --- /dev/null +++ b/bazel/closure_grpc_web_library.bzl @@ -0,0 +1,195 @@ +# This rule was inspired by rules_closure`s implementation of +# |closure_proto_library|, licensed under Apache 2. +# https://github.com/bazelbuild/rules_closure/blob/3555e5ba61fdcc17157dd833eaf7d19b313b1bca/closure/protobuf/closure_proto_library.bzl + +load( + "@io_bazel_rules_closure//closure/compiler:closure_js_library.bzl", + "closure_js_library_impl", +) +load( + "@io_bazel_rules_closure//closure/private:defs.bzl", + "CLOSURE_WORKER_ATTR", + "CLOSURE_LIBRARY_BASE_ATTR", + "unfurl", +) +load( + "@io_bazel_rules_closure//closure/protobuf:closure_proto_library.bzl", + "closure_proto_aspect", +) + +# 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]) + +def _generate_closure_grpc_web_src_progress_message(name): + # TODO(yannic): Add a better message? + return 'Generating GRPC Web %s' % name + +def _generate_closure_grpc_web_srcs( + actions, protoc, protoc_gen_grpc_web, import_style, mode, + sources, transitive_sources): + all_sources = [src for src in sources] + [src for src in transitive_sources] + proto_include_paths = [ + '-I%s' % p for p in _proto_include_paths( + [f for f in all_sources]) + ] + + grpc_web_out_common_options = ','.join([ + 'import_style={}'.format(import_style), + 'mode={}'.format(mode), + ]) + + files = [] + for src in sources: + name = '{}.grpc.js'.format( + '.'.join(src.path.split('/')[-1].split('.')[:-1])) + js = actions.declare_file(name) + files.append(js) + + args = proto_include_paths + [ + '--plugin=protoc-gen-grpc-web={}'.format(protoc_gen_grpc_web.path), + '--grpc-web_out={options},out={out_file}:{path}'.format( + options = grpc_web_out_common_options, + out_file = name, + path = js.path[:js.path.rfind('/')], + ), + src.path + ] + + actions.run( + inputs = [protoc_gen_grpc_web] + all_sources, + outputs = [js], + executable = protoc, + arguments = args, + progress_message = + _generate_closure_grpc_web_src_progress_message(name), + ) + + return files + +_error_multiple_deps = ''.join([ + "'deps' attribute must contain exactly one label ", + "(we didn't name it 'dep' for consistency). ", + "We may revisit this restriction later.", +]) + +def _closure_grpc_web_library_impl(ctx): + if len(ctx.attr.deps) > 1: + # TODO(yannic): Revisit this restriction. + fail(_error_multiple_deps, 'deps'); + + dep = ctx.attr.deps[0] + + srcs = _generate_closure_grpc_web_srcs( + actions = ctx.actions, + protoc = ctx.executable._protoc, + protoc_gen_grpc_web = ctx.executable._protoc_gen_grpc_web, + import_style = ctx.attr.import_style, + mode = ctx.attr.mode, + sources = dep.proto.direct_sources, + transitive_sources = dep.proto.transitive_imports, + ) + + deps = unfurl(ctx.attr.deps, provider = "closure_js_library") + deps += [ + ctx.attr._grpc_web_abstractclientbase, + ctx.attr._grpc_web_clientreadablestream, + ctx.attr._grpc_web_error, + ctx.attr._grpc_web_grpcwebclientbase + ] + + suppress = [ + "misplacedTypeAnnotation", + ] + + library = closure_js_library_impl( + actions = ctx.actions, + label = ctx.label, + workspace_name = ctx.workspace_name, + + srcs = srcs, + deps = deps, + testonly = ctx.attr.testonly, + suppress = suppress, + lenient = False, + + closure_library_base = ctx.files._closure_library_base, + _ClosureWorker = ctx.executable._ClosureWorker, + ) + 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 = srcs), + ) + +closure_grpc_web_library = rule( + implementation = _closure_grpc_web_library_impl, + attrs = { + 'deps': attr.label_list( + mandatory = True, + providers = ['proto', 'closure_js_library'], + # The files generated by this aspect are required dependencies. + aspects = [closure_proto_aspect], + ), + 'import_style': attr.string( + default = 'closure', + values = ['closure'], + ), + 'mode': attr.string( + default = 'grpcwebtext', + values = ['grpcwebtext', 'grpcweb'], + ), + + # Required for closure_js_library_impl + "_ClosureWorker": CLOSURE_WORKER_ATTR, + "_closure_library_base": CLOSURE_LIBRARY_BASE_ATTR, + + # internal only + '_protoc': attr.label( + default = Label('@com_google_protobuf//:protoc'), + executable = True, + cfg = 'host', + ), + '_protoc_gen_grpc_web': attr.label( + default = Label('//javascript/net/grpc/web:protoc-gen-grpc-web'), + executable = True, + cfg = 'host', + ), + "_grpc_web_abstractclientbase": attr.label( + default = Label("//javascript/net/grpc/web:abstractclientbase"), + ), + "_grpc_web_clientreadablestream": attr.label( + default = Label("//javascript/net/grpc/web:clientreadablestream"), + ), + "_grpc_web_error": attr.label( + default = Label("//javascript/net/grpc/web:error"), + ), + "_grpc_web_grpcwebclientbase": attr.label( + default = Label("//javascript/net/grpc/web:grpcwebclientbase"), + ), + }, +) diff --git a/javascript/net/grpc/web/BUILD.bazel b/javascript/net/grpc/web/BUILD.bazel index ae3f4ee71..bbc4f155e 100644 --- a/javascript/net/grpc/web/BUILD.bazel +++ b/javascript/net/grpc/web/BUILD.bazel @@ -86,6 +86,7 @@ closure_js_library( "@io_bazel_rules_closure//closure/library/net:eventtype", "@io_bazel_rules_closure//closure/library/net:xhrio", "@io_bazel_rules_closure//closure/library/net:xmlhttp", + "@io_bazel_rules_closure//closure/library/string", ], suppress = [ "reportUnknownTypes", diff --git a/javascript/net/grpc/web/grpc_generator.cc b/javascript/net/grpc/web/grpc_generator.cc index 2742b1bd4..8df4cddd0 100644 --- a/javascript/net/grpc/web/grpc_generator.cc +++ b/javascript/net/grpc/web/grpc_generator.cc @@ -333,7 +333,7 @@ class GrpcCodeGenerator : public CodeGenerator { string mode; string import_style_str; ImportStyle import_style; - + for (size_t i = 0; i < options.size(); ++i) { if (options[i].first == "out") { file_name = options[i].second; diff --git a/net/grpc/gateway/examples/echo/BUILD.bazel b/net/grpc/gateway/examples/echo/BUILD.bazel new file mode 100644 index 000000000..bb71eac8b --- /dev/null +++ b/net/grpc/gateway/examples/echo/BUILD.bazel @@ -0,0 +1,15 @@ +load("//bazel:closure_grpc_web_library.bzl", "closure_grpc_web_library") + +proto_library( + name = "echo_proto", + srcs = [ + "echo.proto", + ], +) + +closure_grpc_web_library( + name = "echo", + deps = [ + ":echo_proto", + ], +) diff --git a/net/grpc/gateway/examples/echo/echoapp.js b/net/grpc/gateway/examples/echo/echoapp.js index aee224c7d..99604082c 100644 --- a/net/grpc/gateway/examples/echo/echoapp.js +++ b/net/grpc/gateway/examples/echo/echoapp.js @@ -113,7 +113,7 @@ echoapp.EchoApp.prototype.load = function() { if (e.keyCode == 13) self.send(); // enter key return false; }); - + $("#msg").focus(); }); }; diff --git a/scripts/kokoro.sh b/scripts/kokoro.sh index ef970cba1..4983c1346 100644 --- a/scripts/kokoro.sh +++ b/scripts/kokoro.sh @@ -25,6 +25,7 @@ docker build -t grpc-web:ubuntu_16_04 \ docker-compose build bazel test javascript/net/grpc/web/... +bazel test net/grpc/gateway/examples/... cd packages/grpc-web && \ npm install && \