feat(bzlmod): cross-platform builds without experimental_index_url (#2325)

With this change we finally generate the same lock file within the
legacy code `pip.parse` code path and it allows to slowly transition to
using the new code path as much as possible without user doing anything.

This moves the selection of the host-compatible lock file from the
extension evaluation to the build phase - note, we will generate extra
repositories here which might not work on the host platform, however, if
the users are consuming the `whl_library` repos through the hub repo
only, then everything should work. A known issue is that it may break
`bazel query` and in these usecases it is advisable to use `cquery`
until we have `sdist` cross-building from source fully working.

Summary:
- feat: reuse the `render_pkg_aliases` for when filename is not known
  but platform is known
- feat: support generating the extra config settings required
- feat: `get_whl_flag_versions` now generates extra args for the rules
- feat: make lock file generation the same irrespective of the host
  platform
- test: add an extra test with multiple requirements files
- feat: support cross-platform builds using `download_only = True` in
  legacy setups

Note, that users depending on the naming of the whl libraries will need
to start using `extra_hub_aliases` attribute instead to keep their
setups not relying on this implementation detail.

Fixes #2268
Work towards #260

---------

Co-authored-by: Richard Levasseur <richardlev@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d5b757e..8eb269c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,6 +39,12 @@
   [`pip.parse#extra_pip_args`](https://rules-python.readthedocs.io/en/latest/api/rules_python/python/extensions/pip.html#pip.parse.extra_pip_args)
 * (pip.parse) {attr}`pip.parse.whl_modifications` now normalizes the given whl names
   and now `pyyaml` and `PyYAML` will both work.
+* (bzlmod) `pip.parse` spoke repository naming will be changed in an upcoming
+  release in places where the users specify different package versions per
+  platform in the same hub repository. The naming of the spoke repos is considered
+  an implementation detail and we advise the users to use the `hub` repository
+  directly to avoid such breakage in the future. If `rules_python` is missing
+  features to allow one to do that, please raise tickets.
 
 {#v0-0-0-fixed}
 ### Fixed
@@ -51,6 +57,12 @@
   pass the `extra_pip_args` value when building an `sdist`.
 * (pypi) The patched wheel filenames from now on are using local version specifiers
   which fixes usage of the said wheels using standard package managers.
+* (bzlmod) The extension evaluation has been adjusted to always generate the
+  same lock file irrespective if `experimental_index_url` is set by any module
+  or not. Fixes
+  [#2268](https://github.com/bazelbuild/rules_python/issues/2268). A known
+  issue is that it may break `bazel query` and in these use cases it is
+  advisable to use `cquery` or switch to `download_only = True`
 
 {#v0-0-0-added}
 ### Added
@@ -63,6 +75,11 @@
 * (pip.parse) {attr}`pip.parse.extra_hub_aliases` can now be used to expose extra
   targets created by annotations in whl repositories.
   Fixes [#2187](https://github.com/bazelbuild/rules_python/issues/2187).
+* (bzlmod) `pip.parse` now supports `whl-only` setup using
+  `download_only = True` where users can specify multiple requirements files
+  and use the `pip` backend to do the downloading. This was only available for
+  users setting {bzl:obj}`pip.parse.experimental_index_url`, but now users have
+  more options whilst we continue to work on stabilizing the experimental feature.
 
 {#v0-0-0-removed}
 ### Removed
diff --git a/MODULE.bazel b/MODULE.bazel
index de14b86..50f1376 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -56,6 +56,10 @@
 
 pip = use_extension("//python/private/pypi:pip.bzl", "pip_internal")
 pip.parse(
+    # NOTE @aignas 2024-10-26: We have an integration test that depends on us
+    # being able to build sdists for this hub, so explicitly set this to False.
+    download_only = False,
+    experimental_index_url = "https://pypi.org/simple",
     hub_name = "rules_python_publish_deps",
     python_version = "3.11",
     requirements_by_platform = {
@@ -90,17 +94,20 @@
 )
 
 dev_pip = use_extension(
-    "//python/private/pypi:pip.bzl",
-    "pip_internal",
+    "//python/extensions:pip.bzl",
+    "pip",
     dev_dependency = True,
 )
 dev_pip.parse(
-    download_only = True,  # this will not add the `sdist` values to the transitive closures at all.
+    download_only = True,
+    experimental_index_url = "https://pypi.org/simple",
     hub_name = "dev_pip",
     python_version = "3.11",
     requirements_lock = "//docs:requirements.txt",
 )
 dev_pip.parse(
+    download_only = True,
+    experimental_index_url = "https://pypi.org/simple",
     hub_name = "pypiserver",
     python_version = "3.11",
     requirements_lock = "//examples/wheel:requirements_server.txt",
diff --git a/docs/pypi-dependencies.md b/docs/pypi-dependencies.md
index 636fefb..28e630c 100644
--- a/docs/pypi-dependencies.md
+++ b/docs/pypi-dependencies.md
@@ -308,6 +308,59 @@
 
 
 (bazel-downloader)=
+### Multi-platform support
+
+Multi-platform support of cross-building the wheels can be done in two ways - either
+using {bzl:attr}`experimental_index_url` for the {bzl:obj}`pip.parse` bzlmod tag class
+or by using the {bzl:attr}`pip.parse.download_only` setting. In this section we
+are going to outline quickly how one can use the latter option.
+
+Let's say you have 2 requirements files:
+```
+# requirements.linux_x86_64.txt
+--platform=manylinux_2_17_x86_64
+--python-version=39
+--implementation=cp
+--abi=cp39
+
+foo==0.0.1 --hash=sha256:deadbeef
+bar==0.0.1 --hash=sha256:deadb00f
+```
+
+```
+# requirements.osx_aarch64.txt contents
+--platform=macosx_10_9_arm64
+--python-version=39
+--implementation=cp
+--abi=cp39
+
+foo==0.0.3 --hash=sha256:deadbaaf
+```
+
+With these 2 files your {bzl:obj}`pip.parse` could look like:
+```
+pip.parse(
+    hub_name = "pip",
+    python_version = "3.9",
+    # Tell `pip` to ignore sdists
+    download_only = True,
+    requirements_by_platform = {
+        "requirements.linux_x86_64.txt": "linux_x86_64",
+        "requirements.osx_aarch64.txt": "osx_aarch64",
+    },
+)
+```
+
+With this, the `pip.parse` will create a hub repository that is going to
+support only two platforms - `cp39_osx_aarch64` and `cp39_linux_x86_64` and it
+will only use `wheels` and ignore any sdists that it may find on the PyPI
+compatible indexes.
+
+```{note}
+This is only supported on `bzlmd`.
+```
+
+(bazel-downloader)=
 ### Bazel downloader and multi-platform wheel hub repository.
 
 The `bzlmod` `pip.parse` call supports pulling information from `PyPI` (or a
diff --git a/examples/bzlmod/MODULE.bazel b/examples/bzlmod/MODULE.bazel
index 4381cb0..1b8bbbf 100644
--- a/examples/bzlmod/MODULE.bazel
+++ b/examples/bzlmod/MODULE.bazel
@@ -221,6 +221,9 @@
         "host",
     ],
     hub_name = "pip",
+    # Parse all requirements files for the same lock file on all OSes, this will
+    # become the default with 1.0 release
+    parse_all_requirements_files = True,
     python_version = "3.10",
     # The requirements files for each platform that we want to support.
     requirements_by_platform = {
diff --git a/examples/bzlmod/MODULE.bazel.lock b/examples/bzlmod/MODULE.bazel.lock
index c115ef9..eb578f6 100644
--- a/examples/bzlmod/MODULE.bazel.lock
+++ b/examples/bzlmod/MODULE.bazel.lock
@@ -1392,8 +1392,8 @@
     },
     "@@rules_python~//python/extensions:pip.bzl%pip": {
       "general": {
-        "bzlTransitiveDigest": "E5Yr6AjquyIy5ae3c7URmvtPPOm2j+7XOr58GOHp8vw=",
-        "usagesDigest": "iVxh/vcpGrSKpO8rafQwAe7uq+pHhasSXC7Pg4o/1dw=",
+        "bzlTransitiveDigest": "0Qn7Q9FuTxYCxMKm2DsW7mbXYcxL71sS/l1baXvY1vA=",
+        "usagesDigest": "GGeczTmsfE4YHAy32dV/jfOfbYmpyu/QGe35drFuZ5E=",
         "recordedFileInputs": {
           "@@other_module~//requirements_lock_3_11.txt": "a7d0061366569043d5efcf80e34a32c732679367cb3c831c4cdc606adc36d314",
           "@@rules_python~//python/private/pypi/whl_installer/platform.py": "b944b908b25a2f97d6d9f491504ad5d2507402d7e37c802ee878783f87f2aa11",
@@ -6587,8 +6587,8 @@
     },
     "@@rules_python~//python/private/pypi:pip.bzl%pip_internal": {
       "general": {
-        "bzlTransitiveDigest": "wz5L+/+R6gOtD681pNVgPUUipqqPH0bP/b0e22JbSOI=",
-        "usagesDigest": "LYtSAPzhPjmfD9vF39mCED1UQSvHEo2Hv+aK5Z4ZWWc=",
+        "bzlTransitiveDigest": "SnuwsgZv1SGZz4jVPvwaEUwPTnea18fXIueD9vSR3sQ=",
+        "usagesDigest": "O2O2oBIbKEglN2K3FECsRxUKVS/zg/6a86F3MO1ZtmY=",
         "recordedFileInputs": {
           "@@rules_python~//tools/publish/requirements_linux.txt": "8175b4c8df50ae2f22d1706961884beeb54e7da27bd2447018314a175981997d",
           "@@rules_python~//tools/publish/requirements_windows.txt": "7673adc71dc1a81d3661b90924d7a7c0fc998cd508b3cb4174337cef3f2de556",
diff --git a/python/private/pypi/config_settings.bzl b/python/private/pypi/config_settings.bzl
index 492acf1..9f3f4d4 100644
--- a/python/private/pypi/config_settings.bzl
+++ b/python/private/pypi/config_settings.bzl
@@ -148,6 +148,13 @@
             )
 
 def _dist_config_settings(*, suffix, plat_flag_values, **kwargs):
+    if kwargs.get("constraint_values"):
+        # Add python version + platform config settings
+        _dist_config_setting(
+            name = suffix.strip("_"),
+            **kwargs
+        )
+
     flag_values = {_flags.dist: ""}
 
     # First create an sdist, we will be building upon the flag values, which
@@ -277,7 +284,7 @@
 
     return ret
 
-def _dist_config_setting(*, name, is_pip_whl, is_python, python_version, native = native, **kwargs):
+def _dist_config_setting(*, name, is_python, python_version, is_pip_whl = None, native = native, **kwargs):
     """A macro to create a target that matches is_pip_whl_auto and one more value.
 
     Args:
@@ -310,6 +317,10 @@
         # `python_version` setting.
         return
 
+    if not is_pip_whl:
+        native.config_setting(name = _name, **kwargs)
+        return
+
     config_setting_name = _name + "_setting"
     native.config_setting(name = config_setting_name, **kwargs)
 
diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl
index c566027..7b31d0d 100644
--- a/python/private/pypi/extension.bzl
+++ b/python/private/pypi/extension.bzl
@@ -31,7 +31,7 @@
 load(":requirements_files_by_platform.bzl", "requirements_files_by_platform")
 load(":simpleapi_download.bzl", "simpleapi_download")
 load(":whl_library.bzl", "whl_library")
-load(":whl_repo_name.bzl", "whl_repo_name")
+load(":whl_repo_name.bzl", "pypi_repo_name", "whl_repo_name")
 
 def _major_minor_version(version):
     version = semver(version)
@@ -260,10 +260,10 @@
             if v != default
         })
 
+        is_exposed = False
         if get_index_urls:
             # TODO @aignas 2024-05-26: move to a separate function
             found_something = False
-            is_exposed = False
             for requirement in requirements:
                 is_exposed = is_exposed or requirement.is_exposed
                 dists = requirement.whls
@@ -319,35 +319,69 @@
                     exposed_packages[whl_name] = None
                 continue
 
-        requirement = select_requirement(
-            requirements,
-            platform = None if pip_attr.download_only else repository_platform,
-        )
-        if not requirement:
-            # Sometimes the package is not present for host platform if there
-            # are whls specified only in particular requirements files, in that
-            # case just continue, however, if the download_only flag is set up,
-            # then the user can also specify the target platform of the wheel
-            # packages they want to download, in that case there will be always
-            # a requirement here, so we will not be in this code branch.
+        if not pip_attr.parse_all_requirements_files:
+            requirement = select_requirement(
+                requirements,
+                platform = None if pip_attr.download_only else repository_platform,
+            )
+            if not requirement:
+                # Sometimes the package is not present for host platform if there
+                # are whls specified only in particular requirements files, in that
+                # case just continue, however, if the download_only flag is set up,
+                # then the user can also specify the target platform of the wheel
+                # packages they want to download, in that case there will be always
+                # a requirement here, so we will not be in this code branch.
+                continue
+            elif get_index_urls:
+                logger.warn(lambda: "falling back to pip for installing the right file for {}".format(requirement.requirement_line))
+
+            whl_library_args["requirement"] = requirement.requirement_line
+            if requirement.extra_pip_args:
+                whl_library_args["extra_pip_args"] = requirement.extra_pip_args
+
+            # We sort so that the lock-file remains the same no matter the order of how the
+            # args are manipulated in the code going before.
+            repo_name = "{}_{}".format(pip_name, whl_name)
+            whl_libraries[repo_name] = dict(whl_library_args.items())
+            whl_map.setdefault(whl_name, []).append(
+                whl_alias(
+                    repo = repo_name,
+                    version = major_minor,
+                ),
+            )
             continue
-        elif get_index_urls:
-            logger.warn(lambda: "falling back to pip for installing the right file for {}".format(requirement.requirement_line))
 
-        whl_library_args["requirement"] = requirement.requirement_line
-        if requirement.extra_pip_args:
-            whl_library_args["extra_pip_args"] = requirement.extra_pip_args
+        is_exposed = False
+        for requirement in requirements:
+            is_exposed = is_exposed or requirement.is_exposed
+            if get_index_urls:
+                logger.warn(lambda: "falling back to pip for installing the right file for {}".format(requirement.requirement_line))
 
-        # We sort so that the lock-file remains the same no matter the order of how the
-        # args are manipulated in the code going before.
-        repo_name = "{}_{}".format(pip_name, whl_name)
-        whl_libraries[repo_name] = dict(whl_library_args.items())
-        whl_map.setdefault(whl_name, []).append(
-            whl_alias(
-                repo = repo_name,
-                version = major_minor,
-            ),
-        )
+            args = dict(whl_library_args)  # make a copy
+            args["requirement"] = requirement.requirement_line
+            if requirement.extra_pip_args:
+                args["extra_pip_args"] = requirement.extra_pip_args
+
+            if pip_attr.download_only:
+                args.setdefault("experimental_target_platforms", requirement.target_platforms)
+
+            target_platforms = requirement.target_platforms if len(requirements) > 1 else []
+            repo_name = pypi_repo_name(
+                pip_name,
+                whl_name,
+                *target_platforms
+            )
+            whl_libraries[repo_name] = args
+            whl_map.setdefault(whl_name, []).append(
+                whl_alias(
+                    repo = repo_name,
+                    version = major_minor,
+                    target_platforms = target_platforms or None,
+                ),
+            )
+
+        if is_exposed:
+            exposed_packages[whl_name] = None
 
     return struct(
         is_reproducible = is_reproducible,
@@ -502,7 +536,8 @@
             hub_group_map[hub_name] = pip_attr.experimental_requirement_cycles
 
     return struct(
-        # We sort the output here so that the lock file is sorted
+        # We sort so that the lock-file remains the same no matter the order of how the
+        # args are manipulated in the code going before.
         whl_mods = dict(sorted(whl_mods.items())),
         hub_whl_map = {
             hub_name: {
@@ -518,7 +553,10 @@
             }
             for hub_name, group_map in sorted(hub_group_map.items())
         },
-        exposed_packages = {k: sorted(v) for k, v in sorted(exposed_packages.items())},
+        exposed_packages = {
+            k: sorted(v)
+            for k, v in sorted(exposed_packages.items())
+        },
         extra_aliases = {
             hub_name: {
                 whl_name: sorted(aliases)
@@ -526,7 +564,10 @@
             }
             for hub_name, extra_whl_aliases in extra_aliases.items()
         },
-        whl_libraries = dict(sorted(whl_libraries.items())),
+        whl_libraries = {
+            k: dict(sorted(args.items()))
+            for k, args in sorted(whl_libraries.items())
+        },
         is_reproducible = is_reproducible,
     )
 
@@ -601,10 +642,8 @@
     # Build all of the wheel modifications if the tag class is called.
     _whl_mods_impl(mods.whl_mods)
 
-    for name, args in sorted(mods.whl_libraries.items()):
-        # We sort so that the lock-file remains the same no matter the order of how the
-        # args are manipulated in the code going before.
-        whl_library(name = name, **dict(sorted(args.items())))
+    for name, args in mods.whl_libraries.items():
+        whl_library(name = name, **args)
 
     for hub_name, whl_map in mods.hub_whl_map.items():
         hub_repository(
@@ -746,6 +785,20 @@
 """,
             default = True,
         ),
+        "parse_all_requirements_files": attr.bool(
+            default = False,
+            doc = """\
+A temporary flag to enable users to make `pip` extension result always
+the same independent of the whether transitive dependencies use {bzl:attr}`experimental_index_url` or not.
+
+This enables users to migrate to a solution that fixes
+[#2268](https://github.com/bazelbuild/rules_python/issues/2268).
+
+::::{deprecated} 0.38.0
+This is a transition flag and will be removed in a subsequent release.
+::::
+""",
+        ),
         "python_version": attr.string(
             mandatory = True,
             doc = """
@@ -907,24 +960,22 @@
 pypi_internal = module_extension(
     doc = """\
 This extension is used to make dependencies from pypi available.
-
 For now this is intended to be used internally so that usage of the `pip`
 extension in `rules_python` does not affect the evaluations of the extension
 for the consumers.
-
 pip.parse:
 To use, call `pip.parse()` and specify `hub_name` and your requirements file.
 Dependencies will be downloaded and made available in a repo named after the
 `hub_name` argument.
-
 Each `pip.parse()` call configures a particular Python version. Multiple calls
 can be made to configure different Python versions, and will be grouped by
 the `hub_name` argument. This allows the same logical name, e.g. `@pypi//numpy`
 to automatically resolve to different, Python version-specific, libraries.
-
 pip.whl_mods:
 This tag class is used to help create JSON files to describe modifications to
 the BUILD files for wheels.
+
+TODO: will be removed before 1.0.
 """,
     implementation = _pip_non_reproducible,
     tag_classes = {
diff --git a/python/private/pypi/parse_requirements.bzl b/python/private/pypi/parse_requirements.bzl
index aacc8bd..a43217d 100644
--- a/python/private/pypi/parse_requirements.bzl
+++ b/python/private/pypi/parse_requirements.bzl
@@ -168,7 +168,7 @@
         )
 
     ret = {}
-    for whl_name, reqs in requirements_by_platform.items():
+    for whl_name, reqs in sorted(requirements_by_platform.items()):
         requirement_target_platforms = {}
         for r in reqs.values():
             target_platforms = env_marker_target_platforms.get(r.requirement_line, r.target_platforms)
@@ -212,6 +212,8 @@
 def select_requirement(requirements, *, platform):
     """A simple function to get a requirement for a particular platform.
 
+    Only used in WORKSPACE.
+
     Args:
         requirements (list[struct]): The list of requirements as returned by
             the `parse_requirements` function above.
@@ -243,6 +245,8 @@
 def host_platform(ctx):
     """Return a string representation of the repository OS.
 
+    Only used in WORKSPACE.
+
     Args:
         ctx (struct): The `module_ctx` or `repository_ctx` attribute.
 
diff --git a/python/private/pypi/render_pkg_aliases.bzl b/python/private/pypi/render_pkg_aliases.bzl
index 60f4b54..7a75979 100644
--- a/python/private/pypi/render_pkg_aliases.bzl
+++ b/python/private/pypi/render_pkg_aliases.bzl
@@ -326,16 +326,19 @@
     ret = []
     versioned_additions = {}
     for alias in aliases:
-        if not alias.filename:
+        if not alias.filename and not alias.target_platforms:
             ret.append(alias)
             continue
 
         config_settings, all_versioned_settings = get_filename_config_settings(
             # TODO @aignas 2024-05-27: pass the parsed whl to reduce the
             # number of duplicate operations.
-            filename = alias.filename,
+            filename = alias.filename or "",
             target_platforms = alias.target_platforms,
             python_version = alias.version,
+            # If we have multiple platforms but no wheel filename, lets use different
+            # config settings.
+            non_whl_prefix = "sdist" if alias.filename else "",
             **kwargs
         )
 
@@ -437,10 +440,7 @@
         if a.version:
             python_versions[a.version] = None
 
-        if not a.filename:
-            continue
-
-        if a.filename.endswith(".whl") and not a.filename.endswith("-any.whl"):
+        if a.filename and a.filename.endswith(".whl") and not a.filename.endswith("-any.whl"):
             parsed = parse_whl_name(a.filename)
         else:
             for plat in a.target_platforms or []:
@@ -499,10 +499,11 @@
         *,
         filename,
         target_platforms,
-        glibc_versions,
-        muslc_versions,
-        osx_versions,
-        python_version):
+        python_version,
+        glibc_versions = None,
+        muslc_versions = None,
+        osx_versions = None,
+        non_whl_prefix = "sdist"):
     """Get the filename config settings.
 
     Args:
@@ -512,6 +513,8 @@
         muslc_versions: list[tuple[int, int]], list of versions.
         osx_versions: list[tuple[int, int]], list of versions.
         python_version: the python version to generate the config_settings for.
+        non_whl_prefix: the prefix of the config setting when the whl we don't have
+            a filename ending with ".whl".
 
     Returns:
         A tuple:
@@ -520,19 +523,20 @@
     """
     prefixes = []
     suffixes = []
-    if (0, 0) in glibc_versions:
-        fail("Invalid version in 'glibc_versions': cannot specify (0, 0) as a value")
-    if (0, 0) in muslc_versions:
-        fail("Invalid version in 'muslc_versions': cannot specify (0, 0) as a value")
-    if (0, 0) in osx_versions:
-        fail("Invalid version in 'osx_versions': cannot specify (0, 0) as a value")
-
-    glibc_versions = sorted(glibc_versions)
-    muslc_versions = sorted(muslc_versions)
-    osx_versions = sorted(osx_versions)
     setting_supported_versions = {}
 
     if filename.endswith(".whl"):
+        if (0, 0) in glibc_versions:
+            fail("Invalid version in 'glibc_versions': cannot specify (0, 0) as a value")
+        if (0, 0) in muslc_versions:
+            fail("Invalid version in 'muslc_versions': cannot specify (0, 0) as a value")
+        if (0, 0) in osx_versions:
+            fail("Invalid version in 'osx_versions': cannot specify (0, 0) as a value")
+
+        glibc_versions = sorted(glibc_versions)
+        muslc_versions = sorted(muslc_versions)
+        osx_versions = sorted(osx_versions)
+
         parsed = parse_whl_name(filename)
         if parsed.python_tag == "py2.py3":
             py = "py"
@@ -547,10 +551,10 @@
             abi = parsed.abi_tag
 
         if parsed.platform_tag == "any":
-            prefixes = ["{}_{}_any".format(py, abi)]
+            prefixes = ["_{}_{}_any".format(py, abi)]
             suffixes = [_non_versioned_platform(p) for p in target_platforms or []]
         else:
-            prefixes = ["{}_{}".format(py, abi)]
+            prefixes = ["_{}_{}".format(py, abi)]
             suffixes = _whl_config_setting_suffixes(
                 platform_tag = parsed.platform_tag,
                 glibc_versions = glibc_versions,
@@ -559,12 +563,12 @@
                 setting_supported_versions = setting_supported_versions,
             )
     else:
-        prefixes = ["sdist"]
+        prefixes = [""] if not non_whl_prefix else ["_" + non_whl_prefix]
         suffixes = [_non_versioned_platform(p) for p in target_platforms or []]
 
     versioned = {
-        ":is_cp{}_{}_{}".format(python_version, p, suffix): {
-            version: ":is_cp{}_{}_{}".format(python_version, p, setting)
+        ":is_cp{}{}_{}".format(python_version, p, suffix): {
+            version: ":is_cp{}{}_{}".format(python_version, p, setting)
             for version, setting in versions.items()
         }
         for p in prefixes
@@ -572,9 +576,9 @@
     }
 
     if suffixes or versioned:
-        return [":is_cp{}_{}_{}".format(python_version, p, s) for p in prefixes for s in suffixes], versioned
+        return [":is_cp{}{}_{}".format(python_version, p, s) for p in prefixes for s in suffixes], versioned
     else:
-        return [":is_cp{}_{}".format(python_version, p) for p in prefixes], setting_supported_versions
+        return [":is_cp{}{}".format(python_version, p) for p in prefixes], setting_supported_versions
 
 def _whl_config_setting_suffixes(
         platform_tag,
diff --git a/python/private/pypi/whl_repo_name.bzl b/python/private/pypi/whl_repo_name.bzl
index 295f5a4..38ed600 100644
--- a/python/private/pypi/whl_repo_name.bzl
+++ b/python/private/pypi/whl_repo_name.bzl
@@ -22,12 +22,12 @@
     """Return a valid whl_library repo name given a distribution filename.
 
     Args:
-        prefix: str, the prefix of the whl_library.
-        filename: str, the filename of the distribution.
-        sha256: str, the sha256 of the distribution.
+        prefix: {type}`str` the prefix of the whl_library.
+        filename: {type}`str` the filename of the distribution.
+        sha256: {type}`str` the sha256 of the distribution.
 
     Returns:
-        a string that can be used in `whl_library`.
+        a string that can be used in {obj}`whl_library`.
     """
     parts = [prefix]
 
@@ -50,3 +50,22 @@
     parts.append(sha256[:8])
 
     return "_".join(parts)
+
+def pypi_repo_name(prefix, whl_name, *target_platforms):
+    """Return a valid whl_library given a requirement line.
+
+    Args:
+        prefix: {type}`str` the prefix of the whl_library.
+        whl_name: {type}`str` the whl_name to use.
+        *target_platforms: {type}`list[str]` the target platforms to use in the name.
+
+    Returns:
+        {type}`str` that can be used in {obj}`whl_library`.
+    """
+    parts = [
+        prefix,
+        normalize_name(whl_name),
+    ]
+    parts.extend([p.partition("_")[-1] for p in target_platforms])
+
+    return "_".join(parts)
diff --git a/tests/pypi/extension/extension_tests.bzl b/tests/pypi/extension/extension_tests.bzl
index aa120af..0405bad 100644
--- a/tests/pypi/extension/extension_tests.bzl
+++ b/tests/pypi/extension/extension_tests.bzl
@@ -20,14 +20,14 @@
 
 _tests = []
 
-def _mock_mctx(*modules, environ = {}, read = None, os_name = "unittest", os_arch = "exotic"):
+def _mock_mctx(*modules, environ = {}, read = None):
     return struct(
         os = struct(
             environ = environ,
-            name = os_name,
-            arch = os_arch,
+            name = "unittest",
+            arch = "exotic",
         ),
-        read = read or (lambda _: "simple==0.0.1 --hash=sha256:deadbeef"),
+        read = read or (lambda _: "simple==0.0.1 --hash=sha256:deadbeef --hash=sha256:deadbaaf"),
         modules = [
             struct(
                 name = modules[0].name,
@@ -61,7 +61,6 @@
         attrs = dict(
             is_reproducible = subjects.bool,
             exposed_packages = subjects.dict,
-            extra_aliases = subjects.dict,
             hub_group_map = subjects.dict,
             hub_whl_map = subjects.dict,
             whl_libraries = subjects.dict,
@@ -69,29 +68,6 @@
         ),
     )
 
-def _whl_mods(
-        *,
-        whl_name,
-        hub_name,
-        additive_build_content = None,
-        additive_build_content_file = None,
-        copy_executables = {},
-        copy_files = {},
-        data = [],
-        data_exclude_glob = [],
-        srcs_exclude_glob = []):
-    return struct(
-        additive_build_content = additive_build_content,
-        additive_build_content_file = additive_build_content_file,
-        copy_executables = copy_executables,
-        copy_files = copy_files,
-        data = data,
-        data_exclude_glob = data_exclude_glob,
-        hub_name = hub_name,
-        srcs_exclude_glob = srcs_exclude_glob,
-        whl_name = whl_name,
-    )
-
 def _parse(
         *,
         hub_name,
@@ -109,6 +85,7 @@
         extra_pip_args = [],
         isolated = True,
         netrc = None,
+        parse_all_requirements_files = True,
         pip_data_exclude = None,
         python_interpreter = None,
         python_interpreter_target = None,
@@ -136,6 +113,7 @@
         hub_name = hub_name,
         isolated = isolated,
         netrc = netrc,
+        parse_all_requirements_files = parse_all_requirements_files,
         pip_data_exclude = pip_data_exclude,
         python_interpreter = python_interpreter,
         python_interpreter_target = python_interpreter_target,
@@ -176,58 +154,7 @@
     )
 
     pypi.is_reproducible().equals(True)
-    pypi.exposed_packages().contains_exactly({"pypi": []})
-    pypi.hub_group_map().contains_exactly({"pypi": {}})
-    pypi.hub_whl_map().contains_exactly({"pypi": {}})
-    pypi.whl_libraries().contains_exactly({})
-    pypi.whl_mods().contains_exactly({})
-
-_tests.append(_test_simple)
-
-def _test_simple_with_whl_mods(env):
-    pypi = _parse_modules(
-        env,
-        module_ctx = _mock_mctx(
-            _mod(
-                name = "rules_python",
-                whl_mods = [
-                    _whl_mods(
-                        additive_build_content = """\
-filegroup(
-    name = "foo",
-    srcs = ["all"],
-)""",
-                        hub_name = "whl_mods_hub",
-                        whl_name = "simple",
-                    ),
-                ],
-                parse = [
-                    _parse(
-                        hub_name = "pypi",
-                        python_version = "3.15",
-                        requirements_lock = "requirements.txt",
-                        extra_hub_aliases = {
-                            "simple": ["foo"],
-                        },
-                        whl_modifications = {
-                            "@whl_mods_hub//:simple.json": "simple",
-                        },
-                    ),
-                ],
-            ),
-            os_name = "linux",
-            os_arch = "aarch64",
-        ),
-        available_interpreters = {
-            "python_3_15_host": "unit_test_interpreter_target",
-        },
-    )
-
-    pypi.is_reproducible().equals(True)
-    pypi.exposed_packages().contains_exactly({"pypi": []})
-    pypi.extra_aliases().contains_exactly({
-        "pypi": {"simple": ["foo"]},
-    })
+    pypi.exposed_packages().contains_exactly({"pypi": ["simple"]})
     pypi.hub_group_map().contains_exactly({"pypi": {}})
     pypi.hub_whl_map().contains_exactly({"pypi": {
         "simple": [
@@ -242,27 +169,190 @@
     }})
     pypi.whl_libraries().contains_exactly({
         "pypi_315_simple": {
-            "annotation": "@whl_mods_hub//:simple.json",
+            "dep_template": "@pypi//{name}:{target}",
+            "python_interpreter_target": "unit_test_interpreter_target",
+            "repo": "pypi_315",
+            "requirement": "simple==0.0.1 --hash=sha256:deadbeef --hash=sha256:deadbaaf",
+        },
+    })
+    pypi.whl_mods().contains_exactly({})
+
+_tests.append(_test_simple)
+
+def _test_simple_multiple_requirements(env):
+    pypi = _parse_modules(
+        env,
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "rules_python",
+                parse = [
+                    _parse(
+                        hub_name = "pypi",
+                        python_version = "3.15",
+                        requirements_darwin = "darwin.txt",
+                        requirements_windows = "win.txt",
+                    ),
+                ],
+            ),
+            read = lambda x: {
+                "darwin.txt": "simple==0.0.2 --hash=sha256:deadb00f",
+                "win.txt": "simple==0.0.1 --hash=sha256:deadbeef",
+            }[x],
+        ),
+        available_interpreters = {
+            "python_3_15_host": "unit_test_interpreter_target",
+        },
+    )
+
+    pypi.is_reproducible().equals(True)
+    pypi.exposed_packages().contains_exactly({"pypi": ["simple"]})
+    pypi.hub_group_map().contains_exactly({"pypi": {}})
+    pypi.hub_whl_map().contains_exactly({"pypi": {
+        "simple": [
+            struct(
+                config_setting = "//_config:is_python_3.15",
+                filename = None,
+                repo = "pypi_315_simple_windows_x86_64",
+                target_platforms = [
+                    "cp315_windows_x86_64",
+                ],
+                version = "3.15",
+            ),
+            struct(
+                config_setting = "//_config:is_python_3.15",
+                filename = None,
+                repo = "pypi_315_simple_osx_aarch64_osx_x86_64",
+                target_platforms = [
+                    "cp315_osx_aarch64",
+                    "cp315_osx_x86_64",
+                ],
+                version = "3.15",
+            ),
+        ],
+    }})
+    pypi.whl_libraries().contains_exactly({
+        "pypi_315_simple_osx_aarch64_osx_x86_64": {
+            "dep_template": "@pypi//{name}:{target}",
+            "python_interpreter_target": "unit_test_interpreter_target",
+            "repo": "pypi_315",
+            "requirement": "simple==0.0.2 --hash=sha256:deadb00f",
+        },
+        "pypi_315_simple_windows_x86_64": {
             "dep_template": "@pypi//{name}:{target}",
             "python_interpreter_target": "unit_test_interpreter_target",
             "repo": "pypi_315",
             "requirement": "simple==0.0.1 --hash=sha256:deadbeef",
         },
     })
-    pypi.whl_mods().contains_exactly({
-        "whl_mods_hub": {
-            "simple": struct(
-                build_content = "filegroup(\n    name = \"foo\",\n    srcs = [\"all\"],\n)",
-                copy_executables = {},
-                copy_files = {},
-                data = [],
-                data_exclude_glob = [],
-                srcs_exclude_glob = [],
+    pypi.whl_mods().contains_exactly({})
+
+_tests.append(_test_simple_multiple_requirements)
+
+def _test_download_only_multiple(env):
+    pypi = _parse_modules(
+        env,
+        module_ctx = _mock_mctx(
+            _mod(
+                name = "rules_python",
+                parse = [
+                    _parse(
+                        hub_name = "pypi",
+                        python_version = "3.15",
+                        download_only = True,
+                        requirements_by_platform = {
+                            "requirements.linux_x86_64.txt": "linux_x86_64",
+                            "requirements.osx_aarch64.txt": "osx_aarch64",
+                        },
+                    ),
+                ],
             ),
+            read = lambda x: {
+                "requirements.linux_x86_64.txt": """\
+--platform=manylinux_2_17_x86_64
+--python-version=315
+--implementation=cp
+--abi=cp315
+
+simple==0.0.1 --hash=sha256:deadbeef
+extra==0.0.1 --hash=sha256:deadb00f
+""",
+                "requirements.osx_aarch64.txt": """\
+--platform=macosx_10_9_arm64
+--python-version=315
+--implementation=cp
+--abi=cp315
+
+simple==0.0.3 --hash=sha256:deadbaaf
+""",
+            }[x],
+        ),
+        available_interpreters = {
+            "python_3_15_host": "unit_test_interpreter_target",
+        },
+    )
+
+    pypi.is_reproducible().equals(True)
+    pypi.exposed_packages().contains_exactly({"pypi": ["simple"]})
+    pypi.hub_group_map().contains_exactly({"pypi": {}})
+    pypi.hub_whl_map().contains_exactly({"pypi": {
+        "extra": [
+            struct(
+                config_setting = "//_config:is_python_3.15",
+                filename = None,
+                repo = "pypi_315_extra",
+                target_platforms = None,
+                version = "3.15",
+            ),
+        ],
+        "simple": [
+            struct(
+                config_setting = "//_config:is_python_3.15",
+                filename = None,
+                repo = "pypi_315_simple_linux_x86_64",
+                target_platforms = ["cp315_linux_x86_64"],
+                version = "3.15",
+            ),
+            struct(
+                config_setting = "//_config:is_python_3.15",
+                filename = None,
+                repo = "pypi_315_simple_osx_aarch64",
+                target_platforms = ["cp315_osx_aarch64"],
+                version = "3.15",
+            ),
+        ],
+    }})
+    pypi.whl_libraries().contains_exactly({
+        "pypi_315_extra": {
+            "dep_template": "@pypi//{name}:{target}",
+            "download_only": True,
+            "experimental_target_platforms": ["cp315_linux_x86_64"],
+            "extra_pip_args": ["--platform=manylinux_2_17_x86_64", "--python-version=315", "--implementation=cp", "--abi=cp315"],
+            "python_interpreter_target": "unit_test_interpreter_target",
+            "repo": "pypi_315",
+            "requirement": "extra==0.0.1 --hash=sha256:deadb00f",
+        },
+        "pypi_315_simple_linux_x86_64": {
+            "dep_template": "@pypi//{name}:{target}",
+            "download_only": True,
+            "experimental_target_platforms": ["cp315_linux_x86_64"],
+            "extra_pip_args": ["--platform=manylinux_2_17_x86_64", "--python-version=315", "--implementation=cp", "--abi=cp315"],
+            "python_interpreter_target": "unit_test_interpreter_target",
+            "repo": "pypi_315",
+            "requirement": "simple==0.0.1 --hash=sha256:deadbeef",
+        },
+        "pypi_315_simple_osx_aarch64": {
+            "dep_template": "@pypi//{name}:{target}",
+            "download_only": True,
+            "experimental_target_platforms": ["cp315_osx_aarch64"],
+            "extra_pip_args": ["--platform=macosx_10_9_arm64", "--python-version=315", "--implementation=cp", "--abi=cp315"],
+            "python_interpreter_target": "unit_test_interpreter_target",
+            "repo": "pypi_315",
+            "requirement": "simple==0.0.3 --hash=sha256:deadbaaf",
         },
     })
+    pypi.whl_mods().contains_exactly({})
 
-_tests.append(_test_simple_with_whl_mods)
+_tests.append(_test_download_only_multiple)
 
 def _test_simple_get_index(env):
     got_simpleapi_download_args = []
@@ -310,7 +400,10 @@
                 ],
             ),
             read = lambda x: {
-                "requirements.txt": "simple==0.0.1 --hash=sha256:deadbeef --hash=sha256:deadb00f",
+                "requirements.txt": """
+simple==0.0.1 --hash=sha256:deadbeef --hash=sha256:deadb00f
+some_pkg==0.0.1
+""",
             }[x],
         ),
         available_interpreters = {
@@ -320,26 +413,37 @@
     )
 
     pypi.is_reproducible().equals(False)
-    pypi.exposed_packages().contains_exactly({"pypi": ["simple"]})
+    pypi.exposed_packages().contains_exactly({"pypi": ["simple", "some_pkg"]})
     pypi.hub_group_map().contains_exactly({"pypi": {}})
-    pypi.hub_whl_map().contains_exactly({"pypi": {
-        "simple": [
-            struct(
-                config_setting = "//_config:is_python_3.15",
-                filename = "simple-0.0.1-py3-none-any.whl",
-                repo = "pypi_315_simple_py3_none_any_deadb00f",
-                target_platforms = None,
-                version = "3.15",
-            ),
-            struct(
-                config_setting = "//_config:is_python_3.15",
-                filename = "simple-0.0.1.tar.gz",
-                repo = "pypi_315_simple_sdist_deadbeef",
-                target_platforms = None,
-                version = "3.15",
-            ),
-        ],
-    }})
+    pypi.hub_whl_map().contains_exactly({
+        "pypi": {
+            "simple": [
+                struct(
+                    config_setting = "//_config:is_python_3.15",
+                    filename = "simple-0.0.1-py3-none-any.whl",
+                    repo = "pypi_315_simple_py3_none_any_deadb00f",
+                    target_platforms = None,
+                    version = "3.15",
+                ),
+                struct(
+                    config_setting = "//_config:is_python_3.15",
+                    filename = "simple-0.0.1.tar.gz",
+                    repo = "pypi_315_simple_sdist_deadbeef",
+                    target_platforms = None,
+                    version = "3.15",
+                ),
+            ],
+            "some_pkg": [
+                struct(
+                    config_setting = "//_config:is_python_3.15",
+                    filename = None,
+                    repo = "pypi_315_some_pkg",
+                    target_platforms = None,
+                    version = "3.15",
+                ),
+            ],
+        },
+    })
     pypi.whl_libraries().contains_exactly({
         "pypi_315_simple_py3_none_any_deadb00f": {
             "dep_template": "@pypi//{name}:{target}",
@@ -372,9 +476,7 @@
                 "cp315_osx_x86_64",
                 "cp315_windows_x86_64",
             ],
-            "extra_pip_args": [
-                "--extra-args-for-sdist-building",
-            ],
+            "extra_pip_args": ["--extra-args-for-sdist-building"],
             "filename": "simple-0.0.1.tar.gz",
             "python_interpreter_target": "unit_test_interpreter_target",
             "repo": "pypi_315",
@@ -382,8 +484,29 @@
             "sha256": "deadbeef",
             "urls": ["example.org"],
         },
+        # We are falling back to regular `pip`
+        "pypi_315_some_pkg": {
+            "dep_template": "@pypi//{name}:{target}",
+            "extra_pip_args": ["--extra-args-for-sdist-building"],
+            "python_interpreter_target": "unit_test_interpreter_target",
+            "repo": "pypi_315",
+            "requirement": "some_pkg==0.0.1",
+        },
     })
     pypi.whl_mods().contains_exactly({})
+    env.expect.that_dict(got_simpleapi_download_kwargs).contains_exactly({
+        "attr": struct(
+            auth_patterns = {},
+            envsubst = {},
+            extra_index_urls = [],
+            index_url = "pypi.org",
+            index_url_overrides = {},
+            netrc = None,
+            sources = ["simple", "some_pkg"],
+        ),
+        "cache": {},
+        "parallel_download": False,
+    })
 
 _tests.append(_test_simple_get_index)
 
diff --git a/tests/pypi/parse_requirements/parse_requirements_tests.bzl b/tests/pypi/parse_requirements/parse_requirements_tests.bzl
index c719ad6..a6e17be 100644
--- a/tests/pypi/parse_requirements/parse_requirements_tests.bzl
+++ b/tests/pypi/parse_requirements/parse_requirements_tests.bzl
@@ -30,6 +30,16 @@
         "requirements_linux": """\
 foo==0.0.3 --hash=sha256:deadbaaf
 """,
+        # download_only = True
+        "requirements_linux_download_only": """\
+--platform=manylinux_2_17_x86_64
+--python-version=39
+--implementation=cp
+--abi=cp39
+
+foo==0.0.1 --hash=sha256:deadbeef
+bar==0.0.1 --hash=sha256:deadb00f
+""",
         "requirements_lock": """\
 foo[extra]==0.0.1 --hash=sha256:deadbeef
 """,
@@ -45,6 +55,14 @@
         "requirements_osx": """\
 foo==0.0.3 --hash=sha256:deadbaaf
 """,
+        "requirements_osx_download_only": """\
+--platform=macosx_10_9_arm64
+--python-version=39
+--implementation=cp
+--abi=cp39
+
+foo==0.0.3 --hash=sha256:deadbaaf
+""",
         "requirements_windows": """\
 foo[extra]==0.0.2 --hash=sha256:deadbeef
 bar==0.0.1 --hash=sha256:deadb00f
@@ -229,6 +247,66 @@
 
 _tests.append(_test_multi_os)
 
+def _test_multi_os_legacy(env):
+    got = parse_requirements(
+        ctx = _mock_ctx(),
+        requirements_by_platform = {
+            "requirements_linux_download_only": ["cp39_linux_x86_64"],
+            "requirements_osx_download_only": ["cp39_osx_aarch64"],
+        },
+    )
+
+    env.expect.that_dict(got).contains_exactly({
+        "bar": [
+            struct(
+                distribution = "bar",
+                extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"],
+                is_exposed = False,
+                requirement_line = "bar==0.0.1 --hash=sha256:deadb00f",
+                sdist = None,
+                srcs = struct(
+                    requirement = "bar==0.0.1",
+                    shas = ["deadb00f"],
+                    version = "0.0.1",
+                ),
+                target_platforms = ["cp39_linux_x86_64"],
+                whls = [],
+            ),
+        ],
+        "foo": [
+            struct(
+                distribution = "foo",
+                extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"],
+                is_exposed = True,
+                requirement_line = "foo==0.0.1 --hash=sha256:deadbeef",
+                sdist = None,
+                srcs = struct(
+                    requirement = "foo==0.0.1",
+                    shas = ["deadbeef"],
+                    version = "0.0.1",
+                ),
+                target_platforms = ["cp39_linux_x86_64"],
+                whls = [],
+            ),
+            struct(
+                distribution = "foo",
+                extra_pip_args = ["--platform=macosx_10_9_arm64", "--python-version=39", "--implementation=cp", "--abi=cp39"],
+                is_exposed = True,
+                requirement_line = "foo==0.0.3 --hash=sha256:deadbaaf",
+                sdist = None,
+                srcs = struct(
+                    requirement = "foo==0.0.3",
+                    shas = ["deadbaaf"],
+                    version = "0.0.3",
+                ),
+                target_platforms = ["cp39_osx_aarch64"],
+                whls = [],
+            ),
+        ],
+    })
+
+_tests.append(_test_multi_os_legacy)
+
 def _test_select_requirement_none_platform(env):
     got = select_requirement(
         [
diff --git a/tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl b/tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl
index 9de309b..f518778 100644
--- a/tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl
+++ b/tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl
@@ -387,6 +387,24 @@
 
 _tests.append(_test_get_python_versions)
 
+def _test_get_python_versions_with_target_platforms(env):
+    got = get_whl_flag_versions(
+        aliases = [
+            whl_alias(repo = "foo", version = "3.3", target_platforms = ["cp33_linux_x86_64"]),
+            whl_alias(repo = "foo", version = "3.2", target_platforms = ["cp32_linux_x86_64", "cp32_osx_aarch64"]),
+        ],
+    )
+    want = {
+        "python_versions": ["3.2", "3.3"],
+        "target_platforms": [
+            "linux_x86_64",
+            "osx_aarch64",
+        ],
+    }
+    env.expect.that_dict(got).contains_exactly(want)
+
+_tests.append(_test_get_python_versions_with_target_platforms)
+
 def _test_get_python_versions_from_filenames(env):
     got = get_whl_flag_versions(
         aliases = [
@@ -660,6 +678,29 @@
 
 _tests.append(_test_multiplatform_whl_aliases_nofilename)
 
+def _test_multiplatform_whl_aliases_nofilename_target_platforms(env):
+    aliases = [
+        whl_alias(
+            repo = "foo",
+            config_setting = "//:ignored",
+            version = "3.1",
+            target_platforms = [
+                "cp31_linux_x86_64",
+                "cp31_linux_aarch64",
+            ],
+        ),
+    ]
+
+    got = multiplatform_whl_aliases(aliases = aliases)
+
+    want = [
+        whl_alias(config_setting = "//_config:is_cp3.1_linux_x86_64", repo = "foo", version = "3.1"),
+        whl_alias(config_setting = "//_config:is_cp3.1_linux_aarch64", repo = "foo", version = "3.1"),
+    ]
+    env.expect.that_collection(got).contains_exactly(want)
+
+_tests.append(_test_multiplatform_whl_aliases_nofilename_target_platforms)
+
 def _test_multiplatform_whl_aliases_filename(env):
     aliases = [
         whl_alias(
@@ -734,6 +775,52 @@
 
 _tests.append(_test_multiplatform_whl_aliases_filename_versioned)
 
+def _mock_alias(container):
+    return lambda name, **kwargs: container.append(name)
+
+def _mock_config_setting(container):
+    def _inner(name, flag_values = None, constraint_values = None, **_):
+        if flag_values or constraint_values:
+            container.append(name)
+            return
+
+        fail("At least one of 'flag_values' or 'constraint_values' needs to be set")
+
+    return _inner
+
+def _test_config_settings_exist_legacy(env):
+    aliases = [
+        whl_alias(
+            repo = "repo",
+            version = "3.11",
+            target_platforms = [
+                "cp311_linux_aarch64",
+                "cp311_linux_x86_64",
+            ],
+        ),
+    ]
+    available_config_settings = []
+    config_settings(
+        python_versions = ["3.11"],
+        native = struct(
+            alias = _mock_alias(available_config_settings),
+            config_setting = _mock_config_setting(available_config_settings),
+        ),
+        target_platforms = [
+            "linux_aarch64",
+            "linux_x86_64",
+        ],
+    )
+
+    got_aliases = multiplatform_whl_aliases(
+        aliases = aliases,
+    )
+    got = [a.config_setting.partition(":")[-1] for a in got_aliases]
+
+    env.expect.that_collection(available_config_settings).contains_at_least(got)
+
+_tests.append(_test_config_settings_exist_legacy)
+
 def _test_config_settings_exist(env):
     for py_tag in ["py2.py3", "py3", "py311", "cp311"]:
         if py_tag == "py2.py3":
@@ -771,12 +858,11 @@
                     ),
                 ]
                 available_config_settings = []
-                mock_rule = lambda name, **kwargs: available_config_settings.append(name)
                 config_settings(
                     python_versions = ["3.11"],
                     native = struct(
-                        alias = mock_rule,
-                        config_setting = mock_rule,
+                        alias = _mock_alias(available_config_settings),
+                        config_setting = _mock_config_setting(available_config_settings),
                     ),
                     **kwargs
                 )