Skip to content

fix(local_runtime): Search for libs in sys._base_executable when available. #3178

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

Merged
merged 3 commits into from
Aug 16, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 37 additions & 18 deletions python/private/get_local_runtime_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
_IS_DARWIN = sys.platform == "darwin"


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

if _IS_WINDOWS:
# On Windows DLLs go in the same directory as the executable, while .lib
# files live in the lib/ or libs/ subdirectory.
lib_dirs.append(get_config("BINDIR"))
lib_dirs.append(os.path.join(os.path.dirname(sys.executable)))
lib_dirs.append(os.path.join(os.path.dirname(sys.executable), "lib"))
lib_dirs.append(os.path.join(os.path.dirname(sys.executable), "libs"))
elif not _IS_DARWIN:
# On most systems the executable is in a bin/ directory and the libraries
# are in a sibling lib/ directory.
lib_dirs.append(
os.path.join(os.path.dirname(os.path.dirname(sys.executable)), "lib")
)
if not _IS_DARWIN:
for exec_dir in (
os.path.dirname(base_executable) if base_executable else None,
get_config("BINDIR"),
):
if not exec_dir:
continue
if _IS_WINDOWS:
# On Windows DLLs go in the same directory as the executable, while .lib
# files live in the lib/ or libs/ subdirectory.
lib_dirs.append(exec_dir)
lib_dirs.append(os.path.join(exec_dir, "lib"))
lib_dirs.append(os.path.join(exec_dir, "libs"))
else:
# On most systems the executable is in a bin/ directory and the libraries
# are in a sibling lib/ directory.
lib_dirs.append(os.path.join(os.path.dirname(exec_dir), "lib"))

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


def _get_python_library_info():
def _get_python_library_info(base_executable):
"""Returns a dictionary with the static and dynamic python libraries."""
config_vars = sysconfig.get_config_vars()

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

search_directories = _search_directories(config_vars.get)
search_directories = _search_directories(config_vars.get, base_executable)
search_libnames = _search_library_names(config_vars.get)

def _add_if_exists(target, path):
Expand Down Expand Up @@ -187,13 +191,28 @@ def _add_if_exists(target, path):
}


def _get_base_executable():
"""Returns the base executable path."""
try:
if sys._base_executable: # pylint: disable=protected-access
return sys._base_executable # pylint: disable=protected-access
except AttributeError:
# Bug reports indicate sys._base_executable doesn't exist in some cases,
# but it's not clear why.
# See https://github.com/bazel-contrib/rules_python/issues/3172
pass
# The normal sys.executable is the next-best guess if sys._base_executable
# is missing.
return sys.executable


data = {
"major": sys.version_info.major,
"minor": sys.version_info.minor,
"micro": sys.version_info.micro,
"include": sysconfig.get_path("include"),
"implementation_name": sys.implementation.name,
"base_executable": sys._base_executable,
"base_executable": _get_base_executable(),
}
data.update(_get_python_library_info())
data.update(_get_python_library_info(_get_base_executable()))
print(json.dumps(data))