blob: 0cc195b7ee1a06eb09a4b4a74af8a6aa90ab9abd [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001# -*- mode: python; -*- PYTHON-PREPROCESSING-REQUIRED
2
3def _GetPath(ctx, path):
Brian Silverman3fca9d72016-02-20 02:32:51 -05004 if str(ctx.label).startswith('@'):
5 fail('External labels not supported for now')
6 return path
Brian Silverman9c614bc2016-02-15 20:20:02 -05007
8def _GenDir(ctx):
9 if not ctx.attr.includes:
Brian Silverman3fca9d72016-02-20 02:32:51 -050010 if str(ctx.label).startswith('@'):
11 fail('External labels not supported for now')
12 return ''
Brian Silverman9c614bc2016-02-15 20:20:02 -050013 if not ctx.attr.includes[0]:
14 return _GetPath(ctx, ctx.label.package)
15 if not ctx.label.package:
16 return _GetPath(ctx, ctx.attr.includes[0])
17 return _GetPath(ctx, ctx.label.package + '/' + ctx.attr.includes[0])
18
19def _CcOuts(srcs):
20 return [s[:-len(".proto")] + ".pb.h" for s in srcs] + \
21 [s[:-len(".proto")] + ".pb.cc" for s in srcs]
22
23def _PyOuts(srcs):
24 return [s[:-len(".proto")] + "_pb2.py" for s in srcs]
25
26def _RelativeOutputPath(path, include):
27 if include == None:
28 return path
29
30 if not path.startswith(include):
31 fail("Include path %s isn't part of the path %s." % (include, path))
32
33 if include and include[-1] != '/':
34 include = include + '/'
35
36 path = path[len(include):]
37
Brian Silverman3fca9d72016-02-20 02:32:51 -050038 package_name = PACKAGE_NAME
39 if not package_name.startswith('third_party/protobuf'):
40 fail('The package %s is not a protobuf package' % package_name)
41 package_name = package_name[len('third_party/protobuf/'):]
42 if not path.startswith(package_name):
43 fail("The package %s is not within the path %s" % (package_name, path))
Brian Silverman9c614bc2016-02-15 20:20:02 -050044
Brian Silverman3fca9d72016-02-20 02:32:51 -050045 if not package_name:
Brian Silverman9c614bc2016-02-15 20:20:02 -050046 return path
47
Brian Silverman3fca9d72016-02-20 02:32:51 -050048 return path[len(package_name)+1:]
Brian Silverman9c614bc2016-02-15 20:20:02 -050049
50def _proto_gen_impl(ctx):
51 """General implementation for generating protos"""
52 srcs = ctx.files.srcs
53 deps = []
54 deps += ctx.files.srcs
55 gen_dir = _GenDir(ctx)
56 if gen_dir:
57 import_flags = ["-I" + gen_dir]
58 else:
59 import_flags = ["-I."]
60
61 for dep in ctx.attr.deps:
62 import_flags += dep.proto.import_flags
63 deps += dep.proto.deps
64
65 args = []
66 if ctx.attr.gen_cc:
67 args += ["--cpp_out=" + ctx.var["GENDIR"] + "/" + gen_dir]
68 if ctx.attr.gen_py:
69 args += ["--python_out=" + ctx.var["GENDIR"] + "/" + gen_dir]
70
71 if args:
72 ctx.action(
73 inputs=srcs + deps,
74 outputs=ctx.outputs.outs,
75 arguments=args + import_flags + [s.path for s in srcs],
76 executable=ctx.executable.protoc,
77 )
78
79 return struct(
80 proto=struct(
81 srcs=srcs,
82 import_flags=import_flags,
83 deps=deps,
84 ),
85 )
86
87_proto_gen = rule(
88 attrs = {
89 "srcs": attr.label_list(allow_files = True),
90 "deps": attr.label_list(providers = ["proto"]),
91 "includes": attr.string_list(),
92 "protoc": attr.label(
93 cfg = HOST_CFG,
94 executable = True,
95 single_file = True,
96 mandatory = True,
97 ),
98 "gen_cc": attr.bool(),
99 "gen_py": attr.bool(),
100 "outs": attr.output_list(),
101 },
102 output_to_genfiles = True,
103 implementation = _proto_gen_impl,
104)
105
106def cc_proto_library(
107 name,
108 srcs=[],
109 deps=[],
110 cc_libs=[],
111 include=None,
112 protoc="//google/protobuf:protoc",
113 internal_bootstrap_hack=False,
114 default_runtime="//google/protobuf:protobuf",
115 **kargs):
116 """Bazel rule to create a C++ protobuf library from proto source files
117
118 NOTE: the rule is only an internal workaround to generate protos. The
119 interface may change and the rule may be removed when bazel has introduced
120 the native rule.
121
122 Args:
123 name: the name of the cc_proto_library.
124 srcs: the .proto files of the cc_proto_library.
125 deps: a list of dependency labels; must be cc_proto_library.
126 cc_libs: a list of other cc_library targets depended by the generated
127 cc_library.
128 include: a string indicating the include path of the .proto files.
129 protoc: the label of the protocol compiler to generate the sources.
130 internal_bootstrap_hack: a flag indicate the cc_proto_library is used only
131 for bootstraping. When it is set to True, no files will be generated.
132 The rule will simply be a provider for .proto files, so that other
133 cc_proto_library can depend on it.
134 default_runtime: the implicitly default runtime which will be depended on by
135 the generated cc_library target.
136 **kargs: other keyword arguments that are passed to cc_library.
137
138 """
139
140 includes = []
141 if include != None:
142 includes = [include]
143
144 if internal_bootstrap_hack:
145 # For pre-checked-in generated files, we add the internal_bootstrap_hack
146 # which will skip the codegen action.
147 _proto_gen(
148 name=name + "_genproto",
149 srcs=srcs,
150 deps=[s + "_genproto" for s in deps],
151 includes=includes,
152 protoc=protoc,
153 visibility=["//visibility:public"],
154 )
155 # An empty cc_library to make rule dependency consistent.
156 native.cc_library(
157 name=name,
158 **kargs)
159 return
160
161 outs = _CcOuts(srcs)
162 _proto_gen(
163 name=name + "_genproto",
164 srcs=srcs,
165 deps=[s + "_genproto" for s in deps],
166 includes=includes,
167 protoc=protoc,
168 gen_cc=1,
169 outs=outs,
170 visibility=["//visibility:public"],
171 )
172
173 if default_runtime and not default_runtime in cc_libs:
174 cc_libs += [default_runtime]
175
176 native.cc_library(
177 name=name,
178 srcs=outs,
179 deps=cc_libs + deps,
180 includes=includes,
181 **kargs)
182
183
184def internal_copied_filegroup(
185 name,
186 srcs,
187 include,
188 **kargs):
189 """Bazel rule to fix sources file to workaround with python path issues.
190
191 Args:
192 name: the name of the internal_copied_filegroup rule, which will be the
193 name of the generated filegroup.
194 srcs: the source files to be copied.
195 include: the expected import root of the source.
196 **kargs: extra arguments that will be passed into the filegroup.
197 """
198 outs = [_RelativeOutputPath(s, include) for s in srcs]
199
200 native.genrule(
201 name=name+"_genrule",
202 srcs=srcs,
203 outs=outs,
204 cmd=" && ".join(["cp $(location %s) $(location %s)" %
205 (s, _RelativeOutputPath(s, include))
206 for s in srcs]))
207
208 native.filegroup(
209 name=name,
210 srcs=outs,
211 **kargs)
212
213
214def py_proto_library(
215 name,
216 srcs=[],
217 deps=[],
218 py_libs=[],
219 py_extra_srcs=[],
220 include=None,
221 default_runtime="//google/protobuf:protobuf_python",
222 protoc="//google/protobuf:protoc",
223 **kargs):
224 """Bazel rule to create a Python protobuf library from proto source files
225
226 NOTE: the rule is only an internal workaround to generate protos. The
227 interface may change and the rule may be removed when bazel has introduced
228 the native rule.
229
230 Args:
231 name: the name of the py_proto_library.
232 srcs: the .proto files of the py_proto_library.
233 deps: a list of dependency labels; must be py_proto_library.
234 py_libs: a list of other py_library targets depended by the generated
235 py_library.
236 py_extra_srcs: extra source files that will be added to the output
237 py_library. This attribute is used for internal bootstrapping.
238 include: a string indicating the include path of the .proto files.
239 default_runtime: the implicitly default runtime which will be depended on by
240 the generated py_library target.
241 protoc: the label of the protocol compiler to generate the sources.
242 **kargs: other keyword arguments that are passed to cc_library.
243
244 """
245 outs = _PyOuts(srcs)
246
247 includes = []
248 if include != None:
249 includes = [include]
250
251 _proto_gen(
252 name=name + "_genproto",
253 srcs=srcs,
254 deps=[s + "_genproto" for s in deps],
255 includes=includes,
256 protoc=protoc,
257 gen_py=1,
258 outs=outs,
259 visibility=["//visibility:public"],
260 )
261
262 if include != None:
263 # Copy the output files to the desired location to make the import work.
264 internal_copied_filegroup_name=name + "_internal_copied_filegroup"
265 internal_copied_filegroup(
266 name=internal_copied_filegroup_name,
267 srcs=outs,
268 include=include)
269 outs=[internal_copied_filegroup_name]
270
271 if default_runtime and not default_runtime in py_libs + deps:
272 py_libs += [default_runtime]
Brian Silverman3fca9d72016-02-20 02:32:51 -0500273 py_libs += ['@python_import_helpers//:google_protobuf_importer']
Brian Silverman9c614bc2016-02-15 20:20:02 -0500274
275 native.py_library(
276 name=name,
277 srcs=outs+py_extra_srcs,
278 deps=py_libs+deps,
279 **kargs)
280
281def internal_protobuf_py_tests(
282 name,
283 modules=[],
284 **kargs):
285 """Bazel rules to create batch tests for protobuf internal.
286
287 Args:
288 name: the name of the rule.
289 modules: a list of modules for tests. The macro will create a py_test for
290 each of the parameter with the source "google/protobuf/%s.py"
291 kargs: extra parameters that will be passed into the py_test.
292
293 """
294 for m in modules:
295 s = _RelativeOutputPath(
296 "python/google/protobuf/internal/%s.py" % m, "python")
297 native.py_test(
298 name="py_%s" % m,
299 srcs=[s],
300 main=s,
301 **kargs)