-
Notifications
You must be signed in to change notification settings - Fork 278
Search conda environment before general system search in cuda-pathfinder #919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -173,6 +173,135 @@ def load_with_system_search(libname: str) -> Optional[LoadedDL]: | |
| return None | ||
|
|
||
|
|
||
| def load_with_conda_search(libname: str) -> Optional[LoadedDL]: | ||
| """Try to load a library using conda search paths. | ||
|
|
||
| Args: | ||
| libname: The name of the library to load | ||
|
|
||
| Returns: | ||
| A LoadedDL object if successful, None if the library cannot be loaded | ||
| """ | ||
| in_conda_build = False | ||
| in_conda_env = False | ||
| if os.getenv("CONDA_BUILD") == "1": | ||
| in_conda_build = True | ||
| elif os.getenv("CONDA_PREFIX"): | ||
| in_conda_env = True | ||
| else: | ||
| return None | ||
|
|
||
| normal_conda_lib_path = os.path.join("lib") | ||
| # TODO KEITH: All the libs in the targets directory are symlinked into the lib directory, do we need to search it? | ||
| # TODO KEITH: Should we do platform detection here to avoid extra searches? Any considerations we need to do in a | ||
| # cross compilation build environment? | ||
| nvidia_conda_target_lib_paths = [ | ||
| os.path.join("targets", "x86_64-linux", "lib"), | ||
| os.path.join("targets", "sbsa-linux", "lib"), | ||
| ] | ||
| if libname == "nvvm": | ||
| normal_conda_lib_path = os.path.join("nvvm") | ||
| nvidia_conda_target_lib_paths = [ | ||
| os.path.join("targets", "x86_64-linux", "nvvm", "lib64"), | ||
| os.path.join("targets", "sbsa-linux", "nvvm", "lib64"), | ||
| ] | ||
|
|
||
| for soname in get_candidate_sonames(libname): | ||
| if in_conda_build: | ||
| if prefix := os.getenv("PREFIX"): | ||
| for nvidia_conda_target_lib_path in nvidia_conda_target_lib_paths: | ||
| prefix_target_lib_path = os.path.join(prefix, nvidia_conda_target_lib_path) | ||
| if os.path.isdir(prefix_target_lib_path): | ||
| soname = os.path.join(prefix_target_lib_path, soname) | ||
| try: | ||
| handle = _load_lib(libname, soname) | ||
| except OSError: | ||
| pass | ||
| else: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rwgk was this only needed in the case we were relying on |
||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| if abs_path is None: | ||
| raise RuntimeError(f"No expected symbol for {libname=!r}") | ||
| return LoadedDL(abs_path, False, handle._handle) | ||
|
Comment on lines
+216
to
+226
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be refactored into a helper function. I copy pasted it from |
||
| # Only run if not found in the target lib paths | ||
| prefix_normal_lib_path = os.path.join(prefix, normal_conda_lib_path) | ||
| if os.path.isdir(prefix_normal_lib_path): | ||
| soname = os.path.join(prefix_normal_lib_path, soname) | ||
| try: | ||
| handle = _load_lib(libname, soname) | ||
| except OSError: | ||
| pass | ||
| else: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| if abs_path is None: | ||
| raise RuntimeError(f"No expected symbol for {libname=!r}") | ||
| return LoadedDL(abs_path, False, handle._handle) | ||
| if build_prefix := os.getenv("BUILD_PREFIX"): | ||
| for nvidia_conda_target_lib_path in nvidia_conda_target_lib_paths: | ||
| build_prefix_target_lib_path = os.path.join(build_prefix, nvidia_conda_target_lib_path) | ||
| if os.path.isdir(build_prefix_target_lib_path): | ||
| soname = os.path.join(build_prefix_target_lib_path, soname) | ||
| try: | ||
| handle = _load_lib(libname, soname) | ||
| except OSError: | ||
| pass | ||
| else: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| if abs_path is None: | ||
| raise RuntimeError(f"No expected symbol for {libname=!r}") | ||
| return LoadedDL(abs_path, False, handle._handle) | ||
| # Only run if not found in the target lib paths | ||
| build_prefix_normal_lib_path = os.path.join(build_prefix, normal_conda_lib_path) | ||
| if os.path.isdir(build_prefix_normal_lib_path): | ||
| soname = os.path.join(build_prefix_normal_lib_path, soname) | ||
| try: | ||
| handle = _load_lib(libname, soname) | ||
| except OSError: | ||
| pass | ||
| else: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| if abs_path is None: | ||
| raise RuntimeError(f"No expected symbol for {libname=!r}") | ||
| return LoadedDL(abs_path, False, handle._handle) | ||
| elif in_conda_env: | ||
| if conda_prefix := os.getenv("CONDA_PREFIX"): | ||
| for nvidia_conda_target_lib_path in nvidia_conda_target_lib_paths: | ||
| conda_prefix_target_lib_path = os.path.join(conda_prefix, nvidia_conda_target_lib_path) | ||
| if os.path.isdir(conda_prefix_target_lib_path): | ||
| soname = os.path.join(conda_prefix_target_lib_path, soname) | ||
| try: | ||
| handle = _load_lib(libname, soname) | ||
| except OSError: | ||
| pass | ||
| else: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| if abs_path is None: | ||
| raise RuntimeError(f"No expected symbol for {libname=!r}") | ||
| return LoadedDL(abs_path, False, handle._handle) | ||
| # Only run if not found in the target lib paths | ||
| conda_prefix_normal_lib_path = os.path.join(conda_prefix, normal_conda_lib_path) | ||
| if os.path.isdir(conda_prefix_normal_lib_path): | ||
| soname = os.path.join(conda_prefix_normal_lib_path, soname) | ||
| try: | ||
| handle = _load_lib(libname, soname) | ||
| except OSError: | ||
| pass | ||
| else: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| return None | ||
|
|
||
|
|
||
| def _work_around_known_bugs(libname: str, found_path: str) -> None: | ||
| if libname == "nvrtc": | ||
| # Work around bug/oversight in | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -132,6 +132,64 @@ def load_with_system_search(libname: str) -> Optional[LoadedDL]: | |
| return None | ||
|
|
||
|
|
||
| def load_with_conda_search(libname: str) -> Optional[LoadedDL]: | ||
| """Try to load a DLL using conda search paths. | ||
|
|
||
| Args: | ||
| libname: The name of the library to load | ||
|
|
||
| Returns: | ||
| A LoadedDL object if successful, None if the library cannot be loaded | ||
| """ | ||
| in_conda_build = False | ||
| in_conda_env = False | ||
| if os.getenv("CONDA_BUILD") == "1": | ||
| in_conda_build = True | ||
| elif os.getenv("CONDA_PREFIX"): | ||
| in_conda_env = True | ||
| else: | ||
| return None | ||
|
|
||
| normal_conda_lib_path = os.path.join("Library", "bin", "x64") | ||
| if libname == "nvvm": | ||
| normal_conda_lib_path = os.path.join("Library", "nvvm", "bin", "x64") | ||
|
|
||
| for dll_name in SUPPORTED_WINDOWS_DLLS.get(libname, ()): | ||
| if in_conda_build: | ||
| if prefix := os.getenv("PREFIX"): | ||
| prefix_normal_lib_path = os.path.join(prefix, normal_conda_lib_path) | ||
| if os.path.isdir(prefix_normal_lib_path): | ||
| dll_name = os.path.join(prefix_normal_lib_path, dll_name) | ||
| handle = kernel32.LoadLibraryExW(dll_name, None, 0) | ||
| if handle: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| return LoadedDL(abs_path, False, ctypes_handle_to_unsigned_int(handle)) | ||
|
Comment on lines
+163
to
+168
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 we should probably refactor this into a helper function |
||
| if build_prefix := os.getenv("BUILD_PREFIX"): | ||
| build_prefix_normal_lib_path = os.path.join(build_prefix, normal_conda_lib_path) | ||
| if os.path.isdir(build_prefix_normal_lib_path): | ||
| dll_name = os.path.join(build_prefix_normal_lib_path, dll_name) | ||
| handle = kernel32.LoadLibraryExW(dll_name, None, 0) | ||
| if handle: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| return LoadedDL(abs_path, False, ctypes_handle_to_unsigned_int(handle)) | ||
| elif in_conda_env: | ||
| if conda_prefix := os.getenv("CONDA_PREFIX"): | ||
| conda_prefix_normal_lib_path = os.path.join(conda_prefix, normal_conda_lib_path) | ||
| if os.path.isdir(conda_prefix_normal_lib_path): | ||
| dll_name = os.path.join(conda_prefix_normal_lib_path, dll_name) | ||
| handle = kernel32.LoadLibraryExW(dll_name, None, 0) | ||
| if handle: | ||
| # TODO KEITH: Do we need this abs_path_for_dynamic_library call? | ||
| # We're already resolving the absolute path based on the conda environment variables | ||
| abs_path = abs_path_for_dynamic_library(libname, handle) | ||
| return LoadedDL(abs_path, False, ctypes_handle_to_unsigned_int(handle)) | ||
| return None | ||
|
|
||
|
|
||
| def load_with_abs_path(libname: str, found_path: str) -> LoadedDL: | ||
| """Load a dynamic library from the given path. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -23,3 +23,8 @@ Highlights | |||||
| - Improves stability in general and supports nvmath specifically | ||||||
| - Proactive change to improve library loading consistency | ||||||
| - Drops boilerplate docstrings for private functions | ||||||
|
|
||||||
| * Add conda search path prioritization for ``load_nvidia_dynamic_lib()`` (`PR #856 <https://github.com/NVIDIA/cuda-python/pull/856>`_) | ||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| - Enables loading libraries from conda environments before system paths | ||||||
| - Handles version mismatches between the conda environment and the system CTK | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spent a few minutes quizzing ChatGPT about the most robust way to figure out if we're in a conda environment. It produced this:
Typical code snippet looks like:
That is generally considered the most robust test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had thought about this as well, but ultimately decided on using the
$CONDA_PREFIXapproach for a few reasons:python, where technically you could have a pure C++ environment and someone could want to usecuda-pathfinderas a utility in a C++ build pipeline where Python would potentially be coming from outside of the conda environment, in which casesys.prefixwouldn't properly point inside of the activated conda environment.