fix(rules): make the srcs trully optional (#2768)
With this PR we mark the srcs attribute as optional as we can
leverage the `main_module` to just run things from the deps.
This also removes a long-standing `TODO` note.
Fixes #2765
---------
Co-authored-by: Richard Levasseur <richardlev@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d9b648..33d99df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -76,6 +76,8 @@
* (pypi) The PyPI extension will no longer write the lock file entries as the
extension has been marked reproducible.
Fixes [#2434](https://github.com/bazel-contrib/rules_python/issues/2434).
+* (rules) {attr}`py_binary.srcs` and {attr}`py_test.srcs` is no longer mandatory when
+ `main_module` is specified (for `--bootstrap_impl=script`)
[20250317]: https://github.com/astral-sh/python-build-standalone/releases/tag/20250317
diff --git a/python/private/py_executable.bzl b/python/private/py_executable.bzl
index e6f4700..dd3ad86 100644
--- a/python/private/py_executable.bzl
+++ b/python/private/py_executable.bzl
@@ -786,6 +786,8 @@
)
template = runtime.bootstrap_template
subs["%shebang%"] = runtime.stub_shebang
+ elif not ctx.files.srcs:
+ fail("mandatory 'srcs' files have not been provided")
else:
if (ctx.configuration.coverage_enabled and
runtime and
@@ -1888,7 +1890,6 @@
),
**kwargs
)
- builder.attrs.get("srcs").set_mandatory(True)
return builder
def cc_configure_features(
diff --git a/tests/base_rules/py_executable_base_tests.bzl b/tests/base_rules/py_executable_base_tests.bzl
index 3cc6dfb..3770783 100644
--- a/tests/base_rules/py_executable_base_tests.bzl
+++ b/tests/base_rules/py_executable_base_tests.bzl
@@ -24,7 +24,7 @@
load("//tests/base_rules:base_tests.bzl", "create_base_tests")
load("//tests/base_rules:util.bzl", "WINDOWS_ATTR", pt_util = "util")
load("//tests/support:py_executable_info_subject.bzl", "PyExecutableInfoSubject")
-load("//tests/support:support.bzl", "CC_TOOLCHAIN", "CROSSTOOL_TOP", "LINUX_X86_64", "WINDOWS_X86_64")
+load("//tests/support:support.bzl", "BOOTSTRAP_IMPL", "CC_TOOLCHAIN", "CROSSTOOL_TOP", "LINUX_X86_64", "WINDOWS_X86_64")
_tests = []
@@ -342,6 +342,53 @@
matching.str_matches("name must not end in*.py"),
)
+def _test_main_module_bootstrap_system_python(name, config):
+ rt_util.helper_target(
+ config.rule,
+ name = name + "_subject",
+ main_module = "dummy",
+ )
+ analysis_test(
+ name = name,
+ impl = _test_main_module_bootstrap_system_python_impl,
+ target = name + "_subject",
+ config_settings = {
+ BOOTSTRAP_IMPL: "system_python",
+ "//command_line_option:platforms": [LINUX_X86_64],
+ },
+ expect_failure = True,
+ )
+
+def _test_main_module_bootstrap_system_python_impl(env, target):
+ env.expect.that_target(target).failures().contains_predicate(
+ matching.str_matches("mandatory*srcs"),
+ )
+
+_tests.append(_test_main_module_bootstrap_system_python)
+
+def _test_main_module_bootstrap_script(name, config):
+ rt_util.helper_target(
+ config.rule,
+ name = name + "_subject",
+ main_module = "dummy",
+ )
+ analysis_test(
+ name = name,
+ impl = _test_main_module_bootstrap_script_impl,
+ target = name + "_subject",
+ config_settings = {
+ BOOTSTRAP_IMPL: "script",
+ "//command_line_option:platforms": [LINUX_X86_64],
+ },
+ )
+
+def _test_main_module_bootstrap_script_impl(env, target):
+ env.expect.that_target(target).default_outputs().contains(
+ "{package}/{test_name}_subject",
+ )
+
+_tests.append(_test_main_module_bootstrap_script)
+
def _test_py_runtime_info_provided(name, config):
rt_util.helper_target(
config.rule,
@@ -365,29 +412,6 @@
_tests.append(_test_py_runtime_info_provided)
-# Can't test this -- mandatory validation happens before analysis test
-# can intercept it
-# TODO(#1069): Once re-implemented in Starlark, modify rule logic to make this
-# testable.
-# def _test_srcs_is_mandatory(name, config):
-# rt_util.helper_target(
-# config.rule,
-# name = name + "_subject",
-# )
-# analysis_test(
-# name = name,
-# impl = _test_srcs_is_mandatory,
-# target = name + "_subject",
-# expect_failure = True,
-# )
-#
-# _tests.append(_test_srcs_is_mandatory)
-#
-# def _test_srcs_is_mandatory_impl(env, target):
-# env.expect.that_target(target).failures().contains_predicate(
-# matching.str_matches("mandatory*srcs"),
-# )
-
# =====
# You were gonna add a test at the end, weren't you?
# Nope. Please keep them sorted; put it in its alphabetical location.
diff --git a/tests/support/support.bzl b/tests/support/support.bzl
index 2b67038..6330155 100644
--- a/tests/support/support.bzl
+++ b/tests/support/support.bzl
@@ -35,6 +35,7 @@
# str() around Label() is necessary because rules_testing's config_settings
# doesn't accept yet Label objects.
ADD_SRCS_TO_RUNFILES = str(Label("//python/config_settings:add_srcs_to_runfiles"))
+BOOTSTRAP_IMPL = str(Label("//python/config_settings:bootstrap_impl"))
EXEC_TOOLS_TOOLCHAIN = str(Label("//python/config_settings:exec_tools_toolchain"))
PRECOMPILE = str(Label("//python/config_settings:precompile"))
PRECOMPILE_SOURCE_RETENTION = str(Label("//python/config_settings:precompile_source_retention"))