Skip to content

Commit ee19c17

Browse files
committed
fix the exec toolchain for RBE
1 parent 9256468 commit ee19c17

File tree

1 file changed

+68
-5
lines changed

1 file changed

+68
-5
lines changed

python/private/py_exec_tools_toolchain.bzl

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
load("@bazel_skylib//lib:paths.bzl", "paths")
1818
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
19+
load(":common.bzl", "runfiles_root_path")
1920
load(":py_exec_tools_info.bzl", "PyExecToolsInfo")
2021
load(":sentinel.bzl", "SentinelInfo")
2122
load(":toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE")
@@ -82,24 +83,86 @@ See {obj}`PyExecToolsInfo.exec_interpreter` for further docs.
8283
},
8384
)
8485

86+
def relative_path(from_, to):
87+
"""Compute a relative path from one path to another.
88+
89+
Args:
90+
from_: {type}`str` the starting directory. Note that it should be
91+
a directory because relative-symlinks are relative to the
92+
directory the symlink resides in.
93+
to: {type}`str` the path that `from_` wants to point to
94+
95+
Returns:
96+
{type}`str` a relative path
97+
"""
98+
from_parts = from_.split("/")
99+
to_parts = to.split("/")
100+
101+
# Strip common leading parts from both paths
102+
n = min(len(from_parts), len(to_parts))
103+
for _ in range(n):
104+
if from_parts[0] == to_parts[0]:
105+
from_parts.pop(0)
106+
to_parts.pop(0)
107+
else:
108+
break
109+
110+
# Impossible to compute a relative path without knowing what ".." is
111+
if from_parts and from_parts[0] == "..":
112+
fail("cannot compute relative path from '%s' to '%s'", from_, to)
113+
114+
parts = ([".."] * len(from_parts)) + to_parts
115+
return paths.join(*parts)
116+
85117
def _current_interpreter_executable_impl(ctx):
86118
toolchain = ctx.toolchains[TARGET_TOOLCHAIN_TYPE]
87119
runtime = toolchain.py3_runtime
120+
direct = []
88121

89122
# NOTE: We name the output filename after the underlying file name
90123
# because of things like pyenv: they use $0 to determine what to
91124
# re-exec. If it's not a recognized name, then they fail.
92125
if runtime.interpreter:
93-
executable = ctx.actions.declare_file(runtime.interpreter.basename)
94-
ctx.actions.symlink(output = executable, target_file = runtime.interpreter, is_executable = True)
126+
# Even though ctx.actions.symlink() could be used, we bump into the issue
127+
# with RBE where bazel is making a copy to the file instead of symlinking
128+
# to the hermetic toolchain repository. This means that we need to employ
129+
# a similar strategy to how the `py_executable` venv is created where the
130+
# file in the `runfiles` is a dangling symlink into the hermetic toolchain
131+
# repository. This smells like a bug in RBE, but I would not be surprised
132+
# if it is not one.
133+
134+
# Create a dangling symlink in `bin/python3` to the real interpreter
135+
# in the hermetic toolchain.
136+
interpreter_basename = runtime.interpreter.basename
137+
executable = ctx.actions.declare_symlink("bin/" + interpreter_basename)
138+
direct.append(executable)
139+
interpreter_actual_path = runfiles_root_path(ctx, runtime.interpreter.short_path)
140+
target_path = relative_path(
141+
# dirname is necessary because a relative symlink is relative to
142+
# the directory the symlink resides within.
143+
from_ = paths.dirname(runfiles_root_path(ctx, executable.short_path)),
144+
to = interpreter_actual_path,
145+
)
146+
ctx.actions.symlink(output = executable, target_path = target_path)
147+
148+
# Create a dangling symlink into the runfiles and use that as the
149+
# entry point.
150+
interpreter_actual_path = runfiles_root_path(ctx, executable.short_path)
151+
executable = ctx.actions.declare_symlink(interpreter_basename)
152+
target_path = interpreter_basename + ".runfiles/" + interpreter_actual_path
153+
ctx.actions.symlink(output = executable, target_path = target_path)
95154
else:
96-
executable = ctx.actions.declare_symlink(paths.basename(runtime.interpreter_path))
97-
ctx.actions.symlink(output = executable, target_path = runtime.interpreter_path)
155+
interpreter_basename = paths.basename(runtime.interpreter.interpreter_path)
156+
executable = ctx.actions.declare_symlink(interpreter_basename)
157+
direct.append(executable)
158+
target_path = runtime.interpreter_path
159+
ctx.actions.symlink(output = executable, target_path = target_path)
160+
98161
return [
99162
toolchain,
100163
DefaultInfo(
101164
executable = executable,
102-
runfiles = ctx.runfiles([executable], transitive_files = runtime.files),
165+
runfiles = ctx.runfiles(direct, transitive_files = runtime.files),
103166
),
104167
]
105168

0 commit comments

Comments
 (0)