1# Copyright 2022 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Defines a rule to check the dependencies of a given target.""" 16 17load("@bazel_skylib//lib:new_sets.bzl", "sets") 18 19# Traverse the dependency graph along the "deps" attribute of the 20# target and return a struct with one field called 'tf_collected_deps'. 21# tf_collected_deps will be the union of the deps of the current target 22# and the tf_collected_deps of the dependencies of this target. 23# Borrowed from TensorFlow (https://github.com/tensorflow/tensorflow). 24def _collect_deps_aspect_impl(target, ctx): 25 direct, transitive = [], [] 26 all_deps = [] 27 if hasattr(ctx.rule.attr, "deps"): 28 all_deps += ctx.rule.attr.deps 29 if hasattr(ctx.rule.attr, "data"): 30 all_deps += ctx.rule.attr.data 31 for dep in all_deps: 32 direct.append(dep.label) 33 if hasattr(dep, "tf_collected_deps"): 34 transitive.append(dep.tf_collected_deps) 35 return struct(tf_collected_deps = depset(direct = direct, transitive = transitive)) 36 37collect_deps_aspect = aspect( 38 attr_aspects = ["deps", "data"], 39 implementation = _collect_deps_aspect_impl, 40) 41 42def _dep_label(dep): 43 label = dep.label 44 return label.package + ":" + label.name 45 46# This rule checks that transitive dependencies don't depend on the targets 47# listed in the 'disallowed_deps' attribute, but do depend on the targets listed 48# in the 'required_deps' attribute. Dependencies considered are targets in the 49# 'deps' attribute or the 'data' attribute. 50# Borrowed from TensorFlow (https://github.com/tensorflow/tensorflow). 51def _check_deps_impl(ctx): 52 required_deps = ctx.attr.required_deps 53 disallowed_deps = ctx.attr.disallowed_deps 54 for input_dep in ctx.attr.deps: 55 if not hasattr(input_dep, "tf_collected_deps"): 56 continue 57 collected_deps = sets.make(input_dep.tf_collected_deps.to_list()) 58 for disallowed_dep in disallowed_deps: 59 if sets.contains(collected_deps, disallowed_dep.label): 60 fail( 61 _dep_label(input_dep) + " cannot depend on " + 62 _dep_label(disallowed_dep), 63 ) 64 for required_dep in required_deps: 65 if not sets.contains(collected_deps, required_dep.label): 66 fail( 67 _dep_label(input_dep) + " must depend on " + 68 _dep_label(required_dep), 69 ) 70 71check_deps = rule( 72 _check_deps_impl, 73 attrs = { 74 "deps": attr.label_list( 75 aspects = [collect_deps_aspect], 76 mandatory = True, 77 allow_files = True, 78 ), 79 "disallowed_deps": attr.label_list( 80 default = [], 81 allow_files = True, 82 ), 83 "required_deps": attr.label_list( 84 default = [], 85 allow_files = True, 86 ), 87 }, 88) 89