blob: 37b82ce7f4efddf2dd5121f1172d5c7bf42e7dd3 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
Ryan Cui6290f032012-11-20 15:44:43 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Generates a sysroot tarball for building a specific package.
6
7Meant for use after setup_board and build_packages have been run.
8"""
9
10import os
11
Mike Frysinger06a51c82021-04-06 11:39:17 -040012from chromite.lib import build_target_lib
Mike Frysinger807d8282022-04-28 22:45:17 -040013from chromite.lib import commandline
Aviv Keshetb7519e12016-10-04 00:50:00 -070014from chromite.lib import constants
Ryan Cui6290f032012-11-20 15:44:43 -080015from chromite.lib import cros_build_lib
Ryan Cui6290f032012-11-20 15:44:43 -080016from chromite.lib import osutils
17from chromite.lib import sudo
Bertrand SIMONNETa5d8b552015-03-25 16:03:34 -070018from chromite.lib import sysroot_lib
Alex Klein8212d492018-08-27 07:47:23 -060019from chromite.lib import toolchain
Ryan Cui6290f032012-11-20 15:44:43 -080020
Mike Frysinger807d8282022-04-28 22:45:17 -040021
Alex Klein1699fab2022-09-08 08:46:06 -060022DEFAULT_NAME = "sysroot_%(package)s.tar.xz"
23PACKAGE_SEPARATOR = "/"
24SYSROOT = "sysroot"
Ryan Cui6290f032012-11-20 15:44:43 -080025
26
27def ParseCommandLine(argv):
Alex Klein1699fab2022-09-08 08:46:06 -060028 """Parse args, and run environment-independent checks."""
29 parser = commandline.ArgumentParser(description=__doc__)
30 parser.add_argument(
31 "--board", required=True, help="The board to generate the sysroot for."
32 )
33 parser.add_argument(
34 "--package",
35 required=True,
36 help="The packages to generate the sysroot for.",
37 )
38 parser.add_argument(
39 "--deps-only",
40 action="store_true",
41 default=False,
42 help="Build dependencies only.",
43 )
44 parser.add_argument(
45 "--out-dir",
46 type="path",
47 required=True,
48 help="Directory to place the generated tarball.",
49 )
50 parser.add_argument(
51 "--out-file",
52 default=DEFAULT_NAME,
Trent Apted66736d82023-05-25 10:38:28 +100053 help="The name to give to the tarball. Defaults to %(default)s.",
Alex Klein1699fab2022-09-08 08:46:06 -060054 )
55 options = parser.parse_args(argv)
Ryan Cui6290f032012-11-20 15:44:43 -080056
Alex Klein1699fab2022-09-08 08:46:06 -060057 options.out_file %= {
58 "package": options.package.split()[0].replace(PACKAGE_SEPARATOR, "_"),
59 }
Ryan Cui6290f032012-11-20 15:44:43 -080060
Alex Klein1699fab2022-09-08 08:46:06 -060061 return options
Ryan Cui6290f032012-11-20 15:44:43 -080062
63
64class GenerateSysroot(object):
Alex Klein1699fab2022-09-08 08:46:06 -060065 """Wrapper for generation functionality."""
Ryan Cui6290f032012-11-20 15:44:43 -080066
Mike Frysinger164ec032023-03-27 16:15:14 -040067 PARALLEL_EMERGE = constants.CHROMITE_BIN_DIR / "parallel_emerge"
Ryan Cui6290f032012-11-20 15:44:43 -080068
Alex Klein1699fab2022-09-08 08:46:06 -060069 def __init__(self, sysroot, options):
70 """Initialize
Ryan Cui6290f032012-11-20 15:44:43 -080071
Alex Klein1699fab2022-09-08 08:46:06 -060072 Args:
Trent Apted66736d82023-05-25 10:38:28 +100073 sysroot: Path to sysroot.
74 options: Parsed options.
Alex Klein1699fab2022-09-08 08:46:06 -060075 """
76 self.sysroot = sysroot
77 self.options = options
78 self.extra_env = {
79 "ROOT": self.sysroot,
80 "USE": os.environ.get("USE", ""),
81 }
David James78d2e942013-07-31 15:34:45 -070082
Alex Klein1699fab2022-09-08 08:46:06 -060083 def _Emerge(self, *args, **kwargs):
84 """Emerge the given packages using parallel_emerge."""
85 cmd = [
86 self.PARALLEL_EMERGE,
87 "--board=%s" % self.options.board,
88 "--usepkgonly",
89 "--noreplace",
90 ] + list(args)
91 kwargs.setdefault("extra_env", self.extra_env)
92 cros_build_lib.sudo_run(cmd, **kwargs)
Ryan Cui6290f032012-11-20 15:44:43 -080093
Alex Klein1699fab2022-09-08 08:46:06 -060094 def _WriteConfig(self, sysroot):
95 sysroot.WriteConfig(
96 sysroot.GenerateBoardSetupConfig(self.options.board)
97 )
98 # For the config to be correctly read, a stub make.conf is needed.
99 # pylint: disable=protected-access
100 make_conf_path = os.path.join(self.sysroot, sysroot_lib._MAKE_CONF)
101 assert not os.path.exists(make_conf_path), "Expecting an empty sysroot."
102 osutils.WriteFile(
103 os.path.join(make_conf_path),
104 "source make.conf.board_setup",
105 makedirs=True,
106 sudo=True,
107 )
Sloan Johnsona85640f2021-10-01 22:32:40 +0000108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 def _InstallToolchain(self):
110 # Create the sysroot's config.
111 sysroot = sysroot_lib.Sysroot(self.sysroot)
112 self._WriteConfig(sysroot)
113 toolchain.InstallToolchain(sysroot, configure=False)
Ryan Cui6290f032012-11-20 15:44:43 -0800114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 def _InstallKernelHeaders(self):
116 self._Emerge("sys-kernel/linux-headers")
Ryan Cui6290f032012-11-20 15:44:43 -0800117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 def _InstallBuildDependencies(self):
119 # Calculate buildtime deps that are not runtime deps.
120 raw_sysroot = build_target_lib.get_default_sysroot_path(
121 self.options.board
122 )
123 packages = []
124 if not self.options.deps_only:
125 packages = self.options.package.split()
126 else:
127 for pkg in self.options.package.split():
128 cmd = ["qdepends", "-q", "-C", pkg]
129 output = cros_build_lib.run(
130 cmd,
131 extra_env={"ROOT": raw_sysroot},
132 capture_output=True,
133 encoding="utf-8",
134 ).stdout
David James78d2e942013-07-31 15:34:45 -0700135
Alex Klein1699fab2022-09-08 08:46:06 -0600136 if output.count("\n") > 1:
137 raise AssertionError(
138 "Too many packages matched for given pattern"
139 )
David James78d2e942013-07-31 15:34:45 -0700140
Alex Klein1699fab2022-09-08 08:46:06 -0600141 # qdepend outputs "package: deps", so only grab the deps.
142 deps = output.partition(":")[2].split()
143 packages.extend(deps)
144 # Install the required packages.
145 if packages:
146 self._Emerge(*packages)
Ryan Cui6290f032012-11-20 15:44:43 -0800147
Alex Klein1699fab2022-09-08 08:46:06 -0600148 def _CreateTarball(self):
149 tarball_path = os.path.join(self.options.out_dir, self.options.out_file)
150 cros_build_lib.CreateTarball(tarball_path, self.sysroot, sudo=True)
Ryan Cui6290f032012-11-20 15:44:43 -0800151
Alex Klein1699fab2022-09-08 08:46:06 -0600152 def Perform(self):
153 """Generate the sysroot."""
154 self._InstallToolchain()
155 self._InstallKernelHeaders()
156 self._InstallBuildDependencies()
157 self._CreateTarball()
Ryan Cui6290f032012-11-20 15:44:43 -0800158
159
160def FinishParsing(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600161 """Run environment dependent checks on parsed args."""
162 target = os.path.join(options.out_dir, options.out_file)
163 if os.path.exists(target):
Alex Kleindf8ee502022-10-18 09:48:15 -0600164 cros_build_lib.Die("Output file %r already exists.", target)
Ryan Cui6290f032012-11-20 15:44:43 -0800165
Alex Klein1699fab2022-09-08 08:46:06 -0600166 if not os.path.isdir(options.out_dir):
167 cros_build_lib.Die(
168 "Non-existent directory %r specified for --out-dir"
169 % options.out_dir
170 )
Ryan Cui6290f032012-11-20 15:44:43 -0800171
172
173def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600174 options = ParseCommandLine(argv)
175 FinishParsing(options)
Ryan Cui6290f032012-11-20 15:44:43 -0800176
Alex Klein1699fab2022-09-08 08:46:06 -0600177 cros_build_lib.AssertInsideChroot()
Ryan Cui6290f032012-11-20 15:44:43 -0800178
Alex Klein1699fab2022-09-08 08:46:06 -0600179 with sudo.SudoKeepAlive(ttyless_sudo=False):
180 with osutils.TempDir(set_global=True, sudo_rm=True) as tempdir:
181 sysroot = os.path.join(tempdir, SYSROOT)
182 os.mkdir(sysroot)
183 GenerateSysroot(sysroot, options).Perform()