protobuf.bzl 8.33 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

Jisi Liu's avatar
Jisi Liu committed
18
def _CcOuts(srcs):
19
  return [s[:-len(".proto")] +  ".pb.h" for s in srcs] + \
Jisi Liu's avatar
Jisi Liu committed
20
         [s[:-len(".proto")] + ".pb.cc" for s in srcs]
21

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

Jisi Liu's avatar
Jisi Liu committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
def _RelativeOutputPath(path, include):
  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 + '/'

  path = path[len(include):]

  if not path.startswith(PACKAGE_NAME):
    fail("The package %s is not within the path %s" % (PACKAGE_NAME, path))

  if not PACKAGE_NAME:
    return path

  return path[len(PACKAGE_NAME)+1:]

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

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

  if args:
    ctx.action(
Jisi Liu's avatar
Jisi Liu committed
68
        inputs=srcs + deps,
69
        outputs=ctx.outputs.outs,
Jisi Liu's avatar
Jisi Liu committed
70
        arguments=args + import_flags + [s.path for s in srcs],
Jisi Liu's avatar
Jisi Liu committed
71
        executable=ctx.executable.protoc,
72 73 74 75
    )

  return struct(
      proto=struct(
Jisi Liu's avatar
Jisi Liu committed
76 77 78 79 80
          srcs=srcs,
          import_flags=import_flags,
          deps=deps,
      ),
  )
81

Jisi Liu's avatar
Jisi Liu committed
82
_proto_gen = rule(
83
    attrs = {
Jisi Liu's avatar
Jisi Liu committed
84 85
        "srcs": attr.label_list(allow_files = True),
        "deps": attr.label_list(providers = ["proto"]),
86
        "includes": attr.string_list(),
Jisi Liu's avatar
Jisi Liu committed
87
        "protoc": attr.label(
88
            cfg = HOST_CFG,
Jisi Liu's avatar
Jisi Liu committed
89 90 91 92 93 94 95 96 97
            executable = True,
            single_file = True,
            mandatory = True,
        ),
        "gen_cc": attr.bool(),
        "gen_py": attr.bool(),
        "outs": attr.output_list(),
    },
    output_to_genfiles = True,
Jisi Liu's avatar
Jisi Liu committed
98
    implementation = _proto_gen_impl,
99 100 101
)

def cc_proto_library(
Jisi Liu's avatar
Jisi Liu committed
102 103 104
        name,
        srcs=[],
        deps=[],
105
        cc_libs=[],
Jisi Liu's avatar
Jisi Liu committed
106
        include=None,
107
        protoc="//google/protobuf:protoc",
108
        internal_bootstrap_hack=False,
109
        default_runtime="//google/protobuf:protobuf",
Jisi Liu's avatar
Jisi Liu committed
110
        **kargs):
111 112
  """Bazel rule to create a C++ protobuf library from proto source files

113 114 115 116
  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.

117 118 119 120 121 122 123 124 125 126 127 128
  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.
129 130
    default_runtime: the implicitly default runtime which will be depended on by
        the generated cc_library target.
131 132 133
    **kargs: other keyword arguments that are passed to cc_library.

  """
134

135 136 137 138
  includes = []
  if include != None:
    includes = [include]

139 140 141
  if internal_bootstrap_hack:
    # For pre-checked-in generated files, we add the internal_bootstrap_hack
    # which will skip the codegen action.
Jisi Liu's avatar
Jisi Liu committed
142
    _proto_gen(
Jisi Liu's avatar
Jisi Liu committed
143 144
        name=name + "_genproto",
        srcs=srcs,
145
        deps=[s + "_genproto" for s in deps],
146
        includes=includes,
Jisi Liu's avatar
Jisi Liu committed
147
        protoc=protoc,
148
        visibility=["//visibility:public"],
149 150 151
    )
    # An empty cc_library to make rule dependency consistent.
    native.cc_library(
Jisi Liu's avatar
Jisi Liu committed
152
        name=name,
153
        **kargs)
154 155
    return

Jisi Liu's avatar
Jisi Liu committed
156
  outs = _CcOuts(srcs)
Jisi Liu's avatar
Jisi Liu committed
157
  _proto_gen(
Jisi Liu's avatar
Jisi Liu committed
158 159
      name=name + "_genproto",
      srcs=srcs,
160
      deps=[s + "_genproto" for s in deps],
161
      includes=includes,
Jisi Liu's avatar
Jisi Liu committed
162 163 164
      protoc=protoc,
      gen_cc=1,
      outs=outs,
165
      visibility=["//visibility:public"],
166 167
  )

168 169
  if default_runtime and not default_runtime in cc_libs:
    cc_libs += [default_runtime]
Jisi Liu's avatar
Jisi Liu committed
170

171
  native.cc_library(
Jisi Liu's avatar
Jisi Liu committed
172 173
      name=name,
      srcs=outs,
174
      deps=cc_libs + deps,
Jisi Liu's avatar
Jisi Liu committed
175
      includes=includes,
176
      **kargs)
Jisi Liu's avatar
Jisi Liu committed
177 178


179
def internal_copied_filegroup(
Jisi Liu's avatar
Jisi Liu committed
180 181 182 183
        name,
        srcs,
        include,
        **kargs):
Jisi Liu's avatar
Jisi Liu committed
184 185 186
  """Bazel rule to fix sources file to workaround with python path issues.

  Args:
187 188
    name: the name of the internal_copied_filegroup rule, which will be the
        name of the generated filegroup.
Jisi Liu's avatar
Jisi Liu committed
189 190 191 192
    srcs: the source files to be copied.
    include: the expected import root of the source.
    **kargs: extra arguments that will be passed into the filegroup.
  """
Jisi Liu's avatar
Jisi Liu committed
193 194 195 196 197 198
  outs = [_RelativeOutputPath(s, include) for s in srcs]

  native.genrule(
      name=name+"_genrule",
      srcs=srcs,
      outs=outs,
199 200 201
      cmd=" && ".join(["cp $(location %s) $(location %s)" %
                       (s, _RelativeOutputPath(s, include))
                       for s in srcs]))
Jisi Liu's avatar
Jisi Liu committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215

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


def py_proto_library(
        name,
        srcs=[],
        deps=[],
        py_libs=[],
        py_extra_srcs=[],
        include=None,
216
        default_runtime="//google/protobuf:protobuf_python",
217
        protoc="//google/protobuf:protoc",
Jisi Liu's avatar
Jisi Liu committed
218
        **kargs):
Jisi Liu's avatar
Jisi Liu committed
219 220
  """Bazel rule to create a Python protobuf library from proto source files

221 222 223 224
  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
225 226 227 228 229 230 231 232 233
  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.
234 235
    default_runtime: the implicitly default runtime which will be depended on by
        the generated py_library target.
Jisi Liu's avatar
Jisi Liu committed
236 237 238 239
    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
240
  outs = _PyOuts(srcs)
241 242 243 244 245

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

Jisi Liu's avatar
Jisi Liu committed
246 247 248 249
  _proto_gen(
      name=name + "_genproto",
      srcs=srcs,
      deps=[s + "_genproto" for s in deps],
250
      includes=includes,
Jisi Liu's avatar
Jisi Liu committed
251 252 253
      protoc=protoc,
      gen_py=1,
      outs=outs,
254
      visibility=["//visibility:public"],
Jisi Liu's avatar
Jisi Liu committed
255 256 257
  )

  if include != None:
Jisi Liu's avatar
Jisi Liu committed
258
    # Copy the output files to the desired location to make the import work.
259 260 261
    internal_copied_filegroup_name=name + "_internal_copied_filegroup"
    internal_copied_filegroup(
        name=internal_copied_filegroup_name,
Jisi Liu's avatar
Jisi Liu committed
262 263
        srcs=outs,
        include=include)
264
    outs=[internal_copied_filegroup_name]
Jisi Liu's avatar
Jisi Liu committed
265

266 267 268
  if default_runtime and not default_runtime in py_libs + deps:
    py_libs += [default_runtime]

Jisi Liu's avatar
Jisi Liu committed
269 270
  native.py_library(
      name=name,
271 272
      srcs=outs+py_extra_srcs,
      deps=py_libs+deps,
Jisi Liu's avatar
Jisi Liu committed
273 274 275 276 277 278
      **kargs)

def internal_protobuf_py_tests(
    name,
    modules=[],
    **kargs):
Jisi Liu's avatar
Jisi Liu committed
279 280 281 282 283 284 285 286 287
  """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
288
  for m in modules:
Jisi Liu's avatar
Jisi Liu committed
289 290
    s = _RelativeOutputPath(
        "python/google/protobuf/internal/%s.py" % m, "python")
Jisi Liu's avatar
Jisi Liu committed
291 292
    native.py_test(
        name="py_%s" % m,
Jisi Liu's avatar
Jisi Liu committed
293 294
        srcs=[s],
        main=s,
Jisi Liu's avatar
Jisi Liu committed
295
        **kargs)