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