kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/indexer/testdata/go_indexer_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 # 16 17 # Bazel rules to extract Go compilations from library targets for testing the 18 # Go cross-reference indexer. 19 load("@bazel_skylib//lib:shell.bzl", "shell") 20 load( 21 "@io_bazel_rules_go//go:def.bzl", 22 "GoSource", 23 "go_library", 24 ) 25 load( 26 "//tools/build_rules/verifier_test:verifier_test.bzl", 27 "KytheEntryProducerInfo", 28 "kythe_integration_test", 29 "verifier_test", 30 ) 31 32 # Emit a shell script that sets up the environment needed by the extractor to 33 # capture dependencies and runs the extractor. 34 def _emit_extractor_script(ctx, mode, script, output, srcs, deps, ipath, data, extra_extractor_args): 35 tmpdir = output.dirname + "/tmp" 36 srcroot = tmpdir + "/src" 37 srcdir = srcroot + "/" + ipath 38 extras = [] 39 cmds = ["#!/bin/sh -e", "mkdir -p " + srcdir] 40 41 # Link the source files and dependencies into a common temporary directory. 42 # Source files need to be made relative to the temp directory. 43 ups = srcdir.count("/") + 1 44 cmds += [ 45 'ln -s "%s%s" "%s"' % ("../" * ups, src.path, srcdir) 46 for src in srcs 47 ] 48 for dep in deps: 49 gosrc = dep[GoSource] 50 path = gosrc.library.importpath 51 fullpath = "/".join([srcroot, path]) 52 tups = fullpath.count("/") + 1 53 cmds += ["mkdir -p " + fullpath] 54 for src in gosrc.srcs: 55 cmds += ["ln -s '%s%s' '%s'" % ("../" * tups, src.path, fullpath + "/" + src.basename)] 56 57 # Gather any extra data dependencies. 58 for target in data: 59 for f in target.files.to_list(): 60 cmds.append('ln -s "%s%s" "%s"' % ("../" * ups, f.path, srcdir)) 61 extras.append(srcdir + "/" + f.path.rsplit("/", 1)[-1]) 62 63 # Invoke the extractor on the temp directory. 64 goroot = "/".join(ctx.files._sdk_files[0].path.split("/")[:-2]) 65 cmds.append("export GOCACHE=\"$PWD/" + tmpdir + "/cache\"") 66 cmds.append("export CGO_ENABLED=0") 67 68 args = [ctx.files._extractor[-1].path] + extra_extractor_args + [ 69 "-output", 70 output.path, 71 "-goos", 72 mode.goos, 73 "-goarch", 74 mode.goarch, 75 "-goroot", 76 goroot, 77 "-gocompiler", 78 "gc", 79 "-gopath", 80 tmpdir, 81 "-extra_files", 82 "'%s'" % ",".join(extras), 83 ipath, 84 ] 85 cmds.append(" ".join(args)) 86 87 f = ctx.actions.declare_file(script) 88 ctx.actions.write(output = f, content = "\n".join(cmds), is_executable = True) 89 return f 90 91 def _go_extract(ctx): 92 gosrc = ctx.attr.library[GoSource] 93 mode = gosrc.mode 94 srcs = gosrc.srcs 95 96 # TODO: handle transitive dependencies 97 deps = gosrc.deps 98 depsrcs = [] 99 for dep in deps: 100 depsrcs += dep[GoSource].srcs 101 102 ipath = gosrc.library.importpath 103 data = ctx.attr.data 104 output = ctx.outputs.kzip 105 script = _emit_extractor_script( 106 ctx, 107 mode, 108 ctx.label.name + "-extract.sh", 109 output, 110 srcs, 111 deps, 112 ipath, 113 data, 114 ctx.attr.extra_extractor_args, 115 ) 116 117 extras = [] 118 for target in data: 119 extras += target.files.to_list() 120 121 tools = ctx.files._extractor + ctx.files._sdk_files 122 ctx.actions.run( 123 mnemonic = "GoExtract", 124 executable = script, 125 outputs = [output], 126 inputs = srcs + extras + depsrcs, 127 tools = tools, 128 ) 129 return struct(kzip = output) 130 131 # Generate a kzip with the compilations captured from a single Go library or 132 # binary rule. 133 go_extract = rule( 134 _go_extract, 135 attrs = { 136 # Additional data files to include in each compilation. 137 "data": attr.label_list( 138 allow_empty = True, 139 allow_files = True, 140 ), 141 "library": attr.label( 142 providers = [GoSource], 143 mandatory = True, 144 ), 145 "_extractor": attr.label( 146 default = Label("//kythe/go/extractors/cmd/gotool"), 147 executable = True, 148 cfg = "exec", 149 ), 150 "_sdk_files": attr.label( 151 allow_files = True, 152 default = "@go_sdk//:files", 153 ), 154 "extra_extractor_args": attr.string_list(), 155 }, 156 outputs = {"kzip": "%{name}.kzip"}, 157 toolchains = ["@io_bazel_rules_go//go:toolchain"], 158 ) 159 160 def _go_entries(ctx): 161 kzip = ctx.attr.kzip.kzip 162 iargs = [] 163 output = ctx.outputs.entries 164 165 # If the test wants marked source, enable support for it in the indexer. 166 if ctx.attr.has_marked_source: 167 iargs.append("-code") 168 169 if ctx.attr.emit_anchor_scopes: 170 iargs.append("-anchor_scopes") 171 172 if ctx.attr.use_compilation_corpus_for_all: 173 iargs.append("-use_compilation_corpus_for_all") 174 175 if ctx.attr.use_file_as_top_level_scope: 176 iargs.append("-use_file_as_top_level_scope") 177 178 if ctx.attr.override_stdlib_corpus: 179 iargs.append("-override_stdlib_corpus=%s" % ctx.attr.override_stdlib_corpus) 180 181 # If the test wants linkage metadata, enable support for it in the indexer. 182 if ctx.attr.metadata_suffix: 183 iargs += ["-meta", ctx.attr.metadata_suffix] 184 185 test_runners = [] 186 eargs = [ctx.expand_location(arg.replace("$(location", "$(rootpath"), ctx.attr.data) for arg in ctx.attr.extra_indexer_args] 187 test_runners.append(_make_test_runner(ctx, {}, arguments = iargs + eargs + [kzip.short_path])) 188 189 iargs += [ctx.expand_location(arg, ctx.attr.data) for arg in ctx.attr.extra_indexer_args] 190 iargs += [kzip.path, "| gzip >" + output.path] 191 iargs.insert(0, ctx.executable._exec_indexer.path) 192 193 cmds = ["set -e", "set -o pipefail", " ".join(iargs), ""] 194 ctx.actions.run_shell( 195 mnemonic = "GoIndexer", 196 command = "\n".join(cmds), 197 outputs = [output], 198 inputs = [kzip] + ctx.files.data, 199 tools = [ctx.executable._exec_indexer], 200 ) 201 202 return [ 203 KytheEntryProducerInfo( 204 executables = test_runners, 205 runfiles = ctx.runfiles( 206 files = (test_runners + [kzip] + ctx.files.data), 207 ).merge(ctx.attr._indexer[DefaultInfo].default_runfiles), 208 ), 209 ] 210 211 # Run the Kythe indexer on the output that results from a go_extract rule. 212 go_entries = rule( 213 _go_entries, 214 attrs = { 215 # Whether to enable explosion of MarkedSource facts. 216 "has_marked_source": attr.bool(default = False), 217 218 # Whether to enable anchor scope edges. 219 "emit_anchor_scopes": attr.bool(default = False), 220 221 # The go_extract output to pass to the indexer. 222 "kzip": attr.label( 223 providers = ["kzip"], 224 mandatory = True, 225 ), 226 227 # The suffix used to recognize linkage metadata files, if non-empty. 228 "metadata_suffix": attr.string(default = ""), 229 "use_compilation_corpus_for_all": attr.bool(default = False), 230 "use_file_as_top_level_scope": attr.bool(default = False), 231 "override_stdlib_corpus": attr.string(default = ""), 232 "extra_indexer_args": attr.string_list(), 233 234 # Extra files required by the indexer 235 "data": attr.label_list( 236 allow_empty = True, 237 allow_files = True, 238 ), 239 240 # The location of the Go indexer binary. 241 "_indexer": attr.label( 242 default = Label("//kythe/go/indexer/cmd/go_indexer"), 243 executable = True, 244 cfg = "target", 245 ), 246 "_exec_indexer": attr.label( 247 default = Label("//kythe/go/indexer/cmd/go_indexer"), 248 executable = True, 249 cfg = "exec", 250 ), 251 "_test_template": attr.label( 252 default = Label("//tools/build_rules/verifier_test:indexer.sh.in"), 253 allow_single_file = True, 254 ), 255 }, 256 outputs = {"entries": "%{name}.entries.gz"}, 257 ) 258 259 def _make_test_runner(ctx, env, arguments): 260 output = ctx.actions.declare_file(ctx.label.name + "_test_runner") 261 ctx.actions.expand_template( 262 output = output, 263 is_executable = True, 264 template = ctx.file._test_template, 265 substitutions = { 266 "@INDEXER@": shell.quote(ctx.executable._indexer.short_path), 267 "@ENV@": "\n".join([ 268 shell.quote("{key}={value}".format(key = key, value = value)) 269 for key, value in env.items() 270 ]), 271 "@ARGS@": "\n".join([ 272 shell.quote(a) 273 for a in arguments 274 ]), 275 }, 276 ) 277 return output 278 279 def go_verifier_test( 280 name, 281 entries, 282 srcs = [], 283 deps = [], 284 size = "small", 285 tags = [], 286 log_entries = False, 287 has_marked_source = False, 288 resolve_code_facts = False, 289 allow_duplicates = False, 290 use_fast_solver = False): 291 opts = ["--use_file_nodes", "--show_goals", "--check_for_singletons", "--goal_regex='\\s*//\\s*-(.*)'"] 292 if log_entries: 293 opts.append("--show_protos") 294 if allow_duplicates or len(deps) > 0: 295 opts.append("--ignore_dups") 296 if len(srcs) > 0: 297 opts.append("--nofile_vnames") 298 299 # If the test wants marked source, enable support for it in the verifier. 300 if has_marked_source: 301 opts.append("--convert_marked_source") 302 if use_fast_solver: 303 opts.append("--use_fast_solver") 304 return verifier_test( 305 name = name, 306 size = size, 307 opts = opts, 308 tags = tags, 309 resolve_code_facts = resolve_code_facts, 310 srcs = srcs, 311 deps = [entries] + deps, 312 ) 313 314 # Shared extract/index logic for the go_indexer_test/go_integration_test rules. 315 def _go_indexer( 316 name, 317 srcs, 318 deps = [], 319 importpath = None, 320 data = None, 321 has_marked_source = False, 322 emit_anchor_scopes = False, 323 allow_duplicates = False, 324 use_compilation_corpus_for_all = False, 325 use_file_as_top_level_scope = False, 326 override_stdlib_corpus = "", 327 metadata_suffix = "", 328 extra_indexer_args = [], 329 extra_extractor_args = []): 330 if importpath == None: 331 importpath = native.package_name() + "/" + name 332 lib = name + "_lib" 333 go_library( 334 name = lib, 335 srcs = srcs, 336 importpath = importpath, 337 deps = [dep + "_lib" for dep in deps], 338 ) 339 kzip = name + "_units" 340 go_extract( 341 name = kzip, 342 data = data, 343 library = lib, 344 extra_extractor_args = extra_extractor_args, 345 ) 346 entries = name + "_entries" 347 go_entries( 348 name = entries, 349 data = data, 350 has_marked_source = has_marked_source, 351 emit_anchor_scopes = emit_anchor_scopes, 352 use_compilation_corpus_for_all = use_compilation_corpus_for_all, 353 use_file_as_top_level_scope = use_file_as_top_level_scope, 354 override_stdlib_corpus = override_stdlib_corpus, 355 extra_indexer_args = extra_indexer_args, 356 kzip = ":" + kzip, 357 metadata_suffix = metadata_suffix, 358 tags = ["manual"], 359 ) 360 return entries 361 362 # A convenience macro to generate a test library, pass it to the Go indexer, 363 # and feed the output of indexing to the Kythe schema verifier. 364 def go_indexer_test( 365 name, 366 srcs, 367 deps = [], 368 import_path = None, 369 size = None, 370 tags = None, 371 log_entries = False, 372 data = None, 373 has_marked_source = False, 374 resolve_code_facts = False, 375 emit_anchor_scopes = False, 376 allow_duplicates = False, 377 use_compilation_corpus_for_all = False, 378 use_file_as_top_level_scope = False, 379 override_stdlib_corpus = "", 380 metadata_suffix = "", 381 extra_goals = [], 382 extra_indexer_args = [], 383 extra_extractor_args = [], 384 use_fast_solver = False): 385 entries = _go_indexer( 386 name = name, 387 srcs = srcs, 388 data = data, 389 has_marked_source = has_marked_source, 390 emit_anchor_scopes = emit_anchor_scopes, 391 use_compilation_corpus_for_all = use_compilation_corpus_for_all, 392 use_file_as_top_level_scope = use_file_as_top_level_scope, 393 override_stdlib_corpus = override_stdlib_corpus, 394 importpath = import_path, 395 metadata_suffix = metadata_suffix, 396 deps = deps, 397 extra_indexer_args = extra_indexer_args, 398 extra_extractor_args = extra_extractor_args, 399 ) 400 go_verifier_test( 401 name = name, 402 srcs = extra_goals, 403 size = size, 404 allow_duplicates = allow_duplicates, 405 entries = ":" + entries, 406 deps = [dep + "_entries" for dep in deps], 407 has_marked_source = has_marked_source, 408 resolve_code_facts = resolve_code_facts, 409 log_entries = log_entries, 410 tags = tags, 411 use_fast_solver = use_fast_solver, 412 ) 413 414 # A convenience macro to generate a test library, pass it to the Go indexer, 415 # and feed the output of indexing to the Kythe integration test pipeline. 416 def go_integration_test( 417 name, 418 srcs, 419 deps = [], 420 data = None, 421 file_tickets = [], 422 import_path = None, 423 size = "small", 424 has_marked_source = False, 425 metadata_suffix = ""): 426 entries = _go_indexer( 427 name = name, 428 srcs = srcs, 429 data = data, 430 has_marked_source = has_marked_source, 431 import_path = import_path, 432 metadata_suffix = metadata_suffix, 433 deps = deps, 434 ) 435 kythe_integration_test( 436 name = name, 437 size = size, 438 srcs = [":" + entries], 439 file_tickets = file_tickets, 440 )