Skip to content

Commit c10ec90

Browse files
committed
eliminate expensive depset expansion in py_test and py_binary rules to improve analysis performance
1 parent 3c3e0be commit c10ec90

File tree

3 files changed

+296
-55
lines changed

3 files changed

+296
-55
lines changed

python/private/BUILD.bazel

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
1616
load("@bazel_skylib//rules:common_settings.bzl", "bool_setting")
17+
load("@rules_cc//cc:defs.bzl", "cc_binary")
1718
load("//python:py_binary.bzl", "py_binary")
1819
load("//python:py_library.bzl", "py_library")
1920
load(":print_toolchain_checksums.bzl", "print_toolchains_checksums")
@@ -829,6 +830,16 @@ py_binary(
829830
],
830831
)
831832

833+
# Used for py_executable rule
834+
# C++ wrapper for zipper to process Python zip manifests
835+
cc_binary(
836+
name = "py_executable_zip_gen",
837+
srcs = ["py_executable_zip_gen.cc"],
838+
data = ["@bazel_tools//tools/zip:zipper"],
839+
visibility = ["//visibility:public"],
840+
deps = ["@bazel_tools//tools/cpp/runfiles"],
841+
)
842+
832843
py_binary(
833844
name = "py_wheel_dist",
834845
srcs = ["py_wheel_dist.py"],

python/private/py_executable.bzl

Lines changed: 22 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ accepting arbitrary Python versions.
236236
"_zipper": lambda: attrb.Label(
237237
cfg = "exec",
238238
executable = True,
239-
default = "@bazel_tools//tools/zip:zipper",
239+
default = ":py_executable_zip_gen",
240240
),
241241
},
242242
)
@@ -380,9 +380,8 @@ def _create_executable(
380380
_create_zip_file(
381381
ctx,
382382
output = zip_file,
383-
original_nonzip_executable = executable,
384383
zip_main = zip_main,
385-
runfiles = runfiles_details.default_runfiles.merge(extra_runfiles),
384+
runfiles = runfiles_details.runfiles_without_exe.merge(extra_runfiles),
386385
)
387386

388387
extra_files_to_build = []
@@ -803,35 +802,16 @@ def _create_windows_exe_launcher(
803802
use_default_shell_env = True,
804803
)
805804

806-
def _create_zip_file(ctx, *, output, original_nonzip_executable, zip_main, runfiles):
805+
def _create_zip_file(ctx, *, output, zip_main, runfiles):
807806
"""Create a Python zipapp (zip with __main__.py entry point)."""
808-
workspace_name = ctx.workspace_name
809807
legacy_external_runfiles = _py_builtins.get_legacy_external_runfiles(ctx)
810808

811-
manifest = ctx.actions.args()
812-
manifest.use_param_file("@%s", use_always = True)
813-
manifest.set_param_file_format("multiline")
814-
815-
manifest.add("__main__.py={}".format(zip_main.path))
816-
manifest.add("__init__.py=")
817-
manifest.add(
818-
"{}=".format(
819-
_get_zip_runfiles_path("__init__.py", workspace_name, legacy_external_runfiles),
820-
),
821-
)
822-
for path in runfiles.empty_filenames.to_list():
823-
manifest.add("{}=".format(_get_zip_runfiles_path(path, workspace_name, legacy_external_runfiles)))
824-
825-
def map_zip_runfiles(file):
826-
if file != original_nonzip_executable and file != output:
827-
return "{}={}".format(
828-
_get_zip_runfiles_path(file.short_path, workspace_name, legacy_external_runfiles),
829-
file.path,
830-
)
831-
else:
832-
return None
833-
834-
manifest.add_all(runfiles.files, map_each = map_zip_runfiles, allow_closure = True)
809+
args = ctx.actions.args()
810+
args.add("--output", output.path)
811+
args.add("--workspace-name", ctx.workspace_name)
812+
args.add("--main-file", zip_main.path)
813+
if legacy_external_runfiles:
814+
args.add("--legacy-external-runfiles")
835815

836816
inputs = [zip_main]
837817
if _py_builtins.is_bzlmod_enabled(ctx):
@@ -844,43 +824,30 @@ def _create_zip_file(ctx, *, output, original_nonzip_executable, zip_main, runfi
844824
runfiles = runfiles,
845825
output = zip_repo_mapping_manifest,
846826
)
847-
manifest.add("{}/_repo_mapping={}".format(
848-
_ZIP_RUNFILES_DIRECTORY_NAME,
849-
zip_repo_mapping_manifest.path,
850-
))
827+
args.add("--repo-mapping-manifest", zip_repo_mapping_manifest.path)
851828
inputs.append(zip_repo_mapping_manifest)
852829

853-
for artifact in runfiles.files.to_list():
854-
# Don't include the original executable because it isn't used by the
855-
# zip file, so no need to build it for the action.
856-
# Don't include the zipfile itself because it's an output.
857-
if artifact != original_nonzip_executable and artifact != output:
858-
inputs.append(artifact)
859-
860-
zip_cli_args = ctx.actions.args()
861-
zip_cli_args.add("cC")
862-
zip_cli_args.add(output)
830+
manifest = ctx.actions.args()
831+
manifest.use_param_file("%s", use_always = True)
832+
manifest.set_param_file_format("multiline")
833+
manifest.add_all(runfiles.empty_filenames, map_each = _get_zip_empty_path_arg)
834+
manifest.add_all(runfiles.files, map_each = _get_zip_path_arg)
863835

864836
ctx.actions.run(
865837
executable = ctx.executable._zipper,
866-
arguments = [zip_cli_args, manifest],
867-
inputs = depset(inputs),
838+
arguments = [args, manifest],
839+
inputs = depset(inputs, transitive = [runfiles.files]),
868840
outputs = [output],
869841
use_default_shell_env = True,
870842
mnemonic = "PythonZipper",
871843
progress_message = "Building Python zip: %{label}",
872844
)
873845

874-
def _get_zip_runfiles_path(path, workspace_name, legacy_external_runfiles):
875-
if legacy_external_runfiles and path.startswith(_EXTERNAL_PATH_PREFIX):
876-
zip_runfiles_path = paths.relativize(path, _EXTERNAL_PATH_PREFIX)
877-
else:
878-
# NOTE: External runfiles (artifacts in other repos) will have a leading
879-
# path component of "../" so that they refer outside the main workspace
880-
# directory and into the runfiles root. By normalizing, we simplify e.g.
881-
# "workspace/../foo/bar" to simply "foo/bar".
882-
zip_runfiles_path = paths.normalize("{}/{}".format(workspace_name, path))
883-
return "{}/{}".format(_ZIP_RUNFILES_DIRECTORY_NAME, zip_runfiles_path)
846+
def _get_zip_empty_path_arg(file):
847+
return "{}=".format(file)
848+
849+
def _get_zip_path_arg(file):
850+
return "{}={}".format(file.short_path, file.path)
884851

885852
def _create_executable_zip_file(
886853
ctx,

0 commit comments

Comments
 (0)