...

Text file src/github.com/bazelbuild/rules_go/go/private/context.bzl

Documentation: github.com/bazelbuild/rules_go/go/private

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

View as plain text