1# Copyright (C) 2024 The Android Open Source Project 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""" 16Action that verifies all EFI protocols used by GBL are explicitly listed in README.md 17""" 18 19load("@rules_rust//rust/private:providers.bzl", "CrateInfo") 20 21def _readme_test_rule_impl(ctx): 22 shell_script = """ 23while [[ $# -gt 0 ]]; do 24 case $1 in 25 --in) 26 INPUT=$2 27 shift 28 shift 29 ;; 30 --out) 31 OUTPUT=$2 32 shift 33 shift 34 ;; 35 --readme) 36 README=$2 37 shift 38 shift 39 ;; 40 *) 41 echo "Unexpected argument: $1" 42 exit 1 43 ;; 44 esac 45done 46 47if [ ! -f $README ]; then 48 echo "README file doesn't exist: ${README}" 49 exit 1 50fi 51 52ALL_INPUTS=$(echo ${INPUT} | sed 's/,/ /g') 53 54# Look for protocols in the source code that do not exist in the documentation. 55# The protocol name we match on here is the Rust struct name. 56DOCLESS_PROTOCOLS="" 57PROTOCOLS=($(grep -hE 'impl ProtocolInfo for .* \\{' ${ALL_INPUTS} | awk '{print $4}' | sort)) 58for P in ${PROTOCOLS[@]} 59do 60 grep -Lq $P ${README} || DOCLESS_PROTOCOLS+="\n\t$P" 61done 62 63if [ ! -z "${DOCLESS_PROTOCOLS}" ]; then 64 echo -e "Missing documentation for protocol(s):$DOCLESS_PROTOCOLS" 65 exit 1 66fi 67 68# Look for protocols in the documentation that are not in the source, to try to 69# prevent stale docs referring to protocols we are no longer using. 70# Here we're matching on words ending in "Protocol", except "Protocol" itself. 71UNUSED_PROTOCOLS="" 72README_PROTOCOLS=($(grep -P " ?[^ ]+Protocol$" ${README} | awk '{print $NF}' | sort | uniq)) 73for P in ${README_PROTOCOLS[@]} 74do 75 grep -qhE "impl ProtocolInfo for $P" ${ALL_INPUTS} || UNUSED_PROTOCOLS+="\n\t$P" 76done 77 78if [ ! -z "${UNUSED_PROTOCOLS}" ]; then 79 echo -e "Unused protocol(s) found in documentation:$UNUSED_PROTOCOLS" 80 exit 1 81fi 82 83touch $OUTPUT 84""" 85 86 out_file = ctx.actions.declare_file("%s.script" % ctx.attr.name) 87 in_files = [s for d in ctx.attr.deps for s in d[CrateInfo].srcs.to_list()] 88 readme = ctx.attr.readme 89 args = ctx.actions.args() 90 args.add_joined( 91 "--in", 92 in_files, 93 join_with = ",", 94 ) 95 args.add( 96 "--out", 97 out_file, 98 ) 99 args.add( 100 "--readme", 101 readme[DefaultInfo].files.to_list()[0], 102 ) 103 ctx.actions.run_shell( 104 inputs = in_files + readme[DefaultInfo].files.to_list(), 105 outputs = [out_file], 106 arguments = [args], 107 command = shell_script, 108 ) 109 return [DefaultInfo(executable = out_file)] 110 111readme_test = rule( 112 implementation = _readme_test_rule_impl, 113 attrs = { 114 "deps": attr.label_list( 115 providers = [CrateInfo], 116 ), 117 "readme": attr.label( 118 allow_single_file = [".md"], 119 ), 120 }, 121 test = True, 122) 123