kythe.io@v0.0.68-0.20240422202219-7225dbc01741/tools/build_rules/verifier_test/verifier_test_impl.bzl (about) 1 # Copyright 2023 The Kythe Authors. All rights reserved. 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 """Private implementation details for verifier_test rules. 15 16 Public API available in verifier_test.bzl. 17 """ 18 19 load("@bazel_skylib//lib:shell.bzl", "shell") 20 21 visibility("private") 22 23 KytheVerifierSources = provider( 24 doc = "Input files which the verifier should inspect for assertions.", 25 fields = { 26 "files": "Depset of files which should be considered.", 27 }, 28 ) 29 30 KytheEntries = provider( 31 doc = "Kythe indexer entry facts.", 32 fields = { 33 "compressed": "Depset of combined, compressed index entries.", 34 "files": "Depset of files which combine to make an index.", 35 "log": "Log file from the indexer execution. The file will be stored in test outputs.", 36 "name": "Name that uniquely identifies these entries within a test. For example it can be language name if test is multi-language.", 37 }, 38 ) 39 40 KytheEntryProducerInfo = provider( 41 doc = "Provider indicating an executable to be called which will produce Kythe entries on stdout.", 42 fields = { 43 "executables": "A list of File objects to run which should produce Kythe entries on stdout.", 44 "runfiles": "Required runfiles.", 45 "log": "Log file from the indexer execution. The file will be stored in test outputs.", 46 "name": "Name that uniquely identifies these entries within a test. For example it can be language name if test is multi-language.", 47 }, 48 ) 49 50 def _verifier_test_impl(ctx): 51 entries = [] 52 entries_gz = [] 53 sources = [] 54 for src in ctx.attr.srcs: 55 if KytheVerifierSources in src: 56 sources.append(src[KytheVerifierSources].files) 57 else: 58 sources.append(src.files) 59 60 # Check that all deps have unique names. 61 unique_names = {} 62 for dep in ctx.attr.deps: 63 info = None 64 if KytheEntryProducerInfo in dep: 65 info = dep[KytheEntryProducerInfo] 66 elif KytheEntries in dep: 67 info = dep[KytheEntries] 68 if info and hasattr(info, "name"): 69 if info.name in unique_names: 70 fail("Multiple deps producers with the same name: %s. Deps: %s, %s" % (info.name, unique_names[info.name], dep.label.name)) 71 unique_names[info.name] = dep.label.name 72 73 # List of '$indexer:$name' strings to execute during the test. 'name' is used in log file. 74 indexers = [] 75 runfiles = [] 76 77 # List of '$logfile:$name_indexer.log' strings. 78 logs = [] 79 80 # List of '$entryfile:$name_entries.json' strings. 81 entries_to_log = [] 82 for dep in ctx.attr.deps: 83 if KytheEntryProducerInfo in dep: 84 runfiles.append(dep[KytheEntryProducerInfo].runfiles) 85 for executable in dep[KytheEntryProducerInfo].executables: 86 name = dep[KytheEntryProducerInfo].name if hasattr(dep[KytheEntryProducerInfo], "name") else dep.label.name.replace("/", "_") 87 indexers.append("%s:%s" % ( 88 shell.quote(executable.short_path), 89 name, 90 )) 91 if KytheEntries in dep: 92 if dep[KytheEntries].files: 93 entries.append(dep[KytheEntries].files) 94 if hasattr(dep[KytheEntries], "name"): 95 for file in dep[KytheEntries].files.to_list(): 96 entries_to_log.append("%s:%s_entries.json" % ( 97 shell.quote(file.short_path), 98 dep[KytheEntries].name, 99 )) 100 else: 101 entries_gz.append(dep[KytheEntries].compressed) 102 103 if hasattr(dep[KytheEntries], "log"): 104 runfiles.append(ctx.runfiles(files = [dep[KytheEntries].log])) 105 logs.append("%s:%s_indexer.log" % ( 106 shell.quote(dep[KytheEntries].log.short_path), 107 dep[KytheEntries].name, 108 )) 109 110 # Flatten input lists 111 entries = depset(transitive = entries).to_list() 112 entries_gz = depset(transitive = entries_gz).to_list() 113 sources = depset(transitive = sources).to_list() 114 115 if not (entries or entries_gz or indexers): 116 fail("Missing required entry stream input (check your deps!)") 117 args = ctx.attr.opts + [shell.quote(src.short_path) for src in sources] 118 119 # If no dependency specifies KytheVerifierSources and 120 # we aren't provided explicit sources, assume `--use_file_nodes`. 121 if not sources and "--use_file_nodes" not in args: 122 args.append("--use_file_nodes") 123 124 ctx.actions.expand_template( 125 template = ctx.file._template, 126 output = ctx.outputs.executable, 127 is_executable = True, 128 substitutions = { 129 "@ARGS@": " ".join(args), 130 "@INDEXERS@": " ".join(indexers), 131 "@INDEXER_LOGS@": " ".join(logs), 132 "@ENTRIES@": " ".join([shell.quote(e.short_path) for e in entries]), 133 "@ENTRIES_TO_LOG@": " ".join(entries_to_log), 134 "@ENTRIES_GZ@": " ".join([shell.quote(e.short_path) for e in entries_gz]), 135 # If failure is expected, invert the sense of the verifier return. 136 "@INVERT@": "!" if not ctx.attr.expect_success else "", 137 "@VERIFIER@": shell.quote(ctx.executable._verifier.short_path), 138 "@REWRITE@": "1" if not ctx.attr.resolve_code_facts else "", 139 "@MARKEDSOURCE@": shell.quote(ctx.executable._markedsource.short_path), 140 "@WORKSPACE_NAME@": ctx.workspace_name, 141 "@ENTRYSTREAM@": shell.quote(ctx.executable._entrystream.short_path), 142 }, 143 ) 144 tools = [ 145 ctx.outputs.executable, 146 ctx.executable._verifier, 147 ctx.executable._markedsource, 148 ctx.executable._entrystream, 149 ] 150 return [ 151 DefaultInfo( 152 runfiles = ctx.runfiles(files = sources + entries + entries_gz + tools).merge_all(runfiles), 153 executable = ctx.outputs.executable, 154 ), 155 ] 156 157 verifier_test = rule( 158 attrs = { 159 "srcs": attr.label_list( 160 doc = "Targets or files containing verifier goals.", 161 allow_files = True, 162 providers = [KytheVerifierSources], 163 ), 164 "resolve_code_facts": attr.bool(default = False), 165 # Arguably, "expect_failure" is more natural, but that 166 # attribute is used by Skylark. 167 "expect_success": attr.bool(default = True), 168 "opts": attr.string_list(), 169 "deps": attr.label_list( 170 doc = "Targets which produce graph entries to verify.", 171 providers = [[KytheEntries], [KytheEntryProducerInfo]], 172 ), 173 "_template": attr.label( 174 default = Label("//tools/build_rules/verifier_test:verifier_test.sh.in"), 175 allow_single_file = True, 176 ), 177 "_verifier": attr.label( 178 default = Label("//kythe/cxx/verifier"), 179 executable = True, 180 cfg = "target", 181 ), 182 "_markedsource": attr.label( 183 default = Label("//kythe/go/util/tools/markedsource"), 184 executable = True, 185 cfg = "target", 186 ), 187 "_entrystream": attr.label( 188 default = Label("//kythe/go/platform/tools/entrystream"), 189 executable = True, 190 cfg = "target", 191 ), 192 }, 193 test = True, 194 implementation = _verifier_test_impl, 195 )