Skip to content

Commit cda5877

Browse files
laramielrickeylev
andauthored
fix(local_runtime): Search for libs in sys._base_executable when available. (#3178)
Search directory for libraries should look in the same directory as sys._base_executable. Since sys._base_executable may be unset, fallback to sys.executable Found this when trying to build using a venv for [tensorstore](https://github.com/google/tensorstore) on Windows: * Github CI uses nuget to download Python. * Build sets up a Python venv. The venv does not include all the lib directories required to link an extension. Fixes #3172 --------- Co-authored-by: Richard Levasseur <[email protected]>
1 parent eb37df0 commit cda5877

File tree

1 file changed

+37
-18
lines changed

1 file changed

+37
-18
lines changed

python/private/get_local_runtime_info.py

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
_IS_DARWIN = sys.platform == "darwin"
2424

2525

26-
def _search_directories(get_config):
26+
def _search_directories(get_config, base_executable):
2727
"""Returns a list of library directories to search for shared libraries."""
2828
# There's several types of libraries with different names and a plethora
2929
# of settings, and many different config variables to check:
@@ -53,19 +53,23 @@ def _search_directories(get_config):
5353
if config_value and not config_value.endswith(multiarch):
5454
lib_dirs.append(os.path.join(config_value, multiarch))
5555

56-
if _IS_WINDOWS:
57-
# On Windows DLLs go in the same directory as the executable, while .lib
58-
# files live in the lib/ or libs/ subdirectory.
59-
lib_dirs.append(get_config("BINDIR"))
60-
lib_dirs.append(os.path.join(os.path.dirname(sys.executable)))
61-
lib_dirs.append(os.path.join(os.path.dirname(sys.executable), "lib"))
62-
lib_dirs.append(os.path.join(os.path.dirname(sys.executable), "libs"))
63-
elif not _IS_DARWIN:
64-
# On most systems the executable is in a bin/ directory and the libraries
65-
# are in a sibling lib/ directory.
66-
lib_dirs.append(
67-
os.path.join(os.path.dirname(os.path.dirname(sys.executable)), "lib")
68-
)
56+
if not _IS_DARWIN:
57+
for exec_dir in (
58+
os.path.dirname(base_executable) if base_executable else None,
59+
get_config("BINDIR"),
60+
):
61+
if not exec_dir:
62+
continue
63+
if _IS_WINDOWS:
64+
# On Windows DLLs go in the same directory as the executable, while .lib
65+
# files live in the lib/ or libs/ subdirectory.
66+
lib_dirs.append(exec_dir)
67+
lib_dirs.append(os.path.join(exec_dir, "lib"))
68+
lib_dirs.append(os.path.join(exec_dir, "libs"))
69+
else:
70+
# On most systems the executable is in a bin/ directory and the libraries
71+
# are in a sibling lib/ directory.
72+
lib_dirs.append(os.path.join(os.path.dirname(exec_dir), "lib"))
6973

7074
# Dedup and remove empty values, keeping the order.
7175
lib_dirs = [v for v in lib_dirs if v]
@@ -126,7 +130,7 @@ def _search_library_names(get_config):
126130
return {k: None for k in lib_names}.keys()
127131

128132

129-
def _get_python_library_info():
133+
def _get_python_library_info(base_executable):
130134
"""Returns a dictionary with the static and dynamic python libraries."""
131135
config_vars = sysconfig.get_config_vars()
132136

@@ -140,7 +144,7 @@ def _get_python_library_info():
140144
f"{sys.version_info.major}.{sys.version_info.minor}"
141145
)
142146

143-
search_directories = _search_directories(config_vars.get)
147+
search_directories = _search_directories(config_vars.get, base_executable)
144148
search_libnames = _search_library_names(config_vars.get)
145149

146150
def _add_if_exists(target, path):
@@ -187,13 +191,28 @@ def _add_if_exists(target, path):
187191
}
188192

189193

194+
def _get_base_executable():
195+
"""Returns the base executable path."""
196+
try:
197+
if sys._base_executable: # pylint: disable=protected-access
198+
return sys._base_executable # pylint: disable=protected-access
199+
except AttributeError:
200+
# Bug reports indicate sys._base_executable doesn't exist in some cases,
201+
# but it's not clear why.
202+
# See https://github.com/bazel-contrib/rules_python/issues/3172
203+
pass
204+
# The normal sys.executable is the next-best guess if sys._base_executable
205+
# is missing.
206+
return sys.executable
207+
208+
190209
data = {
191210
"major": sys.version_info.major,
192211
"minor": sys.version_info.minor,
193212
"micro": sys.version_info.micro,
194213
"include": sysconfig.get_path("include"),
195214
"implementation_name": sys.implementation.name,
196-
"base_executable": sys._base_executable,
215+
"base_executable": _get_base_executable(),
197216
}
198-
data.update(_get_python_library_info())
217+
data.update(_get_python_library_info(_get_base_executable()))
199218
print(json.dumps(data))

0 commit comments

Comments
 (0)