xref: /aosp_15_r20/external/bazelbuild-rules_go/go/private/context.bzl (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
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