aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/gpus/cuda_configure.bzl
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/gpus/cuda_configure.bzl')
-rw-r--r--third_party/gpus/cuda_configure.bzl423
1 files changed, 423 insertions, 0 deletions
diff --git a/third_party/gpus/cuda_configure.bzl b/third_party/gpus/cuda_configure.bzl
new file mode 100644
index 0000000000..3682cb305d
--- /dev/null
+++ b/third_party/gpus/cuda_configure.bzl
@@ -0,0 +1,423 @@
+# -*- Python -*-
+"""Repository rule for CUDA autoconfiguration.
+
+`cuda_configure` depends on the following environment variables:
+
+ * `ENABLE_CUDA`: Whether to enable building with CUDA.
+ * `CC`: The GCC host compiler path
+ * `CUDA_TOOLKIT_PATH`: The path to the CUDA toolkit. Default is
+ `/usr/local/cuda`.
+ * `CUDA_VERSION`: The version of the CUDA toolkit. If this is blank, then
+ use the system default.
+ * `CUDNN_VERSION`: The version of the cuDNN library.
+ * `CUDNN_INSTALL_PATH`: The path to the cuDNN library. Default is
+ `/usr/local/cuda`.
+ * `CUDA_COMPUTE_CAPABILITIES`: The CUDA compute capabilities. Default is
+ `3.5,5.2`.
+"""
+
+
+_DEFAULT_CUDA_VERSION = ""
+_DEFAULT_CUDNN_VERSION = ""
+_DEFAULT_CUDA_TOOLKIT_PATH = "/usr/local/cuda"
+_DEFAULT_CUDNN_INSTALL_PATH = "/usr/local/cuda"
+_DEFAULT_CUDA_COMPUTE_CAPABILITIES = ["3.5", "5.2"]
+
+
+# TODO(dzc): Once these functions have been factored out of Bazel's
+# cc_configure.bzl, load them from @bazel_tools instead.
+# BEGIN cc_configure common functions.
+def find_cc(repository_ctx):
+ """Find the C++ compiler."""
+ cc_name = "gcc"
+ if "CC" in repository_ctx.os.environ:
+ cc_name = repository_ctx.os.environ["CC"].strip()
+ if not cc_name:
+ cc_name = "gcc"
+ if cc_name.startswith("/"):
+ # Absolute path, maybe we should make this suported by our which function.
+ return cc_name
+ cc = repository_ctx.which(cc_name)
+ if cc == None:
+ fail(
+ "Cannot find gcc, either correct your path or set the CC" +
+ " environment variable")
+ return cc
+
+
+_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."""
+ path = path.strip()
+ if path.endswith(_OSX_FRAMEWORK_SUFFIX):
+ path = path[:-_OSX_FRAMEWORK_SUFFIX_LEN].strip()
+ return path
+
+
+def get_cxx_inc_directories(repository_ctx, cc):
+ """Compute the list of default C++ include directories."""
+ result = repository_ctx.execute([cc, "-E", "-xc++", "-", "-v"])
+ 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 [repository_ctx.path(_cxx_inc_convert(p))
+ for p in inc_dirs.split("\n")]
+
+# END cc_configure common functions (see TODO above).
+
+
+def _enable_cuda(repository_ctx):
+ if "TF_NEED_CUDA" in repository_ctx.os.environ:
+ enable_cuda = repository_ctx.os.environ["TF_NEED_CUDA"].strip()
+ return enable_cuda == "1"
+ return False
+
+
+def _cuda_toolkit_path(repository_ctx):
+ """Finds the cuda toolkit directory."""
+ cuda_toolkit_path = _DEFAULT_CUDA_TOOLKIT_PATH
+ if "CUDA_TOOLKIT_PATH" in repository_ctx.os.environ:
+ cuda_toolkit_path = repository_ctx.os.environ["CUDA_TOOLKIT_PATH"].strip()
+ if not repository_ctx.path(cuda_toolkit_path).exists:
+ fail("Cannot find cuda toolkit path.")
+ return cuda_toolkit_path
+
+
+def _cudnn_install_basedir(repository_ctx):
+ """Finds the cudnn install directory."""
+ cudnn_install_path = _DEFAULT_CUDNN_INSTALL_PATH
+ if "CUDNN_INSTALL_PATH" in repository_ctx.os.environ:
+ cudnn_install_path = repository_ctx.os.environ["CUDNN_INSTALL_PATH"].strip()
+ if not repository_ctx.path(cudnn_install_path).exists:
+ fail("Cannot find cudnn install path.")
+ return cudnn_install_path
+
+
+def _cuda_version(repository_ctx):
+ """Detects the cuda version."""
+ if "CUDA_VERSION" in repository_ctx.os.environ:
+ return repository_ctx.os.environ["CUDA_VERSION"].strip()
+ else:
+ return ""
+
+
+def _cudnn_version(repository_ctx):
+ """Detects the cudnn version."""
+ if "CUDNN_VERSION" in repository_ctx.os.environ:
+ return repository_ctx.os.environ["CUDNN_VERSION"].strip()
+ else:
+ return ""
+
+
+def _compute_capabilities(repository_ctx):
+ """Returns a list of strings representing cuda compute capabilities."""
+ if "CUDA_COMPUTE_CAPABILITIES" not in repository_ctx.os.environ:
+ return _DEFAULT_CUDA_COMPUTE_CAPABILITIES
+ capabilities_str = repository_ctx.os.environ["CUDA_COMPUTE_CAPABILITIES"]
+ capabilities = capabilities_str.split(",")
+ for capability in capabilities:
+ # Workaround for Skylark's lack of support for regex. This check should
+ # be equivalent to checking:
+ # if re.match("[0-9]+.[0-9]+", capability) == None:
+ parts = capability.split(".")
+ if len(parts) != 2 or not parts[0].isdigit() or not parts[1].isdigit():
+ fail("Invalid compute capability: %s" % capability)
+ return capabilities
+
+
+def _cpu_value(repository_ctx):
+ result = repository_ctx.execute(["uname", "-s"])
+ return result.stdout.strip()
+
+
+def _cuda_symlink_files(cpu_value, cuda_version, cudnn_version):
+ """Returns a struct containing platform-specific paths.
+
+ Args:
+ cpu_value: The string representing the host OS.
+ cuda_version: The cuda version as returned by _cuda_version
+ cudnn_version: The cudnn version as returned by _cudnn_version
+ """
+ cuda_ext = ".%s" % cuda_version if cuda_version else ""
+ cudnn_ext = ".%s" % cudnn_version if cudnn_version else ""
+ if cpu_value == "Linux":
+ return struct(
+ cuda_lib_path = "lib64",
+ cuda_rt_lib = "lib64/libcudart.so%s" % cuda_ext,
+ cuda_rt_lib_static = "lib64/libcudart_static.a",
+ cuda_blas_lib = "lib64/libcublas.so%s" % cuda_ext,
+ cuda_dnn_lib = "lib64/libcudnn.so%s" % cudnn_ext,
+ cuda_dnn_lib_alt = "libcudnn.so%s" % cudnn_ext,
+ cuda_rand_lib = "lib64/libcurand.so%s" % cuda_ext,
+ cuda_fft_lib = "lib64/libcufft.so%s" % cuda_ext,
+ cuda_cupti_lib = "extras/CUPTI/lib64/libcupti.so%s" % cuda_ext)
+ elif cpu_value == "Darwin":
+ return struct(
+ cuda_lib_path = "lib",
+ cuda_rt_lib = "lib/libcudart%s.dylib" % cuda_ext,
+ cuda_rt_lib_static = "lib/libcudart_static.a",
+ cuda_blas_lib = "lib/libcublas%s.dylib" % cuda_ext,
+ cuda_dnn_lib = "lib/libcudnn%s.dylib" % cudnn_ext,
+ cuda_dnn_lib_alt = "libcudnn%s.dylib" % cudnn_ext,
+ cuda_rand_lib = "lib/libcurand%s.dylib" % cuda_ext,
+ cuda_fft_lib = "lib/libcufft%s.dylib" % cuda_ext,
+ cuda_cupti_lib = "extras/CUPTI/lib/libcupti%s.dylib" % cuda_ext)
+ else:
+ fail("Not supported CPU value %s" % cpu_value)
+
+
+def _check_lib(repository_ctx, cuda_toolkit_path, cuda_lib):
+ """Checks if cuda_lib exists under cuda_toolkit_path or fail if it doesn't.
+
+ Args:
+ repository_ctx: The repository context.
+ cuda_toolkit_path: The cuda toolkit directory containing the cuda libraries.
+ cuda_lib: The library to look for under cuda_toolkit_path.
+ """
+ lib_path = cuda_toolkit_path + "/" + cuda_lib
+ if not repository_ctx.path(lib_path).exists:
+ fail("Cannot find %s" % lib_path)
+
+
+def _check_dir(repository_ctx, directory):
+ """Checks whether the directory exists and fail if it does not.
+
+ Args:
+ repository_ctx: The repository context.
+ directory: The directory to check the existence of.
+ """
+ if not repository_ctx.path(directory).exists:
+ fail("Cannot find dir: %s" % directory)
+
+
+def _find_cudnn_header_dir(repository_ctx, cudnn_install_basedir):
+ """Returns the path to the directory containing cudnn.h
+
+ Args:
+ repository_ctx: The repository context.
+ cudnn_install_basedir: The cudnn install directory as returned by
+ _cudnn_install_basedir.
+
+ Returns:
+ The path of the directory containing the cudnn header.
+ """
+ if repository_ctx.path(cudnn_install_basedir + "/cudnn.h").exists:
+ return cudnn_install_basedir
+ if repository_ctx.path(cudnn_install_basedir + "/include/cudnn.h").exists:
+ return cudnn_install_basedir + "/include"
+ if repository_ctx.path("/usr/include/cudnn.h").exists:
+ return "/usr/include"
+ fail("Cannot find cudnn.h under %s" % cudnn_install_basedir)
+
+
+def _find_cudnn_lib_path(repository_ctx, cudnn_install_basedir, symlink_files):
+ """Returns the path to the directory containing libcudnn
+
+ Args:
+ repository_ctx: The repository context.
+ cudnn_install_basedir: The cudnn install dir as returned by
+ _cudnn_install_basedir.
+ symlink_files: The symlink files as returned by _cuda_symlink_files.
+
+ Returns:
+ The path of the directory containing the cudnn libraries.
+ """
+ lib_dir = cudnn_install_basedir + "/" + symlink_files.cuda_dnn_lib
+ if repository_ctx.path(lib_dir).exists:
+ return lib_dir
+ alt_lib_dir = cudnn_install_basedir + "/" + symlink_files.cuda_dnn_lib_alt
+ if repository_ctx.path(alt_lib_dir).exists:
+ return alt_lib_dir
+
+ fail("Cannot find %s or %s under %s" %
+ (symlink_files.cuda_dnn_lib, symlink_files.cuda_dnn_lib_alt,
+ cudnn_install_basedir))
+
+
+def _tpl(repository_ctx, tpl, substitutions={}, out=None):
+ if not out:
+ out = tpl.replace(":", "/")
+ repository_ctx.template(
+ out,
+ Label("//third_party/gpus/%s.tpl" % tpl),
+ substitutions)
+
+
+def _file(repository_ctx, label):
+ repository_ctx.template(
+ label.replace(":", "/"),
+ Label("//third_party/gpus/%s.tpl" % label),
+ {})
+
+
+def _create_dummy_repository(repository_ctx):
+ cpu_value = _cpu_value(repository_ctx)
+ symlink_files = _cuda_symlink_files(cpu_value, _DEFAULT_CUDA_VERSION,
+ _DEFAULT_CUDNN_VERSION)
+
+ # Set up BUILD file for cuda/.
+ _file(repository_ctx, "cuda:BUILD")
+ _file(repository_ctx, "cuda:build_defs.bzl")
+ _tpl(repository_ctx, "cuda:platform.bzl",
+ {
+ "%{cuda_version}": _DEFAULT_CUDA_VERSION,
+ "%{cudnn_version}": _DEFAULT_CUDNN_VERSION,
+ "%{platform}": cpu_value,
+ })
+
+ # Create dummy files for the CUDA toolkit since they are still required by
+ # tensorflow/core/platform/default/build_config:cuda.
+ repository_ctx.file("cuda/include/cuda.h", "")
+ repository_ctx.file("cuda/include/cublas.h", "")
+ repository_ctx.file("cuda/include/cudnn.h", "")
+ repository_ctx.file("cuda/extras/CUPTI/include/cupti.h", "")
+ repository_ctx.file("cuda/%s" % symlink_files.cuda_rt_lib, "")
+ repository_ctx.file("cuda/%s" % symlink_files.cuda_rt_lib_static, "")
+ repository_ctx.file("cuda/%s" % symlink_files.cuda_blas_lib, "")
+ repository_ctx.file("cuda/%s" % symlink_files.cuda_dnn_lib, "")
+ repository_ctx.file("cuda/%s" % symlink_files.cuda_rand_lib, "")
+ repository_ctx.file("cuda/%s" % symlink_files.cuda_fft_lib, "")
+ repository_ctx.file("cuda/%s" % symlink_files.cuda_cupti_lib, "")
+
+ # Set up cuda_config.h, which is used by
+ # tensorflow/stream_executor/dso_loader.cc.
+ _tpl(repository_ctx, "cuda:cuda_config.h",
+ {
+ "%{cuda_version}": _DEFAULT_CUDA_VERSION,
+ "%{cudnn_version}": _DEFAULT_CUDNN_VERSION,
+ "%{cuda_compute_capabilities}": ",".join([
+ "CudaVersion(\"%s\")" % c
+ for c in _DEFAULT_CUDA_COMPUTE_CAPABILITIES]),
+ })
+
+
+def _symlink_dir(repository_ctx, src_dir, dest_dir):
+ """Symlinks all the files in a directory.
+
+ Args:
+ repository_ctx: The repository context.
+ src_dir: The source directory.
+ dest_dir: The destination directory to create the symlinks in.
+ """
+ files = repository_ctx.path(src_dir).readdir()
+ for src_file in files:
+ repository_ctx.symlink(src_file, dest_dir + "/" + src_file.basename)
+
+
+def _create_cuda_repository(repository_ctx):
+ """Creates the repository containing files set up to build with CUDA."""
+ cuda_toolkit_path = _cuda_toolkit_path(repository_ctx)
+ cuda_version = _cuda_version(repository_ctx)
+ cudnn_install_basedir = _cudnn_install_basedir(repository_ctx)
+ cudnn_version = _cudnn_version(repository_ctx)
+ compute_capabilities = _compute_capabilities(repository_ctx)
+
+ cpu_value = _cpu_value(repository_ctx)
+ symlink_files = _cuda_symlink_files(cpu_value, cuda_version, cudnn_version)
+ _check_lib(repository_ctx, cuda_toolkit_path, symlink_files.cuda_rt_lib)
+ _check_lib(repository_ctx, cuda_toolkit_path, symlink_files.cuda_cupti_lib)
+ _check_dir(repository_ctx, cudnn_install_basedir)
+
+ cudnn_header_dir = _find_cudnn_header_dir(repository_ctx,
+ cudnn_install_basedir)
+ cudnn_lib_path = _find_cudnn_lib_path(repository_ctx, cudnn_install_basedir,
+ symlink_files)
+
+ # Set up symbolic links for the cuda toolkit. We link at the individual file
+ # level not at the directory level. This is because the external library may
+ # have a different file layout from our desired structure.
+ _symlink_dir(repository_ctx, cuda_toolkit_path + "/include", "cuda/include")
+ _symlink_dir(repository_ctx,
+ cuda_toolkit_path + "/" + symlink_files.cuda_lib_path,
+ "cuda/" + symlink_files.cuda_lib_path)
+ _symlink_dir(repository_ctx, cuda_toolkit_path + "/bin", "cuda/bin")
+ _symlink_dir(repository_ctx, cuda_toolkit_path + "/nvvm", "cuda/nvvm")
+ _symlink_dir(repository_ctx, cuda_toolkit_path + "/extras/CUPTI/include",
+ "cuda/extras/CUPTI/include")
+ repository_ctx.symlink(cuda_toolkit_path + "/" + symlink_files.cuda_cupti_lib,
+ "cuda/" + symlink_files.cuda_cupti_lib)
+
+ # Set up the symbolic links for cudnn if cudnn was was not installed to
+ # CUDA_TOOLKIT_PATH.
+ if not repository_ctx.path("cuda/include/cudnn.h").exists:
+ repository_ctx.symlink(cudnn_header_dir + "/cudnn.h",
+ "cuda/include/cudnn.h")
+ if not repository_ctx.path("cuda/" + symlink_files.cuda_dnn_lib).exists:
+ repository_ctx.symlink(cudnn_lib_path, "cuda/" + symlink_files.cuda_dnn_lib)
+
+ # Set up BUILD file for cuda/
+ _file(repository_ctx, "cuda:BUILD")
+ _file(repository_ctx, "cuda:build_defs.bzl")
+ _tpl(repository_ctx, "cuda:platform.bzl",
+ {
+ "%{cuda_version}": cuda_version,
+ "%{cudnn_version}": cudnn_version,
+ "%{platform}": cpu_value,
+ })
+
+ # Set up crosstool/
+ _file(repository_ctx, "crosstool:BUILD")
+ _tpl(repository_ctx, "crosstool:CROSSTOOL",
+ {
+ "%{cuda_version}": ("-%s" % cuda_version) if cuda_version else "",
+ })
+ _tpl(repository_ctx,
+ "crosstool:clang/bin/crosstool_wrapper_driver_is_not_gcc",
+ {
+ "%{cpu_compiler}": str(find_cc(repository_ctx)),
+ "%{gcc_host_compiler_path}": str(find_cc(repository_ctx)),
+ "%{cuda_compute_capabilities}": ", ".join(
+ ["\"%s\"" % c for c in compute_capabilities]),
+ })
+
+ # Set up cuda_config.h, which is used by
+ # tensorflow/stream_executor/dso_loader.cc.
+ _tpl(repository_ctx, "cuda:cuda_config.h",
+ {
+ "%{cuda_version}": cuda_version,
+ "%{cudnn_version}": cudnn_version,
+ "%{cuda_compute_capabilities}": ",".join(
+ ["CudaVersion(\"%s\")" % c for c in compute_capabilities]),
+ })
+
+
+def _cuda_autoconf_impl(repository_ctx):
+ """Implementation of the cuda_autoconf repository rule."""
+ if not _enable_cuda(repository_ctx):
+ _create_dummy_repository(repository_ctx)
+ else:
+ _create_cuda_repository(repository_ctx)
+
+
+cuda_configure = repository_rule(
+ implementation = _cuda_autoconf_impl,
+ local = True,
+)
+"""Detects and configures the local CUDA toolchain.
+
+Add the following to your WORKSPACE FILE:
+
+```python
+cuda_configure(name = "local_config_cuda")
+```
+
+Args:
+ name: A unique name for this workspace rule.
+"""