blob: de6a58c879c6cb744b285822270cc091a3fc9ea8 [file] [log] [blame]
# 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.
def _map_install_group(group):
"""
Computes an --install-target argument for an install group.
Args:
group: list[BinaryPackageInfo]: An install group.
Returns:
str: A value for the --install-target flag.
"""
return ":".join([pkg.file.path for pkg in group])
def _calculate_install_groups(install_list):
"""
Splits a package set to install groups.
Args:
install_list: list[BinaryPackageInfo]: A list of packages to install.
This list must be closed over transitive runtime dependencies.
Returns:
list[list[BinaryPackageInfo]]: An ordered list containing a list of
packages that can be installed in parallel.
"""
groups = []
remaining_packages = install_list[:]
seen = {}
for _ in range(100):
if len(remaining_packages) == 0:
break
satisfied_list = []
not_satisfied_list = []
for package in remaining_packages:
all_seen = True
for dep in package.direct_runtime_deps:
if dep.file.path not in seen:
all_seen = False
break
if all_seen:
satisfied_list.append(package)
else:
not_satisfied_list.append(package)
if len(satisfied_list) == 0:
fail("Dependency list is unsatisfiable")
for dep in satisfied_list:
seen[dep.file.path] = True
groups.append(satisfied_list)
remaining_packages = not_satisfied_list
if len(remaining_packages) > 0:
fail("Too many dependencies")
return groups
def install_deps(
ctx,
output_prefix,
board,
sdk,
overlays,
install_set,
executable_action_wrapper,
executable_install_deps,
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.
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.
executable_action_wrapper: File: An executable file of action_wrapper.
executable_install_deps: File: An executable file of install_deps.
progress_message: str: Progress message for the installation action.
Returns:
list[File]: Files representing file system layers.
"""
output_root = ctx.actions.declare_directory(output_prefix)
output_log_file = ctx.actions.declare_file(output_prefix + ".log")
args = ctx.actions.args()
args.add_all([
"--output=" + output_log_file.path,
executable_install_deps.path,
"--board=" + board,
"--output=" + output_root.path,
])
# TODO: Can we avoid the costly to_list() operation?
install_list = install_set.to_list()
direct_inputs = [pkg.file for pkg in install_list]
layer_inputs = sdk.layers + overlays.layers
args.add_all(layer_inputs, format_each = "--layer=%s", expand_directories = False)
direct_inputs.extend(layer_inputs)
install_groups = _calculate_install_groups(install_list)
args.add_all(install_groups, map_each = _map_install_group, format_each = "--install-target=%s")
ctx.actions.run(
inputs = depset(direct_inputs),
outputs = [output_root, output_log_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 [output_root]