protobuf.bzl 11.2 KB
Newer Older
1 2 3 4 5 6
def _GetPath(ctx, path):
  if ctx.label.workspace_root:
    return ctx.label.workspace_root + '/' + path
  else:
    return path

Jisi Liu's avatar
Jisi Liu committed
7
def _GenDir(ctx):
8
  if not ctx.attr.includes:
9
    return ctx.label.workspace_root
10
  if not ctx.attr.includes[0]:
11
    return _GetPath(ctx, ctx.label.package)
12
  if not ctx.label.package:
13 14
    return _GetPath(ctx, ctx.attr.includes[0])
  return _GetPath(ctx, ctx.label.package + '/' + ctx.attr.includes[0])
15

16 17 18 19 20 21 22 23
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]
  return ret

def _CcSrcs(srcs, use_grpc_plugin=False):
  ret = [s[:-len(".proto")] + ".pb.cc" for s in srcs]
24
  if use_grpc_plugin:
25
    ret += [s[:-len(".proto")] + ".grpc.pb.cc" for s in srcs]
26
  return ret
27

28 29 30
def _CcOuts(srcs, use_grpc_plugin=False):
  return _CcHdrs(srcs, use_grpc_plugin) + _CcSrcs(srcs, use_grpc_plugin)

Jisi Liu's avatar
Jisi Liu committed
31
def _PyOuts(srcs):
Jisi Liu's avatar
Jisi Liu committed
32
  return [s[:-len(".proto")] + "_pb2.py" for s in srcs]
33

34
def _RelativeOutputPath(path, include, dest=""):
Jisi Liu's avatar
Jisi Liu committed
35 36 37 38 39 40 41 42
  if include == None:
    return path

  if not path.startswith(include):
    fail("Include path %s isn't part of the path %s." % (include, path))

  if include and include[-1] != '/':
    include = include + '/'
43 44
  if dest and dest[-1] != '/':
    dest = dest + '/'
Jisi Liu's avatar
Jisi Liu committed
45 46

  path = path[len(include):]
47
  return dest + path
Jisi Liu's avatar
Jisi Liu committed
48

Jisi Liu's avatar
Jisi Liu committed
49 50
def _proto_gen_impl(ctx):
  """General implementation for generating protos"""
51 52 53
  srcs = ctx.files.srcs
  deps = []
  deps += ctx.files.srcs
Jisi Liu's avatar
Jisi Liu committed
54
  gen_dir = _GenDir(ctx)
55
  if gen_dir:
56
    import_flags = ["-I" + gen_dir, "-I" + ctx.var["GENDIR"] + "/" + gen_dir]
57 58 59
  else:
    import_flags = ["-I."]

60 61 62 63 64 65 66 67 68 69
  for dep in ctx.attr.deps:
    import_flags += dep.proto.import_flags
    deps += dep.proto.deps

  args = []
  if ctx.attr.gen_cc:
    args += ["--cpp_out=" + ctx.var["GENDIR"] + "/" + gen_dir]
  if ctx.attr.gen_py:
    args += ["--python_out=" + ctx.var["GENDIR"] + "/" + gen_dir]

70
  inputs = srcs + deps
71 72 73 74 75 76 77 78 79 80 81 82 83
  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 = ctx.var["GENDIR"] + "/" + 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)]
84
    inputs += [plugin]
85

86 87
  if args:
    ctx.action(
88
        inputs=inputs,
89
        outputs=ctx.outputs.outs,
Jisi Liu's avatar
Jisi Liu committed
90
        arguments=args + import_flags + [s.path for s in srcs],
Jisi Liu's avatar
Jisi Liu committed
91
        executable=ctx.executable.protoc,
92
        mnemonic="ProtoCompile",
93 94 95 96
    )

  return struct(
      proto=struct(
Jisi Liu's avatar
Jisi Liu committed
97 98 99 100 101
          srcs=srcs,
          import_flags=import_flags,
          deps=deps,
      ),
  )
102

103
proto_gen = rule(
104
    attrs = {
Jisi Liu's avatar
Jisi Liu committed
105 106
        "srcs": attr.label_list(allow_files = True),
        "deps": attr.label_list(providers = ["proto"]),
107
        "includes": attr.string_list(),
Jisi Liu's avatar
Jisi Liu committed
108
        "protoc": attr.label(
109
            cfg = "host",
Jisi Liu's avatar
Jisi Liu committed
110 111 112 113
            executable = True,
            single_file = True,
            mandatory = True,
        ),
114
        "plugin": attr.label(
115
            cfg = "host",
116
            allow_files = True,
117 118
            executable = True,
        ),
119 120
        "plugin_language": attr.string(),
        "plugin_options": attr.string_list(),
Jisi Liu's avatar
Jisi Liu committed
121 122 123 124 125
        "gen_cc": attr.bool(),
        "gen_py": attr.bool(),
        "outs": attr.output_list(),
    },
    output_to_genfiles = True,
Jisi Liu's avatar
Jisi Liu committed
126
    implementation = _proto_gen_impl,
127
)
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
"""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.
"""
148 149

def cc_proto_library(
Jisi Liu's avatar
Jisi Liu committed
150 151 152
        name,
        srcs=[],
        deps=[],
153
        cc_libs=[],
Jisi Liu's avatar
Jisi Liu committed
154
        include=None,
155
        protoc="//:protoc",
156
        internal_bootstrap_hack=False,
157
        use_grpc_plugin=False,
158
        default_runtime="//:protobuf",
Jisi Liu's avatar
Jisi Liu committed
159
        **kargs):
160 161
  """Bazel rule to create a C++ protobuf library from proto source files

162 163 164 165
  NOTE: the rule is only an internal workaround to generate protos. The
  interface may change and the rule may be removed when bazel has introduced
  the native rule.

166 167 168 169 170 171 172 173 174 175 176 177
  Args:
    name: the name of the cc_proto_library.
    srcs: the .proto files of the cc_proto_library.
    deps: a list of dependency labels; must be cc_proto_library.
    cc_libs: a list of other cc_library targets depended by the generated
        cc_library.
    include: a string indicating the include path of the .proto files.
    protoc: the label of the protocol compiler to generate the sources.
    internal_bootstrap_hack: a flag indicate the cc_proto_library is used only
        for bootstraping. When it is set to True, no files will be generated.
        The rule will simply be a provider for .proto files, so that other
        cc_proto_library can depend on it.
178 179
    use_grpc_plugin: a flag to indicate whether to call the grpc C++ plugin
        when processing the proto files.
180 181
    default_runtime: the implicitly default runtime which will be depended on by
        the generated cc_library target.
182 183 184
    **kargs: other keyword arguments that are passed to cc_library.

  """
185

186 187 188 189
  includes = []
  if include != None:
    includes = [include]

190 191 192
  if internal_bootstrap_hack:
    # For pre-checked-in generated files, we add the internal_bootstrap_hack
    # which will skip the codegen action.
193
    proto_gen(
Jisi Liu's avatar
Jisi Liu committed
194 195
        name=name + "_genproto",
        srcs=srcs,
196
        deps=[s + "_genproto" for s in deps],
197
        includes=includes,
Jisi Liu's avatar
Jisi Liu committed
198
        protoc=protoc,
199
        visibility=["//visibility:public"],
200 201 202
    )
    # An empty cc_library to make rule dependency consistent.
    native.cc_library(
Jisi Liu's avatar
Jisi Liu committed
203
        name=name,
204
        **kargs)
205 206
    return

207 208 209 210
  grpc_cpp_plugin = None
  if use_grpc_plugin:
    grpc_cpp_plugin = "//external:grpc_cpp_plugin"

211 212 213
  gen_srcs = _CcSrcs(srcs, use_grpc_plugin)
  gen_hdrs = _CcHdrs(srcs, use_grpc_plugin)
  outs = gen_srcs + gen_hdrs
214

215
  proto_gen(
Jisi Liu's avatar
Jisi Liu committed
216 217
      name=name + "_genproto",
      srcs=srcs,
218
      deps=[s + "_genproto" for s in deps],
219
      includes=includes,
Jisi Liu's avatar
Jisi Liu committed
220
      protoc=protoc,
221 222
      plugin=grpc_cpp_plugin,
      plugin_language="grpc",
Jisi Liu's avatar
Jisi Liu committed
223 224
      gen_cc=1,
      outs=outs,
225
      visibility=["//visibility:public"],
226 227
  )

228 229
  if default_runtime and not default_runtime in cc_libs:
    cc_libs += [default_runtime]
230 231
  if use_grpc_plugin:
    cc_libs += ["//external:grpc_lib"]
Jisi Liu's avatar
Jisi Liu committed
232

233
  native.cc_library(
Jisi Liu's avatar
Jisi Liu committed
234
      name=name,
235 236
      srcs=gen_srcs,
      hdrs=gen_hdrs,
237
      deps=cc_libs + deps,
Jisi Liu's avatar
Jisi Liu committed
238
      includes=includes,
239
      **kargs)
Jisi Liu's avatar
Jisi Liu committed
240

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
def internal_gen_well_known_protos_java(srcs):
  """Bazel rule to generate the gen_well_known_protos_java genrule

  Args:
    srcs: the well known protos
  """
  root = Label("%s//protobuf_java" % (REPOSITORY_NAME)).workspace_root
  if root == "":
    include = " -Isrc "
  else:
    include = " -I%s/src " % root
  native.genrule(
    name = "gen_well_known_protos_java",
    srcs = srcs,
    outs = [
        "wellknown.srcjar",
    ],
    cmd = "$(location :protoc) --java_out=$(@D)/wellknown.jar" +
          " %s $(SRCS) " % include +
          " && mv $(@D)/wellknown.jar $(@D)/wellknown.srcjar",
    tools = [":protoc"],
  )

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
def internal_copied_filegroup(name, srcs, strip_prefix, dest, **kwargs):
  """Macro to copy files to a different directory and then create a filegroup.

  This is used by the //:protobuf_python py_proto_library target to work around
  an issue caused by Python source files that are part of the same Python
  package being in separate directories.

  Args:
    srcs: The source files to copy and add to the filegroup.
    strip_prefix: Path to the root of the files to copy.
    dest: The directory to copy the source files into.
    **kwargs: extra arguments that will be passesd to the filegroup.
  """
  outs = [_RelativeOutputPath(s, strip_prefix, dest) for s in srcs]

  native.genrule(
      name = name + "_genrule",
      srcs = srcs,
      outs = outs,
      cmd = " && ".join(
          ["cp $(location %s) $(location %s)" %
           (s, _RelativeOutputPath(s, strip_prefix, dest)) for s in srcs]),
  )

  native.filegroup(
      name = name,
      srcs = outs,
      **kwargs)

Jisi Liu's avatar
Jisi Liu committed
293 294 295 296 297 298 299
def py_proto_library(
        name,
        srcs=[],
        deps=[],
        py_libs=[],
        py_extra_srcs=[],
        include=None,
300 301
        default_runtime="//:protobuf_python",
        protoc="//:protoc",
Jisi Liu's avatar
Jisi Liu committed
302
        **kargs):
Jisi Liu's avatar
Jisi Liu committed
303 304
  """Bazel rule to create a Python protobuf library from proto source files

305 306 307 308
  NOTE: the rule is only an internal workaround to generate protos. The
  interface may change and the rule may be removed when bazel has introduced
  the native rule.

Jisi Liu's avatar
Jisi Liu committed
309 310 311 312 313 314 315 316 317
  Args:
    name: the name of the py_proto_library.
    srcs: the .proto files of the py_proto_library.
    deps: a list of dependency labels; must be py_proto_library.
    py_libs: a list of other py_library targets depended by the generated
        py_library.
    py_extra_srcs: extra source files that will be added to the output
        py_library. This attribute is used for internal bootstrapping.
    include: a string indicating the include path of the .proto files.
318 319
    default_runtime: the implicitly default runtime which will be depended on by
        the generated py_library target.
Jisi Liu's avatar
Jisi Liu committed
320 321 322 323
    protoc: the label of the protocol compiler to generate the sources.
    **kargs: other keyword arguments that are passed to cc_library.

  """
Jisi Liu's avatar
Jisi Liu committed
324
  outs = _PyOuts(srcs)
325 326 327 328 329

  includes = []
  if include != None:
    includes = [include]

330
  proto_gen(
Jisi Liu's avatar
Jisi Liu committed
331 332 333
      name=name + "_genproto",
      srcs=srcs,
      deps=[s + "_genproto" for s in deps],
334
      includes=includes,
Jisi Liu's avatar
Jisi Liu committed
335 336 337
      protoc=protoc,
      gen_py=1,
      outs=outs,
338
      visibility=["//visibility:public"],
Jisi Liu's avatar
Jisi Liu committed
339 340
  )

341 342 343
  if default_runtime and not default_runtime in py_libs + deps:
    py_libs += [default_runtime]

Jisi Liu's avatar
Jisi Liu committed
344 345
  native.py_library(
      name=name,
346 347
      srcs=outs+py_extra_srcs,
      deps=py_libs+deps,
348
      imports=includes,
Jisi Liu's avatar
Jisi Liu committed
349 350 351 352 353 354
      **kargs)

def internal_protobuf_py_tests(
    name,
    modules=[],
    **kargs):
Jisi Liu's avatar
Jisi Liu committed
355 356 357 358 359 360 361 362 363
  """Bazel rules to create batch tests for protobuf internal.

  Args:
    name: the name of the rule.
    modules: a list of modules for tests. The macro will create a py_test for
        each of the parameter with the source "google/protobuf/%s.py"
    kargs: extra parameters that will be passed into the py_test.

  """
Jisi Liu's avatar
Jisi Liu committed
364
  for m in modules:
365
    s = "python/google/protobuf/internal/%s.py" % m
Jisi Liu's avatar
Jisi Liu committed
366 367
    native.py_test(
        name="py_%s" % m,
Jisi Liu's avatar
Jisi Liu committed
368 369
        srcs=[s],
        main=s,
Jisi Liu's avatar
Jisi Liu committed
370
        **kargs)