github.com/lab47/exprcore@v0.0.0-20210525052339-fb7d6bd9331e/syntax/testdata/scan.star (about) 1 # Copyright 2014 The Bazel 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 15 # (From https://github.com/bazelbuild/rules_go/blob/master/go/def.bzl@a6f9d0c) 16 17 load("//go/private:repositories.bzl", "go_repositories") 18 load("//go/private:go_repository.bzl", "go_repository", "new_go_repository") 19 load("//go/private:go_prefix.bzl", "go_prefix") 20 load("//go/private:json.bzl", "json_marshal") 21 22 """These are bare-bones Go rules. 23 24 In order of priority: 25 26 - BUILD file must be written by hand. 27 28 - No support for SWIG 29 30 - No test sharding or test XML. 31 32 """ 33 34 _DEFAULT_LIB = "go_default_library" 35 36 _VENDOR_PREFIX = "/vendor/" 37 38 go_filetype = FileType([ 39 ".go", 40 ".s", 41 ".S", 42 ".h", # may be included by .s 43 ]) 44 45 # be consistent to cc_library. 46 hdr_exts = [ 47 ".h", 48 ".hh", 49 ".hpp", 50 ".hxx", 51 ".inc", 52 ] 53 54 cc_hdr_filetype = FileType(hdr_exts) 55 56 # Extensions of files we can build with the Go compiler or with cc_library. 57 # This is a subset of the extensions recognized by go/build. 58 cgo_filetype = FileType([ 59 ".go", 60 ".c", 61 ".cc", 62 ".cxx", 63 ".cpp", 64 ".s", 65 ".S", 66 ".h", 67 ".hh", 68 ".hpp", 69 ".hxx", 70 ]) 71 72 ################ 73 74 def go_environment_vars(ctx): 75 """Return a map of environment variables for use with actions, based on 76 the arguments. Uses the ctx.fragments.cpp.cpu attribute, if present, 77 and picks a default of target_os="linux" and target_arch="amd64" 78 otherwise. 79 80 Args: 81 The starlark Context. 82 83 Returns: 84 A dict of environment variables for running Go tool commands that build for 85 the target OS and architecture. 86 """ 87 default_toolchain = {"GOOS": "linux", "GOARCH": "amd64"} 88 bazel_to_go_toolchain = { 89 "k8": {"GOOS": "linux", "GOARCH": "amd64"}, 90 "piii": {"GOOS": "linux", "GOARCH": "386"}, 91 "darwin": {"GOOS": "darwin", "GOARCH": "amd64"}, 92 "darwin_x86_64": {"GOOS": "darwin", "GOARCH": "amd64"}, 93 "freebsd": {"GOOS": "freebsd", "GOARCH": "amd64"}, 94 "armeabi-v7a": {"GOOS": "linux", "GOARCH": "arm"}, 95 "arm": {"GOOS": "linux", "GOARCH": "arm"} 96 } 97 env = {} 98 if hasattr(ctx.file, "go_tool"): 99 env["GOROOT"] = ctx.file.go_tool.dirname + "/.." 100 env.update(bazel_to_go_toolchain.get(ctx.fragments.cpp.cpu, default_toolchain)) 101 return env 102 103 def _is_darwin_cpu(ctx): 104 cpu = ctx.fragments.cpp.cpu 105 return cpu == "darwin" or cpu == "darwin_x86_64" 106 107 def _emit_generate_params_action(cmds, ctx, fn): 108 cmds_all = [ 109 # Use bash explicitly. /bin/sh is default, and it may be linked to a 110 # different shell, e.g., /bin/dash on Ubuntu. 111 "#!/bin/bash", 112 "set -e", 113 ] 114 cmds_all += cmds 115 cmds_all_str = "\n".join(cmds_all) + "\n" 116 f = ctx.new_file(ctx.configuration.bin_dir, fn) 117 ctx.file_action( 118 output = f, 119 content = cmds_all_str, 120 executable = True) 121 return f 122 123 def _emit_go_asm_action(ctx, source, hdrs, out_obj): 124 """Construct the command line for compiling Go Assembly code. 125 Constructs a symlink tree to accomodate for workspace name. 126 Args: 127 ctx: The starlark Context. 128 source: a source code artifact 129 hdrs: list of .h files that may be included 130 out_obj: the artifact (configured target?) that should be produced 131 """ 132 params = { 133 "go_tool": ctx.file.go_tool.path, 134 "includes": [f.dirname for f in hdrs] + [ctx.file.go_include.path], 135 "source": source.path, 136 "out": out_obj.path, 137 } 138 139 inputs = hdrs + ctx.files.toolchain + [source] 140 ctx.action( 141 inputs = inputs, 142 outputs = [out_obj], 143 mnemonic = "GoAsmCompile", 144 executable = ctx.executable._asm, 145 arguments = [json_marshal(params)], 146 ) 147 148 def _go_importpath(ctx): 149 """Returns the expected importpath of the go_library being built. 150 151 Args: 152 ctx: The starlark Context 153 154 Returns: 155 Go importpath of the library 156 """ 157 path = ctx.attr.importpath 158 if path != "": 159 return path 160 path = ctx.attr.go_prefix.go_prefix 161 if path.endswith("/"): 162 path = path[:-1] 163 if ctx.label.package: 164 path += "/" + ctx.label.package 165 if ctx.label.name != _DEFAULT_LIB: 166 path += "/" + ctx.label.name 167 if path.rfind(_VENDOR_PREFIX) != -1: 168 path = path[len(_VENDOR_PREFIX) + path.rfind(_VENDOR_PREFIX):] 169 if path[0] == "/": 170 path = path[1:] 171 return path 172 173 def _emit_go_compile_action(ctx, sources, deps, libpaths, out_object, gc_goopts): 174 """Construct the command line for compiling Go code. 175 176 Args: 177 ctx: The starlark Context. 178 sources: an iterable of source code artifacts (or CTs? or labels?) 179 deps: an iterable of dependencies. Each dependency d should have an 180 artifact in d.transitive_go_libraries representing all imported libraries. 181 libpaths: the set of paths to search for imported libraries. 182 out_object: the object file that should be produced 183 gc_goopts: additional flags to pass to the compiler. 184 """ 185 if ctx.coverage_instrumented(): 186 sources = _emit_go_cover_action(ctx, sources) 187 188 # Compile filtered files. 189 args = [ 190 "-cgo", 191 ctx.file.go_tool.path, 192 "tool", "compile", 193 "-o", out_object.path, 194 "-trimpath", "-abs-.", 195 "-I", "-abs-.", 196 ] 197 inputs = depset(sources + ctx.files.toolchain) 198 for dep in deps: 199 inputs += dep.transitive_go_libraries 200 for path in libpaths: 201 args += ["-I", path] 202 args += gc_goopts + [("" if i.basename.startswith("_cgo") else "-filter-") + i.path for i in sources] 203 ctx.action( 204 inputs = list(inputs), 205 outputs = [out_object], 206 mnemonic = "GoCompile", 207 executable = ctx.executable._filter_exec, 208 arguments = args, 209 env = go_environment_vars(ctx), 210 ) 211 212 return sources 213 214 def _emit_go_pack_action(ctx, out_lib, objects): 215 """Construct the command line for packing objects together. 216 217 Args: 218 ctx: The starlark Context. 219 out_lib: the archive that should be produced 220 objects: an iterable of object files to be added to the output archive file. 221 """ 222 ctx.action( 223 inputs = objects + ctx.files.toolchain, 224 outputs = [out_lib], 225 mnemonic = "GoPack", 226 executable = ctx.file.go_tool, 227 arguments = ["tool", "pack", "c", out_lib.path] + [a.path for a in objects], 228 env = go_environment_vars(ctx), 229 ) 230 231 def _emit_go_cover_action(ctx, sources): 232 """Construct the command line for test coverage instrument. 233 234 Args: 235 ctx: The starlark Context. 236 sources: an iterable of Go source files. 237 238 Returns: 239 A list of Go source code files which might be coverage instrumented. 240 """ 241 outputs = [] 242 # TODO(linuxerwang): make the mode configurable. 243 count = 0 244 245 for src in sources: 246 if not src.path.endswith(".go") or src.path.endswith("_test.go"): 247 outputs += [src] 248 continue 249 250 cover_var = "GoCover_%d" % count 251 out = ctx.new_file(src, src.basename[:-3] + '_' + cover_var + '.cover.go') 252 outputs += [out] 253 ctx.action( 254 inputs = [src] + ctx.files.toolchain, 255 outputs = [out], 256 mnemonic = "GoCover", 257 executable = ctx.file.go_tool, 258 arguments = ["tool", "cover", "--mode=set", "-var=%s" % cover_var, "-o", out.path, src.path], 259 env = go_environment_vars(ctx), 260 ) 261 count += 1 262 263 return outputs 264 265 def go_library_impl(ctx): 266 """Implements the go_library() rule.""" 267 268 sources = depset(ctx.files.srcs) 269 go_srcs = depset([s for s in sources if s.basename.endswith('.go')]) 270 asm_srcs = [s for s in sources if s.basename.endswith('.s') or s.basename.endswith('.S')] 271 asm_hdrs = [s for s in sources if s.basename.endswith('.h')] 272 deps = ctx.attr.deps 273 dep_runfiles = [d.data_runfiles for d in deps] 274 275 cgo_object = None 276 if hasattr(ctx.attr, "cgo_object"): 277 cgo_object = ctx.attr.cgo_object 278 279 if ctx.attr.library: 280 go_srcs += ctx.attr.library.go_sources 281 asm_srcs += ctx.attr.library.asm_sources 282 asm_hdrs += ctx.attr.library.asm_headers 283 deps += ctx.attr.library.direct_deps 284 dep_runfiles += [ctx.attr.library.data_runfiles] 285 if ctx.attr.library.cgo_object: 286 if cgo_object: 287 fail("go_library %s cannot have cgo_object because the package " + 288 "already has cgo_object in %s" % (ctx.label.name, 289 ctx.attr.library.name)) 290 cgo_object = ctx.attr.library.cgo_object 291 if not go_srcs: 292 fail("may not be empty", "srcs") 293 294 transitive_cgo_deps = depset([], order="topological") 295 if cgo_object: 296 dep_runfiles += [cgo_object.data_runfiles] 297 transitive_cgo_deps += cgo_object.cgo_deps 298 299 extra_objects = [cgo_object.cgo_obj] if cgo_object else [] 300 for src in asm_srcs: 301 obj = ctx.new_file(src, "%s.dir/%s.o" % (ctx.label.name, src.basename[:-2])) 302 _emit_go_asm_action(ctx, src, asm_hdrs, obj) 303 extra_objects += [obj] 304 305 lib_name = _go_importpath(ctx) + ".a" 306 out_lib = ctx.new_file(lib_name) 307 out_object = ctx.new_file(ctx.label.name + ".o") 308 search_path = out_lib.path[:-len(lib_name)] 309 gc_goopts = _gc_goopts(ctx) 310 transitive_go_libraries = depset([out_lib]) 311 transitive_go_library_paths = depset([search_path]) 312 for dep in deps: 313 transitive_go_libraries += dep.transitive_go_libraries 314 transitive_cgo_deps += dep.transitive_cgo_deps 315 transitive_go_library_paths += dep.transitive_go_library_paths 316 317 go_srcs = _emit_go_compile_action(ctx, 318 sources = go_srcs, 319 deps = deps, 320 libpaths = transitive_go_library_paths, 321 out_object = out_object, 322 gc_goopts = gc_goopts, 323 ) 324 _emit_go_pack_action(ctx, out_lib, [out_object] + extra_objects) 325 326 dylibs = [] 327 if cgo_object: 328 dylibs += [d for d in cgo_object.cgo_deps if d.path.endswith(".so")] 329 330 runfiles = ctx.runfiles(files = dylibs, collect_data = True) 331 for d in dep_runfiles: 332 runfiles = runfiles.merge(d) 333 334 return struct( 335 label = ctx.label, 336 files = depset([out_lib]), 337 runfiles = runfiles, 338 go_sources = go_srcs, 339 asm_sources = asm_srcs, 340 asm_headers = asm_hdrs, 341 cgo_object = cgo_object, 342 direct_deps = ctx.attr.deps, 343 transitive_cgo_deps = transitive_cgo_deps, 344 transitive_go_libraries = transitive_go_libraries, 345 transitive_go_library_paths = transitive_go_library_paths, 346 gc_goopts = gc_goopts, 347 ) 348 349 def _c_linker_options(ctx, blacklist=[]): 350 """Extracts flags to pass to $(CC) on link from the current context 351 352 Args: 353 ctx: the current context 354 blacklist: Any flags starts with any of these prefixes are filtered out from 355 the return value. 356 357 Returns: 358 A list of command line flags 359 """ 360 cpp = ctx.fragments.cpp 361 features = ctx.features 362 options = cpp.compiler_options(features) 363 options += cpp.unfiltered_compiler_options(features) 364 options += cpp.link_options 365 options += cpp.mostly_static_link_options(ctx.features, False) 366 filtered = [] 367 for opt in options: 368 if any([opt.startswith(prefix) for prefix in blacklist]): 369 continue 370 filtered.append(opt) 371 return filtered 372 373 def _gc_goopts(ctx): 374 gc_goopts = [ctx.expand_make_variables("gc_goopts", f, {}) 375 for f in ctx.attr.gc_goopts] 376 if ctx.attr.library: 377 gc_goopts += ctx.attr.library.gc_goopts 378 return gc_goopts 379 380 def _gc_linkopts(ctx): 381 gc_linkopts = [ctx.expand_make_variables("gc_linkopts", f, {}) 382 for f in ctx.attr.gc_linkopts] 383 for k, v in ctx.attr.x_defs.items(): 384 gc_linkopts += ["-X", "%s='%s'" % (k, v)] 385 return gc_linkopts 386 387 def _extract_extldflags(gc_linkopts, extldflags): 388 """Extracts -extldflags from gc_linkopts and combines them into a single list. 389 390 Args: 391 gc_linkopts: a list of flags passed in through the gc_linkopts attributes. 392 ctx.expand_make_variables should have already been applied. 393 extldflags: a list of flags to be passed to the external linker. 394 395 Return: 396 A tuple containing the filtered gc_linkopts with external flags removed, 397 and a combined list of external flags. 398 """ 399 filtered_gc_linkopts = [] 400 is_extldflags = False 401 for opt in gc_linkopts: 402 if is_extldflags: 403 is_extldflags = False 404 extldflags += [opt] 405 elif opt == "-extldflags": 406 is_extldflags = True 407 else: 408 filtered_gc_linkopts += [opt] 409 return filtered_gc_linkopts, extldflags 410 411 def _emit_go_link_action(ctx, transitive_go_library_paths, transitive_go_libraries, cgo_deps, libs, 412 executable, gc_linkopts): 413 """Sets up a symlink tree to libraries to link together.""" 414 config_strip = len(ctx.configuration.bin_dir.path) + 1 415 pkg_depth = executable.dirname[config_strip:].count('/') + 1 416 417 ld = "%s" % ctx.fragments.cpp.compiler_executable 418 extldflags = _c_linker_options(ctx) + [ 419 "-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth), 420 ] 421 for d in cgo_deps: 422 if d.basename.endswith('.so'): 423 short_dir = d.dirname[len(d.root.path):] 424 extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth) + short_dir] 425 gc_linkopts, extldflags = _extract_extldflags(gc_linkopts, extldflags) 426 427 link_cmd = [ 428 ctx.file.go_tool.path, 429 "tool", "link", 430 "-L", "." 431 ] 432 for path in transitive_go_library_paths: 433 link_cmd += ["-L", path] 434 link_cmd += [ 435 "-o", executable.path, 436 ] + gc_linkopts + ['"${STAMP_XDEFS[@]}"'] 437 438 # workaround for a bug in ld(1) on Mac OS X. 439 # http://lists.apple.com/archives/Darwin-dev/2006/Sep/msg00084.html 440 # TODO(yugui) Remove this workaround once rules_go stops supporting XCode 7.2 441 # or earlier. 442 if not _is_darwin_cpu(ctx): 443 link_cmd += ["-s"] 444 445 link_cmd += [ 446 "-extld", ld, 447 "-extldflags", "'%s'" % " ".join(extldflags), 448 ] + [lib.path for lib in libs] 449 450 # Avoided -s on OSX but but it requires dsymutil to be on $PATH. 451 # TODO(yugui) Remove this workaround once rules_go stops supporting XCode 7.2 452 # or earlier. 453 cmds = ["export PATH=$PATH:/usr/bin"] 454 455 cmds += [ 456 "STAMP_XDEFS=()", 457 ] 458 459 stamp_inputs = [] 460 if ctx.attr.linkstamp: 461 # read workspace status files, converting "KEY value" lines 462 # to "-X $linkstamp.KEY=value" arguments to the go linker. 463 stamp_inputs = [ctx.info_file, ctx.version_file] 464 for f in stamp_inputs: 465 cmds += [ 466 "while read -r key value || [[ -n $key ]]; do", 467 " STAMP_XDEFS+=(-X \"%s.$key=$value\")" % ctx.attr.linkstamp, 468 "done < " + f.path, 469 ] 470 471 cmds += [' '.join(link_cmd)] 472 473 f = _emit_generate_params_action(cmds, ctx, lib.basename + ".GoLinkFile.params") 474 475 ctx.action( 476 inputs = [f] + (list(transitive_go_libraries) + [lib] + list(cgo_deps) + 477 ctx.files.toolchain + ctx.files._crosstool) + stamp_inputs, 478 outputs = [executable], 479 command = f.path, 480 mnemonic = "GoLink", 481 env = go_environment_vars(ctx), 482 ) 483 484 def go_binary_impl(ctx): 485 """go_binary_impl emits actions for compiling and linking a go executable.""" 486 lib_result = go_library_impl(ctx) 487 _emit_go_link_action( 488 ctx, 489 transitive_go_libraries=lib_result.transitive_go_libraries, 490 transitive_go_library_paths=lib_result.transitive_go_library_paths, 491 cgo_deps=lib_result.transitive_cgo_deps, 492 libs=lib_result.files, 493 executable=ctx.outputs.executable, 494 gc_linkopts=_gc_linkopts(ctx)) 495 496 return struct( 497 files = depset([ctx.outputs.executable]), 498 runfiles = lib_result.runfiles, 499 cgo_object = lib_result.cgo_object, 500 ) 501 502 def go_test_impl(ctx): 503 """go_test_impl implements go testing. 504 505 It emits an action to run the test generator, and then compiles the 506 test into a binary.""" 507 508 lib_result = go_library_impl(ctx) 509 main_go = ctx.new_file(ctx.label.name + "_main_test.go") 510 main_object = ctx.new_file(ctx.label.name + "_main_test.o") 511 main_lib = ctx.new_file(ctx.label.name + "_main_test.a") 512 go_import = _go_importpath(ctx) 513 514 cmds = [ 515 'UNFILTERED_TEST_FILES=(%s)' % 516 ' '.join(["'%s'" % f.path for f in lib_result.go_sources]), 517 'FILTERED_TEST_FILES=()', 518 'while read -r line; do', 519 ' if [ -n "$line" ]; then', 520 ' FILTERED_TEST_FILES+=("$line")', 521 ' fi', 522 'done < <(\'%s\' -cgo "${UNFILTERED_TEST_FILES[@]}")' % 523 ctx.executable._filter_tags.path, 524 ' '.join([ 525 "'%s'" % ctx.executable.test_generator.path, 526 '--package', 527 go_import, 528 '--output', 529 "'%s'" % main_go.path, 530 '"${FILTERED_TEST_FILES[@]}"', 531 ]), 532 ] 533 f = _emit_generate_params_action( 534 cmds, ctx, ctx.label.name + ".GoTestGenTest.params") 535 inputs = (list(lib_result.go_sources) + list(ctx.files.toolchain) + 536 [f, ctx.executable._filter_tags, ctx.executable.test_generator]) 537 ctx.action( 538 inputs = inputs, 539 outputs = [main_go], 540 command = f.path, 541 mnemonic = "GoTestGenTest", 542 env = dict(go_environment_vars(ctx), RUNDIR=ctx.label.package)) 543 544 _emit_go_compile_action( 545 ctx, 546 sources=depset([main_go]), 547 deps=ctx.attr.deps + [lib_result], 548 libpaths=lib_result.transitive_go_library_paths, 549 out_object=main_object, 550 gc_goopts=_gc_goopts(ctx), 551 ) 552 _emit_go_pack_action(ctx, main_lib, [main_object]) 553 _emit_go_link_action( 554 ctx, 555 transitive_go_library_paths=lib_result.transitive_go_library_paths, 556 transitive_go_libraries=lib_result.transitive_go_libraries, 557 cgo_deps=lib_result.transitive_cgo_deps, 558 libs=[main_lib], 559 executable=ctx.outputs.executable, 560 gc_linkopts=_gc_linkopts(ctx)) 561 562 # TODO(bazel-team): the Go tests should do a chdir to the directory 563 # holding the data files, so open-source go tests continue to work 564 # without code changes. 565 runfiles = ctx.runfiles(files = [ctx.outputs.executable]) 566 runfiles = runfiles.merge(lib_result.runfiles) 567 return struct( 568 files = depset([ctx.outputs.executable]), 569 runfiles = runfiles, 570 ) 571 572 go_env_attrs = { 573 "toolchain": attr.label( 574 default = Label("//go/toolchain:toolchain"), 575 allow_files = True, 576 cfg = "host", 577 ), 578 "go_tool": attr.label( 579 default = Label("//go/toolchain:go_tool"), 580 single_file = True, 581 allow_files = True, 582 cfg = "host", 583 ), 584 "go_prefix": attr.label( 585 providers = ["go_prefix"], 586 default = Label( 587 "//:go_prefix", 588 relative_to_caller_repository = True, 589 ), 590 allow_files = False, 591 cfg = "host", 592 ), 593 "go_src": attr.label( 594 default = Label("//go/toolchain:go_src"), 595 allow_files = True, 596 cfg = "host", 597 ), 598 "go_include": attr.label( 599 default = Label("//go/toolchain:go_include"), 600 single_file = True, 601 allow_files = True, 602 cfg = "host", 603 ), 604 "go_root": attr.label( 605 providers = ["go_root"], 606 default = Label( 607 "//go/toolchain:go_root", 608 ), 609 allow_files = False, 610 cfg = "host", 611 ), 612 "_filter_tags": attr.label( 613 default = Label("//go/tools/filter_tags"), 614 cfg = "host", 615 executable = True, 616 single_file = True, 617 ), 618 "_filter_exec": attr.label( 619 default = Label("//go/tools/filter_exec"), 620 cfg = "host", 621 executable = True, 622 single_file = True, 623 ), 624 "_asm": attr.label( 625 default = Label("//go/tools/builders:asm"), 626 cfg = "host", 627 executable = True, 628 single_file = True, 629 ), 630 } 631 632 go_library_attrs = go_env_attrs + { 633 "data": attr.label_list( 634 allow_files = True, 635 cfg = "data", 636 ), 637 "srcs": attr.label_list(allow_files = go_filetype), 638 "deps": attr.label_list( 639 providers = [ 640 "transitive_go_library_paths", 641 "transitive_go_libraries", 642 "transitive_cgo_deps", 643 ], 644 ), 645 "importpath": attr.string(), 646 "library": attr.label( 647 providers = [ 648 "direct_deps", 649 "go_sources", 650 "asm_sources", 651 "cgo_object", 652 "gc_goopts", 653 ], 654 ), 655 "gc_goopts": attr.string_list(), 656 } 657 658 _crosstool_attrs = { 659 "_crosstool": attr.label( 660 default = Label("//tools/defaults:crosstool"), 661 ), 662 } 663 664 go_link_attrs = go_library_attrs + _crosstool_attrs + { 665 "gc_linkopts": attr.string_list(), 666 "linkstamp": attr.string(), 667 "x_defs": attr.string_dict(), 668 } 669 670 go_library = rule( 671 go_library_impl, 672 attrs = go_library_attrs + { 673 "cgo_object": attr.label( 674 providers = [ 675 "cgo_obj", 676 "cgo_deps", 677 ], 678 ), 679 }, 680 fragments = ["cpp"], 681 ) 682 683 go_binary = rule( 684 go_binary_impl, 685 attrs = go_library_attrs + _crosstool_attrs + go_link_attrs, 686 executable = True, 687 fragments = ["cpp"], 688 ) 689 690 go_test = rule( 691 go_test_impl, 692 attrs = go_library_attrs + _crosstool_attrs + go_link_attrs + { 693 "test_generator": attr.label( 694 executable = True, 695 default = Label( 696 "//go/tools:generate_test_main", 697 ), 698 cfg = "host", 699 ), 700 }, 701 executable = True, 702 fragments = ["cpp"], 703 test = True, 704 ) 705 706 def _pkg_dir(workspace_root, package_name): 707 if workspace_root and package_name: 708 return workspace_root + "/" + package_name 709 if workspace_root: 710 return workspace_root 711 if package_name: 712 return package_name 713 return "." 714 715 def _exec_path(path): 716 if path.startswith('/'): 717 return path 718 return '${execroot}/' + path 719 720 def _cgo_filter_srcs_impl(ctx): 721 srcs = ctx.files.srcs 722 dsts = [] 723 cmds = [] 724 for src in srcs: 725 stem, _, ext = src.path.rpartition('.') 726 dst_basename = "%s.filtered.%s" % (stem, ext) 727 dst = ctx.new_file(src, dst_basename) 728 cmds += [ 729 "if '%s' -cgo -quiet '%s'; then" % 730 (ctx.executable._filter_tags.path, src.path), 731 " cp '%s' '%s'" % (src.path, dst.path), 732 "else", 733 " echo -n >'%s'" % dst.path, 734 "fi", 735 ] 736 dsts.append(dst) 737 738 if ctx.label.package == "": 739 script_name = ctx.label.name + ".CGoFilterSrcs.params" 740 else: 741 script_name = ctx.label.package + "/" + ctx.label.name + ".CGoFilterSrcs.params" 742 f = _emit_generate_params_action(cmds, ctx, script_name) 743 ctx.action( 744 inputs = [f, ctx.executable._filter_tags] + srcs, 745 outputs = dsts, 746 command = f.path, 747 mnemonic = "CgoFilterSrcs", 748 ) 749 return struct( 750 files = depset(dsts), 751 ) 752 753 _cgo_filter_srcs = rule( 754 implementation = _cgo_filter_srcs_impl, 755 attrs = { 756 "srcs": attr.label_list( 757 allow_files = cgo_filetype, 758 ), 759 "_filter_tags": attr.label( 760 default = Label("//go/tools/filter_tags"), 761 cfg = "host", 762 executable = True, 763 single_file = True, 764 ), 765 }, 766 fragments = ["cpp"], 767 ) 768 769 def _cgo_codegen_impl(ctx): 770 go_srcs = ctx.files.srcs 771 srcs = go_srcs + ctx.files.c_hdrs 772 linkopts = ctx.attr.linkopts 773 copts = ctx.fragments.cpp.c_options + ctx.attr.copts 774 deps = depset([], order="topological") 775 for d in ctx.attr.deps: 776 srcs += list(d.cc.transitive_headers) 777 deps += d.cc.libs 778 copts += ['-D' + define for define in d.cc.defines] 779 for inc in d.cc.include_directories: 780 copts += ['-I', _exec_path(inc)] 781 for hdr in ctx.files.c_hdrs: 782 copts += ['-iquote', hdr.dirname] 783 for inc in d.cc.quote_include_directories: 784 copts += ['-iquote', _exec_path(inc)] 785 for inc in d.cc.system_include_directories: 786 copts += ['-isystem', _exec_path(inc)] 787 for lib in d.cc.libs: 788 if lib.basename.startswith('lib') and lib.basename.endswith('.so'): 789 linkopts += ['-L', lib.dirname, '-l', lib.basename[3:-3]] 790 else: 791 linkopts += [lib.path] 792 linkopts += d.cc.link_flags 793 794 p = _pkg_dir(ctx.label.workspace_root, ctx.label.package) + "/" 795 if p == "./": 796 p = "" # workaround when cgo_library in repository root 797 out_dir = (ctx.configuration.genfiles_dir.path + '/' + 798 p + ctx.attr.outdir) 799 cc = ctx.fragments.cpp.compiler_executable 800 cmds = [ 801 # We cannot use env for CC because $(CC) on OSX is relative 802 # and '../' does not work fine due to symlinks. 803 'export CC=$(cd $(dirname {cc}); pwd)/$(basename {cc})'.format(cc=cc), 804 'export CXX=$CC', 805 'objdir="%s/gen"' % out_dir, 806 'execroot=$(pwd)', 807 'mkdir -p "$objdir"', 808 'unfiltered_go_files=(%s)' % ' '.join(["'%s'" % f.path for f in go_srcs]), 809 'filtered_go_files=()', 810 'for file in "${unfiltered_go_files[@]}"; do', 811 ' stem=$(basename "$file" .go)', 812 ' if %s -cgo -quiet "$file"; then' % ctx.executable._filter_tags.path, 813 ' filtered_go_files+=("$file")', 814 ' else', 815 ' grep --max-count 1 "^package " "$file" >"$objdir/$stem.go"', 816 ' echo -n >"$objdir/$stem.c"', 817 ' fi', 818 'done', 819 'if [ ${#filtered_go_files[@]} -eq 0 ]; then', 820 ' echo no buildable Go source files in %s >&1' % str(ctx.label), 821 ' exit 1', 822 'fi', 823 '"$GOROOT/bin/go" tool cgo -objdir "$objdir" -- %s "${filtered_go_files[@]}"' % 824 ' '.join(['"%s"' % copt for copt in copts]), 825 # Rename the outputs using glob so we don't have to understand cgo's mangling 826 # TODO(#350): might be fixed by this?. 827 'for file in "${filtered_go_files[@]}"; do', 828 ' stem=$(basename "$file" .go)', 829 ' mv "$objdir/"*"$stem.cgo1.go" "$objdir/$stem.go"', 830 ' mv "$objdir/"*"$stem.cgo2.c" "$objdir/$stem.c"', 831 'done', 832 'rm -f $objdir/_cgo_.o $objdir/_cgo_flags', 833 ] 834 835 f = _emit_generate_params_action(cmds, ctx, out_dir + ".CGoCodeGenFile.params") 836 837 inputs = (srcs + ctx.files.toolchain + ctx.files._crosstool + 838 [f, ctx.executable._filter_tags]) 839 ctx.action( 840 inputs = inputs, 841 outputs = ctx.outputs.outs, 842 mnemonic = "CGoCodeGen", 843 progress_message = "CGoCodeGen %s" % ctx.label, 844 command = f.path, 845 env = go_environment_vars(ctx) + { 846 "CGO_LDFLAGS": " ".join(linkopts), 847 }, 848 ) 849 return struct( 850 label = ctx.label, 851 files = depset(ctx.outputs.outs), 852 cgo_deps = deps, 853 ) 854 855 _cgo_codegen_rule = rule( 856 _cgo_codegen_impl, 857 attrs = go_env_attrs + _crosstool_attrs + { 858 "srcs": attr.label_list( 859 allow_files = go_filetype, 860 non_empty = True, 861 ), 862 "c_hdrs": attr.label_list( 863 allow_files = cc_hdr_filetype, 864 ), 865 "deps": attr.label_list( 866 allow_files = False, 867 providers = ["cc"], 868 ), 869 "copts": attr.string_list(), 870 "linkopts": attr.string_list(), 871 "outdir": attr.string(mandatory = True), 872 "outs": attr.output_list( 873 mandatory = True, 874 non_empty = True, 875 ), 876 }, 877 fragments = ["cpp"], 878 output_to_genfiles = True, 879 ) 880 881 def _cgo_codegen(name, srcs, c_hdrs=[], deps=[], copts=[], linkopts=[], 882 go_tool=None, toolchain=None): 883 """Generates glue codes for interop between C and Go 884 885 Args: 886 name: A unique name of the rule 887 srcs: list of Go source files. 888 Each of them must contain `import "C"`. 889 c_hdrs: C/C++ header files necessary to determine kinds of 890 C/C++ identifiers in srcs. 891 deps: A list of cc_library rules. 892 The generated codes are expected to be linked with these deps. 893 linkopts: A list of linker options, 894 These flags are passed to the linker when the generated codes 895 are linked into the target binary. 896 """ 897 outdir = name + ".dir" 898 outgen = outdir + "/gen" 899 900 go_thunks = [] 901 c_thunks = [] 902 for s in srcs: 903 if not s.endswith('.go'): 904 fail("not a .go file: %s" % s) 905 basename = s[:-3] 906 if basename.rfind("/") >= 0: 907 basename = basename[basename.rfind("/")+1:] 908 go_thunks.append(outgen + "/" + basename + ".go") 909 c_thunks.append(outgen + "/" + basename + ".c") 910 911 outs = struct( 912 name = name, 913 914 outdir = outgen, 915 go_thunks = go_thunks, 916 c_thunks = c_thunks, 917 c_exports = [ 918 outgen + "/_cgo_export.c", 919 outgen + "/_cgo_export.h", 920 ], 921 c_dummy = outgen + "/_cgo_main.c", 922 gotypes = outgen + "/_cgo_gotypes.go", 923 ) 924 925 _cgo_codegen_rule( 926 name = name, 927 srcs = srcs, 928 c_hdrs = c_hdrs, 929 deps = deps, 930 copts = copts, 931 linkopts = linkopts, 932 933 go_tool = go_tool, 934 toolchain = toolchain, 935 936 outdir = outdir, 937 outs = outs.go_thunks + outs.c_thunks + outs.c_exports + [ 938 outs.c_dummy, outs.gotypes, 939 ], 940 941 visibility = ["//visibility:private"], 942 ) 943 return outs 944 945 def _cgo_import_impl(ctx): 946 cmds = [ 947 (ctx.file.go_tool.path + " tool cgo" + 948 " -dynout " + ctx.outputs.out.path + 949 " -dynimport " + ctx.file.cgo_o.path + 950 " -dynpackage $(%s %s)" % (ctx.executable._extract_package.path, 951 ctx.file.sample_go_src.path)), 952 ] 953 f = _emit_generate_params_action(cmds, ctx, ctx.outputs.out.path + ".CGoImportGenFile.params") 954 ctx.action( 955 inputs = (ctx.files.toolchain + 956 [f, ctx.file.go_tool, ctx.executable._extract_package, 957 ctx.file.cgo_o, ctx.file.sample_go_src]), 958 outputs = [ctx.outputs.out], 959 command = f.path, 960 mnemonic = "CGoImportGen", 961 env = go_environment_vars(ctx), 962 ) 963 return struct( 964 files = depset([ctx.outputs.out]), 965 ) 966 967 _cgo_import = rule( 968 _cgo_import_impl, 969 attrs = go_env_attrs + { 970 "cgo_o": attr.label( 971 allow_files = True, 972 single_file = True, 973 ), 974 "sample_go_src": attr.label( 975 allow_files = True, 976 single_file = True, 977 ), 978 "out": attr.output( 979 mandatory = True, 980 ), 981 "_extract_package": attr.label( 982 default = Label("//go/tools/extract_package"), 983 executable = True, 984 cfg = "host", 985 ), 986 }, 987 fragments = ["cpp"], 988 ) 989 990 def _cgo_genrule_impl(ctx): 991 return struct( 992 label = ctx.label, 993 go_sources = ctx.files.srcs, 994 asm_sources = [], 995 asm_headers = [], 996 cgo_object = ctx.attr.cgo_object, 997 direct_deps = ctx.attr.deps, 998 gc_goopts = [], 999 ) 1000 1001 _cgo_genrule = rule( 1002 _cgo_genrule_impl, 1003 attrs = { 1004 "srcs": attr.label_list(allow_files = FileType([".go"])), 1005 "cgo_object": attr.label( 1006 providers = [ 1007 "cgo_obj", 1008 "cgo_deps", 1009 ], 1010 ), 1011 "deps": attr.label_list( 1012 providers = [ 1013 "direct_deps", 1014 "transitive_go_library_paths", 1015 "transitive_go_libraries", 1016 "transitive_cgo_deps", 1017 ], 1018 ), 1019 }, 1020 fragments = ["cpp"], 1021 ) 1022 1023 """Generates symbol-import directives for cgo 1024 1025 Args: 1026 cgo_o: The loadable object to extract dynamic symbols from. 1027 sample_go_src: A go source which is compiled together with the generated file. 1028 The generated file will have the same Go package name as this file. 1029 out: Destination of the generated codes. 1030 """ 1031 1032 def _cgo_object_impl(ctx): 1033 arguments = _c_linker_options(ctx, blacklist=[ 1034 # never link any dependency libraries 1035 "-l", "-L", 1036 # manage flags to ld(1) by ourselves 1037 "-Wl,"]) 1038 arguments += [ 1039 "-o", ctx.outputs.out.path, 1040 "-nostdlib", 1041 "-Wl,-r", 1042 ] 1043 if _is_darwin_cpu(ctx): 1044 arguments += ["-shared", "-Wl,-all_load"] 1045 else: 1046 arguments += ["-Wl,-whole-archive"] 1047 1048 lo = ctx.files.src[-1] 1049 arguments += [lo.path] 1050 1051 ctx.action( 1052 inputs = [lo] + ctx.files._crosstool, 1053 outputs = [ctx.outputs.out], 1054 mnemonic = "CGoObject", 1055 progress_message = "Linking %s" % ctx.outputs.out.short_path, 1056 executable = ctx.fragments.cpp.compiler_executable, 1057 arguments = arguments, 1058 ) 1059 runfiles = ctx.runfiles(collect_data = True) 1060 runfiles = runfiles.merge(ctx.attr.src.data_runfiles) 1061 return struct( 1062 files = depset([ctx.outputs.out]), 1063 cgo_obj = ctx.outputs.out, 1064 cgo_deps = ctx.attr.cgogen.cgo_deps, 1065 runfiles = runfiles, 1066 ) 1067 1068 _cgo_object = rule( 1069 _cgo_object_impl, 1070 attrs = _crosstool_attrs + { 1071 "src": attr.label( 1072 mandatory = True, 1073 providers = ["cc"], 1074 ), 1075 "cgogen": attr.label( 1076 mandatory = True, 1077 providers = ["cgo_deps"], 1078 ), 1079 "out": attr.output( 1080 mandatory = True, 1081 ), 1082 }, 1083 fragments = ["cpp"], 1084 ) 1085 1086 """Generates _all.o to be archived together with Go objects. 1087 1088 Args: 1089 src: source static library which contains objects 1090 cgogen: _cgo_codegen rule which knows the dependency cc_library() rules 1091 to be linked together with src when we generate the final go binary. 1092 """ 1093 1094 def _setup_cgo_library(name, srcs, cdeps, copts, clinkopts, go_tool, toolchain): 1095 go_srcs = [s for s in srcs if s.endswith('.go')] 1096 c_hdrs = [s for s in srcs if any([s.endswith(ext) for ext in hdr_exts])] 1097 c_srcs = [s for s in srcs if not s in (go_srcs + c_hdrs)] 1098 1099 # Split cgo files into .go parts and .c parts (plus some other files). 1100 cgogen = _cgo_codegen( 1101 name = name + ".cgo", 1102 srcs = go_srcs, 1103 c_hdrs = c_hdrs, 1104 deps = cdeps, 1105 copts = copts, 1106 linkopts = clinkopts, 1107 go_tool = go_tool, 1108 toolchain = toolchain, 1109 ) 1110 1111 # Filter c_srcs with build constraints. 1112 c_filtered_srcs = [] 1113 if len(c_srcs) > 0: 1114 c_filtered_srcs_name = name + "_filter_cgo_srcs" 1115 _cgo_filter_srcs( 1116 name = c_filtered_srcs_name, 1117 srcs = c_srcs, 1118 ) 1119 c_filtered_srcs.append(":" + c_filtered_srcs_name) 1120 1121 pkg_dir = _pkg_dir( 1122 "external/" + REPOSITORY_NAME[1:] if len(REPOSITORY_NAME) > 1 else "", 1123 PACKAGE_NAME) 1124 1125 # Platform-specific settings 1126 native.config_setting( 1127 name = name + "_windows_setting", 1128 values = { 1129 "cpu": "x64_windows_msvc", 1130 }, 1131 ) 1132 platform_copts = select({ 1133 ":" + name + "_windows_setting": ["-mthreads"], 1134 "//conditions:default": ["-pthread"], 1135 }) 1136 platform_linkopts = select({ 1137 ":" + name + "_windows_setting": ["-mthreads"], 1138 "//conditions:default": ["-pthread"], 1139 }) 1140 1141 # Bundles objects into an archive so that _cgo_.o and _all.o can share them. 1142 native.cc_library( 1143 name = cgogen.outdir + "/_cgo_lib", 1144 srcs = cgogen.c_thunks + cgogen.c_exports + c_filtered_srcs + c_hdrs, 1145 deps = cdeps, 1146 copts = copts + platform_copts + [ 1147 "-I", pkg_dir, 1148 "-I", "$(GENDIR)/" + pkg_dir + "/" + cgogen.outdir, 1149 # The generated thunks often contain unused variables. 1150 "-Wno-unused-variable", 1151 ], 1152 linkopts = clinkopts + platform_linkopts, 1153 linkstatic = 1, 1154 # _cgo_.o and _all.o keep all objects in this archive. 1155 # But it should not be very annoying in the final binary target 1156 # because _cgo_object rule does not propagate alwayslink=1 1157 alwayslink = 1, 1158 visibility = ["//visibility:private"], 1159 ) 1160 1161 # Loadable object which cgo reads when it generates _cgo_import.go 1162 native.cc_binary( 1163 name = cgogen.outdir + "/_cgo_.o", 1164 srcs = [cgogen.c_dummy], 1165 deps = cdeps + [cgogen.outdir + "/_cgo_lib"], 1166 copts = copts, 1167 linkopts = clinkopts, 1168 visibility = ["//visibility:private"], 1169 ) 1170 _cgo_import( 1171 name = "%s.cgo.importgen" % name, 1172 cgo_o = cgogen.outdir + "/_cgo_.o", 1173 out = cgogen.outdir + "/_cgo_import.go", 1174 sample_go_src = go_srcs[0], 1175 go_tool = go_tool, 1176 toolchain = toolchain, 1177 visibility = ["//visibility:private"], 1178 ) 1179 1180 _cgo_object( 1181 name = cgogen.outdir + "/_cgo_object", 1182 src = cgogen.outdir + "/_cgo_lib", 1183 out = cgogen.outdir + "/_all.o", 1184 cgogen = cgogen.name, 1185 visibility = ["//visibility:private"], 1186 ) 1187 return cgogen 1188 1189 def cgo_genrule(name, srcs, 1190 copts=[], 1191 clinkopts=[], 1192 cdeps=[], 1193 **kwargs): 1194 cgogen = _setup_cgo_library( 1195 name = name, 1196 srcs = srcs, 1197 cdeps = cdeps, 1198 copts = copts, 1199 clinkopts = clinkopts, 1200 toolchain = None, 1201 go_tool = None, 1202 ) 1203 _cgo_genrule( 1204 name = name, 1205 srcs = cgogen.go_thunks + [ 1206 cgogen.gotypes, 1207 cgogen.outdir + "/_cgo_import.go", 1208 ], 1209 cgo_object = cgogen.outdir + "/_cgo_object", 1210 **kwargs 1211 ) 1212 1213 def cgo_library(name, srcs, 1214 toolchain=None, 1215 go_tool=None, 1216 copts=[], 1217 clinkopts=[], 1218 cdeps=[], 1219 **kwargs): 1220 """Builds a cgo-enabled go library. 1221 1222 Args: 1223 name: A unique name for this rule. 1224 srcs: List of Go, C and C++ files that are processed to build a Go library. 1225 Those Go files must contain `import "C"`. 1226 C and C++ files can be anything allowed in `srcs` attribute of 1227 `cc_library`. 1228 copts: Add these flags to the C++ compiler. 1229 clinkopts: Add these flags to the C++ linker. 1230 cdeps: List of C/C++ libraries to be linked into the binary target. 1231 They must be `cc_library` rules. 1232 deps: List of other libraries to be linked to this library target. 1233 data: List of files needed by this rule at runtime. 1234 1235 NOTE: 1236 `srcs` cannot contain pure-Go files, which do not have `import "C"`. 1237 So you need to define another `go_library` when you build a go package with 1238 both cgo-enabled and pure-Go sources. 1239 1240 ``` 1241 cgo_library( 1242 name = "cgo_enabled", 1243 srcs = ["cgo-enabled.go", "foo.cc", "bar.S", "baz.a"], 1244 ) 1245 1246 go_library( 1247 name = "go_default_library", 1248 srcs = ["pure-go.go"], 1249 library = ":cgo_enabled", 1250 ) 1251 ``` 1252 """ 1253 cgogen = _setup_cgo_library( 1254 name = name, 1255 srcs = srcs, 1256 cdeps = cdeps, 1257 copts = copts, 1258 clinkopts = clinkopts, 1259 go_tool = go_tool, 1260 toolchain = toolchain, 1261 ) 1262 1263 go_library( 1264 name = name, 1265 srcs = cgogen.go_thunks + [ 1266 cgogen.gotypes, 1267 cgogen.outdir + "/_cgo_import.go", 1268 ], 1269 cgo_object = cgogen.outdir + "/_cgo_object", 1270 go_tool = go_tool, 1271 toolchain = toolchain, 1272 **kwargs 1273 )