@@ -26,7 +26,7 @@ load(":parse_whl_name.bzl", "parse_whl_name")
2626load (":patch_whl.bzl" , "patch_whl" )
2727load (":pep508_requirement.bzl" , "requirement" )
2828load (":pypi_repo_utils.bzl" , "pypi_repo_utils" )
29- load (":whl_metadata.bzl" , "whl_metadata" )
29+ load (":whl_metadata.bzl" , "find_whl_metadata" , " whl_metadata" )
3030load (":whl_target_platforms.bzl" , "whl_target_platforms" )
3131
3232_CPPFLAGS = "CPPFLAGS"
@@ -265,6 +265,79 @@ def _create_repository_execution_environment(rctx, python_interpreter, logger =
265265 env [_CPPFLAGS ] = " " .join (cppflags )
266266 return env
267267
268+ def _extract_whl_star (rctx , * , whl_path , logger ):
269+ install_dir_path = whl_path .dirname .get_child ("site-packages" )
270+ repo_utils .extract (
271+ rctx ,
272+ archive = whl_path ,
273+ output = install_dir_path ,
274+ supports_whl_extraction = rp_config .supports_whl_extraction ,
275+ )
276+ metadata_file = find_whl_metadata (
277+ install_dir = install_dir_path ,
278+ logger = logger ,
279+ )
280+
281+ # Get the <prefix>.dist_info dir name
282+ dist_info_dir = metadata_file .dirname
283+ rctx .file (
284+ dist_info_dir .get_child ("INSTALLER" ),
285+ "https://github.com/bazel-contrib/rules_python#pipstar" ,
286+ )
287+ repo_root_dir = whl_path .dirname
288+
289+ # Get the <prefix>.dist_info dir name
290+ data_dir = dist_info_dir .dirname .get_child (dist_info_dir .basename [:- len (".dist-info" )] + ".data" )
291+ if data_dir .exists :
292+ for prefix , dest in {
293+ # https://docs.python.org/3/library/sysconfig.html#posix-prefix
294+ # We are taking this from the legacy whl installer config
295+ "data" : "data" ,
296+ "headers" : "include" ,
297+ "platlib" : "site-packages" ,
298+ "purelib" : "site-packages" ,
299+ "scripts" : "bin" ,
300+ }.items ():
301+ src = data_dir .get_child (prefix )
302+ dest = repo_root_dir .get_child (dest )
303+ if src .exists :
304+ rctx .rename (src , dest )
305+
306+ # TODO @aignas 2025-12-16: when moving scripts to `bin`, rewrite the #!python
307+ # shebang to be something else, for inspiration look at the hermetic
308+ # toolchain wrappers
309+
310+ def _extract_whl_py (rctx , * , python_interpreter , args , whl_path , environment , logger ):
311+ target_platforms = rctx .attr .experimental_target_platforms or []
312+ if target_platforms :
313+ parsed_whl = parse_whl_name (whl_path .basename )
314+
315+ # NOTE @aignas 2023-12-04: if the wheel is a platform specific wheel, we
316+ # only include deps for that target platform
317+ if parsed_whl .platform_tag != "any" :
318+ target_platforms = [
319+ p .target_platform
320+ for p in whl_target_platforms (
321+ platform_tag = parsed_whl .platform_tag ,
322+ abi_tag = parsed_whl .abi_tag .strip ("tm" ),
323+ )
324+ ]
325+
326+ pypi_repo_utils .execute_checked (
327+ rctx ,
328+ op = "whl_library.ExtractWheel({}, {})" .format (rctx .attr .name , whl_path ),
329+ python = python_interpreter ,
330+ arguments = args + [
331+ "--whl-file" ,
332+ whl_path ,
333+ ] + ["--platform={}" .format (p ) for p in target_platforms ],
334+ srcs = rctx .attr ._python_srcs ,
335+ environment = environment ,
336+ quiet = rctx .attr .quiet ,
337+ timeout = rctx .attr .timeout ,
338+ logger = logger ,
339+ )
340+
268341def _whl_library_impl (rctx ):
269342 logger = repo_utils .logger (rctx )
270343 python_interpreter = pypi_repo_utils .resolve_python_interpreter (
@@ -327,6 +400,8 @@ def _whl_library_impl(rctx):
327400
328401 # also enable pipstar for any whls that are downloaded without `pip`
329402 enable_pipstar = (rp_config .enable_pipstar or whl_path ) and rctx .attr .config_load
403+ enable_pipstar_extract = (rp_config .enable_pipstar and rp_config .bazel_8_or_later ) and rctx .attr .config_load
404+
330405 if not whl_path :
331406 if rctx .attr .urls :
332407 op_tmpl = "whl_library.BuildWheelFromSource({name}, {requirement})"
@@ -372,19 +447,24 @@ def _whl_library_impl(rctx):
372447 timeout = rctx .attr .timeout ,
373448 )
374449
450+ if enable_pipstar_extract :
451+ _extract_whl_star (rctx , whl_path = whl_path , logger = logger )
452+ else :
453+ _extract_whl_py (
454+ rctx ,
455+ python_interpreter = python_interpreter ,
456+ args = args ,
457+ whl_path = whl_path ,
458+ environment = environment ,
459+ logger = logger ,
460+ )
461+
375462 # NOTE @aignas 2025-09-28: if someone has an old vendored file that does not have the
376463 # dep_template set or the packages is not set either, we should still not break, best to
377464 # disable pipstar for that particular case.
378465 #
379466 # Remove non-pipstar and config_load check when we release rules_python 2.
380467 if enable_pipstar :
381- repo_utils .extract (
382- rctx ,
383- archive = whl_path ,
384- output = "site-packages" ,
385- supports_whl_extraction = rp_config .supports_whl_extraction ,
386- )
387-
388468 install_dir_path = whl_path .dirname .get_child ("site-packages" )
389469 metadata = whl_metadata (
390470 install_dir = install_dir_path ,
@@ -439,36 +519,6 @@ def _whl_library_impl(rctx):
439519 extras = requirement (rctx .attr .requirement ).extras ,
440520 )
441521 else :
442- target_platforms = rctx .attr .experimental_target_platforms or []
443- if target_platforms :
444- parsed_whl = parse_whl_name (whl_path .basename )
445-
446- # NOTE @aignas 2023-12-04: if the wheel is a platform specific wheel, we
447- # only include deps for that target platform
448- if parsed_whl .platform_tag != "any" :
449- target_platforms = [
450- p .target_platform
451- for p in whl_target_platforms (
452- platform_tag = parsed_whl .platform_tag ,
453- abi_tag = parsed_whl .abi_tag .strip ("tm" ),
454- )
455- ]
456-
457- pypi_repo_utils .execute_checked (
458- rctx ,
459- op = "whl_library.ExtractWheel({}, {})" .format (rctx .attr .name , whl_path ),
460- python = python_interpreter ,
461- arguments = args + [
462- "--whl-file" ,
463- whl_path ,
464- ] + ["--platform={}" .format (p ) for p in target_platforms ],
465- srcs = rctx .attr ._python_srcs ,
466- environment = environment ,
467- quiet = rctx .attr .quiet ,
468- timeout = rctx .attr .timeout ,
469- logger = logger ,
470- )
471-
472522 metadata = json .decode (rctx .read ("metadata.json" ))
473523 rctx .delete ("metadata.json" )
474524
0 commit comments