| # Copyright 2017 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| load( |
| "@bazel_tools//tools/cpp:toolchain_utils.bzl", |
| "find_cpp_toolchain", |
| ) |
| load( |
| "@bazel_tools//tools/build_defs/cc:action_names.bzl", |
| "CPP_COMPILE_ACTION_NAME", |
| "CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME", |
| "CPP_LINK_EXECUTABLE_ACTION_NAME", |
| "CPP_LINK_STATIC_LIBRARY_ACTION_NAME", |
| "C_COMPILE_ACTION_NAME", |
| "OBJCPP_COMPILE_ACTION_NAME", |
| "OBJC_COMPILE_ACTION_NAME", |
| ) |
| load( |
| ":go_toolchain.bzl", |
| "GO_TOOLCHAIN", |
| ) |
| load( |
| ":providers.bzl", |
| "CgoContextInfo", |
| "EXPLICIT_PATH", |
| "EXPORT_PATH", |
| "GoArchive", |
| "GoConfigInfo", |
| "GoContextInfo", |
| "GoLibrary", |
| "GoSource", |
| "GoStdLib", |
| "INFERRED_PATH", |
| "get_source", |
| ) |
| load( |
| ":mode.bzl", |
| "get_mode", |
| "installsuffix", |
| ) |
| load( |
| ":common.bzl", |
| "as_iterable", |
| "goos_to_extension", |
| "goos_to_shared_extension", |
| "is_struct", |
| ) |
| load( |
| "//go/platform:apple.bzl", |
| "apple_ensure_options", |
| ) |
| load( |
| "@bazel_skylib//lib:paths.bzl", |
| "paths", |
| ) |
| load( |
| "@bazel_skylib//rules:common_settings.bzl", |
| "BuildSettingInfo", |
| ) |
| load( |
| "//go/private/rules:transition.bzl", |
| "request_nogo_transition", |
| ) |
| |
| _COMPILER_OPTIONS_BLACKLIST = { |
| # cgo parses the error messages from the compiler. It can't handle colors. |
| # Ignore both variants of the diagnostics color flag. |
| "-fcolor-diagnostics": None, |
| "-fdiagnostics-color": None, |
| |
| # cgo also wants to see all the errors when it is testing the compiler. |
| # fmax-errors limits that and causes build failures. |
| "-fmax-errors=": None, |
| "-Wall": None, |
| |
| # Symbols are needed by Go, so keep them |
| "-g0": None, |
| |
| # Don't compile generated cgo code with coverage. If we do an internal |
| # link, we may have undefined references to coverage functions. |
| "--coverage": None, |
| "-ftest-coverage": None, |
| "-fprofile-arcs": None, |
| } |
| |
| _LINKER_OPTIONS_BLACKLIST = { |
| "-Wl,--gc-sections": None, |
| } |
| |
| _UNSUPPORTED_FEATURES = [ |
| # These toolchain features require special rule support and will thus break |
| # with CGo. |
| # Taken from https://github.com/bazelbuild/rules_rust/blob/521e649ff44e9711fe3c45b0ec1e792f7e1d361e/rust/private/utils.bzl#L20. |
| "thin_lto", |
| "module_maps", |
| "use_header_modules", |
| "fdo_instrument", |
| "fdo_optimize", |
| ] |
| |
| def _match_option(option, pattern): |
| if pattern.endswith("="): |
| return option.startswith(pattern) |
| else: |
| return option == pattern |
| |
| def _filter_options(options, blacklist): |
| return [ |
| option |
| for option in options |
| if not any([_match_option(option, pattern) for pattern in blacklist]) |
| ] |
| |
| def _child_name(go, path, ext, name): |
| if not name: |
| name = go.label.name |
| if path or not ext: |
| # The '_' avoids collisions with another file matching the label name. |
| # For example, hello and hello/testmain.go. |
| name += "_" |
| if path: |
| name += "/" + path |
| if ext: |
| name += ext |
| return name |
| |
| def _declare_file(go, path = "", ext = "", name = ""): |
| return go.actions.declare_file(_child_name(go, path, ext, name)) |
| |
| def _declare_directory(go, path = "", ext = "", name = ""): |
| return go.actions.declare_directory(_child_name(go, path, ext, name)) |
| |
| def _new_args(go): |
| # TODO(jayconrod): print warning. |
| return go.builder_args(go) |
| |
| def _builder_args(go, command = None): |
| args = go.actions.args() |
| args.use_param_file("-param=%s") |
| args.set_param_file_format("shell") |
| if command: |
| args.add(command) |
| args.add("-sdk", go.sdk.root_file.dirname) |
| args.add("-installsuffix", installsuffix(go.mode)) |
| args.add_joined("-tags", go.tags, join_with = ",") |
| return args |
| |
| def _tool_args(go): |
| args = go.actions.args() |
| args.use_param_file("-param=%s") |
| args.set_param_file_format("shell") |
| return args |
| |
| def _new_library(go, name = None, importpath = None, resolver = None, importable = True, testfilter = None, is_main = False, **kwargs): |
| if not importpath: |
| importpath = go.importpath |
| importmap = go.importmap |
| else: |
| importmap = importpath |
| pathtype = go.pathtype |
| if not importable and pathtype == EXPLICIT_PATH: |
| pathtype = EXPORT_PATH |
| |
| return GoLibrary( |
| name = go.label.name if not name else name, |
| label = go.label, |
| importpath = importpath, |
| importmap = importmap, |
| importpath_aliases = go.importpath_aliases, |
| pathtype = pathtype, |
| resolve = resolver, |
| testfilter = testfilter, |
| is_main = is_main, |
| **kwargs |
| ) |
| |
| def _merge_embed(source, embed): |
| s = get_source(embed) |
| source["srcs"] = s.srcs + source["srcs"] |
| source["orig_srcs"] = s.orig_srcs + source["orig_srcs"] |
| source["orig_src_map"].update(s.orig_src_map) |
| source["embedsrcs"] = source["embedsrcs"] + s.embedsrcs |
| source["cover"] = source["cover"] + s.cover |
| source["deps"] = source["deps"] + s.deps |
| source["x_defs"].update(s.x_defs) |
| source["gc_goopts"] = source["gc_goopts"] + s.gc_goopts |
| source["runfiles"] = source["runfiles"].merge(s.runfiles) |
| if s.cgo and source["cgo"]: |
| fail("multiple libraries with cgo enabled") |
| source["cgo"] = source["cgo"] or s.cgo |
| source["cdeps"] = source["cdeps"] or s.cdeps |
| source["cppopts"] = source["cppopts"] or s.cppopts |
| source["copts"] = source["copts"] or s.copts |
| source["cxxopts"] = source["cxxopts"] or s.cxxopts |
| source["clinkopts"] = source["clinkopts"] or s.clinkopts |
| source["cgo_deps"] = source["cgo_deps"] + s.cgo_deps |
| source["cgo_exports"] = source["cgo_exports"] + s.cgo_exports |
| |
| def _dedup_deps(deps): |
| """Returns a list of deps without duplicate import paths. |
| |
| Earlier targets take precedence over later targets. This is intended to |
| allow an embedding library to override the dependencies of its |
| embedded libraries. |
| |
| Args: |
| deps: an iterable containing either Targets or GoArchives. |
| """ |
| deduped_deps = [] |
| importpaths = {} |
| for dep in deps: |
| if hasattr(dep, "data") and hasattr(dep.data, "importpath"): |
| importpath = dep.data.importpath |
| else: |
| importpath = dep[GoLibrary].importpath |
| if importpath in importpaths: |
| continue |
| importpaths[importpath] = None |
| deduped_deps.append(dep) |
| return deduped_deps |
| |
| def _library_to_source(go, attr, library, coverage_instrumented): |
| #TODO: stop collapsing a depset in this line... |
| attr_srcs = [f for t in getattr(attr, "srcs", []) for f in as_iterable(t.files)] |
| generated_srcs = getattr(library, "srcs", []) |
| srcs = attr_srcs + generated_srcs |
| embedsrcs = [f for t in getattr(attr, "embedsrcs", []) for f in as_iterable(t.files)] |
| source = { |
| "library": library, |
| "mode": go.mode, |
| "srcs": srcs, |
| "orig_srcs": srcs, |
| "orig_src_map": {}, |
| "cover": [], |
| "embedsrcs": embedsrcs, |
| "x_defs": {}, |
| "deps": getattr(attr, "deps", []), |
| "gc_goopts": _expand_opts(go, "gc_goopts", getattr(attr, "gc_goopts", [])), |
| "runfiles": _collect_runfiles(go, getattr(attr, "data", []), getattr(attr, "deps", [])), |
| "cgo": getattr(attr, "cgo", False), |
| "cdeps": getattr(attr, "cdeps", []), |
| "cppopts": _expand_opts(go, "cppopts", getattr(attr, "cppopts", [])), |
| "copts": _expand_opts(go, "copts", getattr(attr, "copts", [])), |
| "cxxopts": _expand_opts(go, "cxxopts", getattr(attr, "cxxopts", [])), |
| "clinkopts": _expand_opts(go, "clinkopts", getattr(attr, "clinkopts", [])), |
| "cgo_deps": [], |
| "cgo_exports": [], |
| "cc_info": None, |
| } |
| if coverage_instrumented: |
| source["cover"] = attr_srcs |
| for dep in source["deps"]: |
| _check_binary_dep(go, dep, "deps") |
| for e in getattr(attr, "embed", []): |
| _check_binary_dep(go, e, "embed") |
| _merge_embed(source, e) |
| source["deps"] = _dedup_deps(source["deps"]) |
| x_defs = source["x_defs"] |
| for k, v in getattr(attr, "x_defs", {}).items(): |
| if "." not in k: |
| k = "{}.{}".format(library.importmap, k) |
| x_defs[k] = v |
| source["x_defs"] = x_defs |
| if not source["cgo"]: |
| for k in ("cdeps", "cppopts", "copts", "cxxopts", "clinkopts"): |
| if getattr(attr, k, None): |
| fail(k + " set without cgo = True") |
| for f in source["srcs"]: |
| # This check won't report directory sources that contain C/C++ |
| # sources. compilepkg will catch these instead. |
| if f.extension in ("c", "cc", "cxx", "cpp", "hh", "hpp", "hxx"): |
| fail("source {} has C/C++ extension, but cgo was not enabled (set 'cgo = True')".format(f.path)) |
| if library.resolve: |
| library.resolve(go, attr, source, _merge_embed) |
| source["cc_info"] = _collect_cc_infos(source["deps"], source["cdeps"]) |
| return GoSource(**source) |
| |
| def _collect_runfiles(go, data, deps): |
| """Builds a set of runfiles from the deps and data attributes. |
| |
| srcs and their runfiles are not included.""" |
| files = depset(transitive = [t[DefaultInfo].files for t in data]) |
| runfiles = go._ctx.runfiles(transitive_files = files) |
| for t in data: |
| runfiles = runfiles.merge(t[DefaultInfo].data_runfiles) |
| for t in deps: |
| runfiles = runfiles.merge(get_source(t).runfiles) |
| return runfiles |
| |
| def _collect_cc_infos(deps, cdeps): |
| cc_infos = [] |
| for dep in cdeps: |
| if CcInfo in dep: |
| cc_infos.append(dep[CcInfo]) |
| for dep in deps: |
| # dep may be a struct, which doesn't support indexing by providers. |
| if is_struct(dep): |
| continue |
| if GoSource in dep: |
| cc_infos.append(dep[GoSource].cc_info) |
| return cc_common.merge_cc_infos(cc_infos = cc_infos) |
| |
| def _check_binary_dep(go, dep, edge): |
| """Checks that this rule doesn't depend on a go_binary or go_test. |
| |
| go_binary and go_test may return provides with useful information for other |
| rules (like go_path), but go_binary and go_test may not depend on other |
| go_binary and go_binary targets. Their dependencies may be built in |
| different modes, resulting in conflicts and opaque errors. |
| """ |
| if (type(dep) == "Target" and |
| DefaultInfo in dep and |
| getattr(dep[DefaultInfo], "files_to_run", None) and |
| dep[DefaultInfo].files_to_run.executable): |
| fail("rule {rule} depends on executable {dep} via {edge}. This is not safe for cross-compilation. Depend on go_library instead.".format( |
| rule = str(go.label), |
| dep = str(dep.label), |
| edge = edge, |
| )) |
| |
| def _check_importpaths(ctx): |
| paths = [] |
| p = getattr(ctx.attr, "importpath", "") |
| if p: |
| paths.append(p) |
| p = getattr(ctx.attr, "importmap", "") |
| if p: |
| paths.append(p) |
| paths.extend(getattr(ctx.attr, "importpath_aliases", ())) |
| |
| for p in paths: |
| if ":" in p: |
| fail("import path '%s' contains invalid character :" % p) |
| |
| def _infer_importpath(ctx): |
| DEFAULT_LIB = "go_default_library" |
| VENDOR_PREFIX = "/vendor/" |
| |
| # Check if paths were explicitly set, either in this rule or in an |
| # embedded rule. |
| attr_importpath = getattr(ctx.attr, "importpath", "") |
| attr_importmap = getattr(ctx.attr, "importmap", "") |
| embed_importpath = "" |
| embed_importmap = "" |
| for embed in getattr(ctx.attr, "embed", []): |
| if GoLibrary not in embed: |
| continue |
| lib = embed[GoLibrary] |
| if lib.pathtype == EXPLICIT_PATH: |
| embed_importpath = lib.importpath |
| embed_importmap = lib.importmap |
| break |
| |
| importpath = attr_importpath or embed_importpath |
| importmap = attr_importmap or embed_importmap or importpath |
| if importpath: |
| return importpath, importmap, EXPLICIT_PATH |
| |
| # Guess an import path based on the directory structure |
| # This should only really be relied on for binaries |
| importpath = ctx.label.package |
| if ctx.label.name != DEFAULT_LIB and not importpath.endswith(ctx.label.name): |
| importpath += "/" + ctx.label.name |
| if importpath.rfind(VENDOR_PREFIX) != -1: |
| importpath = importpath[len(VENDOR_PREFIX) + importpath.rfind(VENDOR_PREFIX):] |
| if importpath.startswith("/"): |
| importpath = importpath[1:] |
| return importpath, importpath, INFERRED_PATH |
| |
| def go_context(ctx, attr = None): |
| """Returns an API used to build Go code. |
| |
| See /go/toolchains.rst#go-context |
| """ |
| if not attr: |
| attr = ctx.attr |
| toolchain = ctx.toolchains[GO_TOOLCHAIN] |
| cgo_context_info = None |
| go_config_info = None |
| stdlib = None |
| coverdata = None |
| nogo = None |
| if hasattr(attr, "_go_context_data"): |
| go_context_data = _flatten_possibly_transitioned_attr(attr._go_context_data) |
| if CgoContextInfo in go_context_data: |
| cgo_context_info = go_context_data[CgoContextInfo] |
| go_config_info = go_context_data[GoConfigInfo] |
| stdlib = go_context_data[GoStdLib] |
| coverdata = go_context_data[GoContextInfo].coverdata |
| nogo = go_context_data[GoContextInfo].nogo |
| if getattr(attr, "_cgo_context_data", None) and CgoContextInfo in attr._cgo_context_data: |
| cgo_context_info = attr._cgo_context_data[CgoContextInfo] |
| if getattr(attr, "cgo_context_data", None) and CgoContextInfo in attr.cgo_context_data: |
| cgo_context_info = attr.cgo_context_data[CgoContextInfo] |
| if hasattr(attr, "_go_config"): |
| go_config_info = attr._go_config[GoConfigInfo] |
| if hasattr(attr, "_stdlib"): |
| stdlib = _flatten_possibly_transitioned_attr(attr._stdlib)[GoStdLib] |
| |
| mode = get_mode(ctx, toolchain, cgo_context_info, go_config_info) |
| tags = mode.tags |
| binary = toolchain.sdk.go |
| |
| if stdlib: |
| goroot = stdlib.root_file.dirname |
| else: |
| goroot = toolchain.sdk.root_file.dirname |
| |
| env = { |
| "GOARCH": mode.goarch, |
| "GOOS": mode.goos, |
| "GOROOT": goroot, |
| "GOROOT_FINAL": "GOROOT", |
| "CGO_ENABLED": "0" if mode.pure else "1", |
| |
| # If we use --action_env=GOPATH, or in other cases where environment |
| # variables are passed through to this builder, the SDK build will try |
| # to write to that GOPATH (e.g. for x/net/nettest). This will fail if |
| # the GOPATH is on a read-only mount, and is generally a bad idea. |
| # Explicitly clear this environment variable to ensure that doesn't |
| # happen. See #2291 for more information. |
| "GOPATH": "", |
| } |
| if mode.pure: |
| crosstool = [] |
| cgo_tools = None |
| else: |
| env.update(cgo_context_info.env) |
| crosstool = cgo_context_info.crosstool |
| |
| # Add C toolchain directories to PATH. |
| # On ARM, go tool link uses some features of gcc to complete its work, |
| # so PATH is needed on ARM. |
| path_set = {} |
| if "PATH" in env: |
| for p in env["PATH"].split(ctx.configuration.host_path_separator): |
| path_set[p] = None |
| cgo_tools = cgo_context_info.cgo_tools |
| tool_paths = [ |
| cgo_tools.c_compiler_path, |
| cgo_tools.ld_executable_path, |
| cgo_tools.ld_static_lib_path, |
| cgo_tools.ld_dynamic_lib_path, |
| ] |
| for tool_path in tool_paths: |
| tool_dir, _, _ = tool_path.rpartition("/") |
| path_set[tool_dir] = None |
| paths = sorted(path_set.keys()) |
| if ctx.configuration.host_path_separator == ":": |
| # HACK: ":" is a proxy for a UNIX-like host. |
| # The tools returned above may be bash scripts that reference commands |
| # in directories we might not otherwise include. For example, |
| # on macOS, wrapped_ar calls dirname. |
| if "/bin" not in path_set: |
| paths.append("/bin") |
| if "/usr/bin" not in path_set: |
| paths.append("/usr/bin") |
| env["PATH"] = ctx.configuration.host_path_separator.join(paths) |
| |
| # TODO(jayconrod): remove this. It's way too broad. Everything should |
| # depend on more specific lists. |
| sdk_files = ([toolchain.sdk.go] + |
| toolchain.sdk.srcs + |
| toolchain.sdk.headers + |
| toolchain.sdk.libs + |
| toolchain.sdk.tools) |
| |
| _check_importpaths(ctx) |
| importpath, importmap, pathtype = _infer_importpath(ctx) |
| importpath_aliases = tuple(getattr(attr, "importpath_aliases", ())) |
| |
| return struct( |
| # Fields |
| toolchain = toolchain, |
| sdk = toolchain.sdk, |
| mode = mode, |
| root = goroot, |
| go = binary, |
| stdlib = stdlib, |
| sdk_root = toolchain.sdk.root_file, |
| sdk_files = sdk_files, |
| sdk_tools = toolchain.sdk.tools, |
| actions = ctx.actions, |
| exe_extension = goos_to_extension(mode.goos), |
| shared_extension = goos_to_shared_extension(mode.goos), |
| crosstool = crosstool, |
| package_list = toolchain.sdk.package_list, |
| importpath = importpath, |
| importmap = importmap, |
| importpath_aliases = importpath_aliases, |
| pathtype = pathtype, |
| cgo_tools = cgo_tools, |
| nogo = nogo, |
| coverdata = coverdata, |
| coverage_enabled = ctx.configuration.coverage_enabled, |
| coverage_instrumented = ctx.coverage_instrumented(), |
| env = env, |
| tags = tags, |
| stamp = mode.stamp, |
| label = ctx.label, |
| cover_format = mode.cover_format, |
| |
| # Action generators |
| archive = toolchain.actions.archive, |
| asm = toolchain.actions.asm, |
| binary = toolchain.actions.binary, |
| compile = toolchain.actions.compile, |
| link = toolchain.actions.link, |
| pack = toolchain.actions.pack, |
| |
| # Helpers |
| args = _new_args, # deprecated |
| builder_args = _builder_args, |
| tool_args = _tool_args, |
| new_library = _new_library, |
| library_to_source = _library_to_source, |
| declare_file = _declare_file, |
| declare_directory = _declare_directory, |
| |
| # Private |
| # TODO: All uses of this should be removed |
| _ctx = ctx, |
| ) |
| |
| def _go_context_data_impl(ctx): |
| if "race" in ctx.features: |
| print("WARNING: --features=race is no longer supported. Use --@io_bazel_rules_go//go/config:race instead.") |
| if "msan" in ctx.features: |
| print("WARNING: --features=msan is no longer supported. Use --@io_bazel_rules_go//go/config:msan instead.") |
| coverdata = ctx.attr.coverdata[GoArchive] |
| nogo = ctx.files.nogo[0] if ctx.files.nogo else None |
| providers = [ |
| GoContextInfo( |
| coverdata = ctx.attr.coverdata[GoArchive], |
| nogo = nogo, |
| ), |
| ctx.attr.stdlib[GoStdLib], |
| ctx.attr.go_config[GoConfigInfo], |
| ] |
| if ctx.attr.cgo_context_data and CgoContextInfo in ctx.attr.cgo_context_data: |
| providers.append(ctx.attr.cgo_context_data[CgoContextInfo]) |
| return providers |
| |
| go_context_data = rule( |
| _go_context_data_impl, |
| attrs = { |
| "cgo_context_data": attr.label(), |
| "coverdata": attr.label( |
| mandatory = True, |
| providers = [GoArchive], |
| ), |
| "go_config": attr.label( |
| mandatory = True, |
| providers = [GoConfigInfo], |
| ), |
| "nogo": attr.label( |
| mandatory = True, |
| cfg = "exec", |
| ), |
| "stdlib": attr.label( |
| mandatory = True, |
| providers = [GoStdLib], |
| ), |
| "_whitelist_function_transition": attr.label( |
| default = "@bazel_tools//tools/whitelists/function_transition_whitelist", |
| ), |
| }, |
| doc = """go_context_data gathers information about the build configuration. |
| It is a common dependency of all Go targets.""", |
| toolchains = [GO_TOOLCHAIN], |
| cfg = request_nogo_transition, |
| ) |
| |
| def _cgo_context_data_impl(ctx): |
| # TODO(jayconrod): find a way to get a list of files that comprise the |
| # toolchain (to be inputs into actions that need it). |
| # ctx.files._cc_toolchain won't work when cc toolchain resolution |
| # is switched on. |
| cc_toolchain = find_cpp_toolchain(ctx) |
| feature_configuration = cc_common.configure_features( |
| ctx = ctx, |
| cc_toolchain = cc_toolchain, |
| requested_features = ctx.features, |
| unsupported_features = ctx.disabled_features + _UNSUPPORTED_FEATURES, |
| ) |
| |
| # TODO(jayconrod): keep the environment separate for different actions. |
| env = {} |
| |
| c_compile_variables = cc_common.create_compile_variables( |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| ) |
| c_compiler_path = cc_common.get_tool_for_action( |
| feature_configuration = feature_configuration, |
| action_name = C_COMPILE_ACTION_NAME, |
| ) |
| c_compile_options = _filter_options( |
| cc_common.get_memory_inefficient_command_line( |
| feature_configuration = feature_configuration, |
| action_name = C_COMPILE_ACTION_NAME, |
| variables = c_compile_variables, |
| ) + ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts, |
| _COMPILER_OPTIONS_BLACKLIST, |
| ) |
| env.update(cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = C_COMPILE_ACTION_NAME, |
| variables = c_compile_variables, |
| )) |
| |
| cxx_compile_variables = cc_common.create_compile_variables( |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| ) |
| cxx_compile_options = _filter_options( |
| cc_common.get_memory_inefficient_command_line( |
| feature_configuration = feature_configuration, |
| action_name = CPP_COMPILE_ACTION_NAME, |
| variables = cxx_compile_variables, |
| ) + ctx.fragments.cpp.copts + ctx.fragments.cpp.cxxopts, |
| _COMPILER_OPTIONS_BLACKLIST, |
| ) |
| env.update(cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = CPP_COMPILE_ACTION_NAME, |
| variables = cxx_compile_variables, |
| )) |
| |
| objc_compile_variables = cc_common.create_compile_variables( |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| ) |
| objc_compile_options = _filter_options( |
| cc_common.get_memory_inefficient_command_line( |
| feature_configuration = feature_configuration, |
| action_name = OBJC_COMPILE_ACTION_NAME, |
| variables = objc_compile_variables, |
| ), |
| _COMPILER_OPTIONS_BLACKLIST, |
| ) |
| env.update(cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = OBJC_COMPILE_ACTION_NAME, |
| variables = objc_compile_variables, |
| )) |
| |
| objcxx_compile_variables = cc_common.create_compile_variables( |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| ) |
| objcxx_compile_options = _filter_options( |
| cc_common.get_memory_inefficient_command_line( |
| feature_configuration = feature_configuration, |
| action_name = OBJCPP_COMPILE_ACTION_NAME, |
| variables = objcxx_compile_variables, |
| ), |
| _COMPILER_OPTIONS_BLACKLIST, |
| ) |
| env.update(cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = OBJCPP_COMPILE_ACTION_NAME, |
| variables = objcxx_compile_variables, |
| )) |
| |
| ld_executable_variables = cc_common.create_link_variables( |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| is_linking_dynamic_library = False, |
| ) |
| ld_executable_path = cc_common.get_tool_for_action( |
| feature_configuration = feature_configuration, |
| action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, |
| ) |
| ld_executable_options = _filter_options( |
| cc_common.get_memory_inefficient_command_line( |
| feature_configuration = feature_configuration, |
| action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, |
| variables = ld_executable_variables, |
| ) + ctx.fragments.cpp.linkopts, |
| _LINKER_OPTIONS_BLACKLIST, |
| ) |
| env.update(cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, |
| variables = ld_executable_variables, |
| )) |
| |
| # We don't collect options for static libraries. Go always links with |
| # "ar" in "c-archive" mode. We can set the ar executable path with |
| # -extar, but the options are hard-coded to something like -q -c -s. |
| ld_static_lib_variables = cc_common.create_link_variables( |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| is_linking_dynamic_library = False, |
| ) |
| ld_static_lib_path = cc_common.get_tool_for_action( |
| feature_configuration = feature_configuration, |
| action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, |
| ) |
| env.update(cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, |
| variables = ld_static_lib_variables, |
| )) |
| |
| ld_dynamic_lib_variables = cc_common.create_link_variables( |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| is_linking_dynamic_library = True, |
| ) |
| ld_dynamic_lib_path = cc_common.get_tool_for_action( |
| feature_configuration = feature_configuration, |
| action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, |
| ) |
| ld_dynamic_lib_options = _filter_options( |
| cc_common.get_memory_inefficient_command_line( |
| feature_configuration = feature_configuration, |
| action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, |
| variables = ld_dynamic_lib_variables, |
| ) + ctx.fragments.cpp.linkopts, |
| _LINKER_OPTIONS_BLACKLIST, |
| ) |
| |
| env.update(cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, |
| variables = ld_dynamic_lib_variables, |
| )) |
| |
| tags = [] |
| if "gotags" in ctx.var: |
| tags = ctx.var["gotags"].split(",") |
| apple_ensure_options( |
| ctx, |
| env, |
| tags, |
| (c_compile_options, cxx_compile_options, objc_compile_options, objcxx_compile_options), |
| (ld_executable_options, ld_dynamic_lib_options), |
| cc_toolchain.target_gnu_system_name, |
| ) |
| |
| return [CgoContextInfo( |
| crosstool = cc_toolchain.all_files.to_list(), |
| tags = tags, |
| env = env, |
| cgo_tools = struct( |
| cc_toolchain = cc_toolchain, |
| feature_configuration = feature_configuration, |
| c_compiler_path = c_compiler_path, |
| c_compile_options = c_compile_options, |
| cxx_compile_options = cxx_compile_options, |
| objc_compile_options = objc_compile_options, |
| objcxx_compile_options = objcxx_compile_options, |
| ld_executable_path = ld_executable_path, |
| ld_executable_options = ld_executable_options, |
| ld_static_lib_path = ld_static_lib_path, |
| ld_dynamic_lib_path = ld_dynamic_lib_path, |
| ld_dynamic_lib_options = ld_dynamic_lib_options, |
| ), |
| )] |
| |
| cgo_context_data = rule( |
| implementation = _cgo_context_data_impl, |
| attrs = { |
| "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"), |
| "_xcode_config": attr.label( |
| default = "@bazel_tools//tools/osx:current_xcode_config", |
| ), |
| }, |
| toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], |
| fragments = ["apple", "cpp"], |
| provides = [CgoContextInfo], |
| doc = """Collects information about the C/C++ toolchain. The C/C++ toolchain |
| is needed to build cgo code, but is generally optional. Rules can't have |
| optional toolchains, so instead, we have an optional dependency on this |
| rule.""", |
| ) |
| |
| def _cgo_context_data_proxy_impl(ctx): |
| return [ctx.attr.actual[CgoContextInfo]] if ctx.attr.actual else [] |
| |
| cgo_context_data_proxy = rule( |
| implementation = _cgo_context_data_proxy_impl, |
| attrs = { |
| "actual": attr.label(), |
| }, |
| doc = """Conditionally depends on cgo_context_data and forwards it provider. |
| |
| Useful in situations where select cannot be used, like attribute defaults. |
| """, |
| ) |
| |
| def _go_config_impl(ctx): |
| return [GoConfigInfo( |
| static = ctx.attr.static[BuildSettingInfo].value, |
| race = ctx.attr.race[BuildSettingInfo].value, |
| msan = ctx.attr.msan[BuildSettingInfo].value, |
| pure = ctx.attr.pure[BuildSettingInfo].value, |
| strip = ctx.attr.strip[BuildSettingInfo].value, |
| debug = ctx.attr.debug[BuildSettingInfo].value, |
| linkmode = ctx.attr.linkmode[BuildSettingInfo].value, |
| tags = ctx.attr.gotags[BuildSettingInfo].value, |
| stamp = ctx.attr.stamp, |
| cover_format = ctx.attr.cover_format[BuildSettingInfo].value, |
| )] |
| |
| go_config = rule( |
| implementation = _go_config_impl, |
| attrs = { |
| "static": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| "race": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| "msan": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| "pure": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| "strip": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| "debug": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| "linkmode": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| "gotags": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| "stamp": attr.bool(mandatory = True), |
| "cover_format": attr.label( |
| mandatory = True, |
| providers = [BuildSettingInfo], |
| ), |
| }, |
| provides = [GoConfigInfo], |
| doc = """Collects information about build settings in the current |
| configuration. Rules may depend on this instead of depending on all |
| the build settings directly.""", |
| ) |
| |
| def _expand_opts(go, attribute_name, opts): |
| return [go._ctx.expand_make_variables(attribute_name, opt, {}) for opt in opts] |
| |
| _LIST_TYPE = type([]) |
| |
| # Used to get attribute values which may have been transitioned. |
| # Transitioned attributes end up as lists. |
| # We never use split-transitions, so we always expect exactly one element in those lists. |
| # But if the attribute wasn't transitioned, it won't be a list. |
| def _flatten_possibly_transitioned_attr(maybe_list): |
| if type(maybe_list) == _LIST_TYPE: |
| if len(maybe_list) == 1: |
| return maybe_list[0] |
| else: |
| fail("Expected exactly one element in list but got {}".format(maybe_list)) |
| return maybe_list |