protobuf.bzl 11 KB
Newer Older
1 2
# -*- mode: python; -*- PYTHON-PREPROCESSING-REQUIRED

3 4 5 6 7 8
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
9
def _GenDir(ctx):
10
  if not ctx.attr.includes:
11
    return ctx.label.workspace_root
12
  if not ctx.attr.includes[0]:
13
    return _GetPath(ctx, ctx.label.package)
14
  if not ctx.label.package:
15 16
    return _GetPath(ctx, ctx.attr.includes[0])
  return _GetPath(ctx, ctx.label.package + '/' + ctx.attr.includes[0])
17

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

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

29
def _RelativeOutputPath(path, include, dest=""):
Jisi Liu's avatar
Jisi Liu committed
30 31 32 33 34 35 36 37
  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 + '/'
38 39
  if dest and dest[-1] != '/':
    dest = dest + '/'
Jisi Liu's avatar
Jisi Liu committed
40 41

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

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

55 56 57 58 59 60 61 62 63 64
  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]

65 66 67 68 69 70 71 72 73 74 75 76 77
  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)]
78

79 80
  if args:
    ctx.action(
Jisi Liu's avatar
Jisi Liu committed
81
        inputs=srcs + deps,
82
        outputs=ctx.outputs.outs,
Jisi Liu's avatar
Jisi Liu committed
83
        arguments=args + import_flags + [s.path for s in srcs],
Jisi Liu's avatar
Jisi Liu committed
84
        executable=ctx.executable.protoc,
85
        mnemonic="ProtoCompile",
86 87 88 89
    )

  return struct(
      proto=struct(
Jisi Liu's avatar
Jisi Liu committed
90 91 92 93 94
          srcs=srcs,
          import_flags=import_flags,
          deps=deps,
      ),
  )
95

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

def cc_proto_library(
Jisi Liu's avatar
Jisi Liu committed
143 144 145
        name,
        srcs=[],
        deps=[],
146
        cc_libs=[],
Jisi Liu's avatar
Jisi Liu committed
147
        include=None,
148
        protoc="//:protoc",
149
        internal_bootstrap_hack=False,
150
        use_grpc_plugin=False,
151
        default_runtime="//:protobuf",
Jisi Liu's avatar
Jisi Liu committed
152
        **kargs):
153 154
  """Bazel rule to create a C++ protobuf library from proto source files

155 156 157 158
  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.

159 160 161 162 163 164 165 166 167 168 169 170
  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.
171 172
    use_grpc_plugin: a flag to indicate whether to call the grpc C++ plugin
        when processing the proto files.
173 174
    default_runtime: the implicitly default runtime which will be depended on by
        the generated cc_library target.
175 176 177
    **kargs: other keyword arguments that are passed to cc_library.

  """
178

179 180 181 182
  includes = []
  if include != None:
    includes = [include]

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

200 201 202 203
  grpc_cpp_plugin = None
  if use_grpc_plugin:
    grpc_cpp_plugin = "//external:grpc_cpp_plugin"

204 205
  outs = _CcOuts(srcs, use_grpc_plugin)

206
  proto_gen(
Jisi Liu's avatar
Jisi Liu committed
207 208
      name=name + "_genproto",
      srcs=srcs,
209
      deps=[s + "_genproto" for s in deps],
210
      includes=includes,
Jisi Liu's avatar
Jisi Liu committed
211
      protoc=protoc,
212 213
      plugin=grpc_cpp_plugin,
      plugin_language="grpc",
Jisi Liu's avatar
Jisi Liu committed
214 215
      gen_cc=1,
      outs=outs,
216
      visibility=["//visibility:public"],
217 218
  )

219 220
  if default_runtime and not default_runtime in cc_libs:
    cc_libs += [default_runtime]
221 222
  if use_grpc_plugin:
    cc_libs += ["//external:grpc_lib"]
Jisi Liu's avatar
Jisi Liu committed
223

224
  native.cc_library(
Jisi Liu's avatar
Jisi Liu committed
225 226
      name=name,
      srcs=outs,
227
      deps=cc_libs + deps,
Jisi Liu's avatar
Jisi Liu committed
228
      includes=includes,
229
      **kargs)
Jisi Liu's avatar
Jisi Liu committed
230

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

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"],
  )


256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
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
286 287 288 289 290 291 292
def py_proto_library(
        name,
        srcs=[],
        deps=[],
        py_libs=[],
        py_extra_srcs=[],
        include=None,
293 294
        default_runtime="//:protobuf_python",
        protoc="//:protoc",
Jisi Liu's avatar
Jisi Liu committed
295
        **kargs):
Jisi Liu's avatar
Jisi Liu committed
296 297
  """Bazel rule to create a Python protobuf library from proto source files

298 299 300 301
  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
302 303 304 305 306 307 308 309 310
  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.
311 312
    default_runtime: the implicitly default runtime which will be depended on by
        the generated py_library target.
Jisi Liu's avatar
Jisi Liu committed
313 314 315 316
    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
317
  outs = _PyOuts(srcs)
318 319 320 321 322

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

323
  proto_gen(
Jisi Liu's avatar
Jisi Liu committed
324 325 326
      name=name + "_genproto",
      srcs=srcs,
      deps=[s + "_genproto" for s in deps],
327
      includes=includes,
Jisi Liu's avatar
Jisi Liu committed
328 329 330
      protoc=protoc,
      gen_py=1,
      outs=outs,
331
      visibility=["//visibility:public"],
Jisi Liu's avatar
Jisi Liu committed
332 333
  )

334 335 336
  if default_runtime and not default_runtime in py_libs + deps:
    py_libs += [default_runtime]

Jisi Liu's avatar
Jisi Liu committed
337 338
  native.py_library(
      name=name,
339 340
      srcs=outs+py_extra_srcs,
      deps=py_libs+deps,
341
      imports=includes,
Jisi Liu's avatar
Jisi Liu committed
342 343 344 345 346 347
      **kargs)

def internal_protobuf_py_tests(
    name,
    modules=[],
    **kargs):
Jisi Liu's avatar
Jisi Liu committed
348 349 350 351 352 353 354 355 356
  """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
357
  for m in modules:
358
    s = "python/google/protobuf/internal/%s.py" % m
Jisi Liu's avatar
Jisi Liu committed
359 360
    native.py_test(
        name="py_%s" % m,
Jisi Liu's avatar
Jisi Liu committed
361 362
        srcs=[s],
        main=s,
Jisi Liu's avatar
Jisi Liu committed
363
        **kargs)