1- # Copyright 2022-2025 Broadcom.
1+ # Copyright 2022-2026 Broadcom.
22# SPDX-License-Identifier: Apache-2.0
33"""
44Installation and finalization functions for the build process.
55"""
66from __future__ import annotations
77
8+ import base64
89import fnmatch
910import hashlib
1011import io
1819import shutil
1920import sys
2021import tarfile
22+ import zipfile
2123from types import ModuleType
2224from typing import IO , MutableMapping , Optional , Sequence , Union , TYPE_CHECKING
2325
2729 MissingDependencyError ,
2830 Version ,
2931 download_url ,
32+ extract_archive ,
3033 format_shebang ,
3134 runcmd ,
3235)
@@ -246,8 +249,22 @@ def update_ensurepip(directory: pathlib.Path) -> None:
246249
247250 # Detect existing whl. Later versions of python don't include setuptools. We
248251 # only want to update whl files that python expects to be there
249- pip_version = "25.2"
252+ pip_version = "25.3"
253+ pip_whl = f"pip-{ pip_version } -py3-none-any.whl"
254+ pip_whl_path = "44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068"
255+
250256 setuptools_version = "80.9.0"
257+ setuptools_whl = f"setuptools-{ setuptools_version } -py3-none-any.whl"
258+ setuptools_whl_path = (
259+ "a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772"
260+ )
261+
262+ urllib3_version = "2.6.2"
263+ urllib3_tarball = f"urllib3-{ urllib3_version } .tar.gz"
264+ urllib3_tarball_path = (
265+ "1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd"
266+ )
267+
251268 update_pip = False
252269 update_setuptools = False
253270 for file in bundle_dir .glob ("*.whl" ):
@@ -275,11 +292,9 @@ def update_ensurepip(directory: pathlib.Path) -> None:
275292 # Download whl files and update __init__.py
276293 init_file = directory / "ensurepip" / "__init__.py"
277294 if update_pip :
278- whl = f"pip-{ pip_version } -py3-none-any.whl"
279- whl_path = "b7/3f/945ef7ab14dc4f9d7f40288d2df998d1837ee0888ec3659c813487572faa"
280- url = f"https://files.pythonhosted.org/packages/{ whl_path } /{ whl } "
295+ url = f"https://files.pythonhosted.org/packages/{ pip_whl_path } /{ pip_whl } "
281296 download_url (url = url , dest = bundle_dir )
282- assert (bundle_dir / whl ).exists ()
297+ assert (bundle_dir / pip_whl ).exists ()
283298
284299 # Update __init__.py
285300 old = "^_PIP_VERSION.*"
@@ -288,11 +303,9 @@ def update_ensurepip(directory: pathlib.Path) -> None:
288303
289304 # setuptools
290305 if update_setuptools :
291- whl = f"setuptools-{ setuptools_version } -py3-none-any.whl"
292- whl_path = "a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772"
293- url = f"https://files.pythonhosted.org/packages/{ whl_path } /{ whl } "
306+ url = f"https://files.pythonhosted.org/packages/{ setuptools_whl_path } /{ setuptools_whl } "
294307 download_url (url = url , dest = bundle_dir )
295- assert (bundle_dir / whl ).exists ()
308+ assert (bundle_dir / setuptools_whl ).exists ()
296309
297310 # setuptools
298311 old = "^_SETUPTOOLS_VERSION.*"
@@ -302,6 +315,92 @@ def update_ensurepip(directory: pathlib.Path) -> None:
302315 log .debug ("ensurepip __init__.py contents:" )
303316 log .debug (init_file .read_text ())
304317
318+ # TODO: unpack the pip whl using zipfile (wheel isn't installed yet)
319+ pip_whl_extracted = bundle_dir / "pip_whl_extracted"
320+ with zipfile .ZipFile (bundle_dir / pip_whl ) as whl_file :
321+ whl_file .extractall (path = pip_whl_extracted )
322+
323+ # TODO: pull down urllib3 tarball
324+ url = f"https://files.pythonhosted.org/packages/{ urllib3_tarball_path } /{ urllib3_tarball } "
325+ download_url (url = url , dest = bundle_dir )
326+ assert (bundle_dir / urllib3_tarball ).exists ()
327+
328+ # TODO: Extract the tarball
329+ urllib3_extracted = bundle_dir / "urllib3_extracted"
330+ extract_archive (to_dir = urllib3_extracted , archive = bundle_dir / urllib3_tarball )
331+
332+ # TODO: replace urllib3 in pip
333+ # Delete target urllib3
334+ urllib3_target_dir = (
335+ bundle_dir
336+ / pip_whl_extracted
337+ / f"pip-{ pip_version } "
338+ / "pip"
339+ / "_vendor"
340+ / "urllib3"
341+ )
342+ urllib3_source_dir = urllib3_extracted / "src" / "urllib3"
343+ try :
344+ shutil .rmtree (urllib3_target_dir )
345+ log .debug ("Removed urllib3 target directory: %s" , urllib3_target_dir )
346+ except OSError :
347+ log .debug ("Failed to remove urllib3 target directory: %s" , urllib3_target_dir )
348+
349+ # Move source urllib3 to target
350+ urllib3_source_dir .rename (urllib3_target_dir )
351+
352+ # Cleanup urllib3 source and tarball
353+ shutil .rmtree (urllib3_extracted )
354+ (bundle_dir / urllib3_tarball ).unlink (missing_ok = True )
355+
356+ # TODO: recompute the hashes and update dist-info\RECORD
357+ def get_record_entry (file_path , root_dir ):
358+ # 1. Calculate SHA256 and Size
359+ sha256 = hashlib .sha256 ()
360+ size = os .path .getsize (file_path )
361+
362+ with open (file_path , "rb" ) as f :
363+ while chunk := f .read (8192 ):
364+ sha256 .update (chunk )
365+
366+ # 2. Encode to URL-safe Base64 and remove padding '='
367+ hash_base64 = (
368+ base64 .urlsafe_b64encode (sha256 .digest ()).decode ("latin1" ).rstrip ("=" )
369+ )
370+
371+ # 3. Create relative path for RECORD
372+ rel_path = os .path .relpath (file_path , root_dir ).replace (os .sep , "/" )
373+
374+ return f"{ rel_path } ,sha256={ hash_base64 } ,{ size } "
375+
376+ pip_src_dir = pip_whl_extracted / f"pip-{ pip_version } "
377+ # delete existing RECORD file
378+ records_file = pip_src_dir / f"pip-{ pip_version } .dist-info" / "RECORD"
379+ records_file .unlink (missing_ok = True )
380+ # create new RECORD file
381+ files_list = [f for f in pip_src_dir .rglob ("*" ) if f .is_file ()]
382+ with open (records_file , "w" ) as f :
383+ for file in files_list :
384+ f .write (get_record_entry (file , root_dir = pip_src_dir ) + "\n " )
385+ # This is the last line. It shouldn't be there because we removed the
386+ # RECORD file before we listed all files
387+ f .write (f"pip-{ pip_version } .dist-info/RECORD,," )
388+ assert records_file .exists ()
389+
390+ # TODO: pack the pip whl
391+ (bundle_dir / pip_whl ).unlink (missing_ok = True )
392+ # We need to do this again so we include the RECORD file
393+ files_list = [f for f in pip_src_dir .rglob ("*" ) if f .is_file ()]
394+ with zipfile .ZipFile (bundle_dir / pip_whl , "w" , zipfile .ZIP_DEFLATED ) as whl_file :
395+ for file in files_list :
396+ arc_name = file .relative_to (pip_src_dir )
397+ whl_file .write (file , arc_name )
398+ assert (bundle_dir / pip_whl ).exists ()
399+
400+ # TODO: Clean up extracted pip
401+ shutil .rmtree (pip_whl_extracted )
402+ assert not pip_whl_extracted .exists ()
403+
305404
306405def install_sysdata (
307406 mod : ModuleType ,
0 commit comments