From f5e50bc53c52708abd29919a66bac307c6dc2e07 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Fri, 25 Jan 2019 14:34:15 -0500 Subject: [PATCH] Add a build_test rule. (#97) * Add a build_test rule. This rules (marco) provides a 'test' target that can be used to ensure other targets build. --- rules/BUILD | 10 +++++ rules/build_test.bzl | 88 ++++++++++++++++++++++++++++++++++++++ rules/empty_test.sh | 3 ++ tests/BUILD | 3 ++ tests/build_test_tests.bzl | 39 +++++++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 rules/build_test.bzl create mode 100755 rules/empty_test.sh create mode 100644 tests/build_test_tests.bzl diff --git a/rules/BUILD b/rules/BUILD index 4f98c5d..7ab4c30 100644 --- a/rules/BUILD +++ b/rules/BUILD @@ -2,6 +2,12 @@ load("//:bzl_library.bzl", "bzl_library") licenses(["notice"]) +bzl_library( + name = "build_test", + srcs = ["build_test.bzl"], + visibility = ["//visibility:public"], +) + bzl_library( name = "maprule", srcs = ["maprule.bzl"], @@ -18,3 +24,7 @@ bzl_library( "//lib:paths", ], ) + +# Exported for build_test.bzl to make sure of, it is an implementation detail +# of the rule and should not be directly used by anything else. +exports_files(["empty_test.sh"]) diff --git a/rules/build_test.bzl b/rules/build_test.bzl new file mode 100644 index 0000000..b2a9366 --- /dev/null +++ b/rules/build_test.bzl @@ -0,0 +1,88 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A test verifying other targets build as part of a `bazel test`""" + +load("//lib:new_sets.bzl", "sets") + +def build_test(name, targets, **kwargs): + """Test rule checking that other targets build. + + This works not by an instance of this test failing, but instead by + the targets it depends on failing to build, and hence failing + the attempt to run this test. + + NOTE: At the moment, this won't work on Windows; but someone adding + support would be welcomed. + + Typical usage: + + load("@bazel_skylib//rules:build_test.bzl", "build_test") + build_test( + name = "my_build_test", + targets = [ + "//some/package:rule", + ], + ) + + Args: + name: The name of the test rule. + targets: A list of targets to ensure build. + kwargs: The common attributes for tests. + """ + if len(targets) == 0: + fail("targets must be non-empty", "targets") + if kwargs.get("data", None): + fail("data is not supported on a build_test()", "data") + + # Remove any duplicate test targets. + targets = sets.to_list(sets.make(targets)) + + # Use a genrule to ensure the targets are built (works because it forces + # the outputs of the other rules on as data for the genrule) + + # Split into batches to hopefully avoid things becoming so large they are + # too much for a remote execution set up. + batch_size = max(1, len(targets) // 100) + + # Pull a few args over from the test to the genrule. + args_to_reuse = ["compatible_with", "restricted_to", "tags"] + genrule_args = {k: kwargs.get(k) for k in args_to_reuse if k in kwargs} + + # Pass an output from the genrules as data to a shell test to bundle + # it all up in a test. + test_data = [] + + for idx, batch in enumerate([targets[i:i + batch_size] for i in range(0, len(targets), batch_size)]): + full_name = "{name}_{idx}__deps".format(name = name, idx = idx) + test_data.append(full_name) + native.genrule( + name = full_name, + srcs = batch, + outs = [full_name + ".out"], + testonly = 1, + visibility = ["//visibility:private"], + # TODO: Does this need something else for Windows? + cmd = "touch $@", + **genrule_args + ) + + native.sh_test( + name = name, + # TODO: Does this need something else for Windows? + srcs = ["@bazel_skylib//rules:empty_test.sh"], + data = test_data, + size = kwargs.pop("size", "small"), # Default to small for test size + **kwargs + ) diff --git a/rules/empty_test.sh b/rules/empty_test.sh new file mode 100755 index 0000000..fde58b3 --- /dev/null +++ b/rules/empty_test.sh @@ -0,0 +1,3 @@ +#!/bin/bash +# This is a support file for build_test.bzl, it shouldn't be used by anything else. +exit 0 diff --git a/tests/BUILD b/tests/BUILD index 8657cae..fdf00ad 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -1,3 +1,4 @@ +load(":build_test_tests.bzl", "build_test_test_suite") load(":collections_tests.bzl", "collections_test_suite") load(":dicts_tests.bzl", "dicts_test_suite") load(":maprule_tests.bzl", "maprule_test_suite") @@ -13,6 +14,8 @@ load(":versions_tests.bzl", "versions_test_suite") licenses(["notice"]) +build_test_test_suite() + collections_test_suite() dicts_test_suite() diff --git a/tests/build_test_tests.bzl b/tests/build_test_tests.bzl new file mode 100644 index 0000000..df3671f --- /dev/null +++ b/tests/build_test_tests.bzl @@ -0,0 +1,39 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//rules:build_test.bzl", "build_test") + +def build_test_test_suite(): + # Since the rules doesn't do anything really, it just makes some targets + # to get Bazel to build other targets via a `bazel test`, just make some + # targets to exercise the rule. + + # Make a source file + native.genrule( + name = "build_test__make_src", + outs = ["build_test__src.cc"], + cmd = "echo 'int dummy() { return 0; }' > $@", + ) + + # Use it in a non-test target + native.cc_library( + name = "build_test__build_target", + srcs = [":build_test__make_src"], + ) + + # Wrap a build test around the target. + build_test( + name = "build_test__test", + targets = [":build_test__build_target"], + )