From 799f9c3dfdb992cb4a4769752b7327eb5fe48cb7 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 26 Apr 2025 19:54:42 -0700 Subject: [PATCH 01/23] wip: flag to evaluate dep specs --- .../pypi/dependency_specifier_flag.bzl | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 python/private/pypi/dependency_specifier_flag.bzl diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/dependency_specifier_flag.bzl new file mode 100644 index 0000000000..3c50936685 --- /dev/null +++ b/python/private/pypi/dependency_specifier_flag.bzl @@ -0,0 +1,84 @@ +load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") +load(":pep508_evaluate.bzl", "evaluate") + +def _impl(ctx): + env = {} + + runtime = ctx.toolchains[TARGET_TOOLCHAIN_TYPE].py3_runtime + if runtime.interpreter_version_info: + version_info = runtime.interpreter_version_info + env["python_version"] = "{v.major}.{v.minor}".format(v = version_info) + full_version = format_full_version(version_info) + env["python_full_version"] = full_version + env["implementation_version"] = full_version + else: + env["python_version"] = get_flag(ctx.attr._python_version) + full_version = get_flag(ctx.attr._python_full_version) + env["python_full_version"] = full_version + env["implementation_version"] = full_version + + # We assume cpython if the toolchain doesn't specify because it's most + # likely to be true. + env["implementation_name"] = runtime.implementation_name or "cpython" + + env["os_name"] = struct() + env["platform_machine"] = struct() + env["platform_python_implementation"] = struct() + env["platform_release"] = struct() + env["platform_system"] = struct() + env["platform_version"] = struct() + + if evalute(ctx.attr.expression, env): + value = "yes" + else: + value = "no" + return [config_common.FeatureFlagInfo(value = value)] + +# Adapted from spec code at: +# https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers +def format_full_info(info): + kind = info.releaselevel + if kind == "final": + kind = "" + serial = "" + else: + kind = kind[0] if kind else "" + serial = str(info.serial) if info.serial else "" + + return "{v.major}.{v.minor}.{v.micro}{kind}{serial}".format( + v = version_info, + kind = kind, + serial = serial, + ) + return version + +pypa_dependency_specification = rule( + implementation = _impl, + attrs = { + "expression": attt.string(), + "_os_name": attr.label(), + "_sys_platform_flag": attr.label(), + "_platform_machine_flag": attr.label(), + "_platform_python_implementation_flag": attr.label(), + "_platform_release_flag": attr.label(), + "_platform_system_flag": attr.label(), + "_platform_version_flag": attr.label(), + "_python_version_flag": attr.label( + default = "//python/config_settings:_python_version_major_minor", + ), + "_python_full_version_flag": attr.label( + default = "//python/config_settings:python_version", + ), + "_extra_flag": attr.label(), + }, + toolchains = [ + TARGET_TOOLCHAIN_TYPE, + ], +) + +def _get_flag(t): + if config_common.FeatureFlagInfo in t: + return t[config_common.FeatureFlagInfo].value + if BuildSettingInfo in t: + return t[BuildSettingInfo].value + fail("Should not occur: {} does not have necessary providers") From a658e8fe2434803dae71f44efd2f29edd7eca269 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 26 Apr 2025 20:11:40 -0700 Subject: [PATCH 02/23] add some todo, fake some more values --- .../pypi/dependency_specifier_flag.bzl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/dependency_specifier_flag.bzl index 3c50936685..91d135b19c 100644 --- a/python/private/pypi/dependency_specifier_flag.bzl +++ b/python/private/pypi/dependency_specifier_flag.bzl @@ -21,11 +21,26 @@ def _impl(ctx): # likely to be true. env["implementation_name"] = runtime.implementation_name or "cpython" + # todo: map from os constraint env["os_name"] = struct() + + # todo: map from os constraint + env["sys_platform"] = struct() + + # todo: map from cpu flag (x86_64, etc) env["platform_machine"] = struct() - env["platform_python_implementation"] = struct() + + # todo: add PyRuntimeInfo.platform_python_implementation + # The values are slightly different to implementation_name + env["platform_python_implementation"] = runtime.implementation_name + + # todo: add flag to carry this env["platform_release"] = struct() + + # todo: map from os constraint env["platform_system"] = struct() + + # todo: add flag to carry this env["platform_version"] = struct() if evalute(ctx.attr.expression, env): @@ -58,8 +73,6 @@ pypa_dependency_specification = rule( "expression": attt.string(), "_os_name": attr.label(), "_sys_platform_flag": attr.label(), - "_platform_machine_flag": attr.label(), - "_platform_python_implementation_flag": attr.label(), "_platform_release_flag": attr.label(), "_platform_system_flag": attr.label(), "_platform_version_flag": attr.label(), From e65c97379fad0446c9ab4ad04093144ab992f2b2 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sun, 27 Apr 2025 12:26:42 -0700 Subject: [PATCH 03/23] source values from select and flags --- python/config_settings/BUILD.bazel | 18 ++++ .../pypi/dependency_specifier_flag.bzl | 82 +++++++++++++------ python/private/pypi/flags.bzl | 46 +++++++++++ 3 files changed, 123 insertions(+), 23 deletions(-) diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel index 872d7d1bda..81dba6db4a 100644 --- a/python/config_settings/BUILD.bazel +++ b/python/config_settings/BUILD.bazel @@ -220,3 +220,21 @@ string_flag( define_pypi_internal_flags( name = "define_pypi_internal_flags", ) + +# todo: have single --pypi_env_config flag instead? Under the hood, it +# could be driven more more discrete flags, which could be considered an +# implementation detail of our particular impl. +# A label is used to allow users to use select() to affect the value. +# A target providing BuildSettingInfo is used to allow users to define +# a flag as the target, which lets them CLI-override or transition it easily. +label_flag( + name = "pip_platform_release_config", + build_setting_default = ":_pip_platform_release_default_config", +) + +# docs: points to a label that provides BuildSettingInfo for the value. +# A label and BuildSettingInfo are used for the same reason as platform_release +label_flag( + name = "pip_platform_version_config", + build_setting_default = ":_pip_platform_version_default_config", +) diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/dependency_specifier_flag.bzl index 91d135b19c..b0c63a63a3 100644 --- a/python/private/pypi/dependency_specifier_flag.bzl +++ b/python/private/pypi/dependency_specifier_flag.bzl @@ -1,7 +1,10 @@ load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") load(":pep508_evaluate.bzl", "evaluate") +# todo: maybe put all the env into a single target and have a +# PyPiEnvMarkersInfo provider? Have --pypi_env=//some:target? def _impl(ctx): + # todo: should unify with pep508_env.bzl env = {} runtime = ctx.toolchains[TARGET_TOOLCHAIN_TYPE].py3_runtime @@ -20,28 +23,16 @@ def _impl(ctx): # We assume cpython if the toolchain doesn't specify because it's most # likely to be true. env["implementation_name"] = runtime.implementation_name or "cpython" - - # todo: map from os constraint - env["os_name"] = struct() - - # todo: map from os constraint - env["sys_platform"] = struct() - - # todo: map from cpu flag (x86_64, etc) - env["platform_machine"] = struct() + env["os_name"] = ctx.attr.os_name + env["sys_platform"] = ctx.attr.sys_platform + env["platform_machine"] = ctx.attr.platform_machine # todo: add PyRuntimeInfo.platform_python_implementation # The values are slightly different to implementation_name env["platform_python_implementation"] = runtime.implementation_name - - # todo: add flag to carry this - env["platform_release"] = struct() - - # todo: map from os constraint - env["platform_system"] = struct() - - # todo: add flag to carry this - env["platform_version"] = struct() + env["platform_release"] = ctx.attr._platform_release_config_flag[BuildSettingInfo].value + env["platform_system"] = ctx.attr.platform_system + env["platform_version"] = ctx.attr._platform_version_config_flag[BuildSettingInfo].value if evalute(ctx.attr.expression, env): value = "yes" @@ -67,15 +58,60 @@ def format_full_info(info): ) return version +def pypa_dep_spec(**kwargs): + pypa_dependency_specification( + # todo: copied from pep508_env.bzl + os_name = select({ + # The "java" value is documented, but with Jython defunct, + # shouldn't occur in practice. + # The osname value is technically a property of the runtime, not the + # targetted OS at runtime, but the distinction shouldn't matter in + # practice. + "@//platforms/os:windows": "nt", + "//conditions:default": "posix", + }), + # todo: copied from pep508_env.bzl + sys_platform = select({ + "@//platforms/os:windows": "win32", + "@//platforms/os:linux": "linux", + "@//platforms/os:osx": "darwin", + # todo: what does spec say unknown value is? + "//conditions:default": "", + }), + # todo: copied from pep508_env.bzl + # todo: there are many more cpus. Unfortunately, it doesn't look like + # the value is directly accessible to starlark. It might be possible to + # get it via CcToolchain.cpu though. + platform_machine = select({ + "@platforms//cpu:x86_64": "x86_64", + "@platforms//cpu:aarch64": "aarch64", + # todo: what does spec say unknown value is? + "//conditions:default": "", + }), + # todo: copied from pep508_env.bzl + platform_system = select({ + "@//platforms/os:windows": "Windows", + "@//platforms/os:linux": "Linux", + "@//platforms/os:osx": "Darwin", + # todo: what does spec say unknown value is? + "//conditions:default": "", + }), + ) + pypa_dependency_specification = rule( implementation = _impl, attrs = { "expression": attt.string(), - "_os_name": attr.label(), - "_sys_platform_flag": attr.label(), - "_platform_release_flag": attr.label(), - "_platform_system_flag": attr.label(), - "_platform_version_flag": attr.label(), + "os_name": attr.string(), + "sys_platform": attr.string(), + "platform_machine": attr.string(), + "platform_system": attr.string(), + "_platform_release_config_flag": attr.label( + default = "//python/config_settings:pip_platform_release_config", + ), + "_platform_version_config_flag": attr.label( + default = "//python/config_settings:pip_platform_version_config", + ), "_python_version_flag": attr.label( default = "//python/config_settings:_python_version_major_minor", ), diff --git a/python/private/pypi/flags.bzl b/python/private/pypi/flags.bzl index a25579a2b8..616012bcaf 100644 --- a/python/private/pypi/flags.bzl +++ b/python/private/pypi/flags.bzl @@ -81,6 +81,20 @@ def define_pypi_internal_flags(name): name = "_internal_pip_whl", visibility = ["//visibility:public"], ) + _platform_release_config( + name = "_pip_platform_release_default_config", + value = select({ + "//conditions:osx": "USE_OSX_VERSION_FLAG", + "//conditions:default": "", + }), + ) + _platform_version_config( + name = "_pip_platform_version_default_config", + value = select({ + "//conditions:osx": "USE_OSX_VERSION_FLAG", + "//conditions:default": "", + }), + ) def _allow_wheels_flag_impl(ctx): input = ctx.attr._setting[BuildSettingInfo].value @@ -97,3 +111,35 @@ This rule allows us to greatly reduce the number of config setting targets at no if we are duplicating some of the functionality of the `native.config_setting`. """, ) + +def _platform_release_config_impl(ctx): + value = ctx.attr.value + if value == "USE_OSX_VERSION_FLAG": + value = ctx.attr._osx_version[BuildSettingInfo].value + + return [BuildSettingInfo(value = ctx.attr.value)] + +# This value is loosely some version-value looking thing, but the format +# varies depending on the OS. +_platform_release_config = rule( + implementation = _platform_release_config_impl, + attrs = { + "value": attr.string(), + "_osx_version": attr.label( + default = "//python/config_settings:pip_whl_osx_version", + ), + }, +) + +def _platform_version_config_impl(ctx): + value = ctx.attr.value + return [BuildSettingInfo(value = ctx.attr.value)] + +# Despite its name, this "version" value is not a simple version value. +# It's a more detailed, arbitrary, description the OS gives about itself. +_platform_version_config = rule( + implementation = _platform_version_config_impl, + attrs = { + "value": attr.string(), + }, +) From 6181509342418f88437eb95c3b653fe356c625d3 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sun, 27 Apr 2025 12:36:24 -0700 Subject: [PATCH 04/23] sorta fix platform_python_implementation --- python/private/pypi/dependency_specifier_flag.bzl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/dependency_specifier_flag.bzl index b0c63a63a3..031e4b613b 100644 --- a/python/private/pypi/dependency_specifier_flag.bzl +++ b/python/private/pypi/dependency_specifier_flag.bzl @@ -27,9 +27,15 @@ def _impl(ctx): env["sys_platform"] = ctx.attr.sys_platform env["platform_machine"] = ctx.attr.platform_machine - # todo: add PyRuntimeInfo.platform_python_implementation - # The values are slightly different to implementation_name - env["platform_python_implementation"] = runtime.implementation_name + # todo: maybe add PyRuntimeInfo.platform_python_implementation? + # The values are slightly different to implementation_name. + # However, digging through old PEPs, it looks like + # platform.python_implementation is legacy, and sys.implementation.name + # "replaced" it. Can probably just special case this. + platform_python_impl = runtime.implementation_name + if platform_python_impl == "cpython": + platform_python_impl = "CPython" + env["platform_python_implementation"] = platform_python_impl env["platform_release"] = ctx.attr._platform_release_config_flag[BuildSettingInfo].value env["platform_system"] = ctx.attr.platform_system env["platform_version"] = ctx.attr._platform_version_config_flag[BuildSettingInfo].value From bf2173cf18c66e910e488fd3f3fae1323aab5168 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sun, 27 Apr 2025 17:07:34 -0700 Subject: [PATCH 05/23] wip: analysis tests basic --- .../pypi/dependency_specifier_flag.bzl | 2 ++ tests/pypi/pep508/BUILD.bazel | 5 ++++ tests/pypi/pep508/depspec_flag_tests.bzl | 28 +++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 tests/pypi/pep508/depspec_flag_tests.bzl diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/dependency_specifier_flag.bzl index 031e4b613b..0a0dca6e36 100644 --- a/python/private/pypi/dependency_specifier_flag.bzl +++ b/python/private/pypi/dependency_specifier_flag.bzl @@ -85,6 +85,8 @@ def pypa_dep_spec(**kwargs): "//conditions:default": "", }), # todo: copied from pep508_env.bzl + # todo: pep508_env and evaluate have an "aliases" thing that needs + # to be incorporated # todo: there are many more cpus. Unfortunately, it doesn't look like # the value is directly accessible to starlark. It might be possible to # get it via CcToolchain.cpu though. diff --git a/tests/pypi/pep508/BUILD.bazel b/tests/pypi/pep508/BUILD.bazel index 7eab2e096a..2dc87bf7be 100644 --- a/tests/pypi/pep508/BUILD.bazel +++ b/tests/pypi/pep508/BUILD.bazel @@ -1,4 +1,5 @@ load(":deps_tests.bzl", "deps_test_suite") +load(":depspec_flag_tests.bzl", "depspec_flag_test_suite") load(":evaluate_tests.bzl", "evaluate_test_suite") load(":requirement_tests.bzl", "requirement_test_suite") @@ -13,3 +14,7 @@ evaluate_test_suite( requirement_test_suite( name = "requirement_tests", ) + +depspec_flag_test_suite( + name = "depspec_flag_tests", +) diff --git a/tests/pypi/pep508/depspec_flag_tests.bzl b/tests/pypi/pep508/depspec_flag_tests.bzl new file mode 100644 index 0000000000..af0af8a49d --- /dev/null +++ b/tests/pypi/pep508/depspec_flag_tests.bzl @@ -0,0 +1,28 @@ +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("//python/private/pypi:pep508_env.bzl", pep508_env = "env") # buildifier: disable=bzl-visibility +load("//python/private/pypi:pep508_evaluate.bzl", "evaluate", "tokenize") # buildifier: disable=bzl-visibility + +_tests = [] + +def test_whatever(name): + def impl(env, target): + # todo: create FeatureFlagInfo subject + actual = target[config_common.FeatureFlagInfo].value + env.expect.that_string(actual).equals("yes") + + depspec_flag( + name = name + "_subject", + ) + analysis_test( + name = name, + impl = impl, + target = name + "_subject", + config_settings = { + }, + ) + +def depspec_flag_tests(name): + test_suite( + name = name, + tests = _tests, + ) From 1e23941a4fc87e5a784e56e5f492d081eaf82d22 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 28 Apr 2025 19:14:27 -0700 Subject: [PATCH 06/23] trying to get tests to work --- .../pypi/dependency_specifier_flag.bzl | 131 +++++++++--------- python/private/pypi/flags.bzl | 6 +- tests/pypi/pep508/depspec_flag_tests.bzl | 11 +- 3 files changed, 79 insertions(+), 69 deletions(-) diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/dependency_specifier_flag.bzl index 0a0dca6e36..d6ef07e8b2 100644 --- a/python/private/pypi/dependency_specifier_flag.bzl +++ b/python/private/pypi/dependency_specifier_flag.bzl @@ -1,6 +1,50 @@ +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") load(":pep508_evaluate.bzl", "evaluate") +def depspec_flag(**kwargs): + pypa_dependency_specification( + # todo: copied from pep508_env.bzl + os_name = select({ + # The "java" value is documented, but with Jython defunct, + # shouldn't occur in practice. + # The osname value is technically a property of the runtime, not the + # targetted OS at runtime, but the distinction shouldn't matter in + # practice. + "@platforms//os:windows": "nt", + "//conditions:default": "posix", + }), + # todo: copied from pep508_env.bzl + sys_platform = select({ + "@platforms//os:windows": "win32", + "@platforms//os:linux": "linux", + "@platforms//os:osx": "darwin", + # todo: what does spec say unknown value is? + "//conditions:default": "", + }), + # todo: copied from pep508_env.bzl + # todo: pep508_env and evaluate have an "aliases" thing that needs + # to be incorporated + # todo: there are many more cpus. Unfortunately, it doesn't look like + # the value is directly accessible to starlark. It might be possible to + # get it via CcToolchain.cpu though. + platform_machine = select({ + "@platforms//cpu:x86_64": "x86_64", + "@platforms//cpu:aarch64": "aarch64", + # todo: what does spec say unknown value is? + "//conditions:default": "", + }), + # todo: copied from pep508_env.bzl + platform_system = select({ + "@platforms//os:windows": "Windows", + "@platforms//os:linux": "Linux", + "@platforms//os:osx": "Darwin", + # todo: what does spec say unknown value is? + "//conditions:default": "", + }), + **kwargs + ) + # todo: maybe put all the env into a single target and have a # PyPiEnvMarkersInfo provider? Have --pypi_env=//some:target? def _impl(ctx): @@ -15,8 +59,8 @@ def _impl(ctx): env["python_full_version"] = full_version env["implementation_version"] = full_version else: - env["python_version"] = get_flag(ctx.attr._python_version) - full_version = get_flag(ctx.attr._python_full_version) + env["python_version"] = _get_flag(ctx.attr._python_version) + full_version = _get_flag(ctx.attr._python_full_version) env["python_full_version"] = full_version env["implementation_version"] = full_version @@ -40,76 +84,16 @@ def _impl(ctx): env["platform_system"] = ctx.attr.platform_system env["platform_version"] = ctx.attr._platform_version_config_flag[BuildSettingInfo].value - if evalute(ctx.attr.expression, env): + if evaluate(ctx.attr.expression, env): value = "yes" else: value = "no" return [config_common.FeatureFlagInfo(value = value)] -# Adapted from spec code at: -# https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers -def format_full_info(info): - kind = info.releaselevel - if kind == "final": - kind = "" - serial = "" - else: - kind = kind[0] if kind else "" - serial = str(info.serial) if info.serial else "" - - return "{v.major}.{v.minor}.{v.micro}{kind}{serial}".format( - v = version_info, - kind = kind, - serial = serial, - ) - return version - -def pypa_dep_spec(**kwargs): - pypa_dependency_specification( - # todo: copied from pep508_env.bzl - os_name = select({ - # The "java" value is documented, but with Jython defunct, - # shouldn't occur in practice. - # The osname value is technically a property of the runtime, not the - # targetted OS at runtime, but the distinction shouldn't matter in - # practice. - "@//platforms/os:windows": "nt", - "//conditions:default": "posix", - }), - # todo: copied from pep508_env.bzl - sys_platform = select({ - "@//platforms/os:windows": "win32", - "@//platforms/os:linux": "linux", - "@//platforms/os:osx": "darwin", - # todo: what does spec say unknown value is? - "//conditions:default": "", - }), - # todo: copied from pep508_env.bzl - # todo: pep508_env and evaluate have an "aliases" thing that needs - # to be incorporated - # todo: there are many more cpus. Unfortunately, it doesn't look like - # the value is directly accessible to starlark. It might be possible to - # get it via CcToolchain.cpu though. - platform_machine = select({ - "@platforms//cpu:x86_64": "x86_64", - "@platforms//cpu:aarch64": "aarch64", - # todo: what does spec say unknown value is? - "//conditions:default": "", - }), - # todo: copied from pep508_env.bzl - platform_system = select({ - "@//platforms/os:windows": "Windows", - "@//platforms/os:linux": "Linux", - "@//platforms/os:osx": "Darwin", - # todo: what does spec say unknown value is? - "//conditions:default": "", - }), - ) - pypa_dependency_specification = rule( implementation = _impl, attrs = { - "expression": attt.string(), + "expression": attr.string(), "os_name": attr.string(), "sys_platform": attr.string(), "platform_machine": attr.string(), @@ -121,7 +105,7 @@ pypa_dependency_specification = rule( default = "//python/config_settings:pip_platform_version_config", ), "_python_version_flag": attr.label( - default = "//python/config_settings:_python_version_major_minor", + default = "//python/config_settings:python_version_major_minor", ), "_python_full_version_flag": attr.label( default = "//python/config_settings:python_version", @@ -133,6 +117,23 @@ pypa_dependency_specification = rule( ], ) +# Adapted from spec code at: +# https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers +def format_full_version(info): + kind = info.releaselevel + if kind == "final": + kind = "" + serial = "" + else: + kind = kind[0] if kind else "" + serial = str(info.serial) if info.serial else "" + + return "{v.major}.{v.minor}.{v.micro}{kind}{serial}".format( + v = info, + kind = kind, + serial = serial, + ) + def _get_flag(t): if config_common.FeatureFlagInfo in t: return t[config_common.FeatureFlagInfo].value diff --git a/python/private/pypi/flags.bzl b/python/private/pypi/flags.bzl index 616012bcaf..8f33c369a8 100644 --- a/python/private/pypi/flags.bzl +++ b/python/private/pypi/flags.bzl @@ -84,16 +84,18 @@ def define_pypi_internal_flags(name): _platform_release_config( name = "_pip_platform_release_default_config", value = select({ - "//conditions:osx": "USE_OSX_VERSION_FLAG", + "@platforms//os:osx": "USE_OSX_VERSION_FLAG", "//conditions:default": "", }), + visibility = ["//visibility:public"], ) _platform_version_config( name = "_pip_platform_version_default_config", value = select({ - "//conditions:osx": "USE_OSX_VERSION_FLAG", + "@platforms//os:osx": "USE_OSX_VERSION_FLAG", "//conditions:default": "", }), + visibility = ["//visibility:public"], ) def _allow_wheels_flag_impl(ctx): diff --git a/tests/pypi/pep508/depspec_flag_tests.bzl b/tests/pypi/pep508/depspec_flag_tests.bzl index af0af8a49d..1230c9eddf 100644 --- a/tests/pypi/pep508/depspec_flag_tests.bzl +++ b/tests/pypi/pep508/depspec_flag_tests.bzl @@ -1,10 +1,13 @@ +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("//python/private/pypi:dependency_specifier_flag.bzl", "depspec_flag") load("//python/private/pypi:pep508_env.bzl", pep508_env = "env") # buildifier: disable=bzl-visibility load("//python/private/pypi:pep508_evaluate.bzl", "evaluate", "tokenize") # buildifier: disable=bzl-visibility +load("//tests/support:support.bzl", "PYTHON_VERSION") _tests = [] -def test_whatever(name): +def _test_whatever(name): def impl(env, target): # todo: create FeatureFlagInfo subject actual = target[config_common.FeatureFlagInfo].value @@ -12,16 +15,20 @@ def test_whatever(name): depspec_flag( name = name + "_subject", + expression = "python_version >= '3.12.0'", ) analysis_test( name = name, impl = impl, target = name + "_subject", config_settings = { + PYTHON_VERSION: "3.12.0", }, ) -def depspec_flag_tests(name): +_tests.append(_test_whatever) + +def depspec_flag_test_suite(name): test_suite( name = name, tests = _tests, From ef6172465b0dfb8f622122f055c8e1bfd6438843 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 28 Apr 2025 20:41:49 -0700 Subject: [PATCH 07/23] got basic test passing --- python/config_settings/BUILD.bazel | 2 ++ python/private/pypi/dependency_specifier_flag.bzl | 12 +++++++++--- tests/pypi/pep508/depspec_flag_tests.bzl | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel index 81dba6db4a..58bc48b06a 100644 --- a/python/config_settings/BUILD.bazel +++ b/python/config_settings/BUILD.bazel @@ -230,6 +230,7 @@ define_pypi_internal_flags( label_flag( name = "pip_platform_release_config", build_setting_default = ":_pip_platform_release_default_config", + visibility = ["//visibility:public"], ) # docs: points to a label that provides BuildSettingInfo for the value. @@ -237,4 +238,5 @@ label_flag( label_flag( name = "pip_platform_version_config", build_setting_default = ":_pip_platform_version_default_config", + visibility = ["//visibility:public"], ) diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/dependency_specifier_flag.bzl index d6ef07e8b2..d3eb81f8c7 100644 --- a/python/private/pypi/dependency_specifier_flag.bzl +++ b/python/private/pypi/dependency_specifier_flag.bzl @@ -54,7 +54,10 @@ def _impl(ctx): runtime = ctx.toolchains[TARGET_TOOLCHAIN_TYPE].py3_runtime if runtime.interpreter_version_info: version_info = runtime.interpreter_version_info - env["python_version"] = "{v.major}.{v.minor}".format(v = version_info) + env["python_version"] = "{major}.{minor}".format( + major = version_info.major, + minor = version_info.minor, + ) full_version = format_full_version(version_info) env["python_full_version"] = full_version env["implementation_version"] = full_version @@ -84,7 +87,7 @@ def _impl(ctx): env["platform_system"] = ctx.attr.platform_system env["platform_version"] = ctx.attr._platform_version_config_flag[BuildSettingInfo].value - if evaluate(ctx.attr.expression, env): + if evaluate(ctx.attr.expression, env = env): value = "yes" else: value = "no" @@ -128,8 +131,11 @@ def format_full_version(info): kind = kind[0] if kind else "" serial = str(info.serial) if info.serial else "" - return "{v.major}.{v.minor}.{v.micro}{kind}{serial}".format( + return "{major}.{minor}.{micro}{kind}{serial}".format( v = info, + major = info.major, + minor = info.minor, + micro = info.micro, kind = kind, serial = serial, ) diff --git a/tests/pypi/pep508/depspec_flag_tests.bzl b/tests/pypi/pep508/depspec_flag_tests.bzl index 1230c9eddf..a16cfd8318 100644 --- a/tests/pypi/pep508/depspec_flag_tests.bzl +++ b/tests/pypi/pep508/depspec_flag_tests.bzl @@ -11,7 +11,7 @@ def _test_whatever(name): def impl(env, target): # todo: create FeatureFlagInfo subject actual = target[config_common.FeatureFlagInfo].value - env.expect.that_string(actual).equals("yes") + env.expect.that_str(actual).equals("yes") depspec_flag( name = name + "_subject", From 77decbcdc7a565cdab9b21c8176074ea60330144 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 28 Apr 2025 20:58:49 -0700 Subject: [PATCH 08/23] parameterize test --- tests/pypi/pep508/depspec_flag_tests.bzl | 46 +++++++++++++++++------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/tests/pypi/pep508/depspec_flag_tests.bzl b/tests/pypi/pep508/depspec_flag_tests.bzl index a16cfd8318..b5bbd5d84a 100644 --- a/tests/pypi/pep508/depspec_flag_tests.bzl +++ b/tests/pypi/pep508/depspec_flag_tests.bzl @@ -1,5 +1,6 @@ load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("@rules_testing//lib:util.bzl", "TestingAspectInfo") load("//python/private/pypi:dependency_specifier_flag.bzl", "depspec_flag") load("//python/private/pypi:pep508_env.bzl", pep508_env = "env") # buildifier: disable=bzl-visibility load("//python/private/pypi:pep508_evaluate.bzl", "evaluate", "tokenize") # buildifier: disable=bzl-visibility @@ -7,26 +8,45 @@ load("//tests/support:support.bzl", "PYTHON_VERSION") _tests = [] -def _test_whatever(name): +def _test_expr(name): def impl(env, target): + attrs = target[TestingAspectInfo].attrs + # todo: create FeatureFlagInfo subject actual = target[config_common.FeatureFlagInfo].value - env.expect.that_str(actual).equals("yes") + env.expect.where( + expression = attrs.expression, + ).that_str(actual).equals("yes") - depspec_flag( - name = name + "_subject", - expression = "python_version >= '3.12.0'", - ) - analysis_test( - name = name, - impl = impl, - target = name + "_subject", - config_settings = { - PYTHON_VERSION: "3.12.0", + cases = { + "python_version_gte": { + "expression": "python_version >= '3.12.0'", + "config_settings": { + PYTHON_VERSION: "3.12.0", + }, }, + } + + tests = [] + for case_name, case in cases.items(): + test_name = name + "_" + case_name + tests.append(test_name) + depspec_flag( + name = test_name + "_subject", + expression = case["expression"], + ) + analysis_test( + name = test_name, + impl = impl, + target = test_name + "_subject", + config_settings = case["config_settings"], + ) + native.test_suite( + name = name, + tests = tests, ) -_tests.append(_test_whatever) +_tests.append(_test_expr) def depspec_flag_test_suite(name): test_suite( From bda07f7c0bb91c0fb5a4d581e7d9209bd6d7006f Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 28 Apr 2025 21:15:15 -0700 Subject: [PATCH 09/23] expand testing a bit --- tests/pypi/pep508/BUILD.bazel | 1 + tests/pypi/pep508/depspec_flag_tests.bzl | 25 +++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/pypi/pep508/BUILD.bazel b/tests/pypi/pep508/BUILD.bazel index 2dc87bf7be..486428f485 100644 --- a/tests/pypi/pep508/BUILD.bazel +++ b/tests/pypi/pep508/BUILD.bazel @@ -15,6 +15,7 @@ requirement_test_suite( name = "requirement_tests", ) +# todo: move to subdir; it generates many test targets depspec_flag_test_suite( name = "depspec_flag_tests", ) diff --git a/tests/pypi/pep508/depspec_flag_tests.bzl b/tests/pypi/pep508/depspec_flag_tests.bzl index b5bbd5d84a..0a36723ece 100644 --- a/tests/pypi/pep508/depspec_flag_tests.bzl +++ b/tests/pypi/pep508/depspec_flag_tests.bzl @@ -10,17 +10,26 @@ _tests = [] def _test_expr(name): def impl(env, target): - attrs = target[TestingAspectInfo].attrs - # todo: create FeatureFlagInfo subject - actual = target[config_common.FeatureFlagInfo].value env.expect.where( - expression = attrs.expression, - ).that_str(actual).equals("yes") + expression = target[TestingAspectInfo].attrs.expression, + ).that_str( + target[config_common.FeatureFlagInfo].value, + ).equals( + env.ctx.attr.expected, + ) cases = { "python_version_gte": { "expression": "python_version >= '3.12.0'", + "expected": "yes", + "config_settings": { + PYTHON_VERSION: "3.12.0", + }, + }, + "python_full_version_lt_negative": { + "expression": "python_full_version < '3.8'", + "expected": "no", "config_settings": { PYTHON_VERSION: "3.12.0", }, @@ -40,6 +49,12 @@ def _test_expr(name): impl = impl, target = test_name + "_subject", config_settings = case["config_settings"], + attr_values = { + "expected": case["expected"], + }, + attrs = { + "expected": attr.string(), + }, ) native.test_suite( name = name, From 5d37d7daf34af86e3f4e65781ca1d9f7409a36dc Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Tue, 29 Apr 2025 22:46:46 +0900 Subject: [PATCH 10/23] a few notes and links and continue with impl --- .../pypi/dependency_specifier_flag.bzl | 119 +++++++++++++++--- 1 file changed, 102 insertions(+), 17 deletions(-) diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/dependency_specifier_flag.bzl index b8a3189988..e31a8e2f5e 100644 --- a/python/private/pypi/dependency_specifier_flag.bzl +++ b/python/private/pypi/dependency_specifier_flag.bzl @@ -1,7 +1,19 @@ +"""Implement a flag for matching the dependency specifiers at analysis time.""" + load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") load(":pep508_evaluate.bzl", "evaluate") +# TODO @aignas 2025-04-29: this is copied from ./pep508_env.bzl +_platform_machine_aliases = { + # These pairs mean the same hardware, but different values may be used + # on different host platforms. + "amd64": "x86_64", + "arm64": "aarch64", + "i386": "x86_32", + "i686": "x86_32", +} + def depspec_flag(**kwargs): pypa_dependency_specification( # todo: copied from pep508_env.bzl @@ -16,30 +28,72 @@ def depspec_flag(**kwargs): }), # todo: copied from pep508_env.bzl sys_platform = select({ - "@platforms//os:windows": "win32", + # Taken from + # https://docs.python.org/3/library/sys.html#sys.platform + "@platforms//os:android": "android", + "@platforms//os:emscripten": "emscripten", + # NOTE, the below values here are from the time when the Python + # interpreter is built and it is hard to know for sure, maybe this + # should be something from the toolchain? + "@platforms//os:freebsd": "freebsd8", + "@platforms//os:ios": "ios", "@platforms//os:linux": "linux", + "@platforms//os:openbsd": "openbsd6", "@platforms//os:osx": "darwin", - # todo: what does spec say unknown value is? + "@platforms//os:wasi": "wasi", + "@platforms//os:windows": "win32", "//conditions:default": "", }), # todo: copied from pep508_env.bzl - # todo: pep508_env and evaluate have an "aliases" thing that needs - # to be incorporated - # todo: there are many more cpus. Unfortunately, it doesn't look like + # TODO: there are many cpus and unfortunately, it doesn't look like # the value is directly accessible to starlark. It might be possible to # get it via CcToolchain.cpu though. platform_machine = select({ - "@platforms//cpu:x86_64": "x86_64", + "@platforms//cpu:aarch32": "aarch32", "@platforms//cpu:aarch64": "aarch64", - # todo: what does spec say unknown value is? + "@platforms//cpu:arm": "arm", + "@platforms//cpu:arm64": "arm64", + "@platforms//cpu:arm64_32": "arm64_32", + "@platforms//cpu:arm64e": "arm64e", + "@platforms//cpu:armv6-m": "armv6-m", + "@platforms//cpu:armv7": "armv7", + "@platforms//cpu:armv7-m": "armv7-m", + "@platforms//cpu:armv7e-m": "armv7e-m", + "@platforms//cpu:armv7e-mf": "armv7e-mf", + "@platforms//cpu:armv7k": "armv7k", + "@platforms//cpu:armv8-m": "armv8-m", + "@platforms//cpu:cortex-r52": "cortex-r52", + "@platforms//cpu:cortex-r82": "cortex-r82", + "@platforms//cpu:i386": "i386", + "@platforms//cpu:mips64": "mips64", + "@platforms//cpu:ppc": "ppc", + "@platforms//cpu:ppc32": "ppc32", + "@platforms//cpu:ppc64le": "ppc64le", + "@platforms//cpu:riscv32": "riscv32", + "@platforms//cpu:riscv64": "riscv64", + "@platforms//cpu:s390x": "s390x", + "@platforms//cpu:wasm32": "wasm32", + "@platforms//cpu:wasm64": "wasm64", + "@platforms//cpu:x86_32": "x86_32", + "@platforms//cpu:x86_64": "x86_64", + # The value is empty string if it cannot be determined: + # https://docs.python.org/3/library/platform.html#platform.machine "//conditions:default": "", }), # todo: copied from pep508_env.bzl platform_system = select({ - "@platforms//os:windows": "Windows", + # See https://peps.python.org/pep-0738/#platform + "@platforms//os:android": "Android", + "@platforms//os:freebsd": "FreeBSD", + # See https://peps.python.org/pep-0730/#platform + "@platforms//os:ios": "iOS", # can also be iPadOS? "@platforms//os:linux": "Linux", + "@platforms//os:netbsd": "NetBSD", + "@platforms//os:openbsd": "OpenBSD", "@platforms//os:osx": "Darwin", - # todo: what does spec say unknown value is? + "@platforms//os:windows": "Windows", + # The value is empty string if it cannot be determined: + # https://docs.python.org/3/library/platform.html#platform.machine "//conditions:default": "", }), **kwargs @@ -83,10 +137,24 @@ def _impl(ctx): if platform_python_impl == "cpython": platform_python_impl = "CPython" env["platform_python_implementation"] = platform_python_impl + + # NOTE: Platform release for Android will be Android version: + # https://peps.python.org/pep-0738/#platform + # Similar for iOS: + # https://peps.python.org/pep-0730/#platform env["platform_release"] = ctx.attr._platform_release_config_flag[BuildSettingInfo].value env["platform_system"] = ctx.attr.platform_system env["platform_version"] = ctx.attr._platform_version_config_flag[BuildSettingInfo].value + # TODO @aignas 2025-04-29: figure out how to correctly share the aliases + # between the two. Maybe the select statements above should be part of the + # `pep508_env.bzl` file? + env = env | { + "_aliases": { + "platform_machine": _platform_machine_aliases, + }, + } + if evaluate(ctx.attr.expression, env = env): value = "yes" else: @@ -98,32 +166,49 @@ pypa_dependency_specification = rule( attrs = { "expression": attr.string(), "os_name": attr.string(), - "sys_platform": attr.string(), "platform_machine": attr.string(), "platform_system": attr.string(), + "sys_platform": attr.string(), + # todo: what to do with this? + # NOTE(aignas) - with the `evaluate` function we can evaluate a + # particular value. For example we can have an expression and just + # evaluate extras. I.e. if the extras don't match, then the whole thing + # is false, if it matches, then it is a string with a remaining + # expression. This means that the `pypa_dependency_specification` + # should not receive any `extra_flags` because these are not properties + # of the target configuration, but rather of a particular package, + # hence we could drop it. + "_extra_flag": attr.label(), "_platform_release_config_flag": attr.label( default = "//python/config_settings:pip_platform_release_config", ), "_platform_version_config_flag": attr.label( default = "//python/config_settings:pip_platform_version_config", ), - "_python_version_flag": attr.label( - default = "//python/config_settings:python_version_major_minor", - ), "_python_full_version_flag": attr.label( default = "//python/config_settings:python_version", ), - # todo: what to do with this? - "_extra_flag": attr.label(), + "_python_version_flag": attr.label( + default = "//python/config_settings:python_version_major_minor", + ), }, toolchains = [ TARGET_TOOLCHAIN_TYPE, ], ) -# Adapted from spec code at: -# https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers def format_full_version(info): + """Format the full python interpreter version. + + Adapted from spec code at: + https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers + + Args: + info: The provider from the Python runtime. + + Returns: + a {type}`str` with the version + """ kind = info.releaselevel if kind == "final": kind = "" From 38e6f5bc7ce1a5ab8c9941887c8e81894ddb18af Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Tue, 29 Apr 2025 16:38:13 -0700 Subject: [PATCH 11/23] rename to env_marker_setting --- ...endency_specifier_flag.bzl => env_marker_setting.bzl} | 9 ++++++--- tests/pypi/env_marker_setting/BUILD.bazel | 5 +++++ .../env_marker_setting_tests.bzl} | 6 +++--- tests/pypi/pep508/BUILD.bazel | 6 ------ 4 files changed, 14 insertions(+), 12 deletions(-) rename python/private/pypi/{dependency_specifier_flag.bzl => env_marker_setting.bzl} (95%) create mode 100644 tests/pypi/env_marker_setting/BUILD.bazel rename tests/pypi/{pep508/depspec_flag_tests.bzl => env_marker_setting/env_marker_setting_tests.bzl} (93%) diff --git a/python/private/pypi/dependency_specifier_flag.bzl b/python/private/pypi/env_marker_setting.bzl similarity index 95% rename from python/private/pypi/dependency_specifier_flag.bzl rename to python/private/pypi/env_marker_setting.bzl index b8a3189988..db143b3e5f 100644 --- a/python/private/pypi/dependency_specifier_flag.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -2,8 +2,8 @@ load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") load(":pep508_evaluate.bzl", "evaluate") -def depspec_flag(**kwargs): - pypa_dependency_specification( +def env_marker_setting(**kwargs): + _env_marker_setting( # todo: copied from pep508_env.bzl os_name = select({ # The "java" value is documented, but with Jython defunct, @@ -88,12 +88,14 @@ def _impl(ctx): env["platform_version"] = ctx.attr._platform_version_config_flag[BuildSettingInfo].value if evaluate(ctx.attr.expression, env = env): + # todo: better return value than "yes" and "no" + # matched/unmatched, satisfied/unsatisfied ? value = "yes" else: value = "no" return [config_common.FeatureFlagInfo(value = value)] -pypa_dependency_specification = rule( +_env_marker_setting = rule( implementation = _impl, attrs = { "expression": attr.string(), @@ -116,6 +118,7 @@ pypa_dependency_specification = rule( # todo: what to do with this? "_extra_flag": attr.label(), }, + provides = [config_common.FeatureFlagInfo], toolchains = [ TARGET_TOOLCHAIN_TYPE, ], diff --git a/tests/pypi/env_marker_setting/BUILD.bazel b/tests/pypi/env_marker_setting/BUILD.bazel new file mode 100644 index 0000000000..9605e650ce --- /dev/null +++ b/tests/pypi/env_marker_setting/BUILD.bazel @@ -0,0 +1,5 @@ +load(":env_marker_setting_tests.bzl", "env_marker_setting_test_suite") + +env_marker_setting_test_suite( + name = "env_marker_setting_tests", +) diff --git a/tests/pypi/pep508/depspec_flag_tests.bzl b/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl similarity index 93% rename from tests/pypi/pep508/depspec_flag_tests.bzl rename to tests/pypi/env_marker_setting/env_marker_setting_tests.bzl index 0a36723ece..6d4537332c 100644 --- a/tests/pypi/pep508/depspec_flag_tests.bzl +++ b/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl @@ -1,7 +1,7 @@ load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") load("@rules_testing//lib:util.bzl", "TestingAspectInfo") -load("//python/private/pypi:dependency_specifier_flag.bzl", "depspec_flag") +load("//python/private/pypi:env_marker_setting.bzl", "env_marker_setting") load("//python/private/pypi:pep508_env.bzl", pep508_env = "env") # buildifier: disable=bzl-visibility load("//python/private/pypi:pep508_evaluate.bzl", "evaluate", "tokenize") # buildifier: disable=bzl-visibility load("//tests/support:support.bzl", "PYTHON_VERSION") @@ -40,7 +40,7 @@ def _test_expr(name): for case_name, case in cases.items(): test_name = name + "_" + case_name tests.append(test_name) - depspec_flag( + env_marker_setting( name = test_name + "_subject", expression = case["expression"], ) @@ -63,7 +63,7 @@ def _test_expr(name): _tests.append(_test_expr) -def depspec_flag_test_suite(name): +def env_marker_setting_test_suite(name): test_suite( name = name, tests = _tests, diff --git a/tests/pypi/pep508/BUILD.bazel b/tests/pypi/pep508/BUILD.bazel index 486428f485..7eab2e096a 100644 --- a/tests/pypi/pep508/BUILD.bazel +++ b/tests/pypi/pep508/BUILD.bazel @@ -1,5 +1,4 @@ load(":deps_tests.bzl", "deps_test_suite") -load(":depspec_flag_tests.bzl", "depspec_flag_test_suite") load(":evaluate_tests.bzl", "evaluate_test_suite") load(":requirement_tests.bzl", "requirement_test_suite") @@ -14,8 +13,3 @@ evaluate_test_suite( requirement_test_suite( name = "requirement_tests", ) - -# todo: move to subdir; it generates many test targets -depspec_flag_test_suite( - name = "depspec_flag_tests", -) From 3f794c2cbb93f178ce7edac67a5f1f22e29d8d6b Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Tue, 29 Apr 2025 16:56:08 -0700 Subject: [PATCH 12/23] move select mappings to constants --- python/private/pypi/env_marker_setting.bzl | 167 +++++++++++---------- 1 file changed, 87 insertions(+), 80 deletions(-) diff --git a/python/private/pypi/env_marker_setting.bzl b/python/private/pypi/env_marker_setting.bzl index 1898881827..72b994bdbf 100644 --- a/python/private/pypi/env_marker_setting.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -4,6 +4,17 @@ load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") load(":pep508_evaluate.bzl", "evaluate") +# todo: copied from pep508_env.bzl +_os_name_select_map = { + # The "java" value is documented, but with Jython defunct, + # shouldn't occur in practice. + # The osname value is technically a property of the runtime, not the + # targetted OS at runtime, but the distinction shouldn't matter in + # practice. + "@platforms//os:windows": "nt", + "//conditions:default": "posix", +} + # TODO @aignas 2025-04-29: this is copied from ./pep508_env.bzl _platform_machine_aliases = { # These pairs mean the same hardware, but different values may be used @@ -14,88 +25,84 @@ _platform_machine_aliases = { "i686": "x86_32", } +# Taken from +# https://docs.python.org/3/library/sys.html#sys.platform +_sys_platform_select_map = { + "@platforms//os:android": "android", + "@platforms//os:emscripten": "emscripten", + # NOTE, the below values here are from the time when the Python + # interpreter is built and it is hard to know for sure, maybe this + # should be something from the toolchain? + "@platforms//os:freebsd": "freebsd8", + "@platforms//os:ios": "ios", + "@platforms//os:linux": "linux", + "@platforms//os:openbsd": "openbsd6", + "@platforms//os:osx": "darwin", + "@platforms//os:wasi": "wasi", + "@platforms//os:windows": "win32", + "//conditions:default": "", +} + +# todo: copied from pep508_env.bzl +# TODO: there are many cpus and unfortunately, it doesn't look like +# the value is directly accessible to starlark. It might be possible to +# get it via CcToolchain.cpu though. +_platform_machine_select_map = { + "@platforms//cpu:aarch32": "aarch32", + "@platforms//cpu:aarch64": "aarch64", + "@platforms//cpu:arm": "arm", + "@platforms//cpu:arm64": "arm64", + "@platforms//cpu:arm64_32": "arm64_32", + "@platforms//cpu:arm64e": "arm64e", + "@platforms//cpu:armv6-m": "armv6-m", + "@platforms//cpu:armv7": "armv7", + "@platforms//cpu:armv7-m": "armv7-m", + "@platforms//cpu:armv7e-m": "armv7e-m", + "@platforms//cpu:armv7e-mf": "armv7e-mf", + "@platforms//cpu:armv7k": "armv7k", + "@platforms//cpu:armv8-m": "armv8-m", + "@platforms//cpu:cortex-r52": "cortex-r52", + "@platforms//cpu:cortex-r82": "cortex-r82", + "@platforms//cpu:i386": "i386", + "@platforms//cpu:mips64": "mips64", + "@platforms//cpu:ppc": "ppc", + "@platforms//cpu:ppc32": "ppc32", + "@platforms//cpu:ppc64le": "ppc64le", + "@platforms//cpu:riscv32": "riscv32", + "@platforms//cpu:riscv64": "riscv64", + "@platforms//cpu:s390x": "s390x", + "@platforms//cpu:wasm32": "wasm32", + "@platforms//cpu:wasm64": "wasm64", + "@platforms//cpu:x86_32": "x86_32", + "@platforms//cpu:x86_64": "x86_64", + # The value is empty string if it cannot be determined: + # https://docs.python.org/3/library/platform.html#platform.machine + "//conditions:default": "", +} + +# todo: copied from pep508_env.bzl +_platform_system_select_map = { + # See https://peps.python.org/pep-0738/#platform + "@platforms//os:android": "Android", + "@platforms//os:freebsd": "FreeBSD", + # See https://peps.python.org/pep-0730/#platform + "@platforms//os:ios": "iOS", # can also be iPadOS? + "@platforms//os:linux": "Linux", + "@platforms//os:netbsd": "NetBSD", + "@platforms//os:openbsd": "OpenBSD", + "@platforms//os:osx": "Darwin", + "@platforms//os:windows": "Windows", + # The value is empty string if it cannot be determined: + # https://docs.python.org/3/library/platform.html#platform.machine + "//conditions:default": "", +} + def env_marker_setting(**kwargs): _env_marker_setting( - # todo: copied from pep508_env.bzl - os_name = select({ - # The "java" value is documented, but with Jython defunct, - # shouldn't occur in practice. - # The osname value is technically a property of the runtime, not the - # targetted OS at runtime, but the distinction shouldn't matter in - # practice. - "@platforms//os:windows": "nt", - "//conditions:default": "posix", - }), - # todo: copied from pep508_env.bzl - sys_platform = select({ - # Taken from - # https://docs.python.org/3/library/sys.html#sys.platform - "@platforms//os:android": "android", - "@platforms//os:emscripten": "emscripten", - # NOTE, the below values here are from the time when the Python - # interpreter is built and it is hard to know for sure, maybe this - # should be something from the toolchain? - "@platforms//os:freebsd": "freebsd8", - "@platforms//os:ios": "ios", - "@platforms//os:linux": "linux", - "@platforms//os:openbsd": "openbsd6", - "@platforms//os:osx": "darwin", - "@platforms//os:wasi": "wasi", - "@platforms//os:windows": "win32", - "//conditions:default": "", - }), - # todo: copied from pep508_env.bzl - # TODO: there are many cpus and unfortunately, it doesn't look like - # the value is directly accessible to starlark. It might be possible to - # get it via CcToolchain.cpu though. - platform_machine = select({ - "@platforms//cpu:aarch32": "aarch32", - "@platforms//cpu:aarch64": "aarch64", - "@platforms//cpu:arm": "arm", - "@platforms//cpu:arm64": "arm64", - "@platforms//cpu:arm64_32": "arm64_32", - "@platforms//cpu:arm64e": "arm64e", - "@platforms//cpu:armv6-m": "armv6-m", - "@platforms//cpu:armv7": "armv7", - "@platforms//cpu:armv7-m": "armv7-m", - "@platforms//cpu:armv7e-m": "armv7e-m", - "@platforms//cpu:armv7e-mf": "armv7e-mf", - "@platforms//cpu:armv7k": "armv7k", - "@platforms//cpu:armv8-m": "armv8-m", - "@platforms//cpu:cortex-r52": "cortex-r52", - "@platforms//cpu:cortex-r82": "cortex-r82", - "@platforms//cpu:i386": "i386", - "@platforms//cpu:mips64": "mips64", - "@platforms//cpu:ppc": "ppc", - "@platforms//cpu:ppc32": "ppc32", - "@platforms//cpu:ppc64le": "ppc64le", - "@platforms//cpu:riscv32": "riscv32", - "@platforms//cpu:riscv64": "riscv64", - "@platforms//cpu:s390x": "s390x", - "@platforms//cpu:wasm32": "wasm32", - "@platforms//cpu:wasm64": "wasm64", - "@platforms//cpu:x86_32": "x86_32", - "@platforms//cpu:x86_64": "x86_64", - # The value is empty string if it cannot be determined: - # https://docs.python.org/3/library/platform.html#platform.machine - "//conditions:default": "", - }), - # todo: copied from pep508_env.bzl - platform_system = select({ - # See https://peps.python.org/pep-0738/#platform - "@platforms//os:android": "Android", - "@platforms//os:freebsd": "FreeBSD", - # See https://peps.python.org/pep-0730/#platform - "@platforms//os:ios": "iOS", # can also be iPadOS? - "@platforms//os:linux": "Linux", - "@platforms//os:netbsd": "NetBSD", - "@platforms//os:openbsd": "OpenBSD", - "@platforms//os:osx": "Darwin", - "@platforms//os:windows": "Windows", - # The value is empty string if it cannot be determined: - # https://docs.python.org/3/library/platform.html#platform.machine - "//conditions:default": "", - }), + os_name = select(_os_name_select_map), + sys_platform = select(_sys_platform_select_map), + platform_machine = select(_platform_machine_select_map), + platform_system = select(_platform_system_select_map), **kwargs ) From 7dae5d017abd67c3079ba1078deb17cfc9034bc0 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Tue, 29 Apr 2025 20:04:14 -0700 Subject: [PATCH 13/23] remove extras_flag; various code/comment cleanup --- python/private/pypi/env_marker_setting.bzl | 70 ++++++++++++---------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/python/private/pypi/env_marker_setting.bzl b/python/private/pypi/env_marker_setting.bzl index 72b994bdbf..d5f4019582 100644 --- a/python/private/pypi/env_marker_setting.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -28,18 +28,22 @@ _platform_machine_aliases = { # Taken from # https://docs.python.org/3/library/sys.html#sys.platform _sys_platform_select_map = { + # These values are decided by the sys.platform docs. "@platforms//os:android": "android", "@platforms//os:emscripten": "emscripten", - # NOTE, the below values here are from the time when the Python - # interpreter is built and it is hard to know for sure, maybe this - # should be something from the toolchain? - "@platforms//os:freebsd": "freebsd8", "@platforms//os:ios": "ios", "@platforms//os:linux": "linux", - "@platforms//os:openbsd": "openbsd6", "@platforms//os:osx": "darwin", - "@platforms//os:wasi": "wasi", "@platforms//os:windows": "win32", + "@platforms//os:wasi": "wasi", + # NOTE: The below values are approximations. The sys.platform() docs + # don't have documented values for these OSes. Per docs, the + # sys.platform() value reflects the OS at the time Python was *built* + # instead of the runtime (target) OS value. + "@platforms//os:freebsd": "freebsd", + "@platforms//os:openbsd": "openbsd", + # For lack of a better option, use empty string. No standard doc/spec + # about sys_platform value. "//conditions:default": "", } @@ -86,7 +90,8 @@ _platform_system_select_map = { "@platforms//os:android": "Android", "@platforms//os:freebsd": "FreeBSD", # See https://peps.python.org/pep-0730/#platform - "@platforms//os:ios": "iOS", # can also be iPadOS? + # NOTE: Per Pep 730, "iPadOS" is also an acceptable value + "@platforms//os:ios": "iOS", "@platforms//os:linux": "Linux", "@platforms//os:netbsd": "NetBSD", "@platforms//os:openbsd": "OpenBSD", @@ -108,7 +113,7 @@ def env_marker_setting(**kwargs): # todo: maybe put all the env into a single target and have a # PyPiEnvMarkersInfo provider? Have --pypi_env=//some:target? -def _impl(ctx): +def _env_marker_setting_impl(ctx): # todo: should unify with pep508_env.bzl env = {} @@ -119,11 +124,11 @@ def _impl(ctx): major = version_info.major, minor = version_info.minor, ) - full_version = format_full_version(version_info) + full_version = _format_full_version(version_info) env["python_full_version"] = full_version env["implementation_version"] = full_version else: - env["python_version"] = _get_flag(ctx.attr._python_version) + env["python_version"] = _get_flag(ctx.attr._python_version_major_minor_flag) full_version = _get_flag(ctx.attr._python_full_version) env["python_full_version"] = full_version env["implementation_version"] = full_version @@ -135,23 +140,26 @@ def _impl(ctx): env["sys_platform"] = ctx.attr.sys_platform env["platform_machine"] = ctx.attr.platform_machine - # todo: maybe add PyRuntimeInfo.platform_python_implementation? - # The values are slightly different to implementation_name. - # However, digging through old PEPs, it looks like - # platform.python_implementation is legacy, and sys.implementation.name - # "replaced" it. Can probably just special case this. + # The `platform_python_implementation` marker value is supposed to come from + # `platform.python_implementation()`, however, PEP 421 introduced + # `sys.implementation.name` to replace it. There's now essentially just two + # possible values it might have: CPython or PyPy. Rather than add a field to + # the toolchain, we just special case the value from + # `sys.implementation.name` platform_python_impl = runtime.implementation_name if platform_python_impl == "cpython": platform_python_impl = "CPython" + elif platform_python_impl == "pypy": + platform_python_impl = "PyPy" env["platform_python_implementation"] = platform_python_impl # NOTE: Platform release for Android will be Android version: # https://peps.python.org/pep-0738/#platform # Similar for iOS: # https://peps.python.org/pep-0730/#platform - env["platform_release"] = ctx.attr._platform_release_config_flag[BuildSettingInfo].value + env["platform_release"] = _get_flag(ctx.attr._platform_release_config_flag) env["platform_system"] = ctx.attr.platform_system - env["platform_version"] = ctx.attr._platform_version_config_flag[BuildSettingInfo].value + env["platform_version"] = _get_flag(ctx.attr._platform_version_config_flag) # TODO @aignas 2025-04-29: figure out how to correctly share the aliases # between the two. Maybe the select statements above should be part of the @@ -171,34 +179,34 @@ def _impl(ctx): return [config_common.FeatureFlagInfo(value = value)] _env_marker_setting = rule( - implementation = _impl, + doc = """ +Config setting to evaluate a PyPA environment marker expression. +""", + implementation = _env_marker_setting_impl, attrs = { - "expression": attr.string(), + "expression": attr.string( + mandatory = True, + doc = "Environment marker expression to evaluate.", + ), "os_name": attr.string(), "platform_machine": attr.string(), "platform_system": attr.string(), "sys_platform": attr.string(), - # todo: what to do with this? - # NOTE(aignas) - with the `evaluate` function we can evaluate a - # particular value. For example we can have an expression and just - # evaluate extras. I.e. if the extras don't match, then the whole thing - # is false, if it matches, then it is a string with a remaining - # expression. This means that the `pypa_dependency_specification` - # should not receive any `extra_flags` because these are not properties - # of the target configuration, but rather of a particular package, - # hence we could drop it. - "_extra_flag": attr.label(), "_platform_release_config_flag": attr.label( default = "//python/config_settings:pip_platform_release_config", + providers = [[config_common.FeatureFlagInfo], [BuildSettingInfo]], ), "_platform_version_config_flag": attr.label( default = "//python/config_settings:pip_platform_version_config", + providers = [[config_common.FeatureFlagInfo], [BuildSettingInfo]], ), "_python_full_version_flag": attr.label( default = "//python/config_settings:python_version", + providers = [config_common.FeatureFlagInfo], ), - "_python_version_flag": attr.label( + "_python_version_major_minor_flag": attr.label( default = "//python/config_settings:python_version_major_minor", + providers = [config_common.FeatureFlagInfo], ), }, provides = [config_common.FeatureFlagInfo], @@ -207,7 +215,7 @@ _env_marker_setting = rule( ], ) -def format_full_version(info): +def _format_full_version(info): """Format the full python interpreter version. Adapted from spec code at: From 2f1b5282b3a39fd4b805609f877a1fa01f1fb47e Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Tue, 29 Apr 2025 22:01:36 -0700 Subject: [PATCH 14/23] minor cleanup, doc --- python/private/pypi/env_marker_setting.bzl | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/python/private/pypi/env_marker_setting.bzl b/python/private/pypi/env_marker_setting.bzl index d5f4019582..c8576831ec 100644 --- a/python/private/pypi/env_marker_setting.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -8,9 +8,9 @@ load(":pep508_evaluate.bzl", "evaluate") _os_name_select_map = { # The "java" value is documented, but with Jython defunct, # shouldn't occur in practice. - # The osname value is technically a property of the runtime, not the - # targetted OS at runtime, but the distinction shouldn't matter in - # practice. + # The os.name value is technically a property of the runtime, not the + # targetted runtime OS, but the distinction shouldn't matter if + # things are properly configured. "@platforms//os:windows": "nt", "//conditions:default": "posix", } @@ -103,7 +103,16 @@ _platform_system_select_map = { } def env_marker_setting(**kwargs): + """Creates an env_marker setting. + + Args: + name: {type}`str` target name + expression: {type}`str` the environment marker string to evaluate + **kwargs: {type}`dict` additionally common kwargs. + """ _env_marker_setting( + name = name, + expression = expression, os_name = select(_os_name_select_map), sys_platform = select(_sys_platform_select_map), platform_machine = select(_platform_machine_select_map), @@ -180,7 +189,11 @@ def _env_marker_setting_impl(ctx): _env_marker_setting = rule( doc = """ -Config setting to evaluate a PyPA environment marker expression. +Evaluates an environment marker expression using target configuration info. + +See +https://packaging.python.org/en/latest/specifications/dependency-specifiers +for the specification of behavior. """, implementation = _env_marker_setting_impl, attrs = { From 7ff220e5d1f78375489768aaf0d4d0217b08f152 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Tue, 29 Apr 2025 22:06:25 -0700 Subject: [PATCH 15/23] fixup: forgot to pass args --- python/private/pypi/env_marker_setting.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/private/pypi/env_marker_setting.bzl b/python/private/pypi/env_marker_setting.bzl index c8576831ec..066a29c6b9 100644 --- a/python/private/pypi/env_marker_setting.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -102,7 +102,7 @@ _platform_system_select_map = { "//conditions:default": "", } -def env_marker_setting(**kwargs): +def env_marker_setting(*, name, expression, **kwargs): """Creates an env_marker setting. Args: From b340de7a64305aebd6d9eb5b2228fb9c6054383a Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 30 Apr 2025 11:05:27 -0700 Subject: [PATCH 16/23] code share with pep508_env.bzl, add config_setting, --- python/private/pypi/env_marker_setting.bzl | 159 +++++------------- python/private/pypi/pep508_env.bzl | 95 ++++++++++- .../env_marker_setting_tests.bzl | 4 +- 3 files changed, 133 insertions(+), 125 deletions(-) diff --git a/python/private/pypi/env_marker_setting.bzl b/python/private/pypi/env_marker_setting.bzl index 066a29c6b9..d2f6a657c0 100644 --- a/python/private/pypi/env_marker_setting.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -2,121 +2,48 @@ load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") +load( + ":pep508_env.bzl", + "env_aliases", + "os_name_select_map", + "platform_machine_aliases", + "platform_machine_select_map", + "platform_system_select_map", + "sys_platform_select_map", +) load(":pep508_evaluate.bzl", "evaluate") -# todo: copied from pep508_env.bzl -_os_name_select_map = { - # The "java" value is documented, but with Jython defunct, - # shouldn't occur in practice. - # The os.name value is technically a property of the runtime, not the - # targetted runtime OS, but the distinction shouldn't matter if - # things are properly configured. - "@platforms//os:windows": "nt", - "//conditions:default": "posix", -} - -# TODO @aignas 2025-04-29: this is copied from ./pep508_env.bzl -_platform_machine_aliases = { - # These pairs mean the same hardware, but different values may be used - # on different host platforms. - "amd64": "x86_64", - "arm64": "aarch64", - "i386": "x86_32", - "i686": "x86_32", -} - -# Taken from -# https://docs.python.org/3/library/sys.html#sys.platform -_sys_platform_select_map = { - # These values are decided by the sys.platform docs. - "@platforms//os:android": "android", - "@platforms//os:emscripten": "emscripten", - "@platforms//os:ios": "ios", - "@platforms//os:linux": "linux", - "@platforms//os:osx": "darwin", - "@platforms//os:windows": "win32", - "@platforms//os:wasi": "wasi", - # NOTE: The below values are approximations. The sys.platform() docs - # don't have documented values for these OSes. Per docs, the - # sys.platform() value reflects the OS at the time Python was *built* - # instead of the runtime (target) OS value. - "@platforms//os:freebsd": "freebsd", - "@platforms//os:openbsd": "openbsd", - # For lack of a better option, use empty string. No standard doc/spec - # about sys_platform value. - "//conditions:default": "", -} - -# todo: copied from pep508_env.bzl -# TODO: there are many cpus and unfortunately, it doesn't look like -# the value is directly accessible to starlark. It might be possible to -# get it via CcToolchain.cpu though. -_platform_machine_select_map = { - "@platforms//cpu:aarch32": "aarch32", - "@platforms//cpu:aarch64": "aarch64", - "@platforms//cpu:arm": "arm", - "@platforms//cpu:arm64": "arm64", - "@platforms//cpu:arm64_32": "arm64_32", - "@platforms//cpu:arm64e": "arm64e", - "@platforms//cpu:armv6-m": "armv6-m", - "@platforms//cpu:armv7": "armv7", - "@platforms//cpu:armv7-m": "armv7-m", - "@platforms//cpu:armv7e-m": "armv7e-m", - "@platforms//cpu:armv7e-mf": "armv7e-mf", - "@platforms//cpu:armv7k": "armv7k", - "@platforms//cpu:armv8-m": "armv8-m", - "@platforms//cpu:cortex-r52": "cortex-r52", - "@platforms//cpu:cortex-r82": "cortex-r82", - "@platforms//cpu:i386": "i386", - "@platforms//cpu:mips64": "mips64", - "@platforms//cpu:ppc": "ppc", - "@platforms//cpu:ppc32": "ppc32", - "@platforms//cpu:ppc64le": "ppc64le", - "@platforms//cpu:riscv32": "riscv32", - "@platforms//cpu:riscv64": "riscv64", - "@platforms//cpu:s390x": "s390x", - "@platforms//cpu:wasm32": "wasm32", - "@platforms//cpu:wasm64": "wasm64", - "@platforms//cpu:x86_32": "x86_32", - "@platforms//cpu:x86_64": "x86_64", - # The value is empty string if it cannot be determined: - # https://docs.python.org/3/library/platform.html#platform.machine - "//conditions:default": "", -} - -# todo: copied from pep508_env.bzl -_platform_system_select_map = { - # See https://peps.python.org/pep-0738/#platform - "@platforms//os:android": "Android", - "@platforms//os:freebsd": "FreeBSD", - # See https://peps.python.org/pep-0730/#platform - # NOTE: Per Pep 730, "iPadOS" is also an acceptable value - "@platforms//os:ios": "iOS", - "@platforms//os:linux": "Linux", - "@platforms//os:netbsd": "NetBSD", - "@platforms//os:openbsd": "OpenBSD", - "@platforms//os:osx": "Darwin", - "@platforms//os:windows": "Windows", - # The value is empty string if it cannot be determined: - # https://docs.python.org/3/library/platform.html#platform.machine - "//conditions:default": "", -} +# Use capitals to hint its not an actual boolean type. +_ENV_MARKER_TRUE = "TRUE" +_ENV_MARKER_FALSE = "FALSE" def env_marker_setting(*, name, expression, **kwargs): """Creates an env_marker setting. + Generated targets: + + * `is_{name}_true`: config_setting that matches when the expression is true. + * `{name}`: env marker target that evalutes the expression. + Args: name: {type}`str` target name expression: {type}`str` the environment marker string to evaluate - **kwargs: {type}`dict` additionally common kwargs. + **kwargs: {type}`dict` additional common kwargs. """ + native.config_setting( + name = "is_{}_true".format(name), + flag_values = { + ":{}".format(name): _ENV_MARKER_TRUE, + }, + **kwargs + ) _env_marker_setting( name = name, expression = expression, - os_name = select(_os_name_select_map), - sys_platform = select(_sys_platform_select_map), - platform_machine = select(_platform_machine_select_map), - platform_system = select(_platform_system_select_map), + os_name = select(os_name_select_map), + sys_platform = select(sys_platform_select_map), + platform_machine = select(platform_machine_select_map), + platform_system = select(platform_system_select_map), **kwargs ) @@ -149,12 +76,13 @@ def _env_marker_setting_impl(ctx): env["sys_platform"] = ctx.attr.sys_platform env["platform_machine"] = ctx.attr.platform_machine - # The `platform_python_implementation` marker value is supposed to come from - # `platform.python_implementation()`, however, PEP 421 introduced - # `sys.implementation.name` to replace it. There's now essentially just two - # possible values it might have: CPython or PyPy. Rather than add a field to - # the toolchain, we just special case the value from - # `sys.implementation.name` + # The `platform_python_implementation` marker value is supposed to come + # from `platform.python_implementation()`, however, PEP 421 introduced + # `sys.implementation.name` and the `implementation_name` env marker to + # replace it. Per the platform.python_implementation docs, there's now + # essentially just two possible "registered" values: CPython or PyPy. + # Rather than add a field to the toolchain, we just special case the value + # from `sys.implementation.name` to handle the two documented values. platform_python_impl = runtime.implementation_name if platform_python_impl == "cpython": platform_python_impl = "CPython" @@ -170,21 +98,12 @@ def _env_marker_setting_impl(ctx): env["platform_system"] = ctx.attr.platform_system env["platform_version"] = _get_flag(ctx.attr._platform_version_config_flag) - # TODO @aignas 2025-04-29: figure out how to correctly share the aliases - # between the two. Maybe the select statements above should be part of the - # `pep508_env.bzl` file? - env = env | { - "_aliases": { - "platform_machine": _platform_machine_aliases, - }, - } + env.update(env_aliases()) if evaluate(ctx.attr.expression, env = env): - # todo: better return value than "yes" and "no" - # matched/unmatched, satisfied/unsatisfied ? - value = "yes" + value = _ENV_MARKER_TRUE else: - value = "no" + value = _ENV_MARKER_FALSE return [config_common.FeatureFlagInfo(value = value)] _env_marker_setting = rule( diff --git a/python/private/pypi/pep508_env.bzl b/python/private/pypi/pep508_env.bzl index 265a8e9b99..4786980bc3 100644 --- a/python/private/pypi/pep508_env.bzl +++ b/python/private/pypi/pep508_env.bzl @@ -18,7 +18,7 @@ load(":pep508_platform.bzl", "platform_from_str") # See https://stackoverflow.com/a/45125525 -_platform_machine_aliases = { +platform_machine_aliases = { # These pairs mean the same hardware, but different values may be used # on different host platforms. "amd64": "x86_64", @@ -27,6 +27,42 @@ _platform_machine_aliases = { "i686": "x86_32", } +# TODO: there are many cpus and unfortunately, it doesn't look like +# the value is directly accessible to starlark. It might be possible to +# get it via CcToolchain.cpu though. +platform_machine_select_map = { + "@platforms//cpu:aarch32": "aarch32", + "@platforms//cpu:aarch64": "aarch64", + "@platforms//cpu:arm": "arm", + "@platforms//cpu:arm64": "arm64", + "@platforms//cpu:arm64_32": "arm64_32", + "@platforms//cpu:arm64e": "arm64e", + "@platforms//cpu:armv6-m": "armv6-m", + "@platforms//cpu:armv7": "armv7", + "@platforms//cpu:armv7-m": "armv7-m", + "@platforms//cpu:armv7e-m": "armv7e-m", + "@platforms//cpu:armv7e-mf": "armv7e-mf", + "@platforms//cpu:armv7k": "armv7k", + "@platforms//cpu:armv8-m": "armv8-m", + "@platforms//cpu:cortex-r52": "cortex-r52", + "@platforms//cpu:cortex-r82": "cortex-r82", + "@platforms//cpu:i386": "i386", + "@platforms//cpu:mips64": "mips64", + "@platforms//cpu:ppc": "ppc", + "@platforms//cpu:ppc32": "ppc32", + "@platforms//cpu:ppc64le": "ppc64le", + "@platforms//cpu:riscv32": "riscv32", + "@platforms//cpu:riscv64": "riscv64", + "@platforms//cpu:s390x": "s390x", + "@platforms//cpu:wasm32": "wasm32", + "@platforms//cpu:wasm64": "wasm64", + "@platforms//cpu:x86_32": "x86_32", + "@platforms//cpu:x86_64": "x86_64", + # The value is empty string if it cannot be determined: + # https://docs.python.org/3/library/platform.html#platform.machine + "//conditions:default": "", +} + # Platform system returns results from the `uname` call. _platform_system_values = { "linux": "Linux", @@ -34,6 +70,23 @@ _platform_system_values = { "windows": "Windows", } +platform_system_select_map = { + # See https://peps.python.org/pep-0738/#platform + "@platforms//os:android": "Android", + "@platforms//os:freebsd": "FreeBSD", + # See https://peps.python.org/pep-0730/#platform + # NOTE: Per Pep 730, "iPadOS" is also an acceptable value + "@platforms//os:ios": "iOS", + "@platforms//os:linux": "Linux", + "@platforms//os:netbsd": "NetBSD", + "@platforms//os:openbsd": "OpenBSD", + "@platforms//os:osx": "Darwin", + "@platforms//os:windows": "Windows", + # The value is empty string if it cannot be determined: + # https://docs.python.org/3/library/platform.html#platform.machine + "//conditions:default": "", +} + # The copy of SO [answer](https://stackoverflow.com/a/13874620) containing # all of the platforms: # ┍━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━┑ @@ -64,12 +117,45 @@ _sys_platform_values = { "osx": "darwin", "windows": "win32", } + +# Taken from +# https://docs.python.org/3/library/sys.html#sys.platform +sys_platform_select_map = { + # These values are decided by the sys.platform docs. + "@platforms//os:android": "android", + "@platforms//os:emscripten": "emscripten", + "@platforms//os:ios": "ios", + "@platforms//os:linux": "linux", + "@platforms//os:osx": "darwin", + "@platforms//os:windows": "win32", + "@platforms//os:wasi": "wasi", + # NOTE: The below values are approximations. The sys.platform() docs + # don't have documented values for these OSes. Per docs, the + # sys.platform() value reflects the OS at the time Python was *built* + # instead of the runtime (target) OS value. + "@platforms//os:freebsd": "freebsd", + "@platforms//os:openbsd": "openbsd", + # For lack of a better option, use empty string. No standard doc/spec + # about sys_platform value. + "//conditions:default": "", +} + _os_name_values = { "linux": "posix", "osx": "posix", "windows": "nt", } +os_name_select_map = { + # The "java" value is documented, but with Jython defunct, + # shouldn't occur in practice. + # The os.name value is technically a property of the runtime, not the + # targetted runtime OS, but the distinction shouldn't matter if + # things are properly configured. + "@platforms//os:windows": "nt", + "//conditions:default": "posix", +} + def env(target_platform, *, extra = None): """Return an env target platform @@ -113,8 +199,11 @@ def env(target_platform, *, extra = None): } # This is split by topic - return env | { + return env | env_aliases() + +def env_aliases(): + return { "_aliases": { - "platform_machine": _platform_machine_aliases, + "platform_machine": platform_machine_aliases, }, } diff --git a/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl b/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl index 6d4537332c..0ebfe00a15 100644 --- a/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl +++ b/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl @@ -22,14 +22,14 @@ def _test_expr(name): cases = { "python_version_gte": { "expression": "python_version >= '3.12.0'", - "expected": "yes", + "expected": "TRUE", "config_settings": { PYTHON_VERSION: "3.12.0", }, }, "python_full_version_lt_negative": { "expression": "python_full_version < '3.8'", - "expected": "no", + "expected": "FALSE", "config_settings": { PYTHON_VERSION: "3.12.0", }, From b2c73c0dfa6a007be090ae6521fca75067aadac0 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 30 Apr 2025 19:18:43 -0700 Subject: [PATCH 17/23] more cleanup --- .bazelrc | 4 ++-- python/config_settings/BUILD.bazel | 8 -------- python/private/pypi/env_marker_setting.bzl | 6 +----- python/private/pypi/flags.bzl | 10 +--------- python/private/pypi/pep508_env.bzl | 10 +++++----- .../env_marker_setting_tests.bzl | 15 ++++++--------- 6 files changed, 15 insertions(+), 38 deletions(-) diff --git a/.bazelrc b/.bazelrc index 4e6f2fa187..24342c4f1f 100644 --- a/.bazelrc +++ b/.bazelrc @@ -4,8 +4,8 @@ # (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it) # To update these lines, execute # `bazel run @rules_bazel_integration_test//tools:update_deleted_packages` -build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma -query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma +build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,examples/scratch,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma +query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,examples/scratch,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma test --test_output=errors diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel index 58bc48b06a..2adb1ac757 100644 --- a/python/config_settings/BUILD.bazel +++ b/python/config_settings/BUILD.bazel @@ -221,20 +221,12 @@ define_pypi_internal_flags( name = "define_pypi_internal_flags", ) -# todo: have single --pypi_env_config flag instead? Under the hood, it -# could be driven more more discrete flags, which could be considered an -# implementation detail of our particular impl. -# A label is used to allow users to use select() to affect the value. -# A target providing BuildSettingInfo is used to allow users to define -# a flag as the target, which lets them CLI-override or transition it easily. label_flag( name = "pip_platform_release_config", build_setting_default = ":_pip_platform_release_default_config", visibility = ["//visibility:public"], ) -# docs: points to a label that provides BuildSettingInfo for the value. -# A label and BuildSettingInfo are used for the same reason as platform_release label_flag( name = "pip_platform_version_config", build_setting_default = ":_pip_platform_version_default_config", diff --git a/python/private/pypi/env_marker_setting.bzl b/python/private/pypi/env_marker_setting.bzl index d2f6a657c0..adf58fa26e 100644 --- a/python/private/pypi/env_marker_setting.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -6,7 +6,6 @@ load( ":pep508_env.bzl", "env_aliases", "os_name_select_map", - "platform_machine_aliases", "platform_machine_select_map", "platform_system_select_map", "sys_platform_select_map", @@ -47,10 +46,7 @@ def env_marker_setting(*, name, expression, **kwargs): **kwargs ) -# todo: maybe put all the env into a single target and have a -# PyPiEnvMarkersInfo provider? Have --pypi_env=//some:target? def _env_marker_setting_impl(ctx): - # todo: should unify with pep508_env.bzl env = {} runtime = ctx.toolchains[TARGET_TOOLCHAIN_TYPE].py3_runtime @@ -65,7 +61,7 @@ def _env_marker_setting_impl(ctx): env["implementation_version"] = full_version else: env["python_version"] = _get_flag(ctx.attr._python_version_major_minor_flag) - full_version = _get_flag(ctx.attr._python_full_version) + full_version = _get_flag(ctx.attr._python_full_version_flag) env["python_full_version"] = full_version env["implementation_version"] = full_version diff --git a/python/private/pypi/flags.bzl b/python/private/pypi/flags.bzl index 8f33c369a8..90605de0d3 100644 --- a/python/private/pypi/flags.bzl +++ b/python/private/pypi/flags.bzl @@ -91,10 +91,6 @@ def define_pypi_internal_flags(name): ) _platform_version_config( name = "_pip_platform_version_default_config", - value = select({ - "@platforms//os:osx": "USE_OSX_VERSION_FLAG", - "//conditions:default": "", - }), visibility = ["//visibility:public"], ) @@ -134,14 +130,10 @@ _platform_release_config = rule( ) def _platform_version_config_impl(ctx): - value = ctx.attr.value - return [BuildSettingInfo(value = ctx.attr.value)] + return [BuildSettingInfo(value = "")] # Despite its name, this "version" value is not a simple version value. # It's a more detailed, arbitrary, description the OS gives about itself. _platform_version_config = rule( implementation = _platform_version_config_impl, - attrs = { - "value": attr.string(), - }, ) diff --git a/python/private/pypi/pep508_env.bzl b/python/private/pypi/pep508_env.bzl index 4786980bc3..28a01682bb 100644 --- a/python/private/pypi/pep508_env.bzl +++ b/python/private/pypi/pep508_env.bzl @@ -124,17 +124,17 @@ sys_platform_select_map = { # These values are decided by the sys.platform docs. "@platforms//os:android": "android", "@platforms//os:emscripten": "emscripten", - "@platforms//os:ios": "ios", - "@platforms//os:linux": "linux", - "@platforms//os:osx": "darwin", - "@platforms//os:windows": "win32", - "@platforms//os:wasi": "wasi", # NOTE: The below values are approximations. The sys.platform() docs # don't have documented values for these OSes. Per docs, the # sys.platform() value reflects the OS at the time Python was *built* # instead of the runtime (target) OS value. "@platforms//os:freebsd": "freebsd", + "@platforms//os:ios": "ios", + "@platforms//os:linux": "linux", "@platforms//os:openbsd": "openbsd", + "@platforms//os:osx": "darwin", + "@platforms//os:wasi": "wasi", + "@platforms//os:windows": "win32", # For lack of a better option, use empty string. No standard doc/spec # about sys_platform value. "//conditions:default": "", diff --git a/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl b/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl index 0ebfe00a15..1d8f5d3b54 100644 --- a/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl +++ b/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl @@ -2,15 +2,12 @@ load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") load("@rules_testing//lib:util.bzl", "TestingAspectInfo") load("//python/private/pypi:env_marker_setting.bzl", "env_marker_setting") -load("//python/private/pypi:pep508_env.bzl", pep508_env = "env") # buildifier: disable=bzl-visibility -load("//python/private/pypi:pep508_evaluate.bzl", "evaluate", "tokenize") # buildifier: disable=bzl-visibility load("//tests/support:support.bzl", "PYTHON_VERSION") _tests = [] def _test_expr(name): def impl(env, target): - # todo: create FeatureFlagInfo subject env.expect.where( expression = target[TestingAspectInfo].attrs.expression, ).that_str( @@ -20,19 +17,19 @@ def _test_expr(name): ) cases = { - "python_version_gte": { - "expression": "python_version >= '3.12.0'", - "expected": "TRUE", + "python_full_version_lt_negative": { "config_settings": { PYTHON_VERSION: "3.12.0", }, - }, - "python_full_version_lt_negative": { - "expression": "python_full_version < '3.8'", "expected": "FALSE", + "expression": "python_full_version < '3.8'", + }, + "python_version_gte": { "config_settings": { PYTHON_VERSION: "3.12.0", }, + "expected": "TRUE", + "expression": "python_version >= '3.12.0'", }, } From ba05d71923a49b5047bef95850d059e53273fbda Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 30 Apr 2025 19:30:28 -0700 Subject: [PATCH 18/23] more cleanup --- python/private/pypi/flags.bzl | 1 + tests/pypi/env_marker_setting/env_marker_setting_tests.bzl | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/python/private/pypi/flags.bzl b/python/private/pypi/flags.bzl index 90605de0d3..984df4fd1c 100644 --- a/python/private/pypi/flags.bzl +++ b/python/private/pypi/flags.bzl @@ -130,6 +130,7 @@ _platform_release_config = rule( ) def _platform_version_config_impl(ctx): + _ = ctx # @unused return [BuildSettingInfo(value = "")] # Despite its name, this "version" value is not a simple version value. diff --git a/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl b/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl index 1d8f5d3b54..549c15c20b 100644 --- a/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl +++ b/tests/pypi/env_marker_setting/env_marker_setting_tests.bzl @@ -1,7 +1,9 @@ +"""env_marker_setting tests.""" + load("@rules_testing//lib:analysis_test.bzl", "analysis_test") load("@rules_testing//lib:test_suite.bzl", "test_suite") load("@rules_testing//lib:util.bzl", "TestingAspectInfo") -load("//python/private/pypi:env_marker_setting.bzl", "env_marker_setting") +load("//python/private/pypi:env_marker_setting.bzl", "env_marker_setting") # buildifier: disable=bzl-visibility load("//tests/support:support.bzl", "PYTHON_VERSION") _tests = [] From 5316f2a35b8d7473bcb7d8f8607475941c2d5afc Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 30 Apr 2025 19:40:21 -0700 Subject: [PATCH 19/23] remove platform_version and platform_release flags for now --- python/config_settings/BUILD.bazel | 12 ------- python/private/pypi/env_marker_setting.bzl | 19 +++++----- python/private/pypi/flags.bzl | 41 ---------------------- 3 files changed, 9 insertions(+), 63 deletions(-) diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel index 2adb1ac757..872d7d1bda 100644 --- a/python/config_settings/BUILD.bazel +++ b/python/config_settings/BUILD.bazel @@ -220,15 +220,3 @@ string_flag( define_pypi_internal_flags( name = "define_pypi_internal_flags", ) - -label_flag( - name = "pip_platform_release_config", - build_setting_default = ":_pip_platform_release_default_config", - visibility = ["//visibility:public"], -) - -label_flag( - name = "pip_platform_version_config", - build_setting_default = ":_pip_platform_version_default_config", - visibility = ["//visibility:public"], -) diff --git a/python/private/pypi/env_marker_setting.bzl b/python/private/pypi/env_marker_setting.bzl index adf58fa26e..f6ef04dd54 100644 --- a/python/private/pypi/env_marker_setting.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -43,6 +43,10 @@ def env_marker_setting(*, name, expression, **kwargs): sys_platform = select(sys_platform_select_map), platform_machine = select(platform_machine_select_map), platform_system = select(platform_system_select_map), + platform_release = select({ + "@platforms//os:osx": "USE_OSX_VERSION_FLAG", + "//conditions:default": "", + }), **kwargs ) @@ -90,9 +94,11 @@ def _env_marker_setting_impl(ctx): # https://peps.python.org/pep-0738/#platform # Similar for iOS: # https://peps.python.org/pep-0730/#platform - env["platform_release"] = _get_flag(ctx.attr._platform_release_config_flag) + env["platform_release"] = ctx.attr.platform_release env["platform_system"] = ctx.attr.platform_system - env["platform_version"] = _get_flag(ctx.attr._platform_version_config_flag) + + # For lack of a better option, just use an empty string for now. + env["platform_version"] = "" env.update(env_aliases()) @@ -118,16 +124,9 @@ for the specification of behavior. ), "os_name": attr.string(), "platform_machine": attr.string(), + "platform_release": attr.string(), "platform_system": attr.string(), "sys_platform": attr.string(), - "_platform_release_config_flag": attr.label( - default = "//python/config_settings:pip_platform_release_config", - providers = [[config_common.FeatureFlagInfo], [BuildSettingInfo]], - ), - "_platform_version_config_flag": attr.label( - default = "//python/config_settings:pip_platform_version_config", - providers = [[config_common.FeatureFlagInfo], [BuildSettingInfo]], - ), "_python_full_version_flag": attr.label( default = "//python/config_settings:python_version", providers = [config_common.FeatureFlagInfo], diff --git a/python/private/pypi/flags.bzl b/python/private/pypi/flags.bzl index 984df4fd1c..a25579a2b8 100644 --- a/python/private/pypi/flags.bzl +++ b/python/private/pypi/flags.bzl @@ -81,18 +81,6 @@ def define_pypi_internal_flags(name): name = "_internal_pip_whl", visibility = ["//visibility:public"], ) - _platform_release_config( - name = "_pip_platform_release_default_config", - value = select({ - "@platforms//os:osx": "USE_OSX_VERSION_FLAG", - "//conditions:default": "", - }), - visibility = ["//visibility:public"], - ) - _platform_version_config( - name = "_pip_platform_version_default_config", - visibility = ["//visibility:public"], - ) def _allow_wheels_flag_impl(ctx): input = ctx.attr._setting[BuildSettingInfo].value @@ -109,32 +97,3 @@ This rule allows us to greatly reduce the number of config setting targets at no if we are duplicating some of the functionality of the `native.config_setting`. """, ) - -def _platform_release_config_impl(ctx): - value = ctx.attr.value - if value == "USE_OSX_VERSION_FLAG": - value = ctx.attr._osx_version[BuildSettingInfo].value - - return [BuildSettingInfo(value = ctx.attr.value)] - -# This value is loosely some version-value looking thing, but the format -# varies depending on the OS. -_platform_release_config = rule( - implementation = _platform_release_config_impl, - attrs = { - "value": attr.string(), - "_osx_version": attr.label( - default = "//python/config_settings:pip_whl_osx_version", - ), - }, -) - -def _platform_version_config_impl(ctx): - _ = ctx # @unused - return [BuildSettingInfo(value = "")] - -# Despite its name, this "version" value is not a simple version value. -# It's a more detailed, arbitrary, description the OS gives about itself. -_platform_version_config = rule( - implementation = _platform_version_config_impl, -) From 2b30b5428340635da16a067d9eadbe29e584a422 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 30 Apr 2025 19:48:34 -0700 Subject: [PATCH 20/23] wire platform_release back to osx flag --- python/private/pypi/env_marker_setting.bzl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/python/private/pypi/env_marker_setting.bzl b/python/private/pypi/env_marker_setting.bzl index f6ef04dd54..bbc59ab110 100644 --- a/python/private/pypi/env_marker_setting.bzl +++ b/python/private/pypi/env_marker_setting.bzl @@ -94,7 +94,10 @@ def _env_marker_setting_impl(ctx): # https://peps.python.org/pep-0738/#platform # Similar for iOS: # https://peps.python.org/pep-0730/#platform - env["platform_release"] = ctx.attr.platform_release + platform_release = ctx.attr.platform_release + if platform_release == "USE_OSX_VERSION_FLAG": + platform_release = _get_flag(ctx.attr._pip_whl_osx_version_flag) + env["platform_release"] = platform_release env["platform_system"] = ctx.attr.platform_system # For lack of a better option, just use an empty string for now. @@ -127,6 +130,10 @@ for the specification of behavior. "platform_release": attr.string(), "platform_system": attr.string(), "sys_platform": attr.string(), + "_pip_whl_osx_version_flag": attr.label( + default = "//python/config_settings:pip_whl_osx_version", + providers = [[BuildSettingInfo], [config_common.FeatureFlagInfo]], + ), "_python_full_version_flag": attr.label( default = "//python/config_settings:python_version", providers = [config_common.FeatureFlagInfo], From a6bc6055660737754adeeed4a6be1763dc58bd58 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Fri, 2 May 2025 14:24:06 -0700 Subject: [PATCH 21/23] cleanup comment --- python/private/pypi/pep508_env.bzl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/private/pypi/pep508_env.bzl b/python/private/pypi/pep508_env.bzl index 28a01682bb..3708c46f1d 100644 --- a/python/private/pypi/pep508_env.bzl +++ b/python/private/pypi/pep508_env.bzl @@ -27,9 +27,8 @@ platform_machine_aliases = { "i686": "x86_32", } -# TODO: there are many cpus and unfortunately, it doesn't look like -# the value is directly accessible to starlark. It might be possible to -# get it via CcToolchain.cpu though. +# NOTE: There are many cpus, and unfortunately, the value isn't directly +# accessible to Starlark. Using CcToolchain.cpu might work, though. platform_machine_select_map = { "@platforms//cpu:aarch32": "aarch32", "@platforms//cpu:aarch64": "aarch64", From 72a8f1e08ff24d522d7afa3977e16fbc12de16b9 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Fri, 2 May 2025 14:26:00 -0700 Subject: [PATCH 22/23] update dleted packages --- .bazelrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelrc b/.bazelrc index 62ead5bdff..afa302fa6e 100644 --- a/.bazelrc +++ b/.bazelrc @@ -4,7 +4,7 @@ # (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it) # To update these lines, execute # `bazel run @rules_bazel_integration_test//tools:update_deleted_packages` -query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma +query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,examples/scratch,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma test --test_output=errors From 8a210d24b3bf471593fe332888f78daa145dc4e1 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Fri, 2 May 2025 14:30:40 -0700 Subject: [PATCH 23/23] restore bazelrc --- .bazelrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.bazelrc b/.bazelrc index afa302fa6e..d2e0721526 100644 --- a/.bazelrc +++ b/.bazelrc @@ -4,7 +4,8 @@ # (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it) # To update these lines, execute # `bazel run @rules_bazel_integration_test//tools:update_deleted_packages` -query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,examples/scratch,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma +build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma +query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma test --test_output=errors