diff options
Diffstat (limited to 'protobuf.bzl')
-rw-r--r-- | protobuf.bzl | 132 |
1 files changed, 102 insertions, 30 deletions
diff --git a/protobuf.bzl b/protobuf.bzl index 0e8c2e23..73c396d5 100644 --- a/protobuf.bzl +++ b/protobuf.bzl @@ -1,12 +1,27 @@ -# -*- mode: python; -*- PYTHON-PREPROCESSING-REQUIRED - def _GetPath(ctx, path): if ctx.label.workspace_root: return ctx.label.workspace_root + '/' + path else: return path +def _IsNewExternal(ctx): + # Bazel 0.4.4 and older have genfiles paths that look like: + # bazel-out/local-fastbuild/genfiles/external/repo/foo + # After the exec root rearrangement, they look like: + # ../repo/bazel-out/local-fastbuild/genfiles/foo + return ctx.label.workspace_root.startswith("../") + def _GenDir(ctx): + if _IsNewExternal(ctx): + # We are using the fact that Bazel 0.4.4+ provides repository-relative paths + # for ctx.genfiles_dir. + return ctx.genfiles_dir.path + ( + "/" + ctx.attr.includes[0] if ctx.attr.includes and ctx.attr.includes[0] else "") + # This means that we're either in the old version OR the new version in the local repo. + # Either way, appending the source path to the genfiles dir works. + return ctx.var["GENDIR"] + "/" + _SourceDir(ctx) + +def _SourceDir(ctx): if not ctx.attr.includes: return ctx.label.workspace_root if not ctx.attr.includes[0]: @@ -15,14 +30,21 @@ def _GenDir(ctx): return _GetPath(ctx, ctx.attr.includes[0]) return _GetPath(ctx, ctx.label.package + '/' + ctx.attr.includes[0]) -def _CcOuts(srcs, use_grpc_plugin=False): - ret = [s[:-len(".proto")] + ".pb.h" for s in srcs] + \ - [s[:-len(".proto")] + ".pb.cc" for s in srcs] +def _CcHdrs(srcs, use_grpc_plugin=False): + ret = [s[:-len(".proto")] + ".pb.h" for s in srcs] if use_grpc_plugin: - ret += [s[:-len(".proto")] + ".grpc.pb.h" for s in srcs] + \ - [s[:-len(".proto")] + ".grpc.pb.cc" for s in srcs] + ret += [s[:-len(".proto")] + ".grpc.pb.h" for s in srcs] return ret +def _CcSrcs(srcs, use_grpc_plugin=False): + ret = [s[:-len(".proto")] + ".pb.cc" for s in srcs] + if use_grpc_plugin: + ret += [s[:-len(".proto")] + ".grpc.pb.cc" for s in srcs] + return ret + +def _CcOuts(srcs, use_grpc_plugin=False): + return _CcHdrs(srcs, use_grpc_plugin) + _CcSrcs(srcs, use_grpc_plugin) + def _PyOuts(srcs): return [s[:-len(".proto")] + "_pb2.py" for s in srcs] @@ -46,9 +68,10 @@ def _proto_gen_impl(ctx): srcs = ctx.files.srcs deps = [] deps += ctx.files.srcs + source_dir = _SourceDir(ctx) gen_dir = _GenDir(ctx) - if gen_dir: - import_flags = ["-I" + gen_dir, "-I" + ctx.var["GENDIR"] + "/" + gen_dir] + if source_dir: + import_flags = ["-I" + source_dir, "-I" + gen_dir] else: import_flags = ["-I."] @@ -58,20 +81,33 @@ def _proto_gen_impl(ctx): args = [] if ctx.attr.gen_cc: - args += ["--cpp_out=" + ctx.var["GENDIR"] + "/" + gen_dir] + args += ["--cpp_out=" + gen_dir] if ctx.attr.gen_py: - args += ["--python_out=" + ctx.var["GENDIR"] + "/" + gen_dir] - - if ctx.executable.grpc_cpp_plugin: - args += ["--plugin=protoc-gen-grpc=" + ctx.executable.grpc_cpp_plugin.path] - args += ["--grpc_out=" + ctx.var["GENDIR"] + "/" + gen_dir] + args += ["--python_out=" + gen_dir] + + inputs = srcs + deps + if ctx.executable.plugin: + plugin = ctx.executable.plugin + lang = ctx.attr.plugin_language + if not lang and plugin.basename.startswith('protoc-gen-'): + lang = plugin.basename[len('protoc-gen-'):] + if not lang: + fail("cannot infer the target language of plugin", "plugin_language") + + outdir = gen_dir + if ctx.attr.plugin_options: + outdir = ",".join(ctx.attr.plugin_options) + ":" + outdir + args += ["--plugin=protoc-gen-%s=%s" % (lang, plugin.path)] + args += ["--%s_out=%s" % (lang, outdir)] + inputs += [plugin] if args: ctx.action( - inputs=srcs + deps, + inputs=inputs, outputs=ctx.outputs.outs, arguments=args + import_flags + [s.path for s in srcs], executable=ctx.executable.protoc, + mnemonic="ProtoCompile", ) return struct( @@ -82,7 +118,7 @@ def _proto_gen_impl(ctx): ), ) -_proto_gen = rule( +proto_gen = rule( attrs = { "srcs": attr.label_list(allow_files = True), "deps": attr.label_list(providers = ["proto"]), @@ -93,11 +129,13 @@ _proto_gen = rule( single_file = True, mandatory = True, ), - "grpc_cpp_plugin": attr.label( + "plugin": attr.label( cfg = "host", + allow_files = True, executable = True, - single_file = True, ), + "plugin_language": attr.string(), + "plugin_options": attr.string_list(), "gen_cc": attr.bool(), "gen_py": attr.bool(), "outs": attr.output_list(), @@ -105,6 +143,26 @@ _proto_gen = rule( output_to_genfiles = True, implementation = _proto_gen_impl, ) +"""Generates codes from Protocol Buffers definitions. + +This rule helps you to implement Skylark macros specific to the target +language. You should prefer more specific `cc_proto_library `, +`py_proto_library` and others unless you are adding such wrapper macros. + +Args: + srcs: Protocol Buffers definition files (.proto) to run the protocol compiler + against. + deps: a list of dependency labels; must be other proto libraries. + includes: a list of include paths to .proto files. + protoc: the label of the protocol compiler to generate the sources. + plugin: the label of the protocol compiler plugin to be passed to the protocol + compiler. + plugin_language: the language of the generated sources + plugin_options: a list of options to be passed to the plugin + gen_cc: generates C++ sources in addition to the ones from the plugin. + gen_py: generates Python sources in addition to the ones from the plugin. + outs: a list of labels of the expected outputs from the protocol compiler. +""" def cc_proto_library( name, @@ -150,7 +208,7 @@ def cc_proto_library( if internal_bootstrap_hack: # For pre-checked-in generated files, we add the internal_bootstrap_hack # which will skip the codegen action. - _proto_gen( + proto_gen( name=name + "_genproto", srcs=srcs, deps=[s + "_genproto" for s in deps], @@ -168,15 +226,18 @@ def cc_proto_library( if use_grpc_plugin: grpc_cpp_plugin = "//external:grpc_cpp_plugin" - outs = _CcOuts(srcs, use_grpc_plugin) + gen_srcs = _CcSrcs(srcs, use_grpc_plugin) + gen_hdrs = _CcHdrs(srcs, use_grpc_plugin) + outs = gen_srcs + gen_hdrs - _proto_gen( + proto_gen( name=name + "_genproto", srcs=srcs, deps=[s + "_genproto" for s in deps], includes=includes, protoc=protoc, - grpc_cpp_plugin=grpc_cpp_plugin, + plugin=grpc_cpp_plugin, + plugin_language="grpc", gen_cc=1, outs=outs, visibility=["//visibility:public"], @@ -189,12 +250,12 @@ def cc_proto_library( native.cc_library( name=name, - srcs=outs, + srcs=gen_srcs, + hdrs=gen_hdrs, deps=cc_libs + deps, includes=includes, **kargs) - def internal_gen_well_known_protos_java(srcs): """Bazel rule to generate the gen_well_known_protos_java genrule @@ -202,10 +263,11 @@ def internal_gen_well_known_protos_java(srcs): srcs: the well known protos """ root = Label("%s//protobuf_java" % (REPOSITORY_NAME)).workspace_root + pkg = PACKAGE_NAME + "/" if PACKAGE_NAME else "" if root == "": - include = " -Isrc " + include = " -I%ssrc " % pkg else: - include = " -I%s/src " % root + include = " -I%s/%ssrc " % (root, pkg) native.genrule( name = "gen_well_known_protos_java", srcs = srcs, @@ -218,7 +280,6 @@ def internal_gen_well_known_protos_java(srcs): tools = [":protoc"], ) - def internal_copied_filegroup(name, srcs, strip_prefix, dest, **kwargs): """Macro to copy files to a different directory and then create a filegroup. @@ -248,7 +309,6 @@ def internal_copied_filegroup(name, srcs, strip_prefix, dest, **kwargs): srcs = outs, **kwargs) - def py_proto_library( name, srcs=[], @@ -258,6 +318,7 @@ def py_proto_library( include=None, default_runtime="//:protobuf_python", protoc="//:protoc", + use_grpc_plugin=False, **kargs): """Bazel rule to create a Python protobuf library from proto source files @@ -277,6 +338,8 @@ def py_proto_library( default_runtime: the implicitly default runtime which will be depended on by the generated py_library target. protoc: the label of the protocol compiler to generate the sources. + use_grpc_plugin: a flag to indicate whether to call the Python C++ plugin + when processing the proto files. **kargs: other keyword arguments that are passed to cc_library. """ @@ -286,7 +349,14 @@ def py_proto_library( if include != None: includes = [include] - _proto_gen( + grpc_python_plugin = None + if use_grpc_plugin: + grpc_python_plugin = "//external:grpc_python_plugin" + # Note: Generated grpc code depends on Python grpc module. This dependency + # is not explicitly listed in py_libs. Instead, host system is assumed to + # have grpc installed. + + proto_gen( name=name + "_genproto", srcs=srcs, deps=[s + "_genproto" for s in deps], @@ -295,6 +365,8 @@ def py_proto_library( gen_py=1, outs=outs, visibility=["//visibility:public"], + plugin=grpc_python_plugin, + plugin_language="grpc" ) if default_runtime and not default_runtime in py_libs + deps: |