Skip to content

Commit

Permalink
Fixing Windows Python Interpreter symlink issues
Browse files Browse the repository at this point in the history
When using Windows you cannot run the symlink directly.

Because of how symlinks work in Windows we need to write a powershell
script that allows us to resolve the link.
Unlike Linux and OSX we cannot execute the Python symlink directly.
Windows throws an error "-1073741515", when we execute the symlink
directory.  This error means that the Python binary cannot find dlls.
This is because the symlink that we create is not in the same folder
as the dlls.

This commit introduces code that resolves the actual location of the
interpreter using powershell.
  • Loading branch information
chrislovecnm committed Jun 13, 2023
1 parent 2c28e61 commit 76e1fe5
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 6 deletions.
22 changes: 21 additions & 1 deletion python/extensions/interpreter.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@

"Module extension that finds the current toolchain Python binary and creates a symlink to it."

load("//python:versions.bzl", "WINDOWS_NAME", "WINDOWS_SYMLINK_SCRIPT")
load(
"//python/private:toolchains_repo.bzl",
"get_host_os_arch",
"get_host_platform",
)
load("@pythons_hub//:interpreters.bzl", "INTERPRETER_LABELS")

def _interpreter_impl(mctx):
Expand Down Expand Up @@ -55,7 +61,21 @@ def _interpreter_repo_impl(rctx):
if actual_interpreter_label == None:
fail("Unable to find interpreter with name '{}'".format(rctx.attr.python_name))

rctx.symlink(actual_interpreter_label, "python")
# We use the name "python" regardless of the OS in order to make this more
# portible.
python_name = "python"
rctx.symlink(actual_interpreter_label, python_name)

# Because of how symlinks work in Windows we need to write a powershell
# script that allows us to resolve the link.
# Unlike Linux and OSX we cannot execute the Python symlink directly.
# Windows throws an error "-1073741515", when we execute the symlink
# directory. This error means that the Python binary cannot find dlls.
# This is because the symlink that we create is not in the same folder
# as the dlls.
os, _ = get_host_os_arch(rctx)
if os == WINDOWS_NAME:
rctx.file(WINDOWS_SYMLINK_SCRIPT, "Get-Item {} | Select-Object -ExpandProperty Target".format(python_name))

_interpreter_repo = repository_rule(
doc = """\
Expand Down
41 changes: 36 additions & 5 deletions python/pip_install/pip_repository.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
""

load("//python:repositories.bzl", "get_interpreter_dirname", "is_standalone_interpreter")
load("//python:versions.bzl", "WINDOWS_NAME", "WINDOWS_SYMLINK_SCRIPT")
load("//python/pip_install:repositories.bzl", "all_requirements")
load("//python/pip_install:requirements_parser.bzl", parse_requirements = "parse")
load("//python/private:toolchains_repo.bzl", "get_host_os_arch")
load("//python/pip_install/private:srcs.bzl", "PIP_INSTALL_PY_SRCS")

CPPFLAGS = "CPPFLAGS"
Expand Down Expand Up @@ -72,8 +74,38 @@ def _resolve_python_interpreter(rctx):
python_interpreter = _get_python_interpreter_attr(rctx)

if rctx.attr.python_interpreter_target != None:
target = rctx.attr.python_interpreter_target
python_interpreter = rctx.path(target)
# If we have @@ we have bzlmod so we need to hand Windows differently.
if str(Label("//:unused")).startswith("@@"):
(os, _) = get_host_os_arch(rctx)
# If we have Windows the symlink will not work directly and we need
# to run a powershell script.
# The powershell script will resolve the Python target and
# return that string.
if os == WINDOWS_NAME:
path = rctx.path(rctx.attr.python_interpreter_target).dirname
ps1 = "{path}/{ps1}".format(path = path, ps1 = WINDOWS_SYMLINK_SCRIPT)
if rctx.path(ps1) == "":
# We are missing the powershell script somehow. Our only option is to
# try to use the target.
return rctx.path(rctx.attr.python_interpreter_target)
# The powershell script returns the "readlink" value of the
# Python binary using a powershell command.
result = rctx.execute(
["powershell", ps1],
working_directory = str(path),
)

# I have had a case where powershell does not return a
# return_code, but we have stderr.
if result.return_code or result.stdout == "" or result.stderr != "":
fail("resolving Python target %s failed: %s (%s) %s" % (rctx.attr.name, result.stdout, result.stderr, result.return_code))
# The powershell script returns newline, and stripping that
# newline in Powershell is non-trivial. We can just remove it.
python_interpreter = result.stdout.strip()
else:
# The OS is Linux or OSX so we can just use the interpreter target
# directly.
python_interpreter = rctx.path(rctx.attr.python_interpreter_target)
elif "/" not in python_interpreter:
found_python_interpreter = rctx.which(python_interpreter)
if not found_python_interpreter:
Expand Down Expand Up @@ -670,7 +702,6 @@ py_binary(

def _whl_library_impl(rctx):
python_interpreter = _resolve_python_interpreter(rctx)

args = [
python_interpreter,
"-m",
Expand Down Expand Up @@ -699,14 +730,14 @@ def _whl_library_impl(rctx):
)

if result.return_code:
fail("whl_library %s failed: %s (%s)" % (rctx.attr.name, result.stdout, result.stderr))
fail("whl_library %s failed: %s (%s) error code: '%s'" % (rctx.attr.name, result.stdout, result.stderr, result.return_code))

return

whl_library_attrs = {
"annotation": attr.label(
doc = (
"Optional json encoded file containing annotation to apply to the extracted wheel. " +
"Optional json encoded file containing annotrctx.attr.quiet to apply to the extracted wheel. " +
"See `package_annotation`"
),
allow_files = True,
Expand Down
1 change: 1 addition & 0 deletions python/versions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
MACOS_NAME = "mac os"
LINUX_NAME = "linux"
WINDOWS_NAME = "windows"
WINDOWS_SYMLINK_SCRIPT = "resolve-link.ps1"

DEFAULT_RELEASE_BASE_URL = "https://github.com/indygreg/python-build-standalone/releases/download"

Expand Down

0 comments on commit 76e1fe5

Please sign in to comment.