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