blob: 52cf6efc5fea664751d817a60923a39980da5946 [file] [log] [blame] [edit]
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
load("install_groups.bzl", "calculate_install_groups", "map_install_group")
def _fast_install_packages(
ctx,
output_prefix,
board,
sdk,
overlays,
install_set,
executable_action_wrapper,
executable_fast_install_packages,
progress_message):
sysroot = "/build/%s" % board if board else "/"
# Skip already installed packages.
installed = {}
for package in sdk.packages.to_list():
installed[package.file] = True
install_list = [
package
for package in install_set.to_list()
if not installed.get(package.file, False)
]
output_log_file = ctx.actions.declare_file("%s.log" % output_prefix)
output_profile_file = ctx.actions.declare_file(
"%s.profile.json" % output_prefix,
)
outputs = [output_log_file, output_profile_file]
args = ctx.actions.args()
args.add_all([
"--log=" + output_log_file.path,
"--profile=" + output_profile_file.path,
executable_fast_install_packages.path,
])
args.add("--root-dir=%s" % sysroot)
input_layers = sdk.layers + overlays.layers
args.add_all(
input_layers,
format_each = "--layer=%s",
expand_directories = False,
)
inputs = input_layers[:]
new_layers = []
for i, package in enumerate(install_list):
package_output_prefix = "%s.%d" % (output_prefix, i)
output_preinst = ctx.actions.declare_directory(
"%s.preinst" % package_output_prefix,
)
output_postinst = ctx.actions.declare_directory(
"%s.postinst" % package_output_prefix,
)
if package.contents.sysroot != sysroot:
fail(
("Requested to install %s/%s-%s to %s, but its installed " +
"contents layers were generated only for %s") % (
package.category,
package.package_name,
package.version,
sysroot,
package.contents.sysroot,
),
)
args.add("--install=%s,%s,%s,%s,%s" % (
package.file.path,
package.contents.installed.path,
package.contents.staged.path,
output_preinst.path,
output_postinst.path,
))
inputs.extend([
package.file,
package.contents.installed,
package.contents.staged,
])
outputs.extend([output_preinst, output_postinst])
new_layers.extend([
output_preinst,
package.contents.installed,
output_postinst,
])
actual_progress_message = progress_message.replace(
"{dep_count}",
str(len(install_list)),
).replace(
"{cached_count}",
str(len(install_list)),
)
ctx.actions.run(
inputs = depset(inputs),
outputs = outputs,
executable = executable_action_wrapper,
tools = [executable_fast_install_packages],
arguments = [args],
execution_requirements = {
# Send SIGTERM instead of SIGKILL on user interruption.
"supports-graceful-termination": "",
# Disable sandbox to avoid creating a symlink forest.
# This does not affect hermeticity since ebuild runs in a container.
"no-sandbox": "",
},
mnemonic = "InstallDeps",
progress_message = actual_progress_message,
)
return new_layers
def install_deps(
ctx,
output_prefix,
board,
sdk,
overlays,
install_set,
strategy,
executable_action_wrapper,
executable_install_deps,
executable_fast_install_packages,
progress_message):
"""
Creates an action which builds file system layers in which the build dependencies are installed.
Args:
ctx: ctx: A context object passed to the rule implementation.
output_prefix: str: A file name prefix to prepend to output files
defined in this function.
board: str: The target board name to install dependencies for. If it is
non-empty, packages are installed to the corresponding sysroot
(ROOT="/build/<board>"). If it is an empty string, packages are
installed to the host (ROOT="/").
sdk: SDKInfo: The provider describing the base file system layers.
overlays: OverlaySetInfo: Overlays providing packages.
install_set: Depset[BinaryPackageInfo]: Binary package targets to
install. This depset must be closed over transitive runtime
dependencies; that is, if the depset contains a package X, it must
also contain all transitive dependencies of the package X. Also,
This depset's to_list() must return packages in a valid installation
order, i.e. a package's runtime dependencies are fully satisfied by
packages that appear before it.
strategy: str: Specifies the strategy to install packages. Valid values
are:
"fast": Uses installed contents layers to fully avoid copying
package contents.
"naive": Similar to "fast" but uses installed contents layers
only for packages without install hooks.
"slow": Simply uses emerge to install packages into a single
layer.
executable_action_wrapper: File: An executable file of action_wrapper.
executable_install_deps: File: An executable file of install_deps.
executable_fast_install_packages: File: An executable file of
fast_install_packages.
progress_message: str: Progress message for the installation action.
Returns:
list[File]: Files representing file system layers.
"""
if strategy == "fast":
return _fast_install_packages(
ctx = ctx,
output_prefix = output_prefix,
board = board,
sdk = sdk,
overlays = overlays,
install_set = install_set,
executable_action_wrapper = executable_action_wrapper,
executable_fast_install_packages = executable_fast_install_packages,
progress_message = progress_message,
)
output_root = ctx.actions.declare_directory(output_prefix)
output_log_file = ctx.actions.declare_file(output_prefix + ".log")
output_profile_file = ctx.actions.declare_file(
output_prefix + ".profile.json",
)
use_layers = strategy == "naive"
args = ctx.actions.args()
args.add_all([
"--log=" + output_log_file.path,
"--profile=" + output_profile_file.path,
executable_install_deps.path,
"--output=" + output_root.path,
])
if board:
args.add("--board=" + board)
install_list = install_set.to_list()
install_tuple = calculate_install_groups(
install_list,
provided_packages = sdk.packages,
use_layers = use_layers,
)
if use_layers:
install_groups, install_layers = install_tuple
else:
install_groups, install_layers = install_tuple, []
args.add_all(install_groups, map_each = map_install_group, format_each = "--install-target=%s")
direct_inputs = []
for group in install_groups:
for package in group:
direct_inputs.append(package.file)
layer_inputs = sdk.layers + overlays.layers + install_layers
args.add_all(layer_inputs, format_each = "--layer=%s", expand_directories = False)
direct_inputs.extend(layer_inputs)
progress_message = progress_message.replace(
"{dep_count}",
str(len(install_list)),
)
progress_message = progress_message.replace(
"{cached_count}",
str(len(install_layers)),
)
ctx.actions.run(
inputs = depset(direct_inputs),
outputs = [output_root, output_log_file, output_profile_file],
executable = executable_action_wrapper,
tools = [executable_install_deps],
arguments = [args],
execution_requirements = {
# Send SIGTERM instead of SIGKILL on user interruption.
"supports-graceful-termination": "",
# Disable sandbox to avoid creating a symlink forest.
# This does not affect hermeticity since ebuild runs in a container.
"no-sandbox": "",
},
mnemonic = "InstallDeps",
progress_message = progress_message,
)
return install_layers + [output_root]