xref: /aosp_15_r20/external/bazel-skylib/rules/build_test.bzl (revision bcb5dc7965af6ee42bf2f21341a2ec00233a8c8a)
1*bcb5dc79SHONG Yifan# Copyright 2019 The Bazel Authors. All rights reserved.
2*bcb5dc79SHONG Yifan#
3*bcb5dc79SHONG Yifan# Licensed under the Apache License, Version 2.0 (the "License");
4*bcb5dc79SHONG Yifan# you may not use this file except in compliance with the License.
5*bcb5dc79SHONG Yifan# You may obtain a copy of the License at
6*bcb5dc79SHONG Yifan#
7*bcb5dc79SHONG Yifan#    http://www.apache.org/licenses/LICENSE-2.0
8*bcb5dc79SHONG Yifan#
9*bcb5dc79SHONG Yifan# Unless required by applicable law or agreed to in writing, software
10*bcb5dc79SHONG Yifan# distributed under the License is distributed on an "AS IS" BASIS,
11*bcb5dc79SHONG Yifan# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*bcb5dc79SHONG Yifan# See the License for the specific language governing permissions and
13*bcb5dc79SHONG Yifan# limitations under the License.
14*bcb5dc79SHONG Yifan
15*bcb5dc79SHONG Yifan"""A test verifying other targets build as part of a `bazel test`"""
16*bcb5dc79SHONG Yifan
17*bcb5dc79SHONG Yifanload("//lib:new_sets.bzl", "sets")
18*bcb5dc79SHONG Yifan
19*bcb5dc79SHONG Yifandef _empty_test_impl(ctx):
20*bcb5dc79SHONG Yifan    extension = ".bat" if ctx.attr.is_windows else ".sh"
21*bcb5dc79SHONG Yifan    content = "exit 0" if ctx.attr.is_windows else "#!/usr/bin/env bash\nexit 0"
22*bcb5dc79SHONG Yifan    executable = ctx.actions.declare_file(ctx.label.name + extension)
23*bcb5dc79SHONG Yifan    ctx.actions.write(
24*bcb5dc79SHONG Yifan        output = executable,
25*bcb5dc79SHONG Yifan        is_executable = True,
26*bcb5dc79SHONG Yifan        content = content,
27*bcb5dc79SHONG Yifan    )
28*bcb5dc79SHONG Yifan
29*bcb5dc79SHONG Yifan    return [DefaultInfo(
30*bcb5dc79SHONG Yifan        files = depset([executable]),
31*bcb5dc79SHONG Yifan        executable = executable,
32*bcb5dc79SHONG Yifan        runfiles = ctx.runfiles(files = ctx.files.data),
33*bcb5dc79SHONG Yifan    )]
34*bcb5dc79SHONG Yifan
35*bcb5dc79SHONG Yifan_empty_test = rule(
36*bcb5dc79SHONG Yifan    implementation = _empty_test_impl,
37*bcb5dc79SHONG Yifan    attrs = {
38*bcb5dc79SHONG Yifan        "data": attr.label_list(allow_files = True),
39*bcb5dc79SHONG Yifan        "is_windows": attr.bool(mandatory = True),
40*bcb5dc79SHONG Yifan    },
41*bcb5dc79SHONG Yifan    test = True,
42*bcb5dc79SHONG Yifan)
43*bcb5dc79SHONG Yifan
44*bcb5dc79SHONG Yifan_GENRULE_ATTRS = [
45*bcb5dc79SHONG Yifan    "compatible_with",
46*bcb5dc79SHONG Yifan    "exec_compatible_with",
47*bcb5dc79SHONG Yifan    "restricted_to",
48*bcb5dc79SHONG Yifan    "tags",
49*bcb5dc79SHONG Yifan    "target_compatible_with",
50*bcb5dc79SHONG Yifan]
51*bcb5dc79SHONG Yifan
52*bcb5dc79SHONG Yifandef build_test(name, targets, **kwargs):
53*bcb5dc79SHONG Yifan    """Test rule checking that other targets build.
54*bcb5dc79SHONG Yifan
55*bcb5dc79SHONG Yifan    This works not by an instance of this test failing, but instead by
56*bcb5dc79SHONG Yifan    the targets it depends on failing to build, and hence failing
57*bcb5dc79SHONG Yifan    the attempt to run this test.
58*bcb5dc79SHONG Yifan
59*bcb5dc79SHONG Yifan    Typical usage:
60*bcb5dc79SHONG Yifan
61*bcb5dc79SHONG Yifan    ```
62*bcb5dc79SHONG Yifan      load("@bazel_skylib//rules:build_test.bzl", "build_test")
63*bcb5dc79SHONG Yifan      build_test(
64*bcb5dc79SHONG Yifan          name = "my_build_test",
65*bcb5dc79SHONG Yifan          targets = [
66*bcb5dc79SHONG Yifan              "//some/package:rule",
67*bcb5dc79SHONG Yifan          ],
68*bcb5dc79SHONG Yifan      )
69*bcb5dc79SHONG Yifan    ```
70*bcb5dc79SHONG Yifan
71*bcb5dc79SHONG Yifan    Args:
72*bcb5dc79SHONG Yifan      name: The name of the test rule.
73*bcb5dc79SHONG Yifan      targets: A list of targets to ensure build.
74*bcb5dc79SHONG Yifan      **kwargs: The [common attributes for tests](https://bazel.build/reference/be/common-definitions#common-attributes-tests).
75*bcb5dc79SHONG Yifan    """
76*bcb5dc79SHONG Yifan    if len(targets) == 0:
77*bcb5dc79SHONG Yifan        fail("targets must be non-empty", "targets")
78*bcb5dc79SHONG Yifan    if kwargs.get("data", None):
79*bcb5dc79SHONG Yifan        fail("data is not supported on a build_test()", "data")
80*bcb5dc79SHONG Yifan
81*bcb5dc79SHONG Yifan    # Remove any duplicate test targets.
82*bcb5dc79SHONG Yifan    targets = sets.to_list(sets.make(targets))
83*bcb5dc79SHONG Yifan
84*bcb5dc79SHONG Yifan    # Use a genrule to ensure the targets are built (works because it forces
85*bcb5dc79SHONG Yifan    # the outputs of the other rules on as data for the genrule)
86*bcb5dc79SHONG Yifan
87*bcb5dc79SHONG Yifan    # Split into batches to hopefully avoid things becoming so large they are
88*bcb5dc79SHONG Yifan    # too much for a remote execution set up.
89*bcb5dc79SHONG Yifan    batch_size = max(1, len(targets) // 100)
90*bcb5dc79SHONG Yifan
91*bcb5dc79SHONG Yifan    # Pull a few args over from the test to the genrule.
92*bcb5dc79SHONG Yifan    genrule_args = {k: kwargs.get(k) for k in _GENRULE_ATTRS if k in kwargs}
93*bcb5dc79SHONG Yifan
94*bcb5dc79SHONG Yifan    # Only the test target should be used to determine whether or not the deps
95*bcb5dc79SHONG Yifan    # are built. Tagging the genrule targets as manual accomplishes this by
96*bcb5dc79SHONG Yifan    # preventing them from being picked up by recursive build patterns (`//...`).
97*bcb5dc79SHONG Yifan    genrule_tags = genrule_args.pop("tags", [])
98*bcb5dc79SHONG Yifan    if "manual" not in genrule_tags:
99*bcb5dc79SHONG Yifan        genrule_tags = genrule_tags + ["manual"]
100*bcb5dc79SHONG Yifan
101*bcb5dc79SHONG Yifan    # Pass an output from the genrules as data to a shell test to bundle
102*bcb5dc79SHONG Yifan    # it all up in a test.
103*bcb5dc79SHONG Yifan    test_data = []
104*bcb5dc79SHONG Yifan
105*bcb5dc79SHONG Yifan    for idx, batch in enumerate([targets[i:i + batch_size] for i in range(0, len(targets), batch_size)]):
106*bcb5dc79SHONG Yifan        full_name = "{name}_{idx}__deps".format(name = name, idx = idx)
107*bcb5dc79SHONG Yifan        test_data.append(full_name)
108*bcb5dc79SHONG Yifan        native.genrule(
109*bcb5dc79SHONG Yifan            name = full_name,
110*bcb5dc79SHONG Yifan            srcs = batch,
111*bcb5dc79SHONG Yifan            outs = [full_name + ".out"],
112*bcb5dc79SHONG Yifan            testonly = 1,
113*bcb5dc79SHONG Yifan            visibility = ["//visibility:private"],
114*bcb5dc79SHONG Yifan            cmd = "touch $@",
115*bcb5dc79SHONG Yifan            cmd_bat = "type nul > $@",
116*bcb5dc79SHONG Yifan            tags = genrule_tags,
117*bcb5dc79SHONG Yifan            **genrule_args
118*bcb5dc79SHONG Yifan        )
119*bcb5dc79SHONG Yifan
120*bcb5dc79SHONG Yifan    _empty_test(
121*bcb5dc79SHONG Yifan        name = name,
122*bcb5dc79SHONG Yifan        data = test_data,
123*bcb5dc79SHONG Yifan        size = kwargs.pop("size", "small"),  # Default to small for test size
124*bcb5dc79SHONG Yifan        is_windows = select({
125*bcb5dc79SHONG Yifan            "@bazel_tools//src/conditions:host_windows": True,
126*bcb5dc79SHONG Yifan            "//conditions:default": False,
127*bcb5dc79SHONG Yifan        }),
128*bcb5dc79SHONG Yifan        **kwargs
129*bcb5dc79SHONG Yifan    )
130