|
16 | 16 |
|
17 | 17 | load("@bazel_skylib//lib:paths.bzl", "paths")
|
18 | 18 | load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
|
| 19 | +load(":common.bzl", "runfiles_root_path") |
19 | 20 | load(":py_exec_tools_info.bzl", "PyExecToolsInfo")
|
20 | 21 | load(":sentinel.bzl", "SentinelInfo")
|
21 | 22 | load(":toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE")
|
@@ -82,24 +83,86 @@ See {obj}`PyExecToolsInfo.exec_interpreter` for further docs.
|
82 | 83 | },
|
83 | 84 | )
|
84 | 85 |
|
| 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 | + |
85 | 117 | def _current_interpreter_executable_impl(ctx):
|
86 | 118 | toolchain = ctx.toolchains[TARGET_TOOLCHAIN_TYPE]
|
87 | 119 | runtime = toolchain.py3_runtime
|
| 120 | + direct = [] |
88 | 121 |
|
89 | 122 | # NOTE: We name the output filename after the underlying file name
|
90 | 123 | # because of things like pyenv: they use $0 to determine what to
|
91 | 124 | # re-exec. If it's not a recognized name, then they fail.
|
92 | 125 | 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) |
95 | 154 | 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 | + |
98 | 161 | return [
|
99 | 162 | toolchain,
|
100 | 163 | DefaultInfo(
|
101 | 164 | executable = executable,
|
102 |
| - runfiles = ctx.runfiles([executable], transitive_files = runtime.files), |
| 165 | + runfiles = ctx.runfiles(direct, transitive_files = runtime.files), |
103 | 166 | ),
|
104 | 167 | ]
|
105 | 168 |
|
|
0 commit comments