Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions python/features.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,45 @@ _LOADABLE_SYMBOLS = {
],
}

def _major_minor(bazel_version):
# An empty version signifies a development build, which is treated as
# the latest version.
if not bazel_version:
return 99999, 99999

parts = bazel_version.split(".")
return int(parts[0]), int(parts[1])

def bazel_features(bazel_version):
"""Return the features of a Bazel version that `rules_python` uses.

The Bazel version is only available when a repository rule or a module
extension runs, so this is a function instead of fields of {obj}`features`,
e.g. `bazel_features(native.bazel_version).supports_whl_extraction`.

:::{versionadded} VERSION_NEXT_FEATURE
:::

Args:
bazel_version: {type}`str` the Bazel version, e.g.
`native.bazel_version`. An empty string is a development build,
which is treated as the latest version.

Returns:
{type}`struct` with:
* `supports_whl_extraction`: {type}`bool` whether
`repository_ctx.extract` can extract `.whl` files, which requires
Bazel 8.3.0 or later.
* `extract_needs_chmod`: {type}`bool` whether the files extracted by
`repository_ctx.extract` need their permissions fixed, which is the
case before Bazel 8.6.0.
"""
major, minor = _major_minor(bazel_version)
return struct(
supports_whl_extraction = major > 8 or (major == 8 and minor >= 3),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets just inline this logic into the whl_extract.bzl / patch_whl.bzl files instead.

features.bzl is intended for user-facing functionality. e.g

  • to know if a target is available so macro-phase code can set deps
  • if certain load() files are available so userspace repo-phase code can generate stubs

The "supports whl extraction" and "extract needs chmod" behavior is an internal detail that users wouldn't vary behavior base don.

extract_needs_chmod = major < 8 or (major == 8 and minor < 6),
)

features = struct(
TYPEDEF = _features_typedef,
# keep sorted
Expand Down
5 changes: 1 addition & 4 deletions python/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -907,10 +907,7 @@ bzl_library(
bzl_library(
name = "util",
srcs = ["util.bzl"],
deps = [
":py_internal",
"@bazel_skylib//lib:types",
],
deps = [":py_internal"],
)

bzl_library(
Expand Down
15 changes: 1 addition & 14 deletions python/private/internal_config_repo.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ _ENABLE_DEPRECATION_WARNINGS_DEFAULT = "0"
_CONFIG_TEMPLATE = """
config = struct(
build_python_zip_default = {build_python_zip_default},
supports_whl_extraction = {supports_whl_extraction},
enable_pystar = True,
enable_deprecation_warnings = {enable_deprecation_warnings},
extract_needs_chmod = {extract_needs_chmod},
bazel_8_or_later = {bazel_8_or_later},
bazel_9_or_later = {bazel_9_or_later},
bazel_10_or_later = {bazel_10_or_later},
Expand Down Expand Up @@ -81,20 +79,11 @@ def _internal_config_repo_impl(rctx):
# An empty version signifies a development build, which is treated as
# the latest version.
if native.bazel_version:
version_parts = native.bazel_version.split(".")
bazel_major_version = int(version_parts[0])
bazel_minor_version = int(version_parts[1])
bazel_major_version = int(native.bazel_version.split(".")[0])
else:
bazel_major_version = 99999
bazel_minor_version = 99999

supports_whl_extraction = False
extract_needs_chmod = bazel_major_version < 8 or (bazel_major_version == 8 and bazel_minor_version < 6)
if bazel_major_version >= 8:
# Extracting .whl files requires Bazel 8.3.0 or later.
if bazel_major_version > 8 or bazel_minor_version >= 3:
supports_whl_extraction = True

builtin_py_info_symbol = "None"
builtin_py_runtime_info_symbol = "None"
builtin_py_cc_link_params_provider = "None"
Expand All @@ -109,8 +98,6 @@ def _internal_config_repo_impl(rctx):
enable_deprecation_warnings = _bool_from_environ(rctx, _ENABLE_DEPRECATION_WARNINGS_ENVVAR_NAME, _ENABLE_DEPRECATION_WARNINGS_DEFAULT),
builtin_py_info_symbol = builtin_py_info_symbol,
builtin_py_runtime_info_symbol = builtin_py_runtime_info_symbol,
supports_whl_extraction = str(supports_whl_extraction),
extract_needs_chmod = str(extract_needs_chmod),
builtin_py_cc_link_params_provider = builtin_py_cc_link_params_provider,
modules_using_explicit_initpy = str(explicit_init_py_modules),
tool_deps_dev_only = str(rctx.attr.tool_deps_dev_only),
Expand Down
5 changes: 2 additions & 3 deletions python/private/pypi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ bzl_library(
srcs = ["patch_whl.bzl"],
deps = [
":parse_whl_name",
"//python:features",
"//python/private:repo_utils",
"@rules_python_internal//:rules_python_config",
],
)

Expand Down Expand Up @@ -371,7 +371,6 @@ bzl_library(
deps = [
"//python/private:repo_utils",
"//python/private:util",
"@bazel_skylib//lib:types",
],
)

Expand Down Expand Up @@ -475,8 +474,8 @@ bzl_library(
srcs = ["whl_extract.bzl"],
deps = [
":whl_metadata",
"//python:features",
"//python/private:repo_utils",
"@rules_python_internal//:rules_python_config",
],
)

Expand Down
6 changes: 3 additions & 3 deletions python/private/pypi/patch_whl.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repack patched wheels. Instead, after extracting and patching, we fix
the RECORD file natively in Starlark and repack using system zip.
"""

load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config")
load("//python:features.bzl", "bazel_features")
load("//python/private:repo_utils.bzl", "repo_utils")
load(":parse_whl_name.bzl", "parse_whl_name")

Expand Down Expand Up @@ -96,8 +96,8 @@ def patch_whl(rctx, *, whl_path, patches):
repo_utils.extract(
rctx,
archive = whl_input,
supports_whl_extraction = rp_config.supports_whl_extraction,
extract_needs_chmod = rp_config.extract_needs_chmod,
supports_whl_extraction = bazel_features(native.bazel_version).supports_whl_extraction,
extract_needs_chmod = bazel_features(native.bazel_version).extract_needs_chmod,
)

if not patches:
Expand Down
7 changes: 5 additions & 2 deletions python/private/pypi/pypi_repo_utils.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

""

load("@bazel_skylib//lib:types.bzl", "types")
load("//python/private:repo_utils.bzl", "repo_utils")
load("//python/private:util.bzl", "is_importable_name")

Expand Down Expand Up @@ -113,7 +112,11 @@ def _execute_prep(mrctx, *, python, srcs, **kwargs):

environment = kwargs.pop("environment", {})
pythonpath = environment.get("PYTHONPATH", "")
if pythonpath and not types.is_string(pythonpath):

# NOTE: `types.is_string` from `@bazel_skylib` is not used, so that this
# file can be loaded before the `WORKSPACE` defines `@bazel_skylib`, e.g.
# via `whl_library` from `python/repositories.bzl`.
if pythonpath and type(pythonpath) != "string":
environment["PYTHONPATH"] = _construct_pypath(mrctx, entries = pythonpath)
kwargs["environment"] = environment

Expand Down
6 changes: 3 additions & 3 deletions python/private/pypi/whl_extract.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""A simple whl extractor."""

load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config")
load("//python:features.bzl", "bazel_features")
load("//python/private:repo_utils.bzl", "repo_utils")
load(":whl_metadata.bzl", "find_whl_metadata")

Expand Down Expand Up @@ -32,8 +32,8 @@ def whl_extract(rctx, *, whl_path, logger):
rctx,
archive = whl_path,
output = install_dir_path,
supports_whl_extraction = rp_config.supports_whl_extraction,
extract_needs_chmod = rp_config.extract_needs_chmod,
supports_whl_extraction = bazel_features(native.bazel_version).supports_whl_extraction,
extract_needs_chmod = bazel_features(native.bazel_version).extract_needs_chmod,
)

metadata_file = find_whl_metadata(
Expand Down
7 changes: 5 additions & 2 deletions python/private/util.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

"""Functionality shared by multiple pieces of code."""

load("@bazel_skylib//lib:types.bzl", "types")
load("//python/private:py_internal.bzl", "py_internal")

def copy_propagating_kwargs(from_kwargs, into_kwargs = None):
Expand Down Expand Up @@ -62,7 +61,11 @@ def add_tag(attrs, tag):

# Preserve the input type: this allows a test verifying the underlying
# rule can accept the tuple for the tags argument.
if types.is_tuple(tags):
# NOTE: `types.is_tuple` from `@bazel_skylib` is not used, so that
# this file can be loaded before the `WORKSPACE` defines
# `@bazel_skylib`, e.g. via `whl_library` from
# `python/repositories.bzl`.
if type(tags) == "tuple":
attrs["tags"] = tags + (tag,)
else:
# List concatenation is necessary because the original value
Expand Down
3 changes: 3 additions & 0 deletions tests/bazel_features/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
load(":bazel_features_tests.bzl", "bazel_features_test_suite")

bazel_features_test_suite(name = "bazel_features_tests")
35 changes: 35 additions & 0 deletions tests/bazel_features/bazel_features_tests.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Tests for the features of the Bazel versions."""

load("@rules_testing//lib:test_suite.bzl", "test_suite")
load("//python:features.bzl", "bazel_features")

_tests = []

def _test_bazel_features(env):
for bazel_version, (supports_whl_extraction, extract_needs_chmod) in {
# A development build is treated as the latest version.
"": (True, False),
"7.4.1": (False, True),
"8.2.1": (False, True),
"8.3.0": (True, True),
"8.5.1": (True, True),
"8.6.0": (True, False),
"9.1.1": (True, False),
}.items():
got = bazel_features(bazel_version)
env.expect.where(bazel_version = bazel_version).that_bool(
got.supports_whl_extraction,
).equals(supports_whl_extraction)
env.expect.where(bazel_version = bazel_version).that_bool(
got.extract_needs_chmod,
).equals(extract_needs_chmod)

_tests.append(_test_bazel_features)

def bazel_features_test_suite(name):
"""Create the test suite.

Args:
name: the name of the test suite
"""
test_suite(name = name, basic_tests = _tests)
Loading