11"""Implementation of the zipapp rules."""
22
33load ("@bazel_skylib//lib:paths.bzl" , "paths" )
4+ load ("@rules_python_internal//:rules_python_config.bzl" , rp_config = "config" )
45load ("//python/private:attributes.bzl" , "apply_config_settings_attr" )
56load ("//python/private:builders.bzl" , "builders" )
6- load ("//python/private:common.bzl" , "BUILTIN_BUILD_PYTHON_ZIP" , "actions_run" , "maybe_builtin_build_python_zip" , "maybe_create_repo_mapping" , "runfiles_root_path" )
7+ 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 " )
78load ("//python/private:common_labels.bzl" , "labels" )
89load ("//python/private:py_executable_info.bzl" , "PyExecutableInfo" )
910load ("//python/private:py_internal.bzl" , "py_internal" )
1011load ("//python/private:py_runtime_info.bzl" , "PyRuntimeInfo" )
11- load ("//python/private:toolchain_types.bzl" , "EXEC_TOOLS_TOOLCHAIN_TYPE" )
12+ load ("//python/private:toolchain_types.bzl" , "EXEC_TOOLS_TOOLCHAIN_TYPE" , "LAUNCHER_MAKER_TOOLCHAIN_TYPE" )
1213load ("//python/private:transition_labels.bzl" , "TRANSITION_LABELS" )
1314
1415def _is_symlink (f ):
@@ -18,13 +19,11 @@ def _is_symlink(f):
1819 return "-1"
1920
2021def _create_zipapp_main_py (ctx , py_runtime , py_executable , stage2_bootstrap ):
21- python_exe = py_executable .venv_python_exe
22- if python_exe :
23- python_exe_path = runfiles_root_path (ctx , python_exe .short_path )
24- elif py_runtime .interpreter :
25- python_exe_path = runfiles_root_path (ctx , py_runtime .interpreter .short_path )
22+ venv_python_exe = py_executable .venv_python_exe
23+ if venv_python_exe :
24+ venv_python_exe_path = runfiles_root_path (ctx , venv_python_exe .short_path )
2625 else :
27- python_exe_path = py_runtime . interpreter_path
26+ venv_python_exe_path = ""
2827
2928 if py_runtime .interpreter :
3029 python_binary_actual_path = runfiles_root_path (ctx , py_runtime .interpreter .short_path )
@@ -36,7 +35,7 @@ def _create_zipapp_main_py(ctx, py_runtime, py_executable, stage2_bootstrap):
3635 template = py_runtime .zip_main_template ,
3736 output = zip_main_py ,
3837 substitutions = {
39- "%python_binary%" : python_exe_path ,
38+ "%python_binary%" : venv_python_exe_path ,
4039 "%python_binary_actual%" : python_binary_actual_path ,
4140 "%stage2_bootstrap%" : runfiles_root_path (ctx , stage2_bootstrap .short_path ),
4241 "%workspace_name%" : ctx .workspace_name ,
@@ -184,20 +183,39 @@ def _py_zipapp_executable_impl(ctx):
184183
185184 zip_file = _create_zip (ctx , py_runtime , py_executable , stage2_bootstrap )
186185 if ctx .attr .executable :
187- preamble = _create_shell_bootstrap (ctx , py_runtime , py_executable , stage2_bootstrap )
188- executable = _create_self_executable_zip (ctx , preamble , zip_file )
189- default_output = executable
186+ if target_platform_has_any_constraint (ctx , ctx .attr ._windows_constraints ):
187+ executable = ctx .actions .declare_file (ctx .label .name + ".exe" )
188+
189+ python_exe = py_executable .venv_python_exe
190+ if python_exe :
191+ python_exe_path = runfiles_root_path (ctx , python_exe .short_path )
192+ elif py_runtime .interpreter :
193+ python_exe_path = runfiles_root_path (ctx , py_runtime .interpreter .short_path )
194+ else :
195+ python_exe_path = py_runtime .interpreter_path
196+
197+ create_windows_exe_launcher (
198+ ctx ,
199+ output = executable ,
200+ python_binary_path = python_exe_path ,
201+ use_zip_file = True ,
202+ )
203+ default_outputs = [executable , zip_file ]
204+ else :
205+ preamble = _create_shell_bootstrap (ctx , py_runtime , py_executable , stage2_bootstrap )
206+ executable = _create_self_executable_zip (ctx , preamble , zip_file )
207+ default_outputs = [executable ]
190208 else :
191209 # Bazel requires executable=True rules to have an executable given, so give
192210 # a fake one to satisfy that.
193- default_output = zip_file
211+ default_outputs = [ zip_file ]
194212 executable = ctx .actions .declare_file (ctx .label .name + "-not-executable" )
195213 ctx .actions .write (executable , "echo 'ERROR: Non executable zip file'; exit 1" )
196214
197215 return [
198216 DefaultInfo (
199- files = depset ([ default_output ] ),
200- runfiles = ctx .runfiles (files = [ default_output ] ),
217+ files = depset (default_outputs ),
218+ runfiles = ctx .runfiles (files = default_outputs ),
201219 executable = executable ,
202220 ),
203221 ]
@@ -277,6 +295,18 @@ Whether the output should be an executable zip file.
277295 cfg = "exec" ,
278296 default = "//tools/private/zipapp:exe_zip_maker" ,
279297 ),
298+ "_launcher" : attr .label (
299+ cfg = "target" ,
300+ # NOTE: This is an executable, but is only used for Windows. It
301+ # can't have executable=True because the backing target is an
302+ # empty target for other platforms.
303+ default = "//tools/launcher:launcher" ,
304+ ),
305+ "_windows_constraints" : attr .label_list (
306+ default = [
307+ "@platforms//os:windows" ,
308+ ],
309+ ),
280310 "_zip_shell_template" : attr .label (
281311 default = ":zip_shell_template" ,
282312 allow_single_file = True ,
@@ -285,8 +315,15 @@ Whether the output should be an executable zip file.
285315 cfg = "exec" ,
286316 default = "//tools/private/zipapp:zipper" ,
287317 ),
288- }
289- _TOOLCHAINS = [EXEC_TOOLS_TOOLCHAIN_TYPE ]
318+ } | ({
319+ "_windows_launcher_maker" : attr .label (
320+ default = "@bazel_tools//tools/launcher:launcher_maker" ,
321+ cfg = "exec" ,
322+ executable = True ,
323+ ),
324+ } if not rp_config .bazel_9_or_later else {})
325+
326+ _TOOLCHAINS = [EXEC_TOOLS_TOOLCHAIN_TYPE ] + ([LAUNCHER_MAKER_TOOLCHAIN_TYPE ] if rp_config .bazel_9_or_later else [])
290327
291328py_zipapp_binary = rule (
292329 doc = """
0 commit comments