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