kythe.io@v0.0.68-0.20240422202219-7225dbc01741/tools/build_rules/verifier_test/verifier_test.bzl (about) 1 # 2 # Copyright 2016 The Kythe Authors. All rights reserved. 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 """Rules and macros related to Kythe verifier-based tests.""" 16 17 load("@bazel_skylib//lib:shell.bzl", "shell") 18 load( 19 ":verifier_test_impl.bzl", 20 _KytheEntries = "KytheEntries", 21 _KytheEntryProducerInfo = "KytheEntryProducerInfo", 22 _KytheVerifierSources = "KytheVerifierSources", 23 _verifier_test = "verifier_test", 24 ) 25 26 KytheEntries = _KytheEntries 27 KytheEntryProducerInfo = _KytheEntryProducerInfo 28 KytheVerifierSources = _KytheVerifierSources 29 30 def _atomize_entries_impl(ctx): 31 zcat = ctx.executable._zcat 32 entrystream = ctx.executable._entrystream 33 postprocessor = ctx.executable._postprocessor 34 atomizer = ctx.executable._atomizer 35 36 inputs = depset(ctx.files.srcs, transitive = [ 37 dep.kythe_entries 38 for dep in ctx.attr.deps 39 ]) 40 41 sort_args = ctx.actions.args() 42 sort_args.add_all([zcat, entrystream, sorted_entries]) 43 sort_args.add_all(inputs) 44 sorted_entries = ctx.actions.declare_file("_sorted_entries", sibling = ctx.outputs.entries) 45 ctx.actions.run_shell( 46 outputs = [sorted_entries], 47 inputs = [zcat, entrystream] + inputs.to_list(), 48 mnemonic = "SortEntries", 49 command = '("$1" "${@:4}" | "$2" --sort) > "$3" || rm -f "$3"', 50 arguments = [sort_args], 51 ) 52 53 process_args = ctx.actions.args() 54 process_args.add_all(["--entries", sorted_entries, "--out", leveldb]) 55 leveldb = ctx.actions.declare_file("_serving_tables", sibling = ctx.outputs.entries) 56 ctx.actions.run( 57 outputs = [leveldb], 58 inputs = [sorted_entries, postprocessor], 59 executable = postprocessor, 60 mnemonic = "PostProcessEntries", 61 arguments = [process_args], 62 ) 63 64 atomize_args = ctx.actions.args() 65 atomize_args.add_all([atomizer, "--api", leveldb]) 66 atomize_args.add_all(ctx.attr.file_tickets) 67 atomize_args.add(ctx.outputs.entries) 68 ctx.actions.run_shell( 69 outputs = [ctx.outputs.entries], 70 inputs = [atomizer, leveldb], 71 mnemonic = "AtomizeEntries", 72 command = '("${@:1:${#@}-1}" || rm -f "${@:${#@}}") | gzip -c > "${@:${#@}}"', 73 arguments = [atomize_args], 74 execution_requirements = { 75 # TODO(shahms): Remove this when we can use a non-LevelDB store. 76 "local": "true", # LevelDB is bad and should feel bad. 77 }, 78 ) 79 return struct() 80 81 atomize_entries = rule( 82 attrs = { 83 "srcs": attr.label_list( 84 mandatory = True, 85 allow_files = [ 86 ".entries", 87 ".entries.gz", 88 ], 89 ), 90 "file_tickets": attr.string_list( 91 mandatory = True, 92 allow_empty = False, 93 ), 94 "deps": attr.label_list( 95 providers = ["kythe_entries"], 96 ), 97 "_atomizer": attr.label( 98 default = Label("//kythe/go/test/tools/xrefs_atomizer"), 99 executable = True, 100 cfg = "exec", 101 ), 102 "_entrystream": attr.label( 103 default = Label("//kythe/go/platform/tools/entrystream"), 104 executable = True, 105 cfg = "exec", 106 ), 107 "_postprocessor": attr.label( 108 default = Label("//kythe/go/serving/tools/write_tables"), 109 executable = True, 110 cfg = "exec", 111 ), 112 "_zcat": attr.label( 113 default = Label("//tools:zcatext"), 114 executable = True, 115 cfg = "exec", 116 ), 117 }, 118 outputs = { 119 "entries": "%{name}.entries.gz", 120 }, 121 implementation = _atomize_entries_impl, 122 ) 123 124 def extract( 125 ctx, 126 kzip, 127 extractor, 128 srcs, 129 opts, 130 deps = [], 131 env = {}, 132 vnames_config = None, 133 mnemonic = "ExtractCompilation"): 134 """Create an extract action using the provided tool and inputs. 135 136 Runs the extractor tool under an environment to produce the given kzip 137 output file. The extractor is passed each string from opts after expanding 138 any build artifact locations and then each File's path from the srcs 139 collection. 140 141 Args: 142 ctx: The Bazel rule context to use for actions. 143 kzip: Declared .kzip output File 144 extractor: Executable extractor tool to invoke 145 srcs: Files passed to extractor tool; the compilation's source file inputs 146 opts: List of options (or Args object) passed to the extractor tool before source files 147 deps: Dependencies for the extractor's action (not passed to extractor on command-line) 148 env: Dictionary of environment variables to provide. 149 vnames_config: Optional path to a VName configuration file 150 mnemonic: Mnemonic of the extractor's action 151 152 Returns: 153 The output file generated. 154 """ 155 final_env = { 156 "KYTHE_OUTPUT_FILE": kzip.path, 157 "KYTHE_ROOT_DIRECTORY": ".", 158 } 159 final_env.update(env) 160 161 if type(srcs) != "depset": 162 srcs = depset(direct = srcs) 163 if type(deps) != "depset": 164 deps = depset(direct = deps) 165 direct_inputs = [] 166 if vnames_config: 167 final_env["KYTHE_VNAMES"] = vnames_config.path 168 direct_inputs.append(vnames_config) 169 inputs = depset(direct = direct_inputs, transitive = [srcs, deps]) 170 171 args = opts 172 if type(args) != "Args": 173 args = ctx.actions.args() 174 args.add_all([ctx.expand_location(o) for o in opts]) 175 args.add_all(srcs) 176 177 ctx.actions.run( 178 inputs = inputs, 179 tools = [extractor], 180 outputs = [kzip], 181 mnemonic = mnemonic, 182 executable = extractor, 183 arguments = [args], 184 env = final_env, 185 toolchain = None, 186 ) 187 return kzip 188 189 def _index_compilation_impl(ctx): 190 sources = [] 191 intermediates = [] 192 test_runners = [] 193 kzips = [] 194 for dep in ctx.attr.deps: 195 if KytheVerifierSources in dep: 196 sources.append(dep[KytheVerifierSources].files) 197 for input in dep.files.to_list(): 198 entries = ctx.actions.declare_file( 199 ctx.label.name + input.basename + ".entries", 200 sibling = ctx.outputs.entries, 201 ) 202 intermediates.append(entries) 203 204 if ctx.attr.target_indexer: 205 iargs = [] 206 iargs += ctx.attr.opts 207 iargs.append(input.short_path) 208 test_runners.append(_make_test_runner(ctx, {}, arguments = iargs)) 209 kzips.append(input) 210 211 args = ctx.actions.args() 212 args.add(ctx.executable.indexer) 213 args.add_all([ctx.expand_location(o) for o in ctx.attr.opts]) 214 args.add_all([input, entries]) 215 ctx.actions.run_shell( 216 outputs = [entries], 217 inputs = [input], 218 tools = [ctx.executable.indexer] + ctx.files.tools, 219 arguments = [args], 220 command = '("${@:1:${#@}-1}" || rm -f "${@:${#@}}") > "${@:${#@}}"', 221 mnemonic = "IndexCompilation", 222 toolchain = None, 223 ) 224 225 args = ctx.actions.args() 226 args.add("cat") 227 args.add_all(intermediates) 228 args.add(ctx.outputs.entries) 229 ctx.actions.run_shell( 230 outputs = [ctx.outputs.entries], 231 inputs = intermediates, 232 command = '("${@:1:${#@}-1}" || rm -f "${@:${#@}}") | gzip -c > "${@:${#@}}"', 233 mnemonic = "CompressEntries", 234 arguments = [args], 235 ) 236 providers = [ 237 KytheVerifierSources(files = depset(transitive = sources)), 238 ] 239 if test_runners: 240 providers.append( 241 KytheEntryProducerInfo( 242 executables = test_runners, 243 runfiles = ctx.runfiles( 244 files = (test_runners + kzips + ctx.files.target_tools), 245 ).merge(ctx.attr.target_indexer[DefaultInfo].default_runfiles), 246 ), 247 ) 248 else: 249 providers.append( 250 KytheEntries(compressed = depset([ctx.outputs.entries]), files = depset(intermediates)), 251 ) 252 return providers 253 254 def _make_test_runner(ctx, env, arguments): 255 output = ctx.actions.declare_file(ctx.label.name + "_test_runner") 256 ctx.actions.expand_template( 257 output = output, 258 is_executable = True, 259 template = ctx.file._test_template, 260 substitutions = { 261 "@INDEXER@": shell.quote(ctx.executable.target_indexer.short_path), 262 "@ENV@": "\n".join([ 263 shell.quote("{key}={value}".format(key = key, value = value)) 264 for key, value in env.items() 265 ]), 266 "@ARGS@": "\n".join([ 267 shell.quote(ctx.expand_location(a.replace("$(location", "$(rootpath"))) 268 for a in arguments 269 ]), 270 }, 271 ) 272 return output 273 274 index_compilation = rule( 275 attrs = { 276 "indexer": attr.label( 277 mandatory = True, 278 executable = True, 279 cfg = "exec", 280 ), 281 "target_indexer": attr.label( 282 executable = True, 283 cfg = "target", 284 ), 285 "opts": attr.string_list(), 286 "tools": attr.label_list( 287 cfg = "exec", 288 allow_files = True, 289 ), 290 "target_tools": attr.label_list( 291 allow_files = True, 292 ), 293 "deps": attr.label_list( 294 mandatory = True, 295 allow_empty = False, 296 allow_files = [".kzip"], 297 ), 298 "_test_template": attr.label( 299 default = Label("//tools/build_rules/verifier_test:indexer.sh.in"), 300 allow_single_file = True, 301 ), 302 }, 303 outputs = { 304 "entries": "%{name}.entries.gz", 305 }, 306 implementation = _index_compilation_impl, 307 ) 308 309 def _invoke(rulefn, name, **kwargs): 310 """Invoke rulefn with name and kwargs, returning the label of the rule.""" 311 rulefn(name = name, **kwargs) 312 return "//{}:{}".format(native.package_name(), name) 313 314 def kythe_integration_test(name, srcs, file_tickets, tags = [], size = "small"): 315 entries = _invoke( 316 atomize_entries, 317 name = name + "_atomized_entries", 318 testonly = True, 319 srcs = [], 320 file_tickets = file_tickets, 321 tags = tags, 322 deps = srcs, 323 ) 324 return _invoke( 325 verifier_test, 326 name = name, 327 size = size, 328 opts = ["--ignore_dups"], 329 tags = tags, 330 deps = [entries], 331 ) 332 333 _VERIFIER_TEST_TAGS = [] 334 335 def verifier_test(**kwargs): 336 tags = kwargs.pop("tags", []) or [] 337 _verifier_test( 338 tags = tags + _VERIFIER_TEST_TAGS, 339 **kwargs 340 )