metalos/defs.bzl (130 lines of code) (raw):
load("@bazel_skylib//lib:types.bzl", "types")
load("//antlir/bzl:image.bzl", "image")
load("//antlir/bzl:oss_shim.bzl", "third_party", antlir_rust_binary = "rust_binary", antlir_rust_library = "rust_library", antlir_rust_unittest = "rust_unittest")
load("//antlir/bzl:shape.bzl", "shape")
load("//antlir/vm/bzl:defs.bzl", "vm")
load(":metalos_tests.shape.bzl", "container_unittest_opts_t", "unittest_opts_t")
_unittest_flavors = ("plain", "container", "vm")
# Nicer rust wrapping logic with a few nice-to-have features.
# 1. Easier access to third-party dependencies.
# Because rust crates are not namespaced, any dependency without a
# tell-tale buck ':' will be assumed to be a third-party dependency
# 2. A variety of unittest flavors.
# MetalOS has a wide range of rust code that demands different testing
# environments. A good rule of thumb for which test environments to use is:
# - logic: plain
# - requires root: container
# - requires simulated hardware interactions: vm
#
# Tests can be written in the regular rust convention, but with additional
# attributes to add to test functions.
# Test functions can be annotated with one of the following:
# - #[test]: regular logic test
# - #[containertest]: only run in containers
# - #[vmtest]: only run in virtual machines
# Tests with an attribute other than the current environment will be
# skipped.
def _rust_common(
rule,
name,
srcs = (),
test_srcs = (),
unittests = True,
unittest_opts = None,
deps = (),
test_deps = (),
test_env = None,
tests = (),
features = (),
vm_opts = (),
**kwargs):
if types.is_bool(unittests):
unittests = ["plain"] if unittests else []
for flavor in unittests:
if flavor not in _unittest_flavors:
fail(
"'{}' is not a supported rust unittest flavor. Options are {}"
.format(flavor, ", ".join(_unittest_flavors)),
)
deps = [_normalize_rust_dep(d) for d in deps]
test_deps = [_normalize_rust_dep(d) for d in test_deps] + ["//metalos/metalos_macros:metalos_macros"]
tests = list(tests)
tests += [":" + name + _unittest_suffix(flavor) for flavor in unittests]
crate = kwargs.pop("crate", name.replace("-", "_"))
if len(srcs) == 1 and not kwargs.get("crate_root", None):
kwargs["crate_root"] = srcs[0]
rule(
name = name,
srcs = srcs,
crate = crate,
unittests = False,
deps = deps,
tests = tests,
features = features,
**kwargs
)
if not unittest_opts:
unittest_opts = shape.new(unittest_opts_t, container = shape.new(container_unittest_opts_t))
features = list(features)
test_kwargs = dict(kwargs)
test_kwargs.pop("link_style", None)
test_kwargs.pop("allocator", None)
test_kwargs.pop("linker_flags", None)
test_kwargs.pop("proc_macro", None)
if test_env:
test_kwargs["env"] = test_env
srcs = list(srcs)
test_srcs = list(test_srcs) if test_srcs else []
if "plain" in unittests:
antlir_rust_unittest(
name = name + _unittest_suffix("plain"),
srcs = srcs + test_srcs,
crate = crate,
deps = deps + test_deps,
features = features + ["metalos_plain_test"],
**test_kwargs
)
if "container" in unittests:
image.rust_unittest(
name = name + _unittest_suffix("container"),
srcs = srcs + test_srcs,
crate = crate,
deps = deps + test_deps,
features = features + ["metalos_container_test"],
layer = unittest_opts.container.layer,
run_as_user = "root",
boot = unittest_opts.container.boot,
**test_kwargs
)
if "vm" in unittests:
vm.rust_unittest(
name = name + _unittest_suffix("vm"),
srcs = srcs + test_srcs,
crate = crate,
deps = deps + test_deps,
features = features + ["metalos_vm_test"],
vm_opts = vm_opts,
**test_kwargs
)
def _unittest_suffix(flavor):
if flavor == "plain":
return "-unittest"
return "-" + flavor + "-unittest"
def _normalize_rust_dep(dep):
if ":" in dep:
return dep
return third_party.library(dep, platform = "rust")
def rust_binary(name, **kwargs):
_rust_common(antlir_rust_binary, name, **kwargs)
def rust_library(name, **kwargs):
_rust_common(antlir_rust_library, name, **kwargs)
def rust_unittest(name, srcs, deps, **kwargs):
deps = [_normalize_rust_dep(d) for d in deps]
antlir_rust_unittest(
name = name,
srcs = srcs,
deps = deps,
**kwargs
)
def default_test_layer():
return shape.new(container_unittest_opts_t).layer