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
54DOCLESS_PROTOCOLS=""
55PROTOCOLS=($(grep -hE 'impl ProtocolInfo for .* \\{' ${ALL_INPUTS} | awk '{print $4}' | sort))
56for P in ${PROTOCOLS[@]}
57do
58  grep -Lq $P ${README} || DOCLESS_PROTOCOLS+="\n\t$P"
59done
60
61if [ ! -z "${DOCLESS_PROTOCOLS}" ]; then
62  echo -e "Missing documentation for protocol(s):$DOCLESS_PROTOCOLS"
63  exit 1
64fi
65
66UNUSED_PROTOCOLS=""
67README_PROTOCOLS=($(grep -P " ?.*?Protocol$" ${README} | awk '{print $NF}' | sort | uniq))
68for P in ${README_PROTOCOLS[@]}
69do
70  grep -qhE "impl ProtocolInfo for $P" ${ALL_INPUTS} || UNUSED_PROTOCOLS+="\n\t$P"
71done
72
73if [ ! -z "${UNUSED_PROTOCOLS}" ]; then
74  echo -e "Unused protocol(s) found in documentation:$UNUSED_PROTOCOLS"
75  exit 1
76fi
77
78touch $OUTPUT
79"""
80
81    out_file = ctx.actions.declare_file("%s.script" % ctx.attr.name)
82    in_files = [s for d in ctx.attr.deps for s in d[CrateInfo].srcs.to_list()]
83    readme = ctx.attr.readme
84    args = ctx.actions.args()
85    args.add_joined(
86        "--in",
87        in_files,
88        join_with = ",",
89    )
90    args.add(
91        "--out",
92        out_file,
93    )
94    args.add(
95        "--readme",
96        readme[DefaultInfo].files.to_list()[0],
97    )
98    ctx.actions.run_shell(
99        inputs = in_files + readme[DefaultInfo].files.to_list(),
100        outputs = [out_file],
101        arguments = [args],
102        command = shell_script,
103    )
104    return [DefaultInfo(executable = out_file)]
105
106readme_test = rule(
107    implementation = _readme_test_rule_impl,
108    attrs = {
109        "deps": attr.label_list(
110            providers = [CrateInfo],
111        ),
112        "readme": attr.label(
113            allow_single_file = [".md"],
114        ),
115    },
116    test = True,
117)
118