github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/gpus/rocm_configure.bzl (about)

     1  # -*- Python -*-
     2  """Repository rule for ROCm autoconfiguration.
     3  
     4  `rocm_configure` depends on the following environment variables:
     5  
     6    * `TF_NEED_ROCM`: Whether to enable building with ROCm.
     7    * `GCC_HOST_COMPILER_PATH`: The GCC host compiler path
     8    * `ROCM_TOOLKIT_PATH`: The path to the ROCm toolkit. Default is
     9      `/opt/rocm`.
    10    * `TF_ROCM_VERSION`: The version of the ROCm toolkit. If this is blank, then
    11      use the system default.
    12    * `TF_MIOPEN_VERSION`: The version of the MIOpen library.
    13    * `TF_ROCM_AMDGPU_TARGETS`: The AMDGPU targets. Default is
    14      `gfx803,gfx900`.
    15  """
    16  
    17  load(
    18      ":cuda_configure.bzl",
    19      "make_copy_dir_rule",
    20      "make_copy_files_rule",
    21      "to_list_of_strings",
    22      "verify_build_defines",
    23  )
    24  
    25  _GCC_HOST_COMPILER_PATH = "GCC_HOST_COMPILER_PATH"
    26  _GCC_HOST_COMPILER_PREFIX = "GCC_HOST_COMPILER_PREFIX"
    27  _ROCM_TOOLKIT_PATH = "ROCM_TOOLKIT_PATH"
    28  _TF_ROCM_VERSION = "TF_ROCM_VERSION"
    29  _TF_MIOPEN_VERSION = "TF_MIOPEN_VERSION"
    30  _TF_ROCM_AMDGPU_TARGETS = "TF_ROCM_AMDGPU_TARGETS"
    31  _TF_ROCM_CONFIG_REPO = "TF_ROCM_CONFIG_REPO"
    32  
    33  _DEFAULT_ROCM_VERSION = ""
    34  _DEFAULT_MIOPEN_VERSION = ""
    35  _DEFAULT_ROCM_TOOLKIT_PATH = "/opt/rocm"
    36  _DEFAULT_ROCM_AMDGPU_TARGETS = ["gfx803", "gfx900"]
    37  
    38  def _get_win_rocm_defines(repository_ctx):
    39      """Return CROSSTOOL defines for Windows"""
    40  
    41      # Return fake vaules for Windows specific fields.
    42      # This ensures the CROSSTOOL file parser is happy.
    43      return {
    44          "%{msvc_env_tmp}": "msvc_not_used",
    45          "%{msvc_env_path}": "msvc_not_used",
    46          "%{msvc_env_include}": "msvc_not_used",
    47          "%{msvc_env_lib}": "msvc_not_used",
    48          "%{msvc_cl_path}": "msvc_not_used",
    49          "%{msvc_ml_path}": "msvc_not_used",
    50          "%{msvc_link_path}": "msvc_not_used",
    51          "%{msvc_lib_path}": "msvc_not_used",
    52      }
    53  
    54  def find_cc(repository_ctx):
    55      """Find the C++ compiler."""
    56  
    57      # Return a dummy value for GCC detection here to avoid error
    58      target_cc_name = "gcc"
    59      cc_path_envvar = _GCC_HOST_COMPILER_PATH
    60      cc_name = target_cc_name
    61  
    62      if cc_path_envvar in repository_ctx.os.environ:
    63          cc_name_from_env = repository_ctx.os.environ[cc_path_envvar].strip()
    64          if cc_name_from_env:
    65              cc_name = cc_name_from_env
    66      if cc_name.startswith("/"):
    67          # Absolute path, maybe we should make this supported by our which function.
    68          return cc_name
    69      cc = repository_ctx.which(cc_name)
    70      if cc == None:
    71          fail(("Cannot find {}, either correct your path or set the {}" +
    72                " environment variable").format(target_cc_name, cc_path_envvar))
    73      return cc
    74  
    75  _INC_DIR_MARKER_BEGIN = "#include <...>"
    76  
    77  def _cxx_inc_convert(path):
    78      """Convert path returned by cc -E xc++ in a complete path."""
    79      path = path.strip()
    80      return path
    81  
    82  def _get_cxx_inc_directories_impl(repository_ctx, cc, lang_is_cpp):
    83      """Compute the list of default C or C++ include directories."""
    84      if lang_is_cpp:
    85          lang = "c++"
    86      else:
    87          lang = "c"
    88  
    89      # TODO: We pass -no-canonical-prefixes here to match the compiler flags,
    90      #       but in rocm_clang CROSSTOOL file that is a `feature` and we should
    91      #       handle the case when it's disabled and no flag is passed
    92      result = repository_ctx.execute([
    93          cc,
    94          "-no-canonical-prefixes",
    95          "-E",
    96          "-x" + lang,
    97          "-",
    98          "-v",
    99      ])
   100      index1 = result.stderr.find(_INC_DIR_MARKER_BEGIN)
   101      if index1 == -1:
   102          return []
   103      index1 = result.stderr.find("\n", index1)
   104      if index1 == -1:
   105          return []
   106      index2 = result.stderr.rfind("\n ")
   107      if index2 == -1 or index2 < index1:
   108          return []
   109      index2 = result.stderr.find("\n", index2 + 1)
   110      if index2 == -1:
   111          inc_dirs = result.stderr[index1 + 1:]
   112      else:
   113          inc_dirs = result.stderr[index1 + 1:index2].strip()
   114  
   115      return [
   116          str(repository_ctx.path(_cxx_inc_convert(p)))
   117          for p in inc_dirs.split("\n")
   118      ]
   119  
   120  def get_cxx_inc_directories(repository_ctx, cc):
   121      """Compute the list of default C and C++ include directories."""
   122  
   123      # For some reason `clang -xc` sometimes returns include paths that are
   124      # different from the ones from `clang -xc++`. (Symlink and a dir)
   125      # So we run the compiler with both `-xc` and `-xc++` and merge resulting lists
   126      includes_cpp = _get_cxx_inc_directories_impl(repository_ctx, cc, True)
   127      includes_c = _get_cxx_inc_directories_impl(repository_ctx, cc, False)
   128  
   129      includes_cpp_set = depset(includes_cpp)
   130      return includes_cpp + [
   131          inc
   132          for inc in includes_c
   133          if inc not in includes_cpp_set.to_list()
   134      ]
   135  
   136  def auto_configure_fail(msg):
   137      """Output failure message when rocm configuration fails."""
   138      red = "\033[0;31m"
   139      no_color = "\033[0m"
   140      fail("\n%sROCm Configuration Error:%s %s\n" % (red, no_color, msg))
   141  
   142  # END cc_configure common functions (see TODO above).
   143  
   144  def _host_compiler_includes(repository_ctx, cc):
   145      """Computed the list of gcc include directories.
   146  
   147      Args:
   148        repository_ctx: The repository context.
   149        cc: The path to the gcc host compiler.
   150  
   151      Returns:
   152        A list of gcc include directories.
   153      """
   154      inc_dirs = get_cxx_inc_directories(repository_ctx, cc)
   155  
   156      # Add numpy headers
   157      inc_dirs.append("/usr/lib/python2.7/dist-packages/numpy/core/include")
   158  
   159      return inc_dirs
   160  
   161  def _rocm_include_path(repository_ctx, rocm_config):
   162      """Generates the cxx_builtin_include_directory entries for rocm inc dirs.
   163  
   164      Args:
   165        repository_ctx: The repository context.
   166        rocm_config: The path to the gcc host compiler.
   167  
   168      Returns:
   169        A string containing the Starlark string for each of the gcc
   170        host compiler include directories, which can be added to the CROSSTOOL
   171        file.
   172      """
   173      inc_dirs = []
   174  
   175      # general ROCm include path
   176      inc_dirs.append(rocm_config.rocm_toolkit_path + "/include")
   177  
   178      # Add HSA headers
   179      inc_dirs.append("/opt/rocm/hsa/include")
   180  
   181      # Add HIP headers
   182      inc_dirs.append("/opt/rocm/include/hip")
   183      inc_dirs.append("/opt/rocm/include/hip/hcc_detail")
   184      inc_dirs.append("/opt/rocm/hip/include")
   185  
   186      # Add HIP-Clang headers
   187      inc_dirs.append("/opt/rocm/llvm/lib/clang/8.0/include")
   188      inc_dirs.append("/opt/rocm/llvm/lib/clang/9.0.0/include")
   189      inc_dirs.append("/opt/rocm/llvm/lib/clang/10.0.0/include")
   190  
   191      # Add rocrand and hiprand headers
   192      inc_dirs.append("/opt/rocm/rocrand/include")
   193      inc_dirs.append("/opt/rocm/hiprand/include")
   194  
   195      # Add rocfft headers
   196      inc_dirs.append("/opt/rocm/rocfft/include")
   197  
   198      # Add rocBLAS headers
   199      inc_dirs.append("/opt/rocm/rocblas/include")
   200  
   201      # Add MIOpen headers
   202      inc_dirs.append("/opt/rocm/miopen/include")
   203  
   204      # Add RCCL headers
   205      inc_dirs.append("/opt/rocm/rccl/include")
   206  
   207      # Add hcc headers
   208      inc_dirs.append("/opt/rocm/hcc/include")
   209      inc_dirs.append("/opt/rocm/hcc/compiler/lib/clang/7.0.0/include/")
   210      inc_dirs.append("/opt/rocm/hcc/lib/clang/7.0.0/include")
   211  
   212      # Newer hcc builds use/are based off of clang 8.0.0.
   213      inc_dirs.append("/opt/rocm/hcc/compiler/lib/clang/8.0.0/include/")
   214      inc_dirs.append("/opt/rocm/hcc/lib/clang/8.0.0/include")
   215  
   216      # Support hcc based off clang 9.0.0, included in ROCm2.2
   217      inc_dirs.append("/opt/rocm/hcc/compiler/lib/clang/9.0.0/include/")
   218      inc_dirs.append("/opt/rocm/hcc/lib/clang/9.0.0/include")
   219  
   220      # Support hcc based off clang 10.0.0, included in ROCm2.8
   221      inc_dirs.append("/opt/rocm/hcc/compiler/lib/clang/10.0.0/include/")
   222      inc_dirs.append("/opt/rocm/hcc/lib/clang/10.0.0/include")
   223  
   224      return inc_dirs
   225  
   226  def _enable_rocm(repository_ctx):
   227      if "TF_NEED_ROCM" in repository_ctx.os.environ:
   228          enable_rocm = repository_ctx.os.environ["TF_NEED_ROCM"].strip()
   229          return enable_rocm == "1"
   230      return False
   231  
   232  def _rocm_toolkit_path(repository_ctx):
   233      """Finds the rocm toolkit directory.
   234  
   235      Args:
   236        repository_ctx: The repository context.
   237  
   238      Returns:
   239        A speculative real path of the rocm toolkit install directory.
   240      """
   241      rocm_toolkit_path = _DEFAULT_ROCM_TOOLKIT_PATH
   242      if _ROCM_TOOLKIT_PATH in repository_ctx.os.environ:
   243          rocm_toolkit_path = repository_ctx.os.environ[_ROCM_TOOLKIT_PATH].strip()
   244      if not repository_ctx.path(rocm_toolkit_path).exists:
   245          auto_configure_fail("Cannot find rocm toolkit path.")
   246      return str(repository_ctx.path(rocm_toolkit_path).realpath)
   247  
   248  def _amdgpu_targets(repository_ctx):
   249      """Returns a list of strings representing AMDGPU targets."""
   250      if _TF_ROCM_AMDGPU_TARGETS not in repository_ctx.os.environ:
   251          return _DEFAULT_ROCM_AMDGPU_TARGETS
   252      amdgpu_targets_str = repository_ctx.os.environ[_TF_ROCM_AMDGPU_TARGETS]
   253      amdgpu_targets = amdgpu_targets_str.split(",")
   254      for amdgpu_target in amdgpu_targets:
   255          if amdgpu_target[:3] != "gfx" or not amdgpu_target[3:].isdigit():
   256              auto_configure_fail("Invalid AMDGPU target: %s" % amdgpu_target)
   257      return amdgpu_targets
   258  
   259  def _hipcc_env(repository_ctx):
   260      """Returns the environment variable string for hipcc.
   261  
   262      Args:
   263          repository_ctx: The repository context.
   264  
   265      Returns:
   266          A string containing environment variables for hipcc.
   267      """
   268      hipcc_env = ""
   269      for name in [
   270          "HIP_CLANG_PATH",
   271          "DEVICE_LIB_PATH",
   272          "HIP_VDI_HOME",
   273          "HIPCC_VERBOSE",
   274          "HIPCC_COMPILE_FLAGS_APPEND",
   275          "HIPPCC_LINK_FLAGS_APPEND",
   276          "HCC_AMDGPU_TARGET",
   277          "HIP_PLATFORM",
   278      ]:
   279          if name in repository_ctx.os.environ:
   280              hipcc_env = (hipcc_env + " " + name + "=\"" +
   281                           repository_ctx.os.environ[name].strip() + "\";")
   282      return hipcc_env.strip()
   283  
   284  def _hipcc_is_hipclang(repository_ctx):
   285      """Returns if hipcc is based on hip-clang toolchain.
   286  
   287      Args:
   288          repository_ctx: The repository context.
   289  
   290      Returns:
   291          A string "True" if hipcc is based on hip-clang toolchain.
   292          The functions returns "False" if not (ie: based on HIP/HCC toolchain).
   293      """
   294  
   295      #  check user-defined hip-clang environment variables
   296      for name in ["HIP_CLANG_PATH", "HIP_VDI_HOME"]:
   297          if name in repository_ctx.os.environ:
   298              return "True"
   299  
   300      # grep for "HIP_COMPILER=clang" in /opt/rocm/hip/lib/.hipInfo
   301      grep_result = _execute(
   302          repository_ctx,
   303          ["grep", "HIP_COMPILER=clang", "/opt/rocm/hip/lib/.hipInfo"],
   304          empty_stdout_fine = True,
   305      )
   306      result = grep_result.stdout
   307      if result == "HIP_COMPILER=clang":
   308          return "True"
   309      return "False"
   310  
   311  def _if_hipcc_is_hipclang(repository_ctx, if_true, if_false = []):
   312      """
   313      Returns either the if_true or if_false arg based on whether hipcc
   314      is based on the hip-clang toolchain
   315  
   316      Args :
   317          repository_ctx: The repository context.
   318          if_true : value to return if hipcc is hip-clang based
   319          if_false : value to return if hipcc is not hip-clang based
   320                     (optional, defaults to empty list)
   321  
   322      Returns :
   323          either the if_true arg or the of_False arg
   324      """
   325      if _hipcc_is_hipclang(repository_ctx) == "True":
   326          return if_true
   327      return if_false
   328  
   329  def _crosstool_verbose(repository_ctx):
   330      """Returns the environment variable value CROSSTOOL_VERBOSE.
   331  
   332      Args:
   333          repository_ctx: The repository context.
   334  
   335      Returns:
   336          A string containing value of environment variable CROSSTOOL_VERBOSE.
   337      """
   338      name = "CROSSTOOL_VERBOSE"
   339      if name in repository_ctx.os.environ:
   340          return repository_ctx.os.environ[name].strip()
   341      return "0"
   342  
   343  def _cpu_value(repository_ctx):
   344      """Returns the name of the host operating system.
   345  
   346      Args:
   347        repository_ctx: The repository context.
   348  
   349      Returns:
   350        A string containing the name of the host operating system.
   351      """
   352      os_name = repository_ctx.os.name.lower()
   353      if os_name.startswith("mac os"):
   354          return "Darwin"
   355      if os_name.find("windows") != -1:
   356          return "Windows"
   357      result = repository_ctx.execute(["uname", "-s"])
   358      return result.stdout.strip()
   359  
   360  def _lib_name(lib, cpu_value, version = "", static = False):
   361      """Constructs the platform-specific name of a library.
   362  
   363      Args:
   364        lib: The name of the library, such as "hip"
   365        cpu_value: The name of the host operating system.
   366        version: The version of the library.
   367        static: True the library is static or False if it is a shared object.
   368  
   369      Returns:
   370        The platform-specific name of the library.
   371      """
   372      if cpu_value in ("Linux", "FreeBSD"):
   373          if static:
   374              return "lib%s.a" % lib
   375          else:
   376              if version:
   377                  version = ".%s" % version
   378              return "lib%s.so%s" % (lib, version)
   379      elif cpu_value == "Windows":
   380          return "%s.lib" % lib
   381      elif cpu_value == "Darwin":
   382          if static:
   383              return "lib%s.a" % lib
   384          elif version:
   385              version = ".%s" % version
   386          return "lib%s%s.dylib" % (lib, version)
   387      else:
   388          auto_configure_fail("Invalid cpu_value: %s" % cpu_value)
   389  
   390  def _find_rocm_lib(
   391          lib,
   392          repository_ctx,
   393          cpu_value,
   394          basedir,
   395          version = "",
   396          static = False):
   397      """Finds the given ROCm libraries on the system.
   398  
   399      Args:
   400        lib: The name of the library, such as "hip"
   401        repository_ctx: The repository context.
   402        cpu_value: The name of the host operating system.
   403        basedir: The install directory of ROCm.
   404        version: The version of the library.
   405        static: True if static library, False if shared object.
   406  
   407      Returns:
   408        Returns a struct with the following fields:
   409          file_name: The basename of the library found on the system.
   410          path: The full path to the library.
   411      """
   412      file_name = _lib_name(lib, cpu_value, version, static)
   413      if cpu_value == "Linux":
   414          path = repository_ctx.path("%s/lib64/%s" % (basedir, file_name))
   415          if path.exists:
   416              return struct(file_name = file_name, path = str(path.realpath))
   417          path = repository_ctx.path("%s/lib64/stubs/%s" % (basedir, file_name))
   418          if path.exists:
   419              return struct(file_name = file_name, path = str(path.realpath))
   420          path = repository_ctx.path(
   421              "%s/lib/x86_64-linux-gnu/%s" % (basedir, file_name),
   422          )
   423          if path.exists:
   424              return struct(file_name = file_name, path = str(path.realpath))
   425  
   426      path = repository_ctx.path("%s/lib/%s" % (basedir, file_name))
   427      if path.exists:
   428          return struct(file_name = file_name, path = str(path.realpath))
   429      path = repository_ctx.path("%s/%s" % (basedir, file_name))
   430      if path.exists:
   431          return struct(file_name = file_name, path = str(path.realpath))
   432  
   433      auto_configure_fail("Cannot find rocm library %s" % file_name)
   434  
   435  def _find_libs(repository_ctx, rocm_config):
   436      """Returns the ROCm libraries on the system.
   437  
   438      Args:
   439        repository_ctx: The repository context.
   440        rocm_config: The ROCm config as returned by _get_rocm_config
   441  
   442      Returns:
   443        Map of library names to structs of filename and path as returned by
   444        _find_rocm_lib.
   445      """
   446      cpu_value = rocm_config.cpu_value
   447      return {
   448          "hip": _find_rocm_lib(
   449              "hip_hcc",
   450              repository_ctx,
   451              cpu_value,
   452              rocm_config.rocm_toolkit_path,
   453          ),
   454          "rocblas": _find_rocm_lib(
   455              "rocblas",
   456              repository_ctx,
   457              cpu_value,
   458              rocm_config.rocm_toolkit_path + "/rocblas",
   459          ),
   460          "rocfft": _find_rocm_lib(
   461              "rocfft",
   462              repository_ctx,
   463              cpu_value,
   464              rocm_config.rocm_toolkit_path + "/rocfft",
   465          ),
   466          "hiprand": _find_rocm_lib(
   467              "hiprand",
   468              repository_ctx,
   469              cpu_value,
   470              rocm_config.rocm_toolkit_path + "/hiprand",
   471          ),
   472          "miopen": _find_rocm_lib(
   473              "MIOpen",
   474              repository_ctx,
   475              cpu_value,
   476              rocm_config.rocm_toolkit_path + "/miopen",
   477          ),
   478          "rccl": _find_rocm_lib(
   479              "rccl",
   480              repository_ctx,
   481              cpu_value,
   482              rocm_config.rocm_toolkit_path + "/rccl",
   483          ),
   484      }
   485  
   486  def _get_rocm_config(repository_ctx):
   487      """Detects and returns information about the ROCm installation on the system.
   488  
   489      Args:
   490        repository_ctx: The repository context.
   491  
   492      Returns:
   493        A struct containing the following fields:
   494          rocm_toolkit_path: The ROCm toolkit installation directory.
   495          amdgpu_targets: A list of the system's AMDGPU targets.
   496          cpu_value: The name of the host operating system.
   497      """
   498      cpu_value = _cpu_value(repository_ctx)
   499      rocm_toolkit_path = _rocm_toolkit_path(repository_ctx)
   500      return struct(
   501          rocm_toolkit_path = rocm_toolkit_path,
   502          amdgpu_targets = _amdgpu_targets(repository_ctx),
   503          cpu_value = cpu_value,
   504      )
   505  
   506  def _tpl(repository_ctx, tpl, substitutions = {}, out = None):
   507      if not out:
   508          out = tpl.replace(":", "/")
   509      repository_ctx.template(
   510          out,
   511          Label("//third_party/gpus/%s.tpl" % tpl),
   512          substitutions,
   513      )
   514  
   515  def _file(repository_ctx, label):
   516      repository_ctx.template(
   517          label.replace(":", "/"),
   518          Label("//third_party/gpus/%s.tpl" % label),
   519          {},
   520      )
   521  
   522  _DUMMY_CROSSTOOL_BZL_FILE = """
   523  def error_gpu_disabled():
   524    fail("ERROR: Building with --config=rocm but TensorFlow is not configured " +
   525         "to build with GPU support. Please re-run ./configure and enter 'Y' " +
   526         "at the prompt to build with GPU support.")
   527  
   528    native.genrule(
   529        name = "error_gen_crosstool",
   530        outs = ["CROSSTOOL"],
   531        cmd = "echo 'Should not be run.' && exit 1",
   532    )
   533  
   534    native.filegroup(
   535        name = "crosstool",
   536        srcs = [":CROSSTOOL"],
   537        output_licenses = ["unencumbered"],
   538    )
   539  """
   540  
   541  _DUMMY_CROSSTOOL_BUILD_FILE = """
   542  load("//crosstool:error_gpu_disabled.bzl", "error_gpu_disabled")
   543  
   544  error_gpu_disabled()
   545  """
   546  
   547  def _create_dummy_repository(repository_ctx):
   548      cpu_value = _cpu_value(repository_ctx)
   549  
   550      # Set up BUILD file for rocm/.
   551      _tpl(
   552          repository_ctx,
   553          "rocm:build_defs.bzl",
   554          {
   555              "%{rocm_is_configured}": "False",
   556              "%{rocm_extra_copts}": "[]",
   557          },
   558      )
   559      _tpl(
   560          repository_ctx,
   561          "rocm:BUILD",
   562          {
   563              "%{hip_lib}": _lib_name("hip", cpu_value),
   564              "%{rocblas_lib}": _lib_name("rocblas", cpu_value),
   565              "%{miopen_lib}": _lib_name("miopen", cpu_value),
   566              "%{rccl_lib}": _lib_name("rccl", cpu_value),
   567              "%{rocfft_lib}": _lib_name("rocfft", cpu_value),
   568              "%{hiprand_lib}": _lib_name("hiprand", cpu_value),
   569              "%{copy_rules}": "",
   570              "%{rocm_headers}": "",
   571          },
   572      )
   573  
   574      # Create dummy files for the ROCm toolkit since they are still required by
   575      # tensorflow/core/platform/default/build_config:rocm.
   576      repository_ctx.file("rocm/hip/include/hip/hip_runtime.h", "")
   577  
   578      # Set up rocm_config.h, which is used by
   579      # tensorflow/stream_executor/dso_loader.cc.
   580      _tpl(
   581          repository_ctx,
   582          "rocm:rocm_config.h",
   583          {
   584              "%{rocm_toolkit_path}": _DEFAULT_ROCM_TOOLKIT_PATH,
   585          },
   586          "rocm/rocm/rocm_config.h",
   587      )
   588  
   589      # If rocm_configure is not configured to build with GPU support, and the user
   590      # attempts to build with --config=rocm, add a dummy build rule to intercept
   591      # this and fail with an actionable error message.
   592      repository_ctx.file(
   593          "crosstool/error_gpu_disabled.bzl",
   594          _DUMMY_CROSSTOOL_BZL_FILE,
   595      )
   596      repository_ctx.file("crosstool/BUILD", _DUMMY_CROSSTOOL_BUILD_FILE)
   597  
   598  def _execute(
   599          repository_ctx,
   600          cmdline,
   601          error_msg = None,
   602          error_details = None,
   603          empty_stdout_fine = False):
   604      """Executes an arbitrary shell command.
   605  
   606      Args:
   607        repository_ctx: the repository_ctx object
   608        cmdline: list of strings, the command to execute
   609        error_msg: string, a summary of the error if the command fails
   610        error_details: string, details about the error or steps to fix it
   611        empty_stdout_fine: bool, if True, an empty stdout result is fine, otherwise
   612          it's an error
   613      Return:
   614        the result of repository_ctx.execute(cmdline)
   615      """
   616      result = repository_ctx.execute(cmdline)
   617      if result.stderr or not (empty_stdout_fine or result.stdout):
   618          auto_configure_fail(
   619              "\n".join([
   620                  error_msg.strip() if error_msg else "Repository command failed",
   621                  result.stderr.strip(),
   622                  error_details if error_details else "",
   623              ]),
   624          )
   625      return result
   626  
   627  def _norm_path(path):
   628      """Returns a path with '/' and remove the trailing slash."""
   629      path = path.replace("\\", "/")
   630      if path[-1] == "/":
   631          path = path[:-1]
   632      return path
   633  
   634  def _genrule(src_dir, genrule_name, command, outs):
   635      """Returns a string with a genrule.
   636  
   637      Genrule executes the given command and produces the given outputs.
   638      """
   639      return (
   640          "genrule(\n" +
   641          '    name = "' +
   642          genrule_name + '",\n' +
   643          "    outs = [\n" +
   644          outs +
   645          "\n    ],\n" +
   646          '    cmd = """\n' +
   647          command +
   648          '\n   """,\n' +
   649          ")\n"
   650      )
   651  
   652  def _read_dir(repository_ctx, src_dir):
   653      """Returns a string with all files in a directory.
   654  
   655      Finds all files inside a directory, traversing subfolders and following
   656      symlinks. The returned string contains the full path of all files
   657      separated by line breaks.
   658      """
   659      find_result = _execute(
   660          repository_ctx,
   661          ["find", src_dir, "-follow", "-type", "f"],
   662          empty_stdout_fine = True,
   663      )
   664      result = find_result.stdout
   665      return result
   666  
   667  def _compute_rocm_extra_copts(repository_ctx, amdgpu_targets):
   668      if False:
   669          amdgpu_target_flags = ["--amdgpu-target=" +
   670                                 amdgpu_target for amdgpu_target in amdgpu_targets]
   671      else:
   672          # AMDGPU targets are handled in the "crosstool_wrapper_driver_is_not_gcc"
   673          amdgpu_target_flags = []
   674      return str(amdgpu_target_flags)
   675  
   676  def _create_local_rocm_repository(repository_ctx):
   677      """Creates the repository containing files set up to build with ROCm."""
   678      rocm_config = _get_rocm_config(repository_ctx)
   679  
   680      # Copy header and library files to execroot.
   681      # rocm_toolkit_path
   682      rocm_toolkit_path = rocm_config.rocm_toolkit_path
   683      copy_rules = [
   684          make_copy_dir_rule(
   685              repository_ctx,
   686              name = "rocm-include",
   687              src_dir = rocm_toolkit_path + "/include",
   688              out_dir = "rocm/include",
   689          ),
   690          make_copy_dir_rule(
   691              repository_ctx,
   692              name = "rocfft-include",
   693              src_dir = rocm_toolkit_path + "/rocfft/include",
   694              out_dir = "rocm/include/rocfft",
   695          ),
   696          make_copy_dir_rule(
   697              repository_ctx,
   698              name = "rocblas-include",
   699              src_dir = rocm_toolkit_path + "/rocblas/include",
   700              out_dir = "rocm/include/rocblas",
   701          ),
   702          make_copy_dir_rule(
   703              repository_ctx,
   704              name = "miopen-include",
   705              src_dir = rocm_toolkit_path + "/miopen/include",
   706              out_dir = "rocm/include/miopen",
   707          ),
   708          make_copy_dir_rule(
   709              repository_ctx,
   710              name = "rccl-include",
   711              src_dir = rocm_toolkit_path + "/rccl/include",
   712              out_dir = "rocm/include/rccl",
   713          ),
   714      ]
   715  
   716      rocm_libs = _find_libs(repository_ctx, rocm_config)
   717      rocm_lib_srcs = []
   718      rocm_lib_outs = []
   719      for lib in rocm_libs.values():
   720          rocm_lib_srcs.append(lib.path)
   721          rocm_lib_outs.append("rocm/lib/" + lib.file_name)
   722      copy_rules.append(make_copy_files_rule(
   723          repository_ctx,
   724          name = "rocm-lib",
   725          srcs = rocm_lib_srcs,
   726          outs = rocm_lib_outs,
   727      ))
   728  
   729      # Set up BUILD file for rocm/
   730      _tpl(
   731          repository_ctx,
   732          "rocm:build_defs.bzl",
   733          {
   734              "%{rocm_is_configured}": "True",
   735              "%{rocm_extra_copts}": _compute_rocm_extra_copts(
   736                  repository_ctx,
   737                  rocm_config.amdgpu_targets,
   738              ),
   739          },
   740      )
   741      _tpl(
   742          repository_ctx,
   743          "rocm:BUILD",
   744          {
   745              "%{hip_lib}": rocm_libs["hip"].file_name,
   746              "%{rocblas_lib}": rocm_libs["rocblas"].file_name,
   747              "%{rocfft_lib}": rocm_libs["rocfft"].file_name,
   748              "%{hiprand_lib}": rocm_libs["hiprand"].file_name,
   749              "%{miopen_lib}": rocm_libs["miopen"].file_name,
   750              "%{rccl_lib}": rocm_libs["rccl"].file_name,
   751              "%{copy_rules}": "\n".join(copy_rules),
   752              "%{rocm_headers}": ('":rocm-include",\n' +
   753                                  '":rocfft-include",\n' +
   754                                  '":rocblas-include",\n' +
   755                                  '":miopen-include",\n' +
   756                                  '":rccl-include",'),
   757          },
   758      )
   759  
   760      # Set up crosstool/
   761      cc = find_cc(repository_ctx)
   762  
   763      host_compiler_includes = get_cxx_inc_directories(repository_ctx, cc)
   764  
   765      host_compiler_prefix = "/usr/bin"
   766      if _GCC_HOST_COMPILER_PREFIX in repository_ctx.os.environ:
   767          host_compiler_prefix = repository_ctx.os.environ[_GCC_HOST_COMPILER_PREFIX].strip()
   768  
   769      rocm_defines = {}
   770  
   771      rocm_defines["%{host_compiler_prefix}"] = host_compiler_prefix
   772  
   773      rocm_defines["%{linker_bin_path}"] = "/opt/rocm/hcc/compiler/bin"
   774  
   775      # For gcc, do not canonicalize system header paths; some versions of gcc
   776      # pick the shortest possible path for system includes when creating the
   777      # .d file - given that includes that are prefixed with "../" multiple
   778      # time quickly grow longer than the root of the tree, this can lead to
   779      # bazel's header check failing.
   780      rocm_defines["%{extra_no_canonical_prefixes_flags}"] = "\"-fno-canonical-system-headers\""
   781  
   782      rocm_defines["%{unfiltered_compile_flags}"] = to_list_of_strings([
   783          "-DTENSORFLOW_USE_ROCM=1",
   784          "-D__HIP_PLATFORM_HCC__",
   785          "-DEIGEN_USE_HIP",
   786      ] + _if_hipcc_is_hipclang(repository_ctx, [
   787          #
   788          # define "TENSORFLOW_COMPILER_IS_HIP_CLANG" when we are using clang
   789          # based hipcc to compile/build tensorflow
   790          #
   791          # Note that this #define should not be used to check whether or not
   792          # tensorflow is being built with ROCm support
   793          # (only TENSORFLOW_USE_ROCM should be used for that purpose)
   794          #
   795          "-DTENSORFLOW_COMPILER_IS_HIP_CLANG=1",
   796      ]))
   797  
   798      rocm_defines["%{host_compiler_path}"] = "clang/bin/crosstool_wrapper_driver_is_not_gcc"
   799  
   800      # # Enable a few more warnings that aren't part of -Wall.
   801      # compiler_flag: "-Wunused-but-set-parameter"
   802  
   803      # # But disable some that are problematic.
   804      # compiler_flag: "-Wno-free-nonheap-object" # has false positives
   805  
   806      rocm_defines["%{host_compiler_warnings}"] = to_list_of_strings(["-Wunused-but-set-parameter", "-Wno-free-nonheap-object"])
   807  
   808      rocm_defines["%{cxx_builtin_include_directories}"] = to_list_of_strings(host_compiler_includes +
   809                                                                              _rocm_include_path(repository_ctx, rocm_config))
   810  
   811      rocm_defines["%{linker_files}"] = "clang/bin/crosstool_wrapper_driver_is_not_gcc"
   812  
   813      rocm_defines["%{win_linker_files}"] = ":empty"
   814  
   815      # Add the dummy defines for windows...requried to pass the "verify_build_defines" check
   816      rocm_defines.update(_get_win_rocm_defines(repository_ctx))
   817  
   818      verify_build_defines(rocm_defines)
   819  
   820      # Only expand template variables in the BUILD file
   821      _tpl(repository_ctx, "crosstool:BUILD", rocm_defines)
   822  
   823      # No templating of cc_toolchain_config - use attributes and templatize the
   824      # BUILD file.
   825      _tpl(
   826          repository_ctx,
   827          "crosstool:hipcc_cc_toolchain_config.bzl",
   828          out = "crosstool/cc_toolchain_config.bzl",
   829      )
   830  
   831      _tpl(
   832          repository_ctx,
   833          "crosstool:clang/bin/crosstool_wrapper_driver_rocm",
   834          {
   835              "%{cpu_compiler}": str(cc),
   836              "%{hipcc_path}": "/opt/rocm/bin/hipcc",
   837              "%{hipcc_env}": _hipcc_env(repository_ctx),
   838              "%{hipcc_is_hipclang}": _hipcc_is_hipclang(repository_ctx),
   839              "%{rocr_runtime_path}": "/opt/rocm/lib",
   840              "%{rocr_runtime_library}": "hsa-runtime64",
   841              "%{hip_runtime_path}": "/opt/rocm/hip/lib",
   842              "%{hip_runtime_library}": "hip_hcc",
   843              "%{hcc_runtime_path}": "/opt/rocm/hcc/lib",
   844              "%{hcc_runtime_library}": "mcwamp",
   845              "%{crosstool_verbose}": _crosstool_verbose(repository_ctx),
   846              "%{gcc_host_compiler_path}": str(cc),
   847              "%{rocm_amdgpu_targets}": ",".join(
   848                  ["\"%s\"" % c for c in rocm_config.amdgpu_targets],
   849              ),
   850          },
   851          out = "crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc",
   852      )
   853  
   854      # Set up rocm_config.h, which is used by
   855      # tensorflow/stream_executor/dso_loader.cc.
   856      _tpl(
   857          repository_ctx,
   858          "rocm:rocm_config.h",
   859          {
   860              "%{rocm_amdgpu_targets}": ",".join(
   861                  ["\"%s\"" % c for c in rocm_config.amdgpu_targets],
   862              ),
   863              "%{rocm_toolkit_path}": rocm_config.rocm_toolkit_path,
   864          },
   865          "rocm/rocm/rocm_config.h",
   866      )
   867  
   868  def _create_remote_rocm_repository(repository_ctx, remote_config_repo):
   869      """Creates pointers to a remotely configured repo set up to build with ROCm."""
   870      _tpl(
   871          repository_ctx,
   872          "rocm:build_defs.bzl",
   873          {
   874              "%{rocm_is_configured}": "True",
   875              "%{rocm_extra_copts}": _compute_rocm_extra_copts(
   876                  repository_ctx,
   877                  [],  #_compute_capabilities(repository_ctx)
   878              ),
   879          },
   880      )
   881      repository_ctx.template(
   882          "rocm/BUILD",
   883          Label(remote_config_repo + "/rocm:BUILD"),
   884          {},
   885      )
   886      repository_ctx.template(
   887          "rocm/build_defs.bzl",
   888          Label(remote_config_repo + "/rocm:build_defs.bzl"),
   889          {},
   890      )
   891      repository_ctx.template(
   892          "rocm/rocm/rocm_config.h",
   893          Label(remote_config_repo + "/rocm:rocm/rocm_config.h"),
   894          {},
   895      )
   896  
   897  def _rocm_autoconf_impl(repository_ctx):
   898      """Implementation of the rocm_autoconf repository rule."""
   899      if not _enable_rocm(repository_ctx):
   900          _create_dummy_repository(repository_ctx)
   901      elif _TF_ROCM_CONFIG_REPO in repository_ctx.os.environ:
   902          _create_remote_rocm_repository(
   903              repository_ctx,
   904              repository_ctx.os.environ[_TF_ROCM_CONFIG_REPO],
   905          )
   906      else:
   907          _create_local_rocm_repository(repository_ctx)
   908  
   909  rocm_configure = repository_rule(
   910      implementation = _rocm_autoconf_impl,
   911      environ = [
   912          _GCC_HOST_COMPILER_PATH,
   913          _GCC_HOST_COMPILER_PREFIX,
   914          "TF_NEED_ROCM",
   915          _ROCM_TOOLKIT_PATH,
   916          _TF_ROCM_VERSION,
   917          _TF_MIOPEN_VERSION,
   918          _TF_ROCM_AMDGPU_TARGETS,
   919          _TF_ROCM_CONFIG_REPO,
   920      ],
   921  )
   922  
   923  """Detects and configures the local ROCm toolchain.
   924  
   925  Add the following to your WORKSPACE FILE:
   926  
   927  ```python
   928  rocm_configure(name = "local_config_rocm")
   929  ```
   930  
   931  Args:
   932    name: A unique name for this workspace rule.
   933  """