blob: d8612eb6403114e576c78b2ac7b02cf149a2064c [file] [log] [blame]
# Copyright 2012 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates a sysroot tarball for building a specific package.
Meant for use after cros build-packases has been ran.
"""
import os
from chromite.lib import build_target_lib
from chromite.lib import commandline
from chromite.lib import constants
from chromite.lib import cros_build_lib
from chromite.lib import osutils
from chromite.lib import sudo
from chromite.lib import sysroot_lib
from chromite.lib import toolchain
DEFAULT_NAME = "sysroot_%(package)s.tar.xz"
PACKAGE_SEPARATOR = "/"
SYSROOT = "sysroot"
def ParseCommandLine(argv):
"""Parse args, and run environment-independent checks."""
parser = commandline.ArgumentParser(description=__doc__)
parser.add_argument(
"--board", required=True, help="The board to generate the sysroot for."
)
parser.add_argument(
"--package",
required=True,
help="The packages to generate the sysroot for.",
)
parser.add_argument(
"--deps-only",
action="store_true",
default=False,
help="Build dependencies only.",
)
parser.add_argument(
"--out-dir",
type="str_path",
required=True,
help="Directory to place the generated tarball.",
)
parser.add_argument(
"--out-file",
default=DEFAULT_NAME,
help="The name to give to the tarball. Defaults to %(default)s.",
)
options = parser.parse_args(argv)
options.out_file %= {
"package": options.package.split()[0].replace(PACKAGE_SEPARATOR, "_"),
}
options.build_target = build_target_lib.BuildTarget(options.board)
return options
class GenerateSysroot:
"""Wrapper for generation functionality."""
PARALLEL_EMERGE = constants.CHROMITE_BIN_DIR / "parallel_emerge"
def __init__(self, sysroot, options) -> None:
"""Initialize
Args:
sysroot: Path to sysroot.
options: Parsed options.
"""
self.sysroot = sysroot
self.options = options
self.extra_env = {
"ROOT": self.sysroot,
"USE": os.environ.get("USE", ""),
}
def _Emerge(self, *args, **kwargs) -> None:
"""Emerge the given packages using parallel_emerge."""
cmd = [
self.PARALLEL_EMERGE,
"--board=%s" % self.options.board,
"--usepkgonly",
"--noreplace",
] + list(args)
kwargs.setdefault("extra_env", self.extra_env)
cros_build_lib.sudo_run(cmd, **kwargs)
def _WriteConfig(self, sysroot) -> None:
sysroot.WriteConfig(
sysroot.GenerateBoardSetupConfig(self.options.build_target)
)
# For the config to be correctly read, a stub make.conf is needed.
# pylint: disable=protected-access
make_conf_path = os.path.join(self.sysroot, sysroot_lib._MAKE_CONF)
assert not os.path.exists(make_conf_path), "Expecting an empty sysroot."
osutils.WriteFile(
os.path.join(make_conf_path),
"source make.conf.board_setup",
makedirs=True,
sudo=True,
)
def _InstallToolchain(self) -> None:
# Create the sysroot's config.
sysroot = sysroot_lib.Sysroot(self.sysroot)
self._WriteConfig(sysroot)
toolchain.InstallToolchain(sysroot, configure=False)
def _InstallKernelHeaders(self) -> None:
self._Emerge("sys-kernel/linux-headers")
def _InstallBuildDependencies(self) -> None:
# Calculate buildtime deps that are not runtime deps.
raw_sysroot = build_target_lib.get_default_sysroot_path(
self.options.board
)
packages = []
if not self.options.deps_only:
packages = self.options.package.split()
else:
for pkg in self.options.package.split():
cmd = [
"qdepends",
"-q",
"-C",
"--depend",
"--root",
raw_sysroot,
pkg,
]
output = cros_build_lib.run(
cmd,
capture_output=True,
encoding="utf-8",
).stdout
if output.count("\n") > 1:
raise AssertionError(
"Too many packages matched for given pattern"
)
# qdepend outputs "package: deps", so only grab the deps.
deps = output.partition(":")[2].split()
packages.extend(deps)
# Install the required packages.
if packages:
self._Emerge(*packages)
def _CreateTarball(self) -> None:
tarball_path = os.path.join(self.options.out_dir, self.options.out_file)
cros_build_lib.CreateTarball(tarball_path, self.sysroot, sudo=True)
def Perform(self) -> None:
"""Generate the sysroot."""
self._InstallToolchain()
self._InstallKernelHeaders()
self._InstallBuildDependencies()
self._CreateTarball()
def FinishParsing(options) -> None:
"""Run environment dependent checks on parsed args."""
target = os.path.join(options.out_dir, options.out_file)
if os.path.exists(target):
cros_build_lib.Die("Output file %r already exists.", target)
if not os.path.isdir(options.out_dir):
cros_build_lib.Die(
"Non-existent directory %r specified for --out-dir"
% options.out_dir
)
def main(argv) -> None:
options = ParseCommandLine(argv)
FinishParsing(options)
cros_build_lib.AssertInsideChroot()
with sudo.SudoKeepAlive(ttyless_sudo=False):
with osutils.TempDir(set_global=True, sudo_rm=True) as tempdir:
sysroot = os.path.join(tempdir, SYSROOT)
os.mkdir(sysroot)
GenerateSysroot(sysroot, options).Perform()