Skip to content

Commit 43706c4

Browse files
committed
fix: better bootstrap manifest handling
1 parent 4187c31 commit 43706c4

File tree

1 file changed

+45
-4
lines changed

1 file changed

+45
-4
lines changed

python/private/python_bootstrap_template.txt

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import sys
1010
import os
1111
import subprocess
1212
import uuid
13-
# runfiles-relative path
1413
# NOTE: The sentinel strings are split (e.g., "%stage2" + "_bootstrap%") so that
1514
# the substitution logic won't replace them. This allows runtime detection of
1615
# unsubstituted placeholders, which occurs when native py_binary is used in
1716
# external repositories. In that case, we fall back to %main% which Bazel's
1817
# native rule does substitute.
1918
_STAGE2_BOOTSTRAP_SENTINEL = "%stage2" + "_bootstrap%"
19+
# runfiles-root-relative path
2020
STAGE2_BOOTSTRAP="%stage2_bootstrap%"
2121

2222
# NOTE: The fallback logic from stage2_bootstrap to main is only present
@@ -165,6 +165,29 @@ def print_verbose(*args, mapping=None, values=None):
165165
else:
166166
print("bootstrap: stage 1:", *args, file=sys.stderr, flush=True)
167167

168+
def maybe_find_in_manifest(rf_root_path, show_lines=False):
169+
if not os.environ.get("RUNFILES_MANIFEST_FILE"):
170+
return None
171+
if not os.path.exists(os.environ["RUNFILES_MANIFEST_FILE"]):
172+
return None
173+
manifest_path = os.environ["RUNFILES_MANIFEST_FILE"]
174+
print_verbose("search manifest for:", rf_root_path)
175+
176+
# Add trailing space to avoid suffix-string matching
177+
search_for_prefix = rf_root_path.encode("utf8") + b" "
178+
# Use binary to avoid BOM issues on Windows
179+
with open(manifest_path, 'rb') as fp:
180+
for line in fp:
181+
if show_lines:
182+
print_verbose("manifest line:", repr(line))
183+
# NOTE: This doesn't handle escaped manifest lines
184+
if line.startswith(search_for_prefix):
185+
_, _, main_filename = line.partition(b" ")
186+
return main_filename.strip().decode("utf8")
187+
188+
return None
189+
190+
168191
def FindBinary(module_space, bin_name):
169192
"""Finds the real binary if it's not a normal absolute path."""
170193
if not bin_name:
@@ -180,7 +203,13 @@ def FindBinary(module_space, bin_name):
180203
# Use normpath() to convert slashes to os.sep on Windows.
181204
elif os.sep in os.path.normpath(bin_name):
182205
# Case 3: Path is relative to the repo root.
183-
return os.path.join(module_space, bin_name)
206+
full_path = os.path.join(module_space, bin_name)
207+
if os.path.exists(full_path):
208+
return full_path
209+
full_path = maybe_find_in_manifest(bin_name, True)
210+
if not full_path:
211+
raise AssertionError(f"Unable to find Python: {bin_name}")
212+
return full_path
184213
else:
185214
# Case 4: Path has to be looked up in the search path.
186215
return SearchPath(bin_name)
@@ -233,6 +262,19 @@ def FindModuleSpace(main_rel_path):
233262

234263
raise AssertionError('Cannot find .runfiles directory for %s' % sys.argv[0])
235264

265+
def find_main_file(module_space, main_rel_path):
266+
main_filename = os.path.join(module_space, main_rel_path)
267+
main_filename = GetWindowsPathWithUNCPrefix(main_filename)
268+
269+
if os.path.exists(main_filename):
270+
return main_filename
271+
272+
main_filename = maybe_find_in_manifest(STAGE2_BOOTSTRAP)
273+
if main_filename:
274+
return main_filename
275+
raise AssertionError(f"Cannot find main filename: {main_rel_path}")
276+
277+
236278
def ExtractZip(zip_path, dest_dir):
237279
"""Extracts the contents of a zip file, preserving the unix file mode bits.
238280
@@ -420,8 +462,7 @@ def Main():
420462
# See: https://docs.python.org/3.11/using/cmdline.html#envvar-PYTHONSAFEPATH
421463
new_env['PYTHONSAFEPATH'] = '1'
422464

423-
main_filename = os.path.join(module_space, main_rel_path)
424-
main_filename = GetWindowsPathWithUNCPrefix(main_filename)
465+
main_filename = find_main_file(module_space, main_rel_path)
425466
assert os.path.exists(main_filename), \
426467
'Cannot exec() %r: file not found.' % main_filename
427468
assert os.access(main_filename, os.R_OK), \

0 commit comments

Comments
 (0)