1# Copyright 2017 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 15load( 16 "@bazel_tools//tools/cpp:toolchain_utils.bzl", 17 "find_cpp_toolchain", 18) 19load( 20 "@bazel_tools//tools/build_defs/cc:action_names.bzl", 21 "CPP_COMPILE_ACTION_NAME", 22 "CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME", 23 "CPP_LINK_EXECUTABLE_ACTION_NAME", 24 "CPP_LINK_STATIC_LIBRARY_ACTION_NAME", 25 "C_COMPILE_ACTION_NAME", 26 "OBJCPP_COMPILE_ACTION_NAME", 27 "OBJC_COMPILE_ACTION_NAME", 28) 29load( 30 ":go_toolchain.bzl", 31 "GO_TOOLCHAIN", 32) 33load( 34 ":providers.bzl", 35 "CgoContextInfo", 36 "EXPLICIT_PATH", 37 "EXPORT_PATH", 38 "GoArchive", 39 "GoConfigInfo", 40 "GoContextInfo", 41 "GoLibrary", 42 "GoSource", 43 "GoStdLib", 44 "INFERRED_PATH", 45 "get_source", 46) 47load( 48 ":mode.bzl", 49 "get_mode", 50 "installsuffix", 51) 52load( 53 ":common.bzl", 54 "COVERAGE_OPTIONS_DENYLIST", 55 "as_iterable", 56 "goos_to_extension", 57 "goos_to_shared_extension", 58 "is_struct", 59) 60load( 61 "//go/platform:apple.bzl", 62 "apple_ensure_options", 63) 64load( 65 "@bazel_skylib//rules:common_settings.bzl", 66 "BuildSettingInfo", 67) 68load( 69 "//go/private/rules:transition.bzl", 70 "request_nogo_transition", 71) 72 73# cgo requires a gcc/clang style compiler. 74# We use a denylist instead of an allowlist: 75# - Bazel's auto-detected toolchains used to set the compiler name to "compiler" 76# for gcc (fixed in 6.0.0), which defeats the purpose of an allowlist. 77# - The compiler name field is free-form and user-defined, so we would have to 78# provide a way to override this list. 79# TODO: Convert to a denylist once we can assume Bazel 6.0.0 or later and have a 80# way for users to extend the list. 81_UNSUPPORTED_C_COMPILERS = { 82 "msvc-cl": None, 83 "clang-cl": None, 84} 85 86_COMPILER_OPTIONS_DENYLIST = dict({ 87 # cgo parses the error messages from the compiler. It can't handle colors. 88 # Ignore both variants of the diagnostics color flag. 89 "-fcolor-diagnostics": None, 90 "-fdiagnostics-color": None, 91 92 # cgo also wants to see all the errors when it is testing the compiler. 93 # fmax-errors limits that and causes build failures. 94 "-fmax-errors=": None, 95 "-Wall": None, 96 97 # Symbols are needed by Go, so keep them 98 "-g0": None, 99 100 # Don't compile generated cgo code with coverage. If we do an internal 101 # link, we may have undefined references to coverage functions. 102 "--coverage": None, 103 "-ftest-coverage": None, 104 "-fprofile-arcs": None, 105 "-fprofile-instr-generate": None, 106 "-fcoverage-mapping": None, 107}, **COVERAGE_OPTIONS_DENYLIST) 108 109_LINKER_OPTIONS_DENYLIST = { 110 "-Wl,--gc-sections": None, 111} 112 113_UNSUPPORTED_FEATURES = [ 114 # These toolchain features require special rule support and will thus break 115 # with CGo. 116 # Taken from https://github.com/bazelbuild/rules_rust/blob/521e649ff44e9711fe3c45b0ec1e792f7e1d361e/rust/private/utils.bzl#L20. 117 "thin_lto", 118 "module_maps", 119 "use_header_modules", 120 "fdo_instrument", 121 "fdo_optimize", 122] 123 124def _match_option(option, pattern): 125 if pattern.endswith("="): 126 return option.startswith(pattern) 127 else: 128 return option == pattern 129 130def _filter_options(options, denylist): 131 return [ 132 option 133 for option in options 134 if not any([_match_option(option, pattern) for pattern in denylist]) 135 ] 136 137def _child_name(go, path, ext, name): 138 if not name: 139 name = go.label.name 140 if path or not ext: 141 # The '_' avoids collisions with another file matching the label name. 142 # For example, hello and hello/testmain.go. 143 name += "_" 144 if path: 145 name += "/" + path 146 if ext: 147 name += ext 148 return name 149 150def _declare_file(go, path = "", ext = "", name = ""): 151 return go.actions.declare_file(_child_name(go, path, ext, name)) 152 153def _declare_directory(go, path = "", ext = "", name = ""): 154 return go.actions.declare_directory(_child_name(go, path, ext, name)) 155 156def _new_args(go): 157 # TODO(jayconrod): print warning. 158 return go.builder_args(go) 159 160def _builder_args(go, command = None): 161 args = go.actions.args() 162 args.use_param_file("-param=%s") 163 args.set_param_file_format("shell") 164 if command: 165 args.add(command) 166 args.add("-sdk", go.sdk.root_file.dirname) 167 args.add("-installsuffix", installsuffix(go.mode)) 168 args.add_joined("-tags", go.tags, join_with = ",") 169 return args 170 171def _tool_args(go): 172 args = go.actions.args() 173 args.use_param_file("-param=%s") 174 args.set_param_file_format("shell") 175 return args 176 177def _new_library(go, name = None, importpath = None, resolver = None, importable = True, testfilter = None, is_main = False, **kwargs): 178 if not importpath: 179 importpath = go.importpath 180 importmap = go.importmap 181 else: 182 importmap = importpath 183 pathtype = go.pathtype 184 if not importable and pathtype == EXPLICIT_PATH: 185 pathtype = EXPORT_PATH 186 187 return GoLibrary( 188 name = go.label.name if not name else name, 189 label = go.label, 190 importpath = importpath, 191 importmap = importmap, 192 importpath_aliases = go.importpath_aliases, 193 pathtype = pathtype, 194 resolve = resolver, 195 testfilter = testfilter, 196 is_main = is_main, 197 **kwargs 198 ) 199 200def _merge_embed(source, embed): 201 s = get_source(embed) 202 source["srcs"] = s.srcs + source["srcs"] 203 source["orig_srcs"] = s.orig_srcs + source["orig_srcs"] 204 source["orig_src_map"].update(s.orig_src_map) 205 source["embedsrcs"] = source["embedsrcs"] + s.embedsrcs 206 source["cover"] = source["cover"] + s.cover 207 source["deps"] = source["deps"] + s.deps 208 source["x_defs"].update(s.x_defs) 209 source["gc_goopts"] = source["gc_goopts"] + s.gc_goopts 210 source["runfiles"] = source["runfiles"].merge(s.runfiles) 211 if s.cgo and source["cgo"]: 212 fail("multiple libraries with cgo enabled") 213 source["cgo"] = source["cgo"] or s.cgo 214 source["cdeps"] = source["cdeps"] or s.cdeps 215 source["cppopts"] = source["cppopts"] or s.cppopts 216 source["copts"] = source["copts"] or s.copts 217 source["cxxopts"] = source["cxxopts"] or s.cxxopts 218 source["clinkopts"] = source["clinkopts"] or s.clinkopts 219 source["cgo_deps"] = source["cgo_deps"] + s.cgo_deps 220 source["cgo_exports"] = source["cgo_exports"] + s.cgo_exports 221 222def _dedup_deps(deps): 223 """Returns a list of deps without duplicate import paths. 224 225 Earlier targets take precedence over later targets. This is intended to 226 allow an embedding library to override the dependencies of its 227 embedded libraries. 228 229 Args: 230 deps: an iterable containing either Targets or GoArchives. 231 """ 232 deduped_deps = [] 233 importpaths = {} 234 for dep in deps: 235 if hasattr(dep, "data") and hasattr(dep.data, "importpath"): 236 importpath = dep.data.importpath 237 else: 238 importpath = dep[GoLibrary].importpath 239 if importpath in importpaths: 240 continue 241 importpaths[importpath] = None 242 deduped_deps.append(dep) 243 return deduped_deps 244 245def _library_to_source(go, attr, library, coverage_instrumented): 246 #TODO: stop collapsing a depset in this line... 247 attr_srcs = [f for t in getattr(attr, "srcs", []) for f in as_iterable(t.files)] 248 generated_srcs = getattr(library, "srcs", []) 249 srcs = attr_srcs + generated_srcs 250 embedsrcs = [f for t in getattr(attr, "embedsrcs", []) for f in as_iterable(t.files)] 251 source = { 252 "library": library, 253 "mode": go.mode, 254 "srcs": srcs, 255 "orig_srcs": srcs, 256 "orig_src_map": {}, 257 "cover": [], 258 "embedsrcs": embedsrcs, 259 "x_defs": {}, 260 "deps": getattr(attr, "deps", []), 261 "gc_goopts": _expand_opts(go, "gc_goopts", getattr(attr, "gc_goopts", [])), 262 "runfiles": _collect_runfiles(go, getattr(attr, "data", []), getattr(attr, "deps", [])), 263 "cgo": getattr(attr, "cgo", False), 264 "cdeps": getattr(attr, "cdeps", []), 265 "cppopts": _expand_opts(go, "cppopts", getattr(attr, "cppopts", [])), 266 "copts": _expand_opts(go, "copts", getattr(attr, "copts", [])), 267 "cxxopts": _expand_opts(go, "cxxopts", getattr(attr, "cxxopts", [])), 268 "clinkopts": _expand_opts(go, "clinkopts", getattr(attr, "clinkopts", [])), 269 "cgo_deps": [], 270 "cgo_exports": [], 271 "cc_info": None, 272 } 273 if coverage_instrumented: 274 source["cover"] = attr_srcs 275 for dep in source["deps"]: 276 _check_binary_dep(go, dep, "deps") 277 for e in getattr(attr, "embed", []): 278 _check_binary_dep(go, e, "embed") 279 _merge_embed(source, e) 280 source["deps"] = _dedup_deps(source["deps"]) 281 x_defs = source["x_defs"] 282 for k, v in getattr(attr, "x_defs", {}).items(): 283 v = _expand_location(go, attr, v) 284 if "." not in k: 285 k = "{}.{}".format(library.importmap, k) 286 x_defs[k] = v 287 source["x_defs"] = x_defs 288 if not source["cgo"]: 289 for k in ("cdeps", "cppopts", "copts", "cxxopts", "clinkopts"): 290 if getattr(attr, k, None): 291 fail(k + " set without cgo = True") 292 for f in source["srcs"]: 293 # This check won't report directory sources that contain C/C++ 294 # sources. compilepkg will catch these instead. 295 if f.extension in ("c", "cc", "cxx", "cpp", "hh", "hpp", "hxx"): 296 fail("source {} has C/C++ extension, but cgo was not enabled (set 'cgo = True')".format(f.path)) 297 if library.resolve: 298 library.resolve(go, attr, source, _merge_embed) 299 source["cc_info"] = _collect_cc_infos(source["deps"], source["cdeps"]) 300 return GoSource(**source) 301 302def _collect_runfiles(go, data, deps): 303 """Builds a set of runfiles from the deps and data attributes. 304 305 srcs and their runfiles are not included.""" 306 files = depset(transitive = [t[DefaultInfo].files for t in data]) 307 runfiles = go._ctx.runfiles(transitive_files = files) 308 for t in data: 309 runfiles = runfiles.merge(t[DefaultInfo].data_runfiles) 310 for t in deps: 311 runfiles = runfiles.merge(get_source(t).runfiles) 312 return runfiles 313 314def _collect_cc_infos(deps, cdeps): 315 cc_infos = [] 316 for dep in cdeps: 317 if CcInfo in dep: 318 cc_infos.append(dep[CcInfo]) 319 for dep in deps: 320 # dep may be a struct, which doesn't support indexing by providers. 321 if is_struct(dep): 322 continue 323 if GoSource in dep: 324 cc_infos.append(dep[GoSource].cc_info) 325 return cc_common.merge_cc_infos(cc_infos = cc_infos) 326 327def _check_binary_dep(go, dep, edge): 328 """Checks that this rule doesn't depend on a go_binary or go_test. 329 330 go_binary and go_test may return provides with useful information for other 331 rules (like go_path), but go_binary and go_test may not depend on other 332 go_binary and go_binary targets. Their dependencies may be built in 333 different modes, resulting in conflicts and opaque errors. 334 """ 335 if (type(dep) == "Target" and 336 DefaultInfo in dep and 337 getattr(dep[DefaultInfo], "files_to_run", None) and 338 dep[DefaultInfo].files_to_run.executable): 339 fail("rule {rule} depends on executable {dep} via {edge}. This is not safe for cross-compilation. Depend on go_library instead.".format( 340 rule = str(go.label), 341 dep = str(dep.label), 342 edge = edge, 343 )) 344 345def _check_importpaths(ctx): 346 paths = [] 347 p = getattr(ctx.attr, "importpath", "") 348 if p: 349 paths.append(p) 350 p = getattr(ctx.attr, "importmap", "") 351 if p: 352 paths.append(p) 353 paths.extend(getattr(ctx.attr, "importpath_aliases", ())) 354 355 for p in paths: 356 if ":" in p: 357 fail("import path '%s' contains invalid character :" % p) 358 359def _infer_importpath(ctx): 360 DEFAULT_LIB = "go_default_library" 361 VENDOR_PREFIX = "/vendor/" 362 363 # Check if paths were explicitly set, either in this rule or in an 364 # embedded rule. 365 attr_importpath = getattr(ctx.attr, "importpath", "") 366 attr_importmap = getattr(ctx.attr, "importmap", "") 367 embed_importpath = "" 368 embed_importmap = "" 369 for embed in getattr(ctx.attr, "embed", []): 370 if GoLibrary not in embed: 371 continue 372 lib = embed[GoLibrary] 373 if lib.pathtype == EXPLICIT_PATH: 374 embed_importpath = lib.importpath 375 embed_importmap = lib.importmap 376 break 377 378 importpath = attr_importpath or embed_importpath 379 importmap = attr_importmap or embed_importmap or importpath 380 if importpath: 381 return importpath, importmap, EXPLICIT_PATH 382 383 # Guess an import path based on the directory structure 384 # This should only really be relied on for binaries 385 importpath = ctx.label.package 386 if ctx.label.name != DEFAULT_LIB and not importpath.endswith(ctx.label.name): 387 importpath += "/" + ctx.label.name 388 if importpath.rfind(VENDOR_PREFIX) != -1: 389 importpath = importpath[len(VENDOR_PREFIX) + importpath.rfind(VENDOR_PREFIX):] 390 if importpath.startswith("/"): 391 importpath = importpath[1:] 392 return importpath, importpath, INFERRED_PATH 393 394def go_context(ctx, attr = None): 395 """Returns an API used to build Go code. 396 397 See /go/toolchains.rst#go-context 398 """ 399 if not attr: 400 attr = ctx.attr 401 toolchain = ctx.toolchains[GO_TOOLCHAIN] 402 cgo_context_info = None 403 go_config_info = None 404 stdlib = None 405 coverdata = None 406 nogo = None 407 if hasattr(attr, "_go_context_data"): 408 go_context_data = _flatten_possibly_transitioned_attr(attr._go_context_data) 409 if CgoContextInfo in go_context_data: 410 cgo_context_info = go_context_data[CgoContextInfo] 411 go_config_info = go_context_data[GoConfigInfo] 412 stdlib = go_context_data[GoStdLib] 413 coverdata = go_context_data[GoContextInfo].coverdata 414 nogo = go_context_data[GoContextInfo].nogo 415 if getattr(attr, "_cgo_context_data", None) and CgoContextInfo in attr._cgo_context_data: 416 cgo_context_info = attr._cgo_context_data[CgoContextInfo] 417 if getattr(attr, "cgo_context_data", None) and CgoContextInfo in attr.cgo_context_data: 418 cgo_context_info = attr.cgo_context_data[CgoContextInfo] 419 if hasattr(attr, "_go_config"): 420 go_config_info = attr._go_config[GoConfigInfo] 421 if hasattr(attr, "_stdlib"): 422 stdlib = _flatten_possibly_transitioned_attr(attr._stdlib)[GoStdLib] 423 424 mode = get_mode(ctx, toolchain, cgo_context_info, go_config_info) 425 tags = mode.tags 426 binary = toolchain.sdk.go 427 428 if stdlib: 429 goroot = stdlib.root_file.dirname 430 else: 431 goroot = toolchain.sdk.root_file.dirname 432 433 env = { 434 "GOARCH": mode.goarch, 435 "GOOS": mode.goos, 436 "GOEXPERIMENT": ",".join(toolchain.sdk.experiments), 437 "GOROOT": goroot, 438 "GOROOT_FINAL": "GOROOT", 439 "CGO_ENABLED": "0" if mode.pure else "1", 440 441 # If we use --action_env=GOPATH, or in other cases where environment 442 # variables are passed through to this builder, the SDK build will try 443 # to write to that GOPATH (e.g. for x/net/nettest). This will fail if 444 # the GOPATH is on a read-only mount, and is generally a bad idea. 445 # Explicitly clear this environment variable to ensure that doesn't 446 # happen. See #2291 for more information. 447 "GOPATH": "", 448 } 449 450 # The level of support is determined by the platform constraints in 451 # //go/constraints/amd64. 452 # See https://github.com/golang/go/wiki/MinimumRequirements#amd64 453 if mode.amd64: 454 env["GOAMD64"] = mode.amd64 455 if not cgo_context_info: 456 crosstool = [] 457 cgo_tools = None 458 else: 459 env.update(cgo_context_info.env) 460 crosstool = cgo_context_info.crosstool 461 462 # Add C toolchain directories to PATH. 463 # On ARM, go tool link uses some features of gcc to complete its work, 464 # so PATH is needed on ARM. 465 path_set = {} 466 if "PATH" in env: 467 for p in env["PATH"].split(ctx.configuration.host_path_separator): 468 path_set[p] = None 469 cgo_tools = cgo_context_info.cgo_tools 470 tool_paths = [ 471 cgo_tools.c_compiler_path, 472 cgo_tools.ld_executable_path, 473 cgo_tools.ld_static_lib_path, 474 cgo_tools.ld_dynamic_lib_path, 475 ] 476 for tool_path in tool_paths: 477 tool_dir, _, _ = tool_path.rpartition("/") 478 path_set[tool_dir] = None 479 paths = sorted(path_set.keys()) 480 if ctx.configuration.host_path_separator == ":": 481 # HACK: ":" is a proxy for a UNIX-like host. 482 # The tools returned above may be bash scripts that reference commands 483 # in directories we might not otherwise include. For example, 484 # on macOS, wrapped_ar calls dirname. 485 if "/bin" not in path_set: 486 paths.append("/bin") 487 if "/usr/bin" not in path_set: 488 paths.append("/usr/bin") 489 env["PATH"] = ctx.configuration.host_path_separator.join(paths) 490 491 # TODO(jayconrod): remove this. It's way too broad. Everything should 492 # depend on more specific lists. 493 sdk_files = ([toolchain.sdk.go] + 494 toolchain.sdk.srcs + 495 toolchain.sdk.headers + 496 toolchain.sdk.libs + 497 toolchain.sdk.tools) 498 499 _check_importpaths(ctx) 500 importpath, importmap, pathtype = _infer_importpath(ctx) 501 importpath_aliases = tuple(getattr(attr, "importpath_aliases", ())) 502 503 return struct( 504 # Fields 505 toolchain = toolchain, 506 sdk = toolchain.sdk, 507 mode = mode, 508 root = goroot, 509 go = binary, 510 stdlib = stdlib, 511 sdk_root = toolchain.sdk.root_file, 512 sdk_files = sdk_files, 513 sdk_tools = toolchain.sdk.tools, 514 actions = ctx.actions, 515 exe_extension = goos_to_extension(mode.goos), 516 shared_extension = goos_to_shared_extension(mode.goos), 517 crosstool = crosstool, 518 package_list = toolchain.sdk.package_list, 519 importpath = importpath, 520 importmap = importmap, 521 importpath_aliases = importpath_aliases, 522 pathtype = pathtype, 523 cgo_tools = cgo_tools, 524 nogo = nogo, 525 coverdata = coverdata, 526 coverage_enabled = ctx.configuration.coverage_enabled, 527 coverage_instrumented = ctx.coverage_instrumented(), 528 env = env, 529 tags = tags, 530 stamp = mode.stamp, 531 label = ctx.label, 532 cover_format = mode.cover_format, 533 # Action generators 534 archive = toolchain.actions.archive, 535 binary = toolchain.actions.binary, 536 link = toolchain.actions.link, 537 538 # Helpers 539 args = _new_args, # deprecated 540 builder_args = _builder_args, 541 tool_args = _tool_args, 542 new_library = _new_library, 543 library_to_source = _library_to_source, 544 declare_file = _declare_file, 545 declare_directory = _declare_directory, 546 547 # Private 548 # TODO: All uses of this should be removed 549 _ctx = ctx, 550 ) 551 552def _go_context_data_impl(ctx): 553 if "race" in ctx.features: 554 print("WARNING: --features=race is no longer supported. Use --@io_bazel_rules_go//go/config:race instead.") 555 if "msan" in ctx.features: 556 print("WARNING: --features=msan is no longer supported. Use --@io_bazel_rules_go//go/config:msan instead.") 557 nogo = ctx.files.nogo[0] if ctx.files.nogo else None 558 providers = [ 559 GoContextInfo( 560 coverdata = ctx.attr.coverdata[GoArchive], 561 nogo = nogo, 562 ), 563 ctx.attr.stdlib[GoStdLib], 564 ctx.attr.go_config[GoConfigInfo], 565 ] 566 if ctx.attr.cgo_context_data and CgoContextInfo in ctx.attr.cgo_context_data: 567 providers.append(ctx.attr.cgo_context_data[CgoContextInfo]) 568 return providers 569 570go_context_data = rule( 571 _go_context_data_impl, 572 attrs = { 573 "cgo_context_data": attr.label(), 574 "coverdata": attr.label( 575 mandatory = True, 576 providers = [GoArchive], 577 ), 578 "go_config": attr.label( 579 mandatory = True, 580 providers = [GoConfigInfo], 581 ), 582 "nogo": attr.label( 583 mandatory = True, 584 cfg = "exec", 585 ), 586 "stdlib": attr.label( 587 mandatory = True, 588 providers = [GoStdLib], 589 ), 590 "_allowlist_function_transition": attr.label( 591 default = "@bazel_tools//tools/allowlists/function_transition_allowlist", 592 ), 593 }, 594 doc = """go_context_data gathers information about the build configuration. 595 It is a common dependency of all Go targets.""", 596 toolchains = [GO_TOOLCHAIN], 597 cfg = request_nogo_transition, 598) 599 600def _cgo_context_data_impl(ctx): 601 # TODO(jayconrod): find a way to get a list of files that comprise the 602 # toolchain (to be inputs into actions that need it). 603 # ctx.files._cc_toolchain won't work when cc toolchain resolution 604 # is switched on. 605 cc_toolchain = find_cpp_toolchain(ctx) 606 if cc_toolchain.compiler in _UNSUPPORTED_C_COMPILERS: 607 return [] 608 609 feature_configuration = cc_common.configure_features( 610 ctx = ctx, 611 cc_toolchain = cc_toolchain, 612 requested_features = ctx.features, 613 unsupported_features = ctx.disabled_features + _UNSUPPORTED_FEATURES, 614 ) 615 616 # TODO(jayconrod): keep the environment separate for different actions. 617 env = {} 618 619 c_compile_variables = cc_common.create_compile_variables( 620 feature_configuration = feature_configuration, 621 cc_toolchain = cc_toolchain, 622 ) 623 c_compiler_path = cc_common.get_tool_for_action( 624 feature_configuration = feature_configuration, 625 action_name = C_COMPILE_ACTION_NAME, 626 ) 627 c_compile_options = _filter_options( 628 cc_common.get_memory_inefficient_command_line( 629 feature_configuration = feature_configuration, 630 action_name = C_COMPILE_ACTION_NAME, 631 variables = c_compile_variables, 632 ) + ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts, 633 _COMPILER_OPTIONS_DENYLIST, 634 ) 635 env.update(cc_common.get_environment_variables( 636 feature_configuration = feature_configuration, 637 action_name = C_COMPILE_ACTION_NAME, 638 variables = c_compile_variables, 639 )) 640 641 cxx_compile_variables = cc_common.create_compile_variables( 642 feature_configuration = feature_configuration, 643 cc_toolchain = cc_toolchain, 644 ) 645 cxx_compile_options = _filter_options( 646 cc_common.get_memory_inefficient_command_line( 647 feature_configuration = feature_configuration, 648 action_name = CPP_COMPILE_ACTION_NAME, 649 variables = cxx_compile_variables, 650 ) + ctx.fragments.cpp.copts + ctx.fragments.cpp.cxxopts, 651 _COMPILER_OPTIONS_DENYLIST, 652 ) 653 env.update(cc_common.get_environment_variables( 654 feature_configuration = feature_configuration, 655 action_name = CPP_COMPILE_ACTION_NAME, 656 variables = cxx_compile_variables, 657 )) 658 659 objc_compile_variables = cc_common.create_compile_variables( 660 feature_configuration = feature_configuration, 661 cc_toolchain = cc_toolchain, 662 ) 663 objc_compile_options = _filter_options( 664 cc_common.get_memory_inefficient_command_line( 665 feature_configuration = feature_configuration, 666 action_name = OBJC_COMPILE_ACTION_NAME, 667 variables = objc_compile_variables, 668 ), 669 _COMPILER_OPTIONS_DENYLIST, 670 ) 671 env.update(cc_common.get_environment_variables( 672 feature_configuration = feature_configuration, 673 action_name = OBJC_COMPILE_ACTION_NAME, 674 variables = objc_compile_variables, 675 )) 676 677 objcxx_compile_variables = cc_common.create_compile_variables( 678 feature_configuration = feature_configuration, 679 cc_toolchain = cc_toolchain, 680 ) 681 objcxx_compile_options = _filter_options( 682 cc_common.get_memory_inefficient_command_line( 683 feature_configuration = feature_configuration, 684 action_name = OBJCPP_COMPILE_ACTION_NAME, 685 variables = objcxx_compile_variables, 686 ), 687 _COMPILER_OPTIONS_DENYLIST, 688 ) 689 env.update(cc_common.get_environment_variables( 690 feature_configuration = feature_configuration, 691 action_name = OBJCPP_COMPILE_ACTION_NAME, 692 variables = objcxx_compile_variables, 693 )) 694 695 ld_executable_variables = cc_common.create_link_variables( 696 feature_configuration = feature_configuration, 697 cc_toolchain = cc_toolchain, 698 is_linking_dynamic_library = False, 699 ) 700 ld_executable_path = cc_common.get_tool_for_action( 701 feature_configuration = feature_configuration, 702 action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, 703 ) 704 ld_executable_options = _filter_options( 705 cc_common.get_memory_inefficient_command_line( 706 feature_configuration = feature_configuration, 707 action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, 708 variables = ld_executable_variables, 709 ) + ctx.fragments.cpp.linkopts, 710 _LINKER_OPTIONS_DENYLIST, 711 ) 712 env.update(cc_common.get_environment_variables( 713 feature_configuration = feature_configuration, 714 action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, 715 variables = ld_executable_variables, 716 )) 717 718 # We don't collect options for static libraries. Go always links with 719 # "ar" in "c-archive" mode. We can set the ar executable path with 720 # -extar, but the options are hard-coded to something like -q -c -s. 721 ld_static_lib_variables = cc_common.create_link_variables( 722 feature_configuration = feature_configuration, 723 cc_toolchain = cc_toolchain, 724 is_linking_dynamic_library = False, 725 ) 726 ld_static_lib_path = cc_common.get_tool_for_action( 727 feature_configuration = feature_configuration, 728 action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, 729 ) 730 env.update(cc_common.get_environment_variables( 731 feature_configuration = feature_configuration, 732 action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, 733 variables = ld_static_lib_variables, 734 )) 735 736 ld_dynamic_lib_variables = cc_common.create_link_variables( 737 feature_configuration = feature_configuration, 738 cc_toolchain = cc_toolchain, 739 is_linking_dynamic_library = True, 740 ) 741 ld_dynamic_lib_path = cc_common.get_tool_for_action( 742 feature_configuration = feature_configuration, 743 action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, 744 ) 745 ld_dynamic_lib_options = _filter_options( 746 cc_common.get_memory_inefficient_command_line( 747 feature_configuration = feature_configuration, 748 action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, 749 variables = ld_dynamic_lib_variables, 750 ) + ctx.fragments.cpp.linkopts, 751 _LINKER_OPTIONS_DENYLIST, 752 ) 753 754 env.update(cc_common.get_environment_variables( 755 feature_configuration = feature_configuration, 756 action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, 757 variables = ld_dynamic_lib_variables, 758 )) 759 760 tags = [] 761 if "gotags" in ctx.var: 762 tags = ctx.var["gotags"].split(",") 763 apple_ensure_options( 764 ctx, 765 env, 766 tags, 767 (c_compile_options, cxx_compile_options, objc_compile_options, objcxx_compile_options), 768 (ld_executable_options, ld_dynamic_lib_options), 769 cc_toolchain.target_gnu_system_name, 770 ) 771 772 return [CgoContextInfo( 773 crosstool = cc_toolchain.all_files.to_list(), 774 tags = tags, 775 env = env, 776 cgo_tools = struct( 777 cc_toolchain = cc_toolchain, 778 feature_configuration = feature_configuration, 779 c_compiler_path = c_compiler_path, 780 c_compile_options = c_compile_options, 781 cxx_compile_options = cxx_compile_options, 782 objc_compile_options = objc_compile_options, 783 objcxx_compile_options = objcxx_compile_options, 784 ld_executable_path = ld_executable_path, 785 ld_executable_options = ld_executable_options, 786 ld_static_lib_path = ld_static_lib_path, 787 ld_dynamic_lib_path = ld_dynamic_lib_path, 788 ld_dynamic_lib_options = ld_dynamic_lib_options, 789 ), 790 )] 791 792cgo_context_data = rule( 793 implementation = _cgo_context_data_impl, 794 attrs = { 795 "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"), 796 "_xcode_config": attr.label( 797 default = "@bazel_tools//tools/osx:current_xcode_config", 798 ), 799 }, 800 toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], 801 fragments = ["apple", "cpp"], 802 doc = """Collects information about the C/C++ toolchain. The C/C++ toolchain 803 is needed to build cgo code, but is generally optional. Rules can't have 804 optional toolchains, so instead, we have an optional dependency on this 805 rule.""", 806) 807 808def _cgo_context_data_proxy_impl(ctx): 809 if ctx.attr.actual and CgoContextInfo in ctx.attr.actual: 810 return [ctx.attr.actual[CgoContextInfo]] 811 return [] 812 813cgo_context_data_proxy = rule( 814 implementation = _cgo_context_data_proxy_impl, 815 attrs = { 816 "actual": attr.label(), 817 }, 818 doc = """Conditionally depends on cgo_context_data and forwards it provider. 819 820 Useful in situations where select cannot be used, like attribute defaults. 821 """, 822) 823 824def _go_config_impl(ctx): 825 return [GoConfigInfo( 826 static = ctx.attr.static[BuildSettingInfo].value, 827 race = ctx.attr.race[BuildSettingInfo].value, 828 msan = ctx.attr.msan[BuildSettingInfo].value, 829 pure = ctx.attr.pure[BuildSettingInfo].value, 830 strip = ctx.attr.strip, 831 debug = ctx.attr.debug[BuildSettingInfo].value, 832 linkmode = ctx.attr.linkmode[BuildSettingInfo].value, 833 gc_linkopts = ctx.attr.gc_linkopts[BuildSettingInfo].value, 834 tags = ctx.attr.gotags[BuildSettingInfo].value, 835 stamp = ctx.attr.stamp, 836 cover_format = ctx.attr.cover_format[BuildSettingInfo].value, 837 gc_goopts = ctx.attr.gc_goopts[BuildSettingInfo].value, 838 amd64 = ctx.attr.amd64, 839 )] 840 841go_config = rule( 842 implementation = _go_config_impl, 843 attrs = { 844 "static": attr.label( 845 mandatory = True, 846 providers = [BuildSettingInfo], 847 ), 848 "race": attr.label( 849 mandatory = True, 850 providers = [BuildSettingInfo], 851 ), 852 "msan": attr.label( 853 mandatory = True, 854 providers = [BuildSettingInfo], 855 ), 856 "pure": attr.label( 857 mandatory = True, 858 providers = [BuildSettingInfo], 859 ), 860 "strip": attr.bool(mandatory = True), 861 "debug": attr.label( 862 mandatory = True, 863 providers = [BuildSettingInfo], 864 ), 865 "linkmode": attr.label( 866 mandatory = True, 867 providers = [BuildSettingInfo], 868 ), 869 "gc_linkopts": attr.label( 870 mandatory = True, 871 providers = [BuildSettingInfo], 872 ), 873 "gotags": attr.label( 874 mandatory = True, 875 providers = [BuildSettingInfo], 876 ), 877 "stamp": attr.bool(mandatory = True), 878 "cover_format": attr.label( 879 mandatory = True, 880 providers = [BuildSettingInfo], 881 ), 882 "gc_goopts": attr.label( 883 mandatory = True, 884 providers = [BuildSettingInfo], 885 ), 886 "amd64": attr.string(), 887 }, 888 provides = [GoConfigInfo], 889 doc = """Collects information about build settings in the current 890 configuration. Rules may depend on this instead of depending on all 891 the build settings directly.""", 892) 893 894def _expand_opts(go, attribute_name, opts): 895 return [go._ctx.expand_make_variables(attribute_name, opt, {}) for opt in opts] 896 897def _expand_location(go, attr, s): 898 return go._ctx.expand_location(s, getattr(attr, "data", [])) 899 900_LIST_TYPE = type([]) 901 902# Used to get attribute values which may have been transitioned. 903# Transitioned attributes end up as lists. 904# We never use split-transitions, so we always expect exactly one element in those lists. 905# But if the attribute wasn't transitioned, it won't be a list. 906def _flatten_possibly_transitioned_attr(maybe_list): 907 if type(maybe_list) == _LIST_TYPE: 908 if len(maybe_list) == 1: 909 return maybe_list[0] 910 else: 911 fail("Expected exactly one element in list but got {}".format(maybe_list)) 912 return maybe_list 913