text: # pylint: disable=g-bad-file-header
  # Copyright 2016 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.
  """Configuring the C++ toolchain on Unix platforms."""
  
  load(
      "@bazel_tools//tools/cpp:lib_cc_configure.bzl",
      "auto_configure_fail",
      "auto_configure_warning",
      "escape_string",
      "get_env_var",
      "get_starlark_list",
      "resolve_labels",
      "split_escaped",
      "which",
  )
  
  def _field(name, value):
      """Returns properly indented top level crosstool field."""
      if type(value) == "list":
          return "\n".join(["  " + name + ": '" + v + "'" for v in value])
      elif type(value) == "string":
          return "  " + name + ": '" + value + "'"
      else:
          auto_configure_fail("Unexpected field type: " + type(value))
          return ""
  
  def _uniq(iterable):
      """Remove duplicates from a list."""
  
      unique_elements = {element: None for element in iterable}
      return unique_elements.keys()
  
  def _prepare_include_path(repo_ctx, path):
      """Resolve and sanitize include path before outputting it into the crosstool.
  
      Args:
        repo_ctx: repository_ctx object.
        path: an include path to be sanitized.
  
      Returns:
        Sanitized include path that can be written to the crosstoot. Resulting path
        is absolute if it is outside the repository and relative otherwise.
      """
  
      repo_root = str(repo_ctx.path("."))
  
      # We're on UNIX, so the path delimiter is '/'.
      repo_root += "/"
      path = str(repo_ctx.path(path))
      if path.startswith(repo_root):
          return escape_string(path[len(repo_root):])
      return escape_string(path)
  
  def _get_value(it):
      """Convert `it` in serialized protobuf format."""
      if type(it) == "int":
          return str(it)
      elif type(it) == "bool":
          return "true" if it else "false"
      else:
          return "\"%s\"" % it
  
  def _build_tool_path(d):
      """Build the list of %-escaped tool_path for the CROSSTOOL file."""
      return ["  tool_path { name: \"%s\" path: \"%s\" }" % (k, escape_string(d[k])) for k in d]
  
  def _build_tool_path_starlark(d):
      """Build the list of %-escaped tool_path for the Starlark rule."""
      return "\n".join(["        tool_path ( name= \"%s\", path= \"%s\" )," % (k, escape_string(d[k])) for k in d])
  
  def _find_tool(repository_ctx, tool, overriden_tools):
      """Find a tool for repository, taking overriden tools into account."""
      if tool in overriden_tools:
          return overriden_tools[tool]
      return which(repository_ctx, tool, "/usr/bin/" + tool)
  
  def _get_tool_paths(repository_ctx, overriden_tools):
      """Compute the path to the various tools. Doesn't %-escape the result!"""
      return dict({
          k: _find_tool(repository_ctx, k, overriden_tools)
          for k in [
              "ar",
              "ld",
              "cpp",
              "gcc",
              "dwp",
              "gcov",
              "nm",
              "objcopy",
              "objdump",
              "strip",
          ]
      }.items())
  
  def _escaped_cplus_include_paths(repository_ctx):
      """Use ${CPLUS_INCLUDE_PATH} to compute the %-escaped list of flags for cxxflag."""
      if "CPLUS_INCLUDE_PATH" in repository_ctx.os.environ:
          result = []
          for p in repository_ctx.os.environ["CPLUS_INCLUDE_PATH"].split(":"):
              p = escape_string(str(repository_ctx.path(p)))  # Normalize the path
              result.append("-I" + p)
          return result
      else:
          return []
  
  _INC_DIR_MARKER_BEGIN = "#include <...>"
  
  # OSX add " (framework directory)" at the end of line, strip it.
  _OSX_FRAMEWORK_SUFFIX = " (framework directory)"
  _OSX_FRAMEWORK_SUFFIX_LEN = len(_OSX_FRAMEWORK_SUFFIX)
  
  def _cxx_inc_convert(path):
      """Convert path returned by cc -E xc++ in a complete path. Doesn't %-escape the path!"""
      path = path.strip()
      if path.endswith(_OSX_FRAMEWORK_SUFFIX):
          path = path[:-_OSX_FRAMEWORK_SUFFIX_LEN].strip()
      return path
  
  def get_escaped_cxx_inc_directories(repository_ctx, cc, lang_flag, additional_flags = []):
      """Compute the list of default %-escaped C++ include directories."""
      result = repository_ctx.execute([cc, "-E", lang_flag, "-", "-v"] + additional_flags)
      index1 = result.stderr.find(_INC_DIR_MARKER_BEGIN)
      if index1 == -1:
          return []
      index1 = result.stderr.find("\n", index1)
      if index1 == -1:
          return []
      index2 = result.stderr.rfind("\n ")
      if index2 == -1 or index2 < index1:
          return []
      index2 = result.stderr.find("\n", index2 + 1)
      if index2 == -1:
          inc_dirs = result.stderr[index1 + 1:]
      else:
          inc_dirs = result.stderr[index1 + 1:index2].strip()
  
      return [
          _prepare_include_path(repository_ctx, _cxx_inc_convert(p))
          for p in inc_dirs.split("\n")
      ]
  
  def _is_compiler_option_supported(repository_ctx, cc, option):
      """Checks that `option` is supported by the C compiler. Doesn't %-escape the option."""
      result = repository_ctx.execute([
          cc,
          option,
          "-o",
          "/dev/null",
          "-c",
          str(repository_ctx.path("tools/cpp/empty.cc")),
      ])
      return result.stderr.find(option) == -1
  
  def _is_linker_option_supported(repository_ctx, cc, option, pattern):
      """Checks that `option` is supported by the C linker. Doesn't %-escape the option."""
      result = repository_ctx.execute([
          cc,
          option,
          "-o",
          "/dev/null",
          str(repository_ctx.path("tools/cpp/empty.cc")),
      ])
      return result.stderr.find(pattern) == -1
  
  def _is_gold_supported(repository_ctx, cc):
      """Checks that `gold` is supported by the C compiler."""
      result = repository_ctx.execute([
          cc,
          "-fuse-ld=gold",
          "-o",
          "/dev/null",
          # Some macos clang versions don't fail when setting -fuse-ld=gold, adding
          # these lines to force it to. This also means that we will not detect
          # gold when only a very old (year 2010 and older) is present.
          "-Wl,--start-lib",
          "-Wl,--end-lib",
          str(repository_ctx.path("tools/cpp/empty.cc")),
      ])
      return result.return_code == 0
  
  def _add_compiler_option_if_supported(repository_ctx, cc, option):
      """Returns `[option]` if supported, `[]` otherwise. Doesn't %-escape the option."""
      return [option] if _is_compiler_option_supported(repository_ctx, cc, option) else []
  
  def _add_linker_option_if_supported(repository_ctx, cc, option, pattern):
      """Returns `[option]` if supported, `[]` otherwise. Doesn't %-escape the option."""
      return [option] if _is_linker_option_supported(repository_ctx, cc, option, pattern) else []
  
  def _get_no_canonical_prefixes_opt(repository_ctx, cc):
      # If the compiler sometimes rewrites paths in the .d files without symlinks
      # (ie when they're shorter), it confuses Bazel's logic for verifying all
      # #included header files are listed as inputs to the action.
  
      # The '-fno-canonical-system-headers' should be enough, but clang does not
      # support it, so we also try '-no-canonical-prefixes' if first option does
      # not work.
      opt = _add_compiler_option_if_supported(
          repository_ctx,
          cc,
          "-fno-canonical-system-headers",
      )
      if len(opt) == 0:
          return _add_compiler_option_if_supported(
              repository_ctx,
              cc,
              "-no-canonical-prefixes",
          )
      return opt
  
  def get_env(repository_ctx):
      """Convert the environment in a list of export if in Homebrew. Doesn't %-escape the result!"""
      env = repository_ctx.os.environ
      if "HOMEBREW_RUBY_PATH" in env:
          return "\n".join([
              "export %s='%s'" % (k, env[k].replace("'", "'\\''"))
              for k in env
              if k != "_" and k.find(".") == -1
          ])
      else:
          return ""
  
  def _coverage_feature(repository_ctx, darwin):
      use_llvm_cov = "1" == get_env_var(
          repository_ctx,
          "BAZEL_USE_LLVM_NATIVE_COVERAGE",
          default = "0",
          enable_warning = False,
      )
      if darwin or use_llvm_cov:
          compile_flags = """flag_group (
                      flags = ["-fprofile-instr-generate",  "-fcoverage-mapping"],
                  ),"""
          link_flags = """flag_group (flags = ["-fprofile-instr-generate"]),"""
      else:
          # gcc requires --coverage being passed for compilation and linking
          # https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#Instrumentation-Options
          compile_flags = """flag_group (flags = ["--coverage"]),"""
          link_flags = """flag_group (flags = ["--coverage"]),"""
  
      # Note that we also set --coverage for c++-link-nodeps-dynamic-library. The
      # generated code contains references to gcov symbols, and the dynamic linker
      # can't resolve them unless the library is linked against gcov.
      return """
      coverage_feature = feature (
          name = "coverage",
          provides = ["profile"],
          flag_sets = [
              flag_set(
                  actions = [
                      ACTION_NAMES.preprocess_assemble,
                      ACTION_NAMES.c_compile,
                      ACTION_NAMES.cpp_compile,
                      ACTION_NAMES.cpp_header_parsing,
                      ACTION_NAMES.cpp_module_compile,
                  ],
                  flag_groups = [
                      """ + compile_flags + """
                  ],
              ),
              flag_set (
                  actions = [
                      ACTION_NAMES.cpp_link_dynamic_library,
                      ACTION_NAMES.cpp_link_nodeps_dynamic_library,
                      ACTION_NAMES.cpp_link_executable,
                  ],
                  flag_groups = [
                      """ + link_flags + """
                  ],
              ),
          ],
      )
  """
  
  def _find_generic(repository_ctx, name, env_name, overriden_tools, warn = False, silent = False):
      """Find a generic C++ toolchain tool. Doesn't %-escape the result."""
  
      if name in overriden_tools:
          return overriden_tools[name]
  
      result = name
      env_value = repository_ctx.os.environ.get(env_name)
      env_value_with_paren = ""
      if env_value != None:
          env_value = env_value.strip()
          if env_value:
              result = env_value
              env_value_with_paren = " (%s)" % env_value
      if result.startswith("/"):
          # Absolute path, maybe we should make this suported by our which function.
          return result
      result = repository_ctx.which(result)
      if result == None:
          msg = ("Cannot find %s or %s%s; either correct your path or set the %s" +
                 " environment variable") % (name, env_name, env_value_with_paren, env_name)
          if warn:
              if not silent:
                  auto_configure_warning(msg)
          else:
              auto_configure_fail(msg)
      return result
  
  def find_cc(repository_ctx, overriden_tools):
      return _find_generic(repository_ctx, "gcc", "CC", overriden_tools)
  
  def _feature(name, enabled):
      return "  feature { name: '" + name + "' enabled: " + ("true" if enabled else "false") + " }"
  
  def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools):
      """Configure C++ toolchain on Unix platforms."""
      paths = resolve_labels(repository_ctx, [
          "@bazel_tools//tools/cpp:BUILD.tpl",
          "@bazel_tools//tools/cpp:armeabi_cc_toolchain_config.bzl",
          "@bazel_tools//tools/cpp:cc_toolchain_config.bzl.tpl",
          "@bazel_tools//tools/cpp:linux_cc_wrapper.sh.tpl",
          "@bazel_tools//tools/cpp:osx_cc_wrapper.sh.tpl",
      ])
  
      repository_ctx.symlink(
          paths["@bazel_tools//tools/cpp:armeabi_cc_toolchain_config.bzl"],
          "armeabi_cc_toolchain_config.bzl",
      )
  
      repository_ctx.file("tools/cpp/empty.cc", "int main() {}")
      darwin = cpu_value == "darwin"
  
      cc = _find_generic(repository_ctx, "gcc", "CC", overriden_tools)
      overriden_tools = dict(overriden_tools)
      overriden_tools["gcc"] = cc
      overriden_tools["gcov"] = _find_generic(
          repository_ctx,
          "gcov",
          "GCOV",
          overriden_tools,
          warn = True,
          silent = True,
      )
      if darwin:
          overriden_tools["gcc"] = "cc_wrapper.sh"
          overriden_tools["ar"] = "/usr/bin/libtool"
  
      tool_paths = _get_tool_paths(repository_ctx, overriden_tools)
      cc_toolchain_identifier = get_env_var(repository_ctx, "CC_TOOLCHAIN_NAME", "local", False)
  
      repository_ctx.template(
          "BUILD",
          paths["@bazel_tools//tools/cpp:BUILD.tpl"],
          {
              "%{cc_toolchain_identifier}": cc_toolchain_identifier,
              "%{name}": cpu_value,
              "%{supports_param_files}": "0" if darwin else "1",
              "%{cc_compiler_deps}": ":cc_wrapper" if darwin else ":empty",
              "%{compiler}": get_env_var(
                  repository_ctx,
                  "BAZEL_COMPILER",
                  "compiler",
                  False,
              ),
              "%{target_cpu}": escape_string(get_env_var(repository_ctx, "BAZEL_TARGET_CPU", cpu_value, False)),
          },
      )
  
      cc_wrapper_src = (
          "@bazel_tools//tools/cpp:osx_cc_wrapper.sh.tpl" if darwin else "@bazel_tools//tools/cpp:linux_cc_wrapper.sh.tpl"
      )
      repository_ctx.template(
          "cc_wrapper.sh",
          paths[cc_wrapper_src],
          {
              "%{cc}": escape_string(str(cc)),
              "%{env}": escape_string(get_env(repository_ctx)),
          },
      )
  
      cxx_opts = split_escaped(get_env_var(repository_ctx, "BAZEL_CXXOPTS", "-std=c++0x", False), ":")
      link_opts = split_escaped(get_env_var(repository_ctx, "BAZEL_LINKOPTS", "-lstdc++:-lm", False), ":")
      supports_gold_linker = _is_gold_supported(repository_ctx, cc)
      cc_path = repository_ctx.path(cc)
      if not str(cc_path).startswith(str(repository_ctx.path(".")) + "/"):
          # cc is outside the repository, set -B
          bin_search_flag = ["-B" + escape_string(str(cc_path.dirname))]
      else:
          # cc is inside the repository, don't set -B.
          bin_search_flag = []
  
      repository_ctx.template(
          "cc_toolchain_config.bzl",
          paths["@bazel_tools//tools/cpp:cc_toolchain_config.bzl.tpl"],
          {
              "%{toolchain_identifier}": escape_string(cc_toolchain_identifier),
              "%{abi_version}": escape_string(get_env_var(repository_ctx, "ABI_VERSION", "local", False)),
              "%{abi_libc_version}": escape_string(get_env_var(repository_ctx, "ABI_LIBC_VERSION", "local", False)),
              "%{builtin_sysroot}": "",
              "%{compiler}": escape_string(get_env_var(repository_ctx, "BAZEL_COMPILER", "compiler", False)),
              "%{host_system_name}": escape_string(get_env_var(repository_ctx, "BAZEL_HOST_SYSTEM", "local", False)),
              "%{target_libc}": "macosx" if darwin else escape_string(get_env_var(repository_ctx, "BAZEL_TARGET_LIBC", "local", False)),
              "%{target_cpu}": escape_string(get_env_var(repository_ctx, "BAZEL_TARGET_CPU", cpu_value, False)),
              "%{target_system_name}": escape_string(get_env_var(repository_ctx, "BAZEL_TARGET_SYSTEM", "local", False)),
              "%{tool_paths}": _build_tool_path_starlark(tool_paths),
              "%{cxx_builtin_include_directories}": get_starlark_list(
                  _uniq(
                      get_escaped_cxx_inc_directories(repository_ctx, cc, "-xc") +
                      get_escaped_cxx_inc_directories(repository_ctx, cc, "-xc++", cxx_opts) +
                      get_escaped_cxx_inc_directories(
                          repository_ctx,
                          cc,
                          "-xc",
                          _get_no_canonical_prefixes_opt(repository_ctx, cc),
                      ) +
                      get_escaped_cxx_inc_directories(
                          repository_ctx,
                          cc,
                          "-xc++",
                          cxx_opts + _get_no_canonical_prefixes_opt(repository_ctx, cc),
                      ),
                  ),
              ),
              "%{compile_content}": get_starlark_list(
                  [
                      # Security hardening requires optimization.
                      # We need to undef it as some distributions now have it enabled by default.
                      "-U_FORTIFY_SOURCE",
                      "-fstack-protector",
                      # All warnings are enabled. Maybe enable -Werror as well?
                      "-Wall",
                      # Enable a few more warnings that aren't part of -Wall.
                  ] + ((
                      _add_compiler_option_if_supported(repository_ctx, cc, "-Wthread-safety") +
                      _add_compiler_option_if_supported(repository_ctx, cc, "-Wself-assign")
                  )) + (
                      # Disable problematic warnings.
                      _add_compiler_option_if_supported(repository_ctx, cc, "-Wunused-but-set-parameter") +
                      # has false positives
                      _add_compiler_option_if_supported(repository_ctx, cc, "-Wno-free-nonheap-object") +
                      # Enable coloring even if there's no attached terminal. Bazel removes the
                      # escape sequences if --nocolor is specified.
                      _add_compiler_option_if_supported(repository_ctx, cc, "-fcolor-diagnostics")
                  ) + [
                      # Keep stack frames for debugging, even in opt mode.
                      "-fno-omit-frame-pointer",
                  ],
              ),
              "%{cxx_content}": get_starlark_list(cxx_opts + _escaped_cplus_include_paths(repository_ctx)),
              "%{link_content}": get_starlark_list((
                  ["-fuse-ld=gold"] if supports_gold_linker else []
              ) + _add_linker_option_if_supported(
                  repository_ctx,
                  cc,
                  "-Wl,-no-as-needed",
                  "-no-as-needed",
              ) + _add_linker_option_if_supported(
                  repository_ctx,
                  cc,
                  "-Wl,-z,relro,-z,now",
                  "-z",
              ) + (
                  [
                      "-undefined",
                      "dynamic_lookup",
                      "-headerpad_max_install_names",
                  ] if darwin else bin_search_flag + [
                      # Gold linker only? Can we enable this by default?
                      # "-Wl,--warn-execstack",
                      # "-Wl,--detect-odr-violations"
                  ] + _add_compiler_option_if_supported(
                      # Have gcc return the exit code from ld.
                      repository_ctx,
                      cc,
                      "-pass-exit-codes",
                  )
              ) + link_opts),
              "%{opt_compile_content}": get_starlark_list(
                  [
                      # No debug symbols.
                      # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt or
                      # even generally? However, that can't happen here, as it requires special
                      # handling in Bazel.
                      "-g0",
  
                      # Conservative choice for -O
                      # -O3 can increase binary size and even slow down the resulting binaries.
                      # Profile first and / or use FDO if you need better performance than this.
                      "-O2",
  
                      # Security hardening on by default.
                      # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases.
                      "-D_FORTIFY_SOURCE=1",
  
                      # Disable assertions
                      "-DNDEBUG",
  
                      # Removal of unused code and data at link time (can this increase binary size in some cases?).
                      "-ffunction-sections",
                      "-fdata-sections",
                  ],
              ),
              "%{opt_link_content}": get_starlark_list(
                  [] if darwin else _add_linker_option_if_supported(
                      repository_ctx,
                      cc,
                      "-Wl,--gc-sections",
                      "-gc-sections",
                  ),
              ),
              "%{unfiltered_content}": get_starlark_list(
                  _get_no_canonical_prefixes_opt(repository_ctx, cc) + [
                      # Make C++ compilation deterministic. Use linkstamping instead of these
                      # compiler symbols.
                      "-Wno-builtin-macro-redefined",
                      "-D__DATE__=\\\"redacted\\\"",
                      "-D__TIMESTAMP__=\\\"redacted\\\"",
                      "-D__TIME__=\\\"redacted\\\"",
                  ],
              ),
              "%{dbg_compile_content}": get_starlark_list(["-g"]),
              "%{coverage_feature}": _coverage_feature(repository_ctx, darwin),
              "%{use_coverage_feature}": "coverage_feature,",
              "%{supports_start_end_lib}": "supports_start_end_lib_feature," if supports_gold_linker else "",
          },
      )
  