STT-tensorflow/third_party/flatbuffers/build_defs.bzl
YoungSeok Yoon f9fb66cdb7 Correctly add newline character when concatenating schema generated python srcs
The last sed command used for adding the import statement and the '\n' was
working as intended on Linux, but was failing on macOS, due to the difference in
sed behavior between GNU and BSD.

Fixed the issue by using an actual newline character with $'\n', which correctly
works on both platforms.

Fixes https://github.com/tensorflow/tensorflow/issues/39756

PiperOrigin-RevId: 313691192
Change-Id: I96e5544c03641bae05753e8f7d1346c8aa1c0f6e
2020-05-28 17:34:10 -07:00

612 lines
21 KiB
Python

"""BUILD rules for generating flatbuffer files."""
load("@build_bazel_rules_android//android:rules.bzl", "android_library")
flatc_path = "@flatbuffers//:flatc"
zip_files = "//tensorflow/lite/tools:zip_files"
DEFAULT_INCLUDE_PATHS = [
"./",
"$(GENDIR)",
"$(BINDIR)",
]
DEFAULT_FLATC_ARGS = [
"--no-union-value-namespacing",
"--gen-object-api",
]
def flatbuffer_library_public(
name,
srcs,
outs,
language_flag,
out_prefix = "",
includes = [],
include_paths = [],
flatc_args = DEFAULT_FLATC_ARGS,
reflection_name = "",
reflection_visibility = None,
output_to_bindir = False):
"""Generates code files for reading/writing the given flatbuffers in the requested language using the public compiler.
Outs:
filegroup(name): all generated source files.
Fileset([reflection_name]): (Optional) all generated reflection binaries.
Args:
name: Rule name.
srcs: Source .fbs files. Sent in order to the compiler.
outs: Output files from flatc.
language_flag: Target language flag. One of [-c, -j, -js].
out_prefix: Prepend this path to the front of all generated files except on
single source targets. Usually is a directory name.
includes: Optional, list of filegroups of schemas that the srcs depend on.
include_paths: Optional, list of paths the includes files can be found in.
flatc_args: Optional, list of additional arguments to pass to flatc.
reflection_name: Optional, if set this will generate the flatbuffer
reflection binaries for the schemas.
reflection_visibility: The visibility of the generated reflection Fileset.
output_to_bindir: Passed to genrule for output to bin directory.
"""
include_paths_cmd = ["-I %s" % (s) for s in include_paths]
# '$(@D)' when given a single source target will give the appropriate
# directory. Appending 'out_prefix' is only necessary when given a build
# target with multiple sources.
output_directory = (
("-o $(@D)/%s" % (out_prefix)) if len(srcs) > 1 else ("-o $(@D)")
)
genrule_cmd = " ".join([
"for f in $(SRCS); do",
"$(location %s)" % (flatc_path),
" ".join(flatc_args),
" ".join(include_paths_cmd),
language_flag,
output_directory,
"$$f;",
"done",
])
native.genrule(
name = name,
srcs = srcs,
outs = outs,
output_to_bindir = output_to_bindir,
tools = includes + [flatc_path],
cmd = genrule_cmd,
message = "Generating flatbuffer files for %s:" % (name),
)
if reflection_name:
reflection_genrule_cmd = " ".join([
"for f in $(SRCS); do",
"$(location %s)" % (flatc_path),
"-b --schema",
" ".join(flatc_args),
" ".join(include_paths_cmd),
language_flag,
output_directory,
"$$f;",
"done",
])
reflection_outs = [
(out_prefix + "%s.bfbs") % (s.replace(".fbs", "").split("/")[-1])
for s in srcs
]
native.genrule(
name = "%s_srcs" % reflection_name,
srcs = srcs,
outs = reflection_outs,
output_to_bindir = output_to_bindir,
tools = includes + [flatc_path],
cmd = reflection_genrule_cmd,
message = "Generating flatbuffer reflection binary for %s:" % (name),
)
# TODO(b/114456773): Make bazel rules proper and supported by flatbuffer
# Have to comment this since FilesetEntry is not supported in bazel
# skylark.
# native.Fileset(
# name = reflection_name,
# out = "%s_out" % reflection_name,
# entries = [
# native.FilesetEntry(files = reflection_outs),
# ],
# visibility = reflection_visibility,
# )
def flatbuffer_cc_library(
name,
srcs,
srcs_filegroup_name = "",
out_prefix = "",
includes = [],
include_paths = [],
flatc_args = DEFAULT_FLATC_ARGS,
visibility = None,
srcs_filegroup_visibility = None,
gen_reflections = False):
'''A cc_library with the generated reader/writers for the given flatbuffer definitions.
Outs:
filegroup([name]_srcs): all generated .h files.
filegroup(srcs_filegroup_name if specified, or [name]_includes if not):
Other flatbuffer_cc_library's can pass this in for their `includes`
parameter, if they depend on the schemas in this library.
Fileset([name]_reflection): (Optional) all generated reflection binaries.
cc_library([name]): library with sources and flatbuffers deps.
Remarks:
** Because the genrule used to call flatc does not have any trivial way of
computing the output list of files transitively generated by includes and
--gen-includes (the default) being defined for flatc, the --gen-includes
flag will not work as expected. The way around this is to add a dependency
to the flatbuffer_cc_library defined alongside the flatc included Fileset.
For example you might define:
flatbuffer_cc_library(
name = "my_fbs",
srcs = [ "schemas/foo.fbs" ],
includes = [ "//third_party/bazz:bazz_fbs_includes" ],
)
In which foo.fbs includes a few files from the Fileset defined at
//third_party/bazz:bazz_fbs_includes. When compiling the library that
includes foo_generated.h, and therefore has my_fbs as a dependency, it
will fail to find any of the bazz *_generated.h files unless you also
add bazz's flatbuffer_cc_library to your own dependency list, e.g.:
cc_library(
name = "my_lib",
deps = [
":my_fbs",
"//third_party/bazz:bazz_fbs"
],
)
Happy dependent Flatbuffering!
Args:
name: Rule name.
srcs: Source .fbs files. Sent in order to the compiler.
srcs_filegroup_name: Name of the output filegroup that holds srcs. Pass this
filegroup into the `includes` parameter of any other
flatbuffer_cc_library that depends on this one's schemas.
out_prefix: Prepend this path to the front of all generated files. Usually
is a directory name.
includes: Optional, list of filegroups of schemas that the srcs depend on.
** SEE REMARKS BELOW **
include_paths: Optional, list of paths the includes files can be found in.
flatc_args: Optional list of additional arguments to pass to flatc
(e.g. --gen-mutable).
visibility: The visibility of the generated cc_library. By default, use the
default visibility of the project.
srcs_filegroup_visibility: The visibility of the generated srcs filegroup.
By default, use the value of the visibility parameter above.
gen_reflections: Optional, if true this will generate the flatbuffer
reflection binaries for the schemas.
'''
output_headers = [
(out_prefix + "%s_generated.h") % (s.replace(".fbs", "").split("/")[-1])
for s in srcs
]
reflection_name = "%s_reflection" % name if gen_reflections else ""
flatbuffer_library_public(
name = "%s_srcs" % (name),
srcs = srcs,
outs = output_headers,
language_flag = "-c",
out_prefix = out_prefix,
includes = includes,
include_paths = include_paths,
flatc_args = flatc_args,
reflection_name = reflection_name,
reflection_visibility = visibility,
)
native.cc_library(
name = name,
hdrs = output_headers,
srcs = output_headers,
features = [
"-parse_headers",
],
deps = [
"@flatbuffers//:runtime_cc",
],
includes = ["."],
linkstatic = 1,
visibility = visibility,
)
# A filegroup for the `srcs`. That is, all the schema files for this
# Flatbuffer set.
native.filegroup(
name = srcs_filegroup_name if srcs_filegroup_name else "%s_includes" % (name),
srcs = srcs,
visibility = srcs_filegroup_visibility if srcs_filegroup_visibility != None else visibility,
)
# Custom provider to track dependencies transitively.
FlatbufferInfo = provider(
fields = {
"transitive_srcs": "flatbuffer schema definitions.",
},
)
def _flatbuffer_schemas_aspect_impl(target, ctx):
_ignore = [target]
transitive_srcs = depset()
if hasattr(ctx.rule.attr, "deps"):
for dep in ctx.rule.attr.deps:
if FlatbufferInfo in dep:
transitive_srcs = depset(dep[FlatbufferInfo].transitive_srcs, transitive = [transitive_srcs])
if hasattr(ctx.rule.attr, "srcs"):
for src in ctx.rule.attr.srcs:
if FlatbufferInfo in src:
transitive_srcs = depset(src[FlatbufferInfo].transitive_srcs, transitive = [transitive_srcs])
for f in src.files:
if f.extension == "fbs":
transitive_srcs = depset([f], transitive = [transitive_srcs])
return [FlatbufferInfo(transitive_srcs = transitive_srcs)]
# An aspect that runs over all dependencies and transitively collects
# flatbuffer schema files.
_flatbuffer_schemas_aspect = aspect(
attr_aspects = [
"deps",
"srcs",
],
implementation = _flatbuffer_schemas_aspect_impl,
)
# Rule to invoke the flatbuffer compiler.
def _gen_flatbuffer_srcs_impl(ctx):
outputs = ctx.attr.outputs
include_paths = ctx.attr.include_paths
if ctx.attr.no_includes:
no_includes_statement = ["--no-includes"]
else:
no_includes_statement = []
# Need to generate all files in a directory.
if not outputs:
outputs = [ctx.actions.declare_directory("{}_all".format(ctx.attr.name))]
output_directory = outputs[0].path
else:
outputs = [ctx.actions.declare_file(output) for output in outputs]
output_directory = outputs[0].dirname
deps = depset(ctx.files.srcs + ctx.files.deps, transitive = [
dep[FlatbufferInfo].transitive_srcs
for dep in ctx.attr.deps
if FlatbufferInfo in dep
])
include_paths_cmd_line = []
for s in include_paths:
include_paths_cmd_line.extend(["-I", s])
for src in ctx.files.srcs:
ctx.actions.run(
inputs = deps,
outputs = outputs,
executable = ctx.executable._flatc,
arguments = [
ctx.attr.language_flag,
"-o",
output_directory,
# Allow for absolute imports and referencing of generated files.
"-I",
"./",
"-I",
ctx.genfiles_dir.path,
"-I",
ctx.bin_dir.path,
] + no_includes_statement +
include_paths_cmd_line + [
"--no-union-value-namespacing",
"--gen-object-api",
src.path,
],
progress_message = "Generating flatbuffer files for {}:".format(src),
)
return [
DefaultInfo(files = depset(outputs)),
]
_gen_flatbuffer_srcs = rule(
_gen_flatbuffer_srcs_impl,
attrs = {
"srcs": attr.label_list(
allow_files = [".fbs"],
mandatory = True,
),
"outputs": attr.string_list(
default = [],
mandatory = False,
),
"deps": attr.label_list(
default = [],
mandatory = False,
aspects = [_flatbuffer_schemas_aspect],
),
"include_paths": attr.string_list(
default = [],
mandatory = False,
),
"language_flag": attr.string(
mandatory = True,
),
"no_includes": attr.bool(
default = False,
mandatory = False,
),
"_flatc": attr.label(
default = Label("@flatbuffers//:flatc"),
executable = True,
cfg = "host",
),
},
output_to_genfiles = True,
)
def _concat_flatbuffer_py_srcs_impl(ctx):
# Merge all generated python files. The files are concatenated and the
# import statements are removed. Finally we import the flatbuffer runtime
# library.
ctx.actions.run_shell(
inputs = ctx.attr.deps[0].files,
outputs = [ctx.outputs.out],
command = (
"find '%s' -name '*.py' -exec cat {} + |" +
"sed '/import flatbuffers/d' |" +
"sed 's/from flatbuffers." +
"/from flatbuffers.python.flatbuffers./' |" +
"sed '1s/^/from flatbuffers.python " +
"import flatbuffers\\'$'\\n/' > %s"
) % (
ctx.attr.deps[0].files.to_list()[0].path,
ctx.outputs.out.path,
),
)
_concat_flatbuffer_py_srcs = rule(
_concat_flatbuffer_py_srcs_impl,
attrs = {
"deps": attr.label_list(mandatory = True),
},
output_to_genfiles = True,
outputs = {"out": "%{name}.py"},
)
def flatbuffer_py_library(
name,
srcs,
deps = [],
include_paths = []):
"""A py_library with the generated reader/writers for the given schema.
This rule assumes that the schema files define non-conflicting names, so that
they can be merged in a single file. This is e.g. the case if only a single
namespace is used.
The rule call the flatbuffer compiler for all schema files and merges the
generated python files into a single file that is wrapped in a py_library.
Args:
name: Rule name. (required)
srcs: List of source .fbs files. (required)
deps: List of dependencies.
include_paths: Optional, list of paths the includes files can be found in.
"""
all_srcs = "{}_srcs".format(name)
_gen_flatbuffer_srcs(
name = all_srcs,
srcs = srcs,
language_flag = "--python",
deps = deps,
include_paths = include_paths,
)
all_srcs_no_include = "{}_srcs_no_include".format(name)
_gen_flatbuffer_srcs(
name = all_srcs_no_include,
srcs = srcs,
language_flag = "--python",
deps = deps,
no_includes = True,
include_paths = include_paths,
)
concat_py_srcs = "{}_generated".format(name)
_concat_flatbuffer_py_srcs(
name = concat_py_srcs,
deps = [
":{}".format(all_srcs_no_include),
],
)
native.py_library(
name = name,
srcs = [
":{}".format(concat_py_srcs),
],
srcs_version = "PY2AND3",
deps = deps + [
"@flatbuffers//:runtime_py",
],
)
def flatbuffer_java_library(
name,
srcs,
custom_package = "",
package_prefix = "",
include_paths = DEFAULT_INCLUDE_PATHS,
flatc_args = DEFAULT_FLATC_ARGS,
visibility = None):
"""A java library with the generated reader/writers for the given flatbuffer definitions.
Args:
name: Rule name. (required)
srcs: List of source .fbs files including all includes. (required)
custom_package: Package name of generated Java files. If not specified
namespace in the schema files will be used. (optional)
package_prefix: like custom_package, but prefixes to the existing
namespace. (optional)
include_paths: List of paths that includes files can be found in. (optional)
flatc_args: List of additional arguments to pass to flatc. (optional)
visibility: Visibility setting for the java_library rule. (optional)
"""
out_srcjar = "java_%s_all.srcjar" % name
flatbuffer_java_srcjar(
name = "%s_srcjar" % name,
srcs = srcs,
out = out_srcjar,
custom_package = custom_package,
flatc_args = flatc_args,
include_paths = include_paths,
package_prefix = package_prefix,
)
native.filegroup(
name = "%s.srcjar" % name,
srcs = [out_srcjar],
)
native.java_library(
name = name,
srcs = [out_srcjar],
javacopts = ["-source 7 -target 7"],
deps = [
"@flatbuffers//:runtime_java",
],
visibility = visibility,
)
def flatbuffer_java_srcjar(
name,
srcs,
out,
custom_package = "",
package_prefix = "",
include_paths = DEFAULT_INCLUDE_PATHS,
flatc_args = DEFAULT_FLATC_ARGS):
"""Generate flatbuffer Java source files.
Args:
name: Rule name. (required)
srcs: List of source .fbs files including all includes. (required)
out: Output file name. (required)
custom_package: Package name of generated Java files. If not specified
namespace in the schema files will be used. (optional)
package_prefix: like custom_package, but prefixes to the existing
namespace. (optional)
include_paths: List of paths that includes files can be found in. (optional)
flatc_args: List of additional arguments to pass to flatc. (optional)
"""
command_fmt = """set -e
tmpdir=$(@D)
schemas=$$tmpdir/schemas
java_root=$$tmpdir/java
rm -rf $$schemas
rm -rf $$java_root
mkdir -p $$schemas
mkdir -p $$java_root
for src in $(SRCS); do
dest=$$schemas/$$src
rm -rf $$(dirname $$dest)
mkdir -p $$(dirname $$dest)
if [ -z "{custom_package}" ] && [ -z "{package_prefix}" ]; then
cp -f $$src $$dest
else
if [ -z "{package_prefix}" ]; then
sed -e "s/namespace\\s.*/namespace {custom_package};/" $$src > $$dest
else
sed -e "s/namespace \\([^;]\\+\\);/namespace {package_prefix}.\\1;/" $$src > $$dest
fi
fi
done
flatc_arg_I="-I $$tmpdir/schemas"
for include_path in {include_paths}; do
flatc_arg_I="$$flatc_arg_I -I $$schemas/$$include_path"
done
flatc_additional_args=
for arg in {flatc_args}; do
flatc_additional_args="$$flatc_additional_args $$arg"
done
for src in $(SRCS); do
$(location {flatc_path}) $$flatc_arg_I --java $$flatc_additional_args -o $$java_root $$schemas/$$src
done
$(location {zip_files}) -export_zip_path=$@ -file_directory=$$java_root
"""
genrule_cmd = command_fmt.format(
package_name = native.package_name(),
custom_package = custom_package,
package_prefix = package_prefix,
flatc_path = flatc_path,
zip_files = zip_files,
include_paths = " ".join(include_paths),
flatc_args = " ".join(flatc_args),
)
native.genrule(
name = name,
srcs = srcs,
outs = [out],
tools = [flatc_path, zip_files],
cmd = genrule_cmd,
)
def flatbuffer_android_library(
name,
srcs,
custom_package = "",
package_prefix = "",
include_paths = DEFAULT_INCLUDE_PATHS,
flatc_args = DEFAULT_FLATC_ARGS,
visibility = None):
"""An android_library with the generated reader/writers for the given flatbuffer definitions.
Args:
name: Rule name. (required)
srcs: List of source .fbs files including all includes. (required)
custom_package: Package name of generated Java files. If not specified
namespace in the schema files will be used. (optional)
package_prefix: like custom_package, but prefixes to the existing
namespace. (optional)
include_paths: List of paths that includes files can be found in. (optional)
flatc_args: List of additional arguments to pass to flatc. (optional)
visibility: Visibility setting for the android_library rule. (optional)
"""
out_srcjar = "android_%s_all.srcjar" % name
flatbuffer_java_srcjar(
name = "%s_srcjar" % name,
srcs = srcs,
out = out_srcjar,
custom_package = custom_package,
flatc_args = flatc_args,
include_paths = include_paths,
package_prefix = package_prefix,
)
native.filegroup(
name = "%s.srcjar" % name,
srcs = [out_srcjar],
)
# To support org.checkerframework.dataflow.qual.Pure.
checkerframework_annotations = [
"@org_checkerframework_qual",
] if "--java-checkerframework" in flatc_args else []
android_library(
name = name,
srcs = [out_srcjar],
javacopts = ["-source 7 -target 7"],
visibility = visibility,
deps = [
"@flatbuffers//:runtime_android",
] + checkerframework_annotations,
)