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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -227,18 +227,20 @@ tasks:
# for now until we can look into it.
build_targets:
- "--"
- "..."
# As a regression test for #225, check that wheel targets still build when
# their package path is qualified with the repo name.
- "@rules_python//examples/wheel/..."
##- "..."
### As a regression test for #225, check that wheel targets still build when
### their package path is qualified with the repo name.
##- "@rules_python//examples/wheel/..."
- "//tests/py_zipapp:system_python_zipapp_test"
build_flags:
- "--noenable_bzlmod"
- "--enable_workspace"
- "--keep_going"
- "--build_tag_filters=-integration-test"
test_targets:
- "--"
- "..."
##- "..."
- "//tests/py_zipapp:system_python_zipapp_test"
test_flags:
- "--noenable_bzlmod"
- "--enable_workspace"
Expand All @@ -256,8 +258,14 @@ tasks:
<<: *reusable_config
name: "Default: Windows"
platform: windows
build_targets:
- "--"
- "//tests/py_zipapp:system_python_zipapp_test"
test_flags:
- "--test_tag_filters=-integration-test,-fix-windows"
test_targets:
- "--"
- "//tests/py_zipapp:system_python_zipapp_test"
rbe_min:
<<: *minimum_supported_version
<<: *reusable_config
Expand Down
47 changes: 46 additions & 1 deletion python/private/common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
load("@rules_python_internal//:rules_python_config.bzl", "config")
load("//python/private:py_interpreter_program.bzl", "PyInterpreterProgramInfo")
load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE")
load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE", "LAUNCHER_MAKER_TOOLCHAIN_TYPE")
load(":builders.bzl", "builders")
load(":cc_helper.bzl", "cc_helper")
load(":py_cc_link_params_info.bzl", "PyCcLinkParamsInfo")
Expand Down Expand Up @@ -56,6 +56,51 @@ def maybe_builtin_build_python_zip(value, settings = None):

return settings

def _find_launcher_maker(ctx):
if config.bazel_9_or_later:
return (ctx.toolchains[LAUNCHER_MAKER_TOOLCHAIN_TYPE].binary, LAUNCHER_MAKER_TOOLCHAIN_TYPE)
return (ctx.executable._windows_launcher_maker, None)

def create_windows_exe_launcher(
ctx,
*,
output,
python_binary_path,
use_zip_file):
"""Creates a Windows exe launcher.

Args:
ctx: The rule context.
output: The output file for the launcher.
python_binary_path: The path to the Python binary.
use_zip_file: Whether to use a zip file.
"""
launch_info = ctx.actions.args()
launch_info.use_param_file("%s", use_always = True)
launch_info.set_param_file_format("multiline")
launch_info.add("binary_type=Python")
launch_info.add(ctx.workspace_name, format = "workspace_name=%s")
launch_info.add(
"1" if py_internal.runfiles_enabled(ctx) else "0",
format = "symlink_runfiles_enabled=%s",
)
launch_info.add(python_binary_path, format = "python_bin_path=%s")
launch_info.add("1" if use_zip_file else "0", format = "use_zip_file=%s")

launcher = ctx.attr._launcher[DefaultInfo].files_to_run.executable
executable, toolchain = _find_launcher_maker(ctx)
ctx.actions.run(
executable = executable,
arguments = [launcher.path, launch_info, output.path],
inputs = [launcher],
outputs = [output],
mnemonic = "PyBuildLauncher",
progress_message = "Creating launcher for %{label}",
# Needed to inherit PATH when using non-MSVC compilers like MinGW
use_default_shell_env = True,
toolchain = toolchain,
)

def create_binary_semantics_struct(
*,
get_native_deps_dso_name,
Expand Down
45 changes: 4 additions & 41 deletions python/private/py_executable.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ load(
"create_instrumented_files_info",
"create_output_group_info",
"create_py_info",
"create_windows_exe_launcher",
"csv",
"filter_to_py_srcs",
"is_bool",
Expand All @@ -63,15 +64,14 @@ load(":py_internal.bzl", "py_internal")
load(":py_runtime_info.bzl", "DEFAULT_STUB_SHEBANG")
load(":reexports.bzl", "BuiltinPyInfo", "BuiltinPyRuntimeInfo")
load(":rule_builders.bzl", "ruleb")
load(":toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE", TOOLCHAIN_TYPE = "TARGET_TOOLCHAIN_TYPE")
load(":toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE", "LAUNCHER_MAKER_TOOLCHAIN_TYPE", TOOLCHAIN_TYPE = "TARGET_TOOLCHAIN_TYPE")
load(":transition_labels.bzl", "TRANSITION_LABELS")
load(":venv_runfiles.bzl", "create_venv_app_files")

_py_builtins = py_internal
_EXTERNAL_PATH_PREFIX = "external"
_ZIP_RUNFILES_DIRECTORY_NAME = "runfiles"
_INIT_PY = "__init__.py"
_LAUNCHER_MAKER_TOOLCHAIN_TYPE = "@bazel_tools//tools/launcher:launcher_maker_toolchain_type"

# Non-Google-specific attributes for executables
# These attributes are for rules that accept Python sources.
Expand Down Expand Up @@ -398,7 +398,7 @@ def _create_executable(
else:
bootstrap_output = executable
else:
_create_windows_exe_launcher(
create_windows_exe_launcher(
ctx,
output = executable,
use_zip_file = build_zip_enabled,
Expand Down Expand Up @@ -789,43 +789,6 @@ def _create_stage1_bootstrap(
is_executable = True,
)

def _find_launcher_maker(ctx):
if rp_config.bazel_9_or_later:
return (ctx.toolchains[_LAUNCHER_MAKER_TOOLCHAIN_TYPE].binary, _LAUNCHER_MAKER_TOOLCHAIN_TYPE)
return (ctx.executable._windows_launcher_maker, None)

def _create_windows_exe_launcher(
ctx,
*,
output,
python_binary_path,
use_zip_file):
launch_info = ctx.actions.args()
launch_info.use_param_file("%s", use_always = True)
launch_info.set_param_file_format("multiline")
launch_info.add("binary_type=Python")
launch_info.add(ctx.workspace_name, format = "workspace_name=%s")
launch_info.add(
"1" if py_internal.runfiles_enabled(ctx) else "0",
format = "symlink_runfiles_enabled=%s",
)
launch_info.add(python_binary_path, format = "python_bin_path=%s")
launch_info.add("1" if use_zip_file else "0", format = "use_zip_file=%s")

launcher = ctx.attr._launcher[DefaultInfo].files_to_run.executable
executable, toolchain = _find_launcher_maker(ctx)
ctx.actions.run(
executable = executable,
arguments = [launcher.path, launch_info, output.path],
inputs = [launcher],
outputs = [output],
mnemonic = "PyBuildLauncher",
progress_message = "Creating launcher for %{label}",
# Needed to inherit PATH when using non-MSVC compilers like MinGW
use_default_shell_env = True,
toolchain = toolchain,
)

def _create_zip_file(ctx, *, output, zip_main, runfiles):
"""Create a Python zipapp (zip with __main__.py entry point)."""
workspace_name = ctx.workspace_name
Expand Down Expand Up @@ -1848,7 +1811,7 @@ def create_executable_rule_builder(implementation, **kwargs):
ruleb.ToolchainType(TOOLCHAIN_TYPE),
ruleb.ToolchainType(EXEC_TOOLS_TOOLCHAIN_TYPE, mandatory = False),
ruleb.ToolchainType("@bazel_tools//tools/cpp:toolchain_type", mandatory = False),
] + ([ruleb.ToolchainType(_LAUNCHER_MAKER_TOOLCHAIN_TYPE)] if rp_config.bazel_9_or_later else []),
] + ([ruleb.ToolchainType(LAUNCHER_MAKER_TOOLCHAIN_TYPE)] if rp_config.bazel_9_or_later else []),
cfg = dict(
implementation = _transition_executable_impl,
inputs = TRANSITION_LABELS + [
Expand Down
1 change: 1 addition & 0 deletions python/private/toolchain_types.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ implementation of the toolchain.
TARGET_TOOLCHAIN_TYPE = Label("//python:toolchain_type")
EXEC_TOOLS_TOOLCHAIN_TYPE = Label("//python:exec_tools_toolchain_type")
PY_CC_TOOLCHAIN_TYPE = Label("//python/cc:toolchain_type")
LAUNCHER_MAKER_TOOLCHAIN_TYPE = Label("@bazel_tools//tools/launcher:launcher_maker_toolchain_type")
71 changes: 54 additions & 17 deletions python/private/zipapp/py_zipapp_rule.bzl
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
"""Implementation of the zipapp rules."""

load("@bazel_skylib//lib:paths.bzl", "paths")
load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config")
load("//python/private:attributes.bzl", "apply_config_settings_attr")
load("//python/private:builders.bzl", "builders")
load("//python/private:common.bzl", "BUILTIN_BUILD_PYTHON_ZIP", "actions_run", "maybe_builtin_build_python_zip", "maybe_create_repo_mapping", "runfiles_root_path")
load("//python/private:common.bzl", "BUILTIN_BUILD_PYTHON_ZIP", "actions_run", "create_windows_exe_launcher", "maybe_builtin_build_python_zip", "maybe_create_repo_mapping", "runfiles_root_path", "target_platform_has_any_constraint")
load("//python/private:common_labels.bzl", "labels")
load("//python/private:py_executable_info.bzl", "PyExecutableInfo")
load("//python/private:py_internal.bzl", "py_internal")
load("//python/private:py_runtime_info.bzl", "PyRuntimeInfo")
load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE")
load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE", "LAUNCHER_MAKER_TOOLCHAIN_TYPE")
load("//python/private:transition_labels.bzl", "TRANSITION_LABELS")

def _is_symlink(f):
Expand All @@ -18,13 +19,11 @@ def _is_symlink(f):
return "-1"

def _create_zipapp_main_py(ctx, py_runtime, py_executable, stage2_bootstrap):
python_exe = py_executable.venv_python_exe
if python_exe:
python_exe_path = runfiles_root_path(ctx, python_exe.short_path)
elif py_runtime.interpreter:
python_exe_path = runfiles_root_path(ctx, py_runtime.interpreter.short_path)
venv_python_exe = py_executable.venv_python_exe
if venv_python_exe:
venv_python_exe_path = runfiles_root_path(ctx, venv_python_exe.short_path)
else:
python_exe_path = py_runtime.interpreter_path
venv_python_exe_path = ""

if py_runtime.interpreter:
python_binary_actual_path = runfiles_root_path(ctx, py_runtime.interpreter.short_path)
Expand All @@ -36,7 +35,7 @@ def _create_zipapp_main_py(ctx, py_runtime, py_executable, stage2_bootstrap):
template = py_runtime.zip_main_template,
output = zip_main_py,
substitutions = {
"%python_binary%": python_exe_path,
"%python_binary%": venv_python_exe_path,
"%python_binary_actual%": python_binary_actual_path,
"%stage2_bootstrap%": runfiles_root_path(ctx, stage2_bootstrap.short_path),
"%workspace_name%": ctx.workspace_name,
Expand Down Expand Up @@ -184,20 +183,39 @@ def _py_zipapp_executable_impl(ctx):

zip_file = _create_zip(ctx, py_runtime, py_executable, stage2_bootstrap)
if ctx.attr.executable:
preamble = _create_shell_bootstrap(ctx, py_runtime, py_executable, stage2_bootstrap)
executable = _create_self_executable_zip(ctx, preamble, zip_file)
default_output = executable
if target_platform_has_any_constraint(ctx, ctx.attr._windows_constraints):
executable = ctx.actions.declare_file(ctx.label.name + ".exe")

python_exe = py_executable.venv_python_exe
if python_exe:
python_exe_path = runfiles_root_path(ctx, python_exe.short_path)
elif py_runtime.interpreter:
python_exe_path = runfiles_root_path(ctx, py_runtime.interpreter.short_path)
else:
python_exe_path = py_runtime.interpreter_path

create_windows_exe_launcher(
ctx,
output = executable,
python_binary_path = python_exe_path,
use_zip_file = True,
)
default_outputs = [executable, zip_file]
else:
preamble = _create_shell_bootstrap(ctx, py_runtime, py_executable, stage2_bootstrap)
executable = _create_self_executable_zip(ctx, preamble, zip_file)
default_outputs = [executable]
else:
# Bazel requires executable=True rules to have an executable given, so give
# a fake one to satisfy that.
default_output = zip_file
default_outputs = [zip_file]
executable = ctx.actions.declare_file(ctx.label.name + "-not-executable")
ctx.actions.write(executable, "echo 'ERROR: Non executable zip file'; exit 1")

return [
DefaultInfo(
files = depset([default_output]),
runfiles = ctx.runfiles(files = [default_output]),
files = depset(default_outputs),
runfiles = ctx.runfiles(files = default_outputs),
executable = executable,
),
]
Expand Down Expand Up @@ -277,6 +295,18 @@ Whether the output should be an executable zip file.
cfg = "exec",
default = "//tools/private/zipapp:exe_zip_maker",
),
"_launcher": attr.label(
cfg = "target",
# NOTE: This is an executable, but is only used for Windows. It
# can't have executable=True because the backing target is an
# empty target for other platforms.
default = "//tools/launcher:launcher",
),
"_windows_constraints": attr.label_list(
default = [
"@platforms//os:windows",
],
),
"_zip_shell_template": attr.label(
default = ":zip_shell_template",
allow_single_file = True,
Expand All @@ -285,8 +315,15 @@ Whether the output should be an executable zip file.
cfg = "exec",
default = "//tools/private/zipapp:zipper",
),
}
_TOOLCHAINS = [EXEC_TOOLS_TOOLCHAIN_TYPE]
} | ({
"_windows_launcher_maker": attr.label(
default = "@bazel_tools//tools/launcher:launcher_maker",
cfg = "exec",
executable = True,
),
} if not rp_config.bazel_9_or_later else {})

_TOOLCHAINS = [EXEC_TOOLS_TOOLCHAIN_TYPE] + ([LAUNCHER_MAKER_TOOLCHAIN_TYPE] if rp_config.bazel_9_or_later else [])

py_zipapp_binary = rule(
doc = """
Expand Down
Loading