blob: 8f022d526980e4539f013463b1489def275f754d [file] [log] [blame]
"""Build macros for TF Lite."""
load("//tensorflow:tensorflow.bzl", "clean_dep", "if_oss", "tf_binary_additional_srcs", "tf_cc_shared_object")
load("//tensorflow/lite:special_rules.bzl", "tflite_copts_extra")
load("//tensorflow/lite/java:aar_with_jni.bzl", "aar_with_jni")
load("@build_bazel_rules_android//android:rules.bzl", "android_library")
load("@bazel_skylib//rules:build_test.bzl", "build_test")
def register_extension_info(**kwargs):
pass
def tflite_copts():
"""Defines common compile time flags for TFLite libraries."""
copts = [
"-DFARMHASH_NO_CXX_STRING",
] + select({
clean_dep("//tensorflow:android_arm"): [
"-mfpu=neon",
],
# copybara:uncomment_begin(google-only)
# clean_dep("//tensorflow:chromiumos_x86_64"): [],
# copybara:uncomment_end
clean_dep("//tensorflow:ios_x86_64"): [
"-msse4.1",
],
clean_dep("//tensorflow:linux_x86_64"): [
"-msse4.2",
],
clean_dep("//tensorflow:linux_x86_64_no_sse"): [],
clean_dep("//tensorflow:windows"): [
# copybara:uncomment_begin(no MSVC flags in google)
# "-DTFL_COMPILE_LIBRARY",
# "-Wno-sign-compare",
# copybara:uncomment_end_and_comment_begin
"/DTFL_COMPILE_LIBRARY",
"/wd4018", # -Wno-sign-compare
# copybara:comment_end
],
"//conditions:default": [
"-Wno-sign-compare",
],
}) + select({
clean_dep("//tensorflow:optimized"): ["-O3"],
"//conditions:default": [],
}) + select({
clean_dep("//tensorflow:android"): [
"-ffunction-sections", # Helps trim binary size.
"-fdata-sections", # Helps trim binary size.
],
"//conditions:default": [],
}) + select({
clean_dep("//tensorflow:windows"): [],
"//conditions:default": [
"-fno-exceptions", # Exceptions are unused in TFLite.
],
}) + select({
clean_dep("//tensorflow/lite:tflite_with_xnnpack_explicit_false"): ["-DTFLITE_WITHOUT_XNNPACK"],
"//conditions:default": [],
}) + select({
clean_dep("//tensorflow/lite:tensorflow_profiler_config"): ["-DTF_LITE_TENSORFLOW_PROFILER"],
"//conditions:default": [],
}) + select({
clean_dep("//tensorflow/lite/delegates:tflite_debug_delegate"): ["-DTFLITE_DEBUG_DELEGATE"],
"//conditions:default": [],
})
return copts + tflite_copts_extra()
def tflite_copts_warnings():
"""Defines common warning flags used primarily by internal TFLite libraries."""
# TODO(b/155906820): Include with `tflite_copts()` after validating clients.
return select({
clean_dep("//tensorflow:windows"): [
# We run into trouble on Windows toolchains with warning flags,
# as mentioned in the comments below on each flag.
# We could be more aggressive in enabling supported warnings on each
# Windows toolchain, but we compromise with keeping BUILD files simple
# by limiting the number of config_setting's.
],
"//conditions:default": [
"-Wall",
],
})
EXPORTED_SYMBOLS = clean_dep("//tensorflow/lite/java/src/main/native:exported_symbols.lds")
LINKER_SCRIPT = clean_dep("//tensorflow/lite/java/src/main/native:version_script.lds")
def tflite_linkopts_unstripped():
"""Defines linker flags to reduce size of TFLite binary.
These are useful when trying to investigate the relative size of the
symbols in TFLite.
Returns:
a select object with proper linkopts
"""
# In case you wonder why there's no --icf is because the gains were
# negligible, and created potential compatibility problems.
return select({
clean_dep("//tensorflow:android"): [
"-latomic", # Required for some uses of ISO C++11 <atomic> in x86.
"-Wl,--no-export-dynamic", # Only inc syms referenced by dynamic obj.
"-Wl,--gc-sections", # Eliminate unused code and data.
"-Wl,--as-needed", # Don't link unused libs.
],
"//conditions:default": [],
})
def tflite_jni_linkopts_unstripped():
"""Defines linker flags to reduce size of TFLite binary with JNI.
These are useful when trying to investigate the relative size of the
symbols in TFLite.
Returns:
a select object with proper linkopts
"""
# In case you wonder why there's no --icf is because the gains were
# negligible, and created potential compatibility problems.
return select({
clean_dep("//tensorflow:android"): [
"-latomic", # Required for some uses of ISO C++11 <atomic> in x86.
"-Wl,--gc-sections", # Eliminate unused code and data.
"-Wl,--as-needed", # Don't link unused libs.
],
"//conditions:default": [],
})
def tflite_symbol_opts():
"""Defines linker flags whether to include symbols or not."""
return select({
clean_dep("//tensorflow:debug"): [],
clean_dep("//tensorflow/lite:tflite_keep_symbols"): [],
"//conditions:default": [
# Omit symbol table, for all non debug builds
"-Wl,-s",
],
})
def tflite_linkopts_no_undefined():
"""Defines linker flags to enable errors for undefined symbols.
This enables link-time errors for undefined symbols even when linking
shared libraries, where the default behaviour on many systems is to only
report errors for undefined symbols at runtime.
"""
return if_oss(
select({
"//tensorflow:ios": [
# iOS linker uses "--undefined error" instead of "--no-undefined".
"-Wl,-undefined,error",
],
"//conditions:default": ["-Wl,--no-undefined"],
}),
select({
# Can't enable errors for undefined symbols for asan/msan/tsan mode,
# since undefined symbols in shared libraries (references to symbols
# that will be defined in the main executable) are normal and
# expected in those cases.
"//tools/cpp:asan_build": [],
"//tools/cpp:msan_build": [],
"//tools/cpp:tsan_build": [],
"//tensorflow:ios": [
# iOS linker uses "--undefined error" instead of "--no-undefined".
"-Wl,-undefined,error",
],
"//conditions:default": ["-Wl,--no-undefined"],
}),
)
def tflite_linkopts():
"""Defines linker flags for linking TFLite binary."""
return tflite_linkopts_unstripped() + tflite_symbol_opts()
def tflite_jni_linkopts():
"""Defines linker flags for linking TFLite binary with JNI."""
return tflite_jni_linkopts_unstripped() + tflite_symbol_opts()
def tflite_jni_binary(
name,
copts = tflite_copts(),
linkopts = tflite_jni_linkopts(),
linkscript = LINKER_SCRIPT,
exported_symbols = EXPORTED_SYMBOLS,
linkshared = 1,
linkstatic = 1,
testonly = 0,
deps = [],
tags = [],
srcs = [],
visibility = None): # 'None' means use the default visibility.
"""Builds a jni binary for TFLite."""
linkopts = linkopts + select({
clean_dep("//tensorflow:macos"): [
"-Wl,-exported_symbols_list,$(location {})".format(exported_symbols),
"-Wl,-install_name,@rpath/" + name,
],
clean_dep("//tensorflow:windows"): [],
"//conditions:default": [
"-Wl,--version-script,$(location {})".format(linkscript),
# copybara:uncomment_begin(google-only)
# "-Wl,--undefined-version",
# copybara:uncomment_end
"-Wl,-soname," + name,
],
})
native.cc_binary(
name = name,
copts = copts,
linkshared = linkshared,
linkstatic = linkstatic,
deps = deps + [linkscript, exported_symbols],
srcs = srcs,
tags = tags,
linkopts = linkopts,
testonly = testonly,
visibility = visibility,
)
def tflite_cc_shared_object(
name,
copts = tflite_copts(),
linkopts = [],
linkstatic = 1,
per_os_targets = False,
**kwargs):
"""Builds a shared object for TFLite."""
tf_cc_shared_object(
name = name,
copts = copts,
linkstatic = linkstatic,
linkopts = linkopts + tflite_jni_linkopts(),
framework_so = [],
per_os_targets = per_os_targets,
**kwargs
)
def tf_to_tflite(name, src, options, out):
"""Convert a frozen tensorflow graphdef to TF Lite's flatbuffer.
Args:
name: Name of rule.
src: name of the input graphdef file.
options: options passed to TFLite Converter.
out: name of the output flatbuffer file.
"""
toco_cmdline = " ".join([
"$(location //tensorflow/lite/python:tflite_convert)",
"--enable_v1_converter",
("--graph_def_file=$(location %s)" % src),
("--output_file=$(location %s)" % out),
] + options)
native.genrule(
name = name,
srcs = [src],
outs = [out],
cmd = toco_cmdline,
tools = ["//tensorflow/lite/python:tflite_convert"] + tf_binary_additional_srcs(),
)
def DEPRECATED_tf_to_tflite(name, src, options, out):
"""DEPRECATED Convert a frozen tensorflow graphdef to TF Lite's flatbuffer, using toco.
Please use tf_to_tflite instead.
TODO(b/138396996): Migrate away from this deprecated rule.
Args:
name: Name of rule.
src: name of the input graphdef file.
options: options passed to TOCO.
out: name of the output flatbuffer file.
"""
toco_cmdline = " ".join([
"$(location //tensorflow/lite/toco:toco)",
"--input_format=TENSORFLOW_GRAPHDEF",
"--output_format=TFLITE",
("--input_file=$(location %s)" % src),
("--output_file=$(location %s)" % out),
] + options)
native.genrule(
name = name,
srcs = [src],
outs = [out],
cmd = toco_cmdline,
tools = ["//tensorflow/lite/toco:toco"] + tf_binary_additional_srcs(),
)
def tflite_to_json(name, src, out):
"""Convert a TF Lite flatbuffer to JSON.
Args:
name: Name of rule.
src: name of the input flatbuffer file.
out: name of the output JSON file.
"""
flatc = "@flatbuffers//:flatc"
schema = "//tensorflow/lite/schema:schema.fbs"
native.genrule(
name = name,
srcs = [schema, src],
outs = [out],
cmd = ("TMP=`mktemp`; cp $(location %s) $${TMP}.bin &&" +
"$(location %s) --raw-binary --strict-json -t" +
" -o /tmp $(location %s) -- $${TMP}.bin &&" +
"cp $${TMP}.json $(location %s)") %
(src, flatc, schema, out),
tools = [flatc],
)
def json_to_tflite(name, src, out):
"""Convert a JSON file to TF Lite's flatbuffer.
Args:
name: Name of rule.
src: name of the input JSON file.
out: name of the output flatbuffer file.
"""
flatc = "@flatbuffers//:flatc"
schema = "//tensorflow/lite/schema:schema_fbs"
native.genrule(
name = name,
srcs = [schema, src],
outs = [out],
cmd = ("TMP=`mktemp`; cp $(location %s) $${TMP}.json &&" +
"$(location %s) --raw-binary --unknown-json --allow-non-utf8 -b" +
" -o /tmp $(location %s) $${TMP}.json &&" +
"cp $${TMP}.bin $(location %s)") %
(src, flatc, schema, out),
tools = [flatc],
)
def _gen_selected_ops_impl(ctx):
args = ctx.actions.args()
args.add(ctx.attr.namespace, format = "--namespace=%s")
args.add(ctx.outputs.output, format = "--output_registration=%s")
tflite_path = "//tensorflow/lite"
args.add("--tflite_path=%s" % tflite_path[2:])
args.add_joined(
ctx.files.models,
join_with = ",",
format_joined = "--input_models=%s",
)
ctx.actions.run(
outputs = [ctx.outputs.output],
inputs = ctx.files.models,
arguments = [args],
executable = ctx.executable._generate_op_registrations,
mnemonic = "OpRegistration",
progress_message = "gen_selected_ops",
)
gen_selected_ops_rule = rule(
implementation = _gen_selected_ops_impl,
attrs = {
"models": attr.label_list(default = [], allow_files = True),
"namespace": attr.string(default = ""),
"output": attr.output(),
"_generate_op_registrations": attr.label(
executable = True,
default = Label(clean_dep(
"//tensorflow/lite/tools:generate_op_registrations",
)),
cfg = "exec",
),
},
)
def gen_selected_ops(name, model, namespace = "", **kwargs):
"""Generate the source file that includes only used ops.
Args:
name: Prefix of the generated source file.
model: TFLite models to interpret, expect a list in case of multiple models.
namespace: Namespace in which to put RegisterSelectedOps.
**kwargs: Additional kwargs to pass to genrule.
"""
# If there's only one model provided as a string.
if type(model) == type(""):
model = [model]
gen_selected_ops_rule(
name = name,
models = model,
namespace = namespace,
output = name + "_registration.cc",
**kwargs
)
def flex_dep(target_op_sets):
if "SELECT_TF_OPS" in target_op_sets:
return ["//tensorflow/lite/delegates/flex:delegate"]
else:
return []
def gen_model_coverage_test(src, model_name, data, failure_type, tags, size = "medium"):
"""Generates Python test targets for testing TFLite models.
Args:
src: Main source file.
model_name: Name of the model to test (must be also listed in the 'data'
dependencies)
data: List of BUILD targets linking the data.
failure_type: List of failure types (none, toco, crash, inference, evaluation)
expected for the corresponding combinations of op sets
("TFLITE_BUILTINS", "TFLITE_BUILTINS,SELECT_TF_OPS", "SELECT_TF_OPS").
tags: List of strings of additional tags.
"""
i = 0
for target_op_sets in ["TFLITE_BUILTINS", "TFLITE_BUILTINS,SELECT_TF_OPS", "SELECT_TF_OPS"]:
args = []
if failure_type[i] != "none":
args.append("--failure_type=%s" % failure_type[i])
i = i + 1
# Avoid coverage timeouts for large/enormous tests.
coverage_tags = ["nozapfhahn"] if size in ["large", "enormous"] else []
native.py_test(
name = "model_coverage_test_%s_%s" % (model_name, target_op_sets.lower().replace(",", "_")),
srcs = [src],
main = src,
size = size,
args = [
"--model_name=%s" % model_name,
"--target_ops=%s" % target_op_sets,
] + args,
data = data,
srcs_version = "PY3",
python_version = "PY3",
tags = [
"no_gpu", # Executing with TF GPU configurations is redundant.
"no_oss",
"no_windows",
# Disable sanitizer runs as models can be huge and can timeout.
"noasan",
"nomsan",
"notsan",
] + tags + coverage_tags,
deps = [
"//third_party/py/tensorflow",
"//tensorflow/lite/testing/model_coverage:model_coverage_lib",
"//tensorflow/lite/python:lite",
"//tensorflow/python:client_testlib",
] + flex_dep(target_op_sets),
)
def tflite_custom_cc_library(
name,
models = [],
srcs = [],
deps = [],
visibility = ["//visibility:private"],
experimental = False,
**kwargs):
"""Generates a tflite cc library, stripping off unused operators.
This library includes the TfLite runtime as well as all operators needed for the given models.
Op resolver can be retrieved using tflite::CreateOpResolver method.
Args:
name: Str, name of the target.
models: List of models. This TFLite build will only include
operators used in these models. If the list is empty, all builtin
operators are included.
srcs: List of files implementing custom operators if any.
deps: Additional dependencies to build all the custom operators.
visibility: Visibility setting for the generated target. Default to private.
experimental: Whether to include experimental APIs or not.
**kwargs: Additional arguments for native.cc_library.
"""
real_srcs = []
real_srcs.extend(srcs)
real_deps = []
real_deps.extend(deps)
if models:
gen_selected_ops(
name = "%s_registration" % name,
model = models,
testonly = kwargs.get("testonly", False),
)
real_srcs.append(":%s_registration" % name)
real_srcs.append("//tensorflow/lite:create_op_resolver_with_selected_ops.cc")
else:
# Support all operators if `models` not specified.
real_deps.append("//tensorflow/lite:create_op_resolver_with_builtin_ops")
if experimental:
framework = "//tensorflow/lite:framework_experimental"
else:
framework = "//tensorflow/lite:framework_stable"
native.cc_library(
name = name,
srcs = real_srcs,
hdrs = [
"//tensorflow/lite:create_op_resolver.h",
],
copts = tflite_copts(),
linkopts = select({
"//tensorflow:windows": [],
"//conditions:default": ["-lm", "-ldl"],
}),
deps = depset([
framework,
"//tensorflow/lite/kernels:builtin_ops",
"//tensorflow/lite/core:private_create_op_resolver_header",
] + real_deps),
visibility = visibility,
**kwargs
)
def tflite_custom_android_library(
name,
models = [],
srcs = [],
deps = [],
custom_package = "org.tensorflow.lite",
visibility = ["//visibility:private"],
include_xnnpack_delegate = True,
include_nnapi_delegate = True,
experimental = False):
"""Generates a tflite Android library, stripping off unused operators.
Note that due to a limitation in the JNI Java wrapper, the compiled TfLite shared binary
has to be named as tensorflowlite_jni.so so please make sure that there is no naming conflict.
i.e. you can't call this rule multiple times in the same build file.
Args:
name: Name of the target.
models: List of models to be supported. This TFLite build will only include
operators used in these models. If the list is empty, all builtin
operators are included.
srcs: List of files implementing custom operators if any.
deps: Additional dependencies to build all the custom operators.
custom_package: Name of the Java package. It is required by android_library in case
the Java source file can't be inferred from the directory where this rule is used.
visibility: Visibility setting for the generated target. Default to private.
include_xnnpack_delegate: Whether to include the XNNPACK delegate or not.
include_nnapi_delegate: Whether to include the NNAPI delegate or not.
experimental: Whether to include experimental APIs or not.
"""
tflite_custom_cc_library(name = "%s_cc" % name, models = models, srcs = srcs, deps = deps, visibility = visibility)
delegate_deps = []
if include_nnapi_delegate:
delegate_deps.append("//tensorflow/lite/delegates/nnapi/java/src/main/native")
if include_xnnpack_delegate:
delegate_deps.append("//tensorflow/lite/delegates/xnnpack:xnnpack_delegate")
if experimental:
native_framework_only = "//tensorflow/lite/java/src/main/native:native_experimental_framework_only"
else:
native_framework_only = "//tensorflow/lite/java/src/main/native:native_stable_framework_only"
# JNI wrapper expects a binary file called `libtensorflowlite_jni.so` in java path.
tflite_jni_binary(
name = "libtensorflowlite_jni.so",
linkscript = "//tensorflow/lite/java:tflite_version_script.lds",
# Do not sort: "native_framework_only" must come before custom tflite library.
deps = [
native_framework_only,
":%s_cc" % name,
] + delegate_deps,
)
native.cc_library(
name = "%s_jni" % name,
srcs = ["libtensorflowlite_jni.so"],
visibility = visibility,
)
if experimental:
java_srcs = "//tensorflow/lite/java:java_srcs"
else:
java_srcs = "//tensorflow/lite/java:java_stable_srcs"
android_library(
name = name,
manifest = "//tensorflow/lite/java:AndroidManifest.xml",
srcs = [java_srcs],
deps = [
":%s_jni" % name,
"@org_checkerframework_qual",
],
custom_package = custom_package,
visibility = visibility,
)
aar_with_jni(
name = "%s_aar" % name,
android_library = name,
)
def tflite_custom_c_library(
name,
models = [],
experimental = False,
**kwargs):
"""Generates a tflite C library, stripping off unused operators.
This library includes the C API and the op kernels used in the given models.
Args:
name: Str, name of the target.
models: List of models. This TFLite build will only include
operators used in these models. If the list is empty, all builtin
operators are included.
experimental: Whether to include experimental APIs or not.
**kwargs: custom c_api cc_library kwargs.
"""
op_resolver_deps = "//tensorflow/lite:create_op_resolver_with_builtin_ops"
if models:
gen_selected_ops(
name = "%s_registration" % name,
model = models,
testonly = kwargs.get("testonly", False),
)
if experimental:
framework = "//tensorflow/lite:framework_experimental"
else:
framework = "//tensorflow/lite:framework_stable"
native.cc_library(
name = "%s_create_op_resolver" % name,
srcs = [
":%s_registration" % name,
],
hdrs = ["//tensorflow/lite:create_op_resolver.h"],
copts = tflite_copts(),
deps = [
"//tensorflow/lite/core:private_create_op_resolver_header",
"//tensorflow/lite:create_op_resolver_with_selected_ops",
"//tensorflow/lite:op_resolver",
framework,
"//tensorflow/lite/kernels:builtin_ops",
],
# Using alwayslink here is needed, I believe, to avoid warnings about
# backwards references when linking create_op_resolver_with_selected_ops,
# which has a reference to the RegisterSelectedOps function defined by
# '":%s_registration" % name' (the code generated by the call to
# gen_selected_ops above).
alwayslink = True,
**kwargs
)
op_resolver_deps = "%s_create_op_resolver" % name
if experimental:
hdrs = [
"//tensorflow/lite/c:c_api.h",
"//tensorflow/lite/c:c_api_experimental.h",
"//tensorflow/lite/c:c_api_opaque.h",
]
experimental_deps = [
"//tensorflow/lite/c:c_api_experimental_without_op_resolver_without_alwayslink",
"//tensorflow/lite/core/c:c_api_experimental_without_op_resolver_without_alwayslink",
]
else:
hdrs = [
"//tensorflow/lite/c:c_api.h",
]
experimental_deps = []
native.cc_library(
name = name,
hdrs = hdrs,
copts = tflite_copts(),
deps = [
op_resolver_deps,
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite/c:c_api_without_op_resolver_without_alwayslink",
# TODO(bekzhan): Remove this dependency after we move c_api_opaque.h to tflite/core/.
"//tensorflow/lite/core/c:private_c_api_types",
"//tensorflow/lite/core/c:private_c_api_without_op_resolver_without_alwayslink",
"//tensorflow/lite/core/c:private_common",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
] + experimental_deps,
**kwargs
)
# TODO(b/254126721): Move tflite_combine_cc_tests macro to lite/testing/build_def.bzl.
def tflite_combine_cc_tests(
name,
deps_conditions,
extra_cc_test_tags = [],
extra_build_test_tags = [],
generate_cc_library = False,
**kwargs):
"""Combine certain cc_tests into a single cc_test and a build_test.
This rule should normally be placed at the bottom of a package.
Any cc_test rules that appear after the call to this rule will not
be included in the combined cc_test rule, even if they meet the
other conditions.
Args:
name: the name of the combined cc_test.
deps_conditions: the list of deps that those cc_tests need to have in
order to be combined.
extra_cc_test_tags: the list of extra tags appended to the created
combined cc_test.
extra_build_test_tags: the list of extra tags appended to the created
corresponding build_test for the combined cc_test.
generate_cc_library: if set to True, additionally generates a combined
cc_library containing all kernel tests. The generated cc_library
will exclude all dependencies in `deps_conditions`, so that users
can plugin their own test driver and entry point.
**kwargs: kwargs to pass to the cc_test rule of the test suite.
"""
combined_test_srcs = {}
combined_test_deps = {}
for r in native.existing_rules().values():
# We only include cc_test.
if not r["kind"] == "cc_test":
continue
# Tests with data, args or special build option are not counted.
if r["data"] or r["args"] or r["copts"] or r["defines"] or \
r["includes"] or r["linkopts"] or r["additional_linker_inputs"]:
continue
# We only consider a single-src-file unit test.
if len(r["srcs"]) > 1:
continue
dep_attr = r["deps"]
if type(dep_attr) != type(()) and type(dep_attr) != type([]):
# Attributes based on select() is not supported for simplicity.
continue
# The test has to depend on :test_main
if not any([v in deps_conditions for v in dep_attr]):
continue
combined_test_srcs.update({s: True for s in r["srcs"]})
combined_test_deps.update({d: True for d in r["deps"]})
if combined_test_srcs:
native.cc_test(
name = name,
size = "large",
srcs = list(combined_test_srcs),
tags = ["manual", "notap"] + extra_cc_test_tags,
deps = list(combined_test_deps),
**kwargs
)
build_test(
name = "%s_build_test" % name,
targets = [":%s" % name],
tags = [
"manual",
"tflite_portable_build_test",
] + extra_build_test_tags,
)
if generate_cc_library:
native.cc_library(
name = "%s_lib" % name,
srcs = list(combined_test_srcs),
deps = [d for d in combined_test_deps if d not in deps_conditions],
testonly = 1,
alwayslink = 1,
**kwargs
)
def tflite_self_contained_libs_test_suite(name):
"""Indicate that cc_library rules in this package *should* be self-contained.
This adds build tests for each cc_library rule that verify that the
library can be successfully linked with no undefined symbols. It also
adds a test_suite rule that contains all the generated build tests.
Place this rule at the bottom of a package. Any cc_library rules that
appear after the call to this rule will not be checked for undefined
symbols. Rules that are tagged with 'allow_undefined_symbols' in
their 'tags' attribute will also not be checked for undefined symbols.
Args:
name: the name to use for the test_suite rule that contains
the build tests generated by this macro.
"""
build_tests = []
for rule in native.existing_rules().values():
rule_name = rule["name"]
rule_kind = rule["kind"]
rule_tags = rule["tags"]
if rule_kind == "cc_library" and "allow_undefined_symbols" not in rule_tags:
tflite_cc_shared_object(
name = "%s_test_shared_lib" % rule_name,
testonly = True,
linkopts = tflite_linkopts_no_undefined(),
deps = [":%s" % rule_name],
)
build_test(
name = "%s_build_test" % rule_name,
targets = ["%s_test_shared_lib" % rule_name],
)
build_tests.append("%s_build_test" % rule_name)
native.test_suite(
name = name,
tests = build_tests,
)
def _label(target):
"""Return a Label <https://bazel.build/rules/lib/Label#Label> given a string.
Args:
target: (string) a relative or absolute build target.
"""
if target[0:2] == "//":
return Label(target)
if target[0] == ":":
return Label("//" + native.package_name() + target)
return Label("//" + native.package_name() + ":" + target)
def tflite_cc_library_with_c_headers_test(name, hdrs, **kwargs):
"""Defines a C++ library with C-compatible header files.
This generates a cc_library rule, but also generates
build tests that verify that each of the 'hdrs'
can be successfully built in a C (not C++!) compilation unit
that directly includes only that header file.
Args:
name: (string) as per cc_library.
hdrs: (list of string) as per cc_library.
**kwargs: Additional kwargs to pass to cc_library.
"""
native.cc_library(name = name, hdrs = hdrs, **kwargs)
build_tests = []
for hdr in hdrs:
label = _label(hdr)
basename = "%s__test_self_contained_c__%s" % (name, label.name)
compatible_with = kwargs.pop("compatible_with", [])
native.genrule(
name = "%s_gen" % basename,
outs = ["%s.c" % basename],
compatible_with = compatible_with,
cmd = "echo '#include \"%s/%s\"' > $@" % (label.package, label.name),
visibility = ["//visibility:private"],
testonly = True,
)
kwargs.pop("visibility", None)
kwargs.pop("deps", [])
kwargs.pop("srcs", [])
kwargs.pop("tags", [])
kwargs.pop("testonly", [])
native.cc_library(
name = "%s_lib" % basename,
srcs = ["%s.c" % basename],
deps = [":" + name],
compatible_with = compatible_with,
copts = kwargs.pop("copts", []),
visibility = ["//visibility:private"],
testonly = True,
tags = ["allow_undefined_symbols"],
**kwargs
)
build_test(
name = "%s_build_test" % basename,
visibility = ["//visibility:private"],
targets = ["%s_lib" % basename],
)
build_tests.append("%s_build_test" % basename)
native.test_suite(
name = name + "_self_contained_c_build_tests",
tests = build_tests,
)
register_extension_info(
extension = tflite_cc_library_with_c_headers_test,
label_regex_for_dep = "{extension_name}",
)