1"""BUILD rules for generating flatbuffer files.""" 2 3flatc_path = "@flatbuffers//:flatc" 4 5DEFAULT_FLATC_ARGS = [ 6 "--no-union-value-namespacing", 7 "--gen-object-api", 8] 9 10def flatbuffer_library_public( 11 name, 12 srcs, 13 outs, 14 language_flag, 15 out_prefix = "", 16 includes = [], 17 include_paths = [], 18 flatc_args = DEFAULT_FLATC_ARGS, 19 reflection_name = "", 20 reflection_visibility = None, 21 output_to_bindir = False): 22 """Generates code files for reading/writing the given flatbuffers in the requested language using the public compiler. 23 24 Outs: 25 filegroup(name): all generated source files. 26 Fileset([reflection_name]): (Optional) all generated reflection binaries. 27 28 Args: 29 name: Rule name. 30 srcs: Source .fbs files. Sent in order to the compiler. 31 outs: Output files from flatc. 32 language_flag: Target language flag. One of [-c, -j, -js]. 33 out_prefix: Prepend this path to the front of all generated files except on 34 single source targets. Usually is a directory name. 35 includes: Optional, list of filegroups of schemas that the srcs depend on. 36 include_paths: Optional, list of paths the includes files can be found in. 37 flatc_args: Optional, list of additional arguments to pass to flatc. 38 reflection_name: Optional, if set this will generate the flatbuffer 39 reflection binaries for the schemas. 40 reflection_visibility: The visibility of the generated reflection Fileset. 41 output_to_bindir: Passed to genrule for output to bin directory. 42 """ 43 include_paths_cmd = ["-I %s" % (s) for s in include_paths] 44 45 # '$(@D)' when given a single source target will give the appropriate 46 # directory. Appending 'out_prefix' is only necessary when given a build 47 # target with multiple sources. 48 output_directory = ( 49 ("-o $(@D)/%s" % (out_prefix)) if len(srcs) > 1 else ("-o $(@D)") 50 ) 51 genrule_cmd = " ".join([ 52 "for f in $(SRCS); do", 53 "$(location %s)" % (flatc_path), 54 " ".join(flatc_args), 55 " ".join(include_paths_cmd), 56 language_flag, 57 output_directory, 58 "$$f;", 59 "done", 60 ]) 61 native.genrule( 62 name = name, 63 srcs = srcs, 64 outs = outs, 65 output_to_bindir = output_to_bindir, 66 tools = includes + [flatc_path], 67 cmd = genrule_cmd, 68 message = "Generating flatbuffer files for %s:" % (name), 69 ) 70 if reflection_name: 71 reflection_genrule_cmd = " ".join([ 72 "for f in $(SRCS); do", 73 "$(location %s)" % (flatc_path), 74 "-b --schema", 75 " ".join(flatc_args), 76 " ".join(include_paths_cmd), 77 language_flag, 78 output_directory, 79 "$$f;", 80 "done", 81 ]) 82 reflection_outs = [ 83 (out_prefix + "%s.bfbs") % (s.replace(".fbs", "").split("/")[-1]) 84 for s in srcs 85 ] 86 native.genrule( 87 name = "%s_srcs" % reflection_name, 88 srcs = srcs, 89 outs = reflection_outs, 90 output_to_bindir = output_to_bindir, 91 tools = includes + [flatc_path], 92 cmd = reflection_genrule_cmd, 93 message = "Generating flatbuffer reflection binary for %s:" % (name), 94 ) 95 # TODO(b/114456773): Make bazel rules proper and supported by flatbuffer 96 # Have to comment this since FilesetEntry is not supported in bazel 97 # skylark. 98 # native.Fileset( 99 # name = reflection_name, 100 # out = "%s_out" % reflection_name, 101 # entries = [ 102 # native.FilesetEntry(files = reflection_outs), 103 # ], 104 # visibility = reflection_visibility, 105 # ) 106 107def flatbuffer_cc_library( 108 name, 109 srcs, 110 srcs_filegroup_name = "", 111 out_prefix = "", 112 includes = [], 113 include_paths = [], 114 flatc_args = DEFAULT_FLATC_ARGS, 115 visibility = None, 116 srcs_filegroup_visibility = None, 117 gen_reflections = False): 118 '''A cc_library with the generated reader/writers for the given flatbuffer definitions. 119 120 Outs: 121 filegroup([name]_srcs): all generated .h files. 122 filegroup(srcs_filegroup_name if specified, or [name]_includes if not): 123 Other flatbuffer_cc_library's can pass this in for their `includes` 124 parameter, if they depend on the schemas in this library. 125 Fileset([name]_reflection): (Optional) all generated reflection binaries. 126 cc_library([name]): library with sources and flatbuffers deps. 127 128 Remarks: 129 ** Because the genrule used to call flatc does not have any trivial way of 130 computing the output list of files transitively generated by includes and 131 --gen-includes (the default) being defined for flatc, the --gen-includes 132 flag will not work as expected. The way around this is to add a dependency 133 to the flatbuffer_cc_library defined alongside the flatc included Fileset. 134 For example you might define: 135 136 flatbuffer_cc_library( 137 name = "my_fbs", 138 srcs = [ "schemas/foo.fbs" ], 139 includes = [ "//third_party/bazz:bazz_fbs_includes" ], 140 ) 141 142 In which foo.fbs includes a few files from the Fileset defined at 143 //third_party/bazz:bazz_fbs_includes. When compiling the library that 144 includes foo_generated.h, and therefore has my_fbs as a dependency, it 145 will fail to find any of the bazz *_generated.h files unless you also 146 add bazz's flatbuffer_cc_library to your own dependency list, e.g.: 147 148 cc_library( 149 name = "my_lib", 150 deps = [ 151 ":my_fbs", 152 "//third_party/bazz:bazz_fbs" 153 ], 154 ) 155 156 Happy dependent Flatbuffering! 157 158 Args: 159 name: Rule name. 160 srcs: Source .fbs files. Sent in order to the compiler. 161 srcs_filegroup_name: Name of the output filegroup that holds srcs. Pass this 162 filegroup into the `includes` parameter of any other 163 flatbuffer_cc_library that depends on this one's schemas. 164 out_prefix: Prepend this path to the front of all generated files. Usually 165 is a directory name. 166 includes: Optional, list of filegroups of schemas that the srcs depend on. 167 ** SEE REMARKS BELOW ** 168 include_paths: Optional, list of paths the includes files can be found in. 169 flatc_args: Optional list of additional arguments to pass to flatc 170 (e.g. --gen-mutable). 171 visibility: The visibility of the generated cc_library. By default, use the 172 default visibility of the project. 173 srcs_filegroup_visibility: The visibility of the generated srcs filegroup. 174 By default, use the value of the visibility parameter above. 175 gen_reflections: Optional, if true this will generate the flatbuffer 176 reflection binaries for the schemas. 177 ''' 178 output_headers = [ 179 (out_prefix + "%s_generated.h") % (s.replace(".fbs", "").split("/")[-1]) 180 for s in srcs 181 ] 182 reflection_name = "%s_reflection" % name if gen_reflections else "" 183 184 flatbuffer_library_public( 185 name = "%s_srcs" % (name), 186 srcs = srcs, 187 outs = output_headers, 188 language_flag = "-c", 189 out_prefix = out_prefix, 190 includes = includes, 191 include_paths = include_paths, 192 flatc_args = flatc_args, 193 reflection_name = reflection_name, 194 reflection_visibility = visibility, 195 ) 196 native.cc_library( 197 name = name, 198 hdrs = output_headers, 199 srcs = output_headers, 200 features = [ 201 "-parse_headers", 202 ], 203 deps = [ 204 "@flatbuffers//:runtime_cc", 205 ], 206 includes = ["."], 207 linkstatic = 1, 208 visibility = visibility, 209 ) 210 211 # A filegroup for the `srcs`. That is, all the schema files for this 212 # Flatbuffer set. 213 native.filegroup( 214 name = srcs_filegroup_name if srcs_filegroup_name else "%s_includes" % (name), 215 srcs = srcs, 216 visibility = srcs_filegroup_visibility if srcs_filegroup_visibility != None else visibility, 217 ) 218 219# Custom provider to track dependencies transitively. 220FlatbufferInfo = provider( 221 fields = { 222 "transitive_srcs": "flatbuffer schema definitions.", 223 }, 224) 225 226def _flatbuffer_schemas_aspect_impl(target, ctx): 227 _ignore = [target] 228 transitive_srcs = depset() 229 if hasattr(ctx.rule.attr, "deps"): 230 for dep in ctx.rule.attr.deps: 231 if FlatbufferInfo in dep: 232 transitive_srcs = depset(dep[FlatbufferInfo].transitive_srcs, transitive = [transitive_srcs]) 233 if hasattr(ctx.rule.attr, "srcs"): 234 for src in ctx.rule.attr.srcs: 235 if FlatbufferInfo in src: 236 transitive_srcs = depset(src[FlatbufferInfo].transitive_srcs, transitive = [transitive_srcs]) 237 for f in src.files: 238 if f.extension == "fbs": 239 transitive_srcs = depset([f], transitive = [transitive_srcs]) 240 return [FlatbufferInfo(transitive_srcs = transitive_srcs)] 241 242# An aspect that runs over all dependencies and transitively collects 243# flatbuffer schema files. 244_flatbuffer_schemas_aspect = aspect( 245 attr_aspects = [ 246 "deps", 247 "srcs", 248 ], 249 implementation = _flatbuffer_schemas_aspect_impl, 250) 251 252# Rule to invoke the flatbuffer compiler. 253def _gen_flatbuffer_srcs_impl(ctx): 254 outputs = ctx.attr.outputs 255 include_paths = ctx.attr.include_paths 256 if ctx.attr.no_includes: 257 no_includes_statement = ["--no-includes"] 258 else: 259 no_includes_statement = [] 260 261 # Need to generate all files in a directory. 262 if not outputs: 263 outputs = [ctx.actions.declare_directory("{}_all".format(ctx.attr.name))] 264 output_directory = outputs[0].path 265 else: 266 outputs = [ctx.actions.declare_file(output) for output in outputs] 267 output_directory = outputs[0].dirname 268 269 deps = depset(ctx.files.srcs + ctx.files.deps, transitive = [ 270 dep[FlatbufferInfo].transitive_srcs 271 for dep in ctx.attr.deps 272 if FlatbufferInfo in dep 273 ]) 274 275 include_paths_cmd_line = [] 276 for s in include_paths: 277 include_paths_cmd_line.extend(["-I", s]) 278 279 for src in ctx.files.srcs: 280 ctx.actions.run( 281 inputs = deps, 282 outputs = outputs, 283 executable = ctx.executable._flatc, 284 arguments = [ 285 ctx.attr.language_flag, 286 "-o", 287 output_directory, 288 # Allow for absolute imports and referencing of generated files. 289 "-I", 290 "./", 291 "-I", 292 ctx.genfiles_dir.path, 293 "-I", 294 ctx.bin_dir.path, 295 ] + no_includes_statement + 296 include_paths_cmd_line + [ 297 "--no-union-value-namespacing", 298 "--gen-object-api", 299 src.path, 300 ], 301 progress_message = "Generating flatbuffer files for {}:".format(src), 302 ) 303 return [ 304 DefaultInfo(files = depset(outputs)), 305 ] 306 307_gen_flatbuffer_srcs = rule( 308 _gen_flatbuffer_srcs_impl, 309 attrs = { 310 "srcs": attr.label_list( 311 allow_files = [".fbs"], 312 mandatory = True, 313 ), 314 "outputs": attr.string_list( 315 default = [], 316 mandatory = False, 317 ), 318 "deps": attr.label_list( 319 default = [], 320 mandatory = False, 321 aspects = [_flatbuffer_schemas_aspect], 322 ), 323 "include_paths": attr.string_list( 324 default = [], 325 mandatory = False, 326 ), 327 "language_flag": attr.string( 328 mandatory = True, 329 ), 330 "no_includes": attr.bool( 331 default = False, 332 mandatory = False, 333 ), 334 "_flatc": attr.label( 335 default = Label("@flatbuffers//:flatc"), 336 executable = True, 337 cfg = "host", 338 ), 339 }, 340 output_to_genfiles = True, 341) 342 343def _concat_flatbuffer_py_srcs_impl(ctx): 344 # Merge all generated python files. The files are concatenated and the 345 # import statements are removed. Finally we import the flatbuffer runtime 346 # library. 347 ctx.actions.run_shell( 348 inputs = ctx.attr.deps[0].files, 349 outputs = [ctx.outputs.out], 350 command = ( 351 "find '%s' -name '*.py' -exec cat {} + |" + 352 "sed '/import flatbuffers/d' |" + 353 "sed 's/from flatbuffers." + 354 "/from flatbuffers.python.flatbuffers./' |" + 355 "sed '1s/^/from flatbuffers.python " + 356 "import flatbuffers\\n/' > %s" 357 ) % ( 358 ctx.attr.deps[0].files.to_list()[0].path, 359 ctx.outputs.out.path, 360 ), 361 ) 362 363_concat_flatbuffer_py_srcs = rule( 364 _concat_flatbuffer_py_srcs_impl, 365 attrs = { 366 "deps": attr.label_list(mandatory = True), 367 }, 368 output_to_genfiles = True, 369 outputs = {"out": "%{name}.py"}, 370) 371 372def flatbuffer_py_library( 373 name, 374 srcs, 375 deps = [], 376 include_paths = []): 377 """A py_library with the generated reader/writers for the given schema. 378 379 This rule assumes that the schema files define non-conflicting names, so that 380 they can be merged in a single file. This is e.g. the case if only a single 381 namespace is used. 382 The rule call the flatbuffer compiler for all schema files and merges the 383 generated python files into a single file that is wrapped in a py_library. 384 385 Args: 386 name: Rule name. (required) 387 srcs: List of source .fbs files. (required) 388 deps: List of dependencies. 389 include_paths: Optional, list of paths the includes files can be found in. 390 """ 391 all_srcs = "{}_srcs".format(name) 392 _gen_flatbuffer_srcs( 393 name = all_srcs, 394 srcs = srcs, 395 language_flag = "--python", 396 deps = deps, 397 include_paths = include_paths, 398 ) 399 all_srcs_no_include = "{}_srcs_no_include".format(name) 400 _gen_flatbuffer_srcs( 401 name = all_srcs_no_include, 402 srcs = srcs, 403 language_flag = "--python", 404 deps = deps, 405 no_includes = True, 406 include_paths = include_paths, 407 ) 408 concat_py_srcs = "{}_generated".format(name) 409 _concat_flatbuffer_py_srcs( 410 name = concat_py_srcs, 411 deps = [ 412 ":{}".format(all_srcs_no_include), 413 ], 414 ) 415 native.py_library( 416 name = name, 417 srcs = [ 418 ":{}".format(concat_py_srcs), 419 ], 420 srcs_version = "PY2AND3", 421 deps = deps + [ 422 "@flatbuffers//:runtime_py", 423 ], 424 ) 425