diff --git a/news/slash-target-venv-output.fixed.md b/news/slash-target-venv-output.fixed.md new file mode 100644 index 0000000000..780b498db7 --- /dev/null +++ b/news/slash-target-venv-output.fixed.md @@ -0,0 +1,3 @@ +(rules) Fixed venv output paths for `py_binary` and `py_test` targets whose +names contain path separators so distinct targets with the same basename no +longer share the same venv output directory. diff --git a/python/private/py_executable.bzl b/python/private/py_executable.bzl index 04a43be917..11246ec513 100644 --- a/python/private/py_executable.bzl +++ b/python/private/py_executable.bzl @@ -306,6 +306,9 @@ def _create_executable( else: base_executable_name = executable.basename + # Venv outputs are package-relative, so preserve the full target name to + # avoid collisions between targets like foo/tool, bar/tool, and foo_tool. + venv_output_prefix = ctx.label.name venv = None # The check for stage2_bootstrap_template is to support legacy @@ -318,7 +321,7 @@ def _create_executable( ): venv = _create_venv( ctx, - output_prefix = base_executable_name, + output_prefix = venv_output_prefix, imports = imports, runtime_details = runtime_details, add_runfiles_root_to_sys_path = ( diff --git a/tests/base_rules/py_executable_base_tests.bzl b/tests/base_rules/py_executable_base_tests.bzl index 7531de4c30..f6b3c9bb60 100644 --- a/tests/base_rules/py_executable_base_tests.bzl +++ b/tests/base_rules/py_executable_base_tests.bzl @@ -519,6 +519,45 @@ def _test_py_runtime_info_provided_impl(env, target): _tests.append(_test_py_runtime_info_provided) +def _test_venv_output_prefix_with_path_separators(name, config): + rt_util.helper_target( + config.rule, + name = name + "/foo/tool", + srcs = ["main.py"], + main = "main.py", + ) + rt_util.helper_target( + config.rule, + name = name + "/bar/tool", + srcs = ["main.py"], + main = "main.py", + ) + rt_util.helper_target( + config.rule, + name = name + "/foo_tool", + srcs = ["main.py"], + main = "main.py", + ) + analysis_test( + name = name, + impl = _test_venv_output_prefix_with_path_separators_impl, + targets = { + "bar": name + "/bar/tool", + "foo": name + "/foo/tool", + "foo_underscore": name + "/foo_tool", + }, + ) + +def _test_venv_output_prefix_with_path_separators_impl(env, targets): + for target in [targets.foo, targets.bar, targets.foo_underscore]: + target = env.expect.that_target(target) + venv_file = "*/_{}.venv/*site-packages/bazel.pth".format( + target.meta.format_str("{name}"), + ) + target.runfiles().contains_predicate(matching.str_matches(venv_file)) + +_tests.append(_test_venv_output_prefix_with_path_separators) + def _test_windows_target_with_path_separators(name, config): rt_util.helper_target( config.rule,