blob: 265a768038847e078bc12eae955aff8fcc4b99bb [file] [log] [blame]
Trent Apted7d2777b2023-06-29 13:35:03 +10001# Copyright 2023 The ChromiumOS Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""build_sdk_subtools rebuilds binary packages exported by the subtools builder.
6
7The build_sdk_subtools process takes (copies) an amd64-host base SDK, compiles
8and installs additional packages needed by subtools, then creates relocatable
9binary subtool bundles that can be consumed by other build hosts and developer
10machines.
11
12If build_sdk_subtools has already been invoked for the provided chroot, all
13non-toolchain packages in the subtools deptree that have updated revisions or
14changed USE flags will be rebuilt, along with reverse dependencies.
15
16Packages (e.g. an ebuild) provide manifests that describes how files, once
17installed, are to be bundled and exported.
18
19If packages are specified in the command line, only consider the deptree from
20those specific packages rather than all of virtual/target-sdk-subtools.
21"""
22
23import argparse
Trent Apted7d2777b2023-06-29 13:35:03 +100024import os
25from pathlib import Path
Trent Apted7d2777b2023-06-29 13:35:03 +100026import sys
Trent Aptedbd3cb412023-08-18 11:37:02 +100027from typing import List, Optional, Protocol
Trent Apted7d2777b2023-06-29 13:35:03 +100028
Trent Aptedde109222023-09-05 12:04:58 +100029import chromite
Trent Apted7d2777b2023-06-29 13:35:03 +100030from chromite.lib import build_target_lib
31from chromite.lib import commandline
32from chromite.lib import constants
33from chromite.lib import cros_build_lib
Trent Apted7d2777b2023-06-29 13:35:03 +100034from chromite.lib import osutils
Trent Apted7d2777b2023-06-29 13:35:03 +100035from chromite.lib import sysroot_lib
Trent Aptedbd3cb412023-08-18 11:37:02 +100036from chromite.service import sdk_subtools
Trent Apted7d2777b2023-06-29 13:35:03 +100037
38
39assert sys.version_info >= (3, 8), "build_sdk_subtools uses Python 3.8 features"
40
Trent Aptedde109222023-09-05 12:04:58 +100041logger = chromite.ChromiteLogger.getLogger(__name__)
42
Trent Apted7d2777b2023-06-29 13:35:03 +100043
44# Affects where building occurs (e.g. /build/amd64-subtools-host) if not
45# overridden by --output-dir. Note this will be a chroot.
46SUBTOOLS_OUTPUT_DIR = "amd64-subtools-host"
47
Trent Apted7d2777b2023-06-29 13:35:03 +100048# Flag passed to subprocesses in chroots that might not yet be set up as a
49# subtools chroot.
50_RELAUNCH_FOR_SETUP_FLAG = "--relaunch-for-setup"
51
Trent Apted16007b82023-07-06 14:51:57 +100052# Used to populate a test manifest in /etc/cros/standalone-packages.d/.
53# Ebuilds will later be updated to provide these files instead.
Trent Aptedbd3cb412023-08-18 11:37:02 +100054_TEST_PACKAGE = (
55 sdk_subtools.SUBTOOLS_EXPORTS_CONFIG_DIR / "shellcheck.textproto"
56)
Trent Apted16007b82023-07-06 14:51:57 +100057_TEST_PACKAGE_CONTENTS = """\
58# proto-file: chromiumos/build/api/subtools.proto
59# proto-message: chromiumos.build.api.SubtoolPackage
60name: "shellcheck"
61type: EXPORT_CIPD
62max_files: 2
63paths: [{
64 input: "/usr/bin/shellcheck"
Trent Apted16007b82023-07-06 14:51:57 +100065}]
66"""
67
Trent Apted7d2777b2023-06-29 13:35:03 +100068
69class Options(Protocol):
70 """Protocol to formalize commandline arguments."""
71
Trent Apted7d2777b2023-06-29 13:35:03 +100072 clean: bool
73 setup_chroot: bool
74 update_packages: bool
Trent Aptedde109222023-09-05 12:04:58 +100075 production: bool
Trent Apted7d2777b2023-06-29 13:35:03 +100076 relaunch_for_setup: bool
77 output_dir: Path
78 packages: List[str]
Trent Aptedde109222023-09-05 12:04:58 +100079 export: List[str]
Trent Apted7d2777b2023-06-29 13:35:03 +100080 jobs: int
81
82 def Freeze(self) -> None:
83 pass
84
85
86def get_parser() -> commandline.ArgumentParser:
87 """Returns the cmdline argparser, populates the options and descriptions."""
88 parser = commandline.ArgumentParser(description=__doc__)
89
Trent Apted9b8b13d2023-08-04 16:49:04 +100090 parser.add_bool_argument(
91 "--clean",
Trent Apted7d2777b2023-06-29 13:35:03 +100092 False,
93 "Remove the subtools chroot and re-extract the SDK.",
94 "Re-use an existing subtools chroot.",
95 )
96
Trent Apted9b8b13d2023-08-04 16:49:04 +100097 parser.add_bool_argument(
98 "--setup-chroot",
Trent Apted7d2777b2023-06-29 13:35:03 +100099 True,
100 "Look for a newer base SDK and set it up as a subtools SDK.",
101 "Don't look for a newer base SDK and assume the chroot is setup.",
102 )
103
Trent Apted9b8b13d2023-08-04 16:49:04 +1000104 parser.add_bool_argument(
105 "--update-packages",
Trent Apted7d2777b2023-06-29 13:35:03 +1000106 True,
107 "Update and install packages before looking for things to export.",
108 "Only export packages already installed in the subtools SDK.",
109 )
110
Trent Aptedde109222023-09-05 12:04:58 +1000111 parser.add_bool_argument(
112 "--production",
113 False,
114 "Use production environments for subtool exports.",
115 "Use staging environments for subtool exports.",
116 )
117
Trent Apted7d2777b2023-06-29 13:35:03 +1000118 parser.add_argument(
119 "--output-dir",
120 type=osutils.ExpandPath,
121 metavar="PATH",
122 help=f"Extract SDK and build in chroot (e.g. {SUBTOOLS_OUTPUT_DIR}).",
123 )
124
125 parser.add_argument(
Trent Aptedde109222023-09-05 12:04:58 +1000126 "--export",
127 nargs="+",
128 default=[],
129 metavar="BUNDLE",
130 help="Packages to export (e.g. to CIPD). May require auth.",
131 )
132
133 parser.add_argument(
Trent Apted7d2777b2023-06-29 13:35:03 +1000134 "packages",
135 nargs="*",
136 default=["virtual/target-sdk-subtools"],
137 help="Packages to build before looking for export candidates.",
138 )
139
140 parser.add_argument(
141 "--jobs",
142 "-j",
143 type=int,
144 default=os.cpu_count(),
145 help="Number of packages to build in parallel. (Default: %(default)s)",
146 )
147
148 parser.add_argument(
149 _RELAUNCH_FOR_SETUP_FLAG,
150 action="store_true",
151 default=False,
152 help=argparse.SUPPRESS,
153 )
154
155 # TODO(b/277992359): Consider possibly relevant flags from build_packages:
156 # * --rebuild_revdeps=no: don't rebuild reverse dependencies.
157 # * --skip-toolchain-update? Likely no - the SDK is our toolchain.
158 # * --withdebugsymbols
159 # * --rebuild=no "Automatically rebuild dependencies"
160 # * --backtrack
161 # * --bazel "Use Bazel to build packages"
162
163 return parser
164
165
166def parse_args(argv: Optional[List[str]]) -> Options:
167 """Parse and validate CLI arguments."""
168
169 parser = get_parser()
170 opts: Options = parser.parse_args(argv)
Trent Apted7d2777b2023-06-29 13:35:03 +1000171 opts.Freeze()
172 return opts
173
174
Trent Apted7d2777b2023-06-29 13:35:03 +1000175def _setup_base_sdk(
176 build_target: build_target_lib.BuildTarget,
177 setup_chroot: bool,
178) -> None:
179 """SetupBoard workalike that converts a regular SDK into a subtools chroot.
180
181 Runs inside the /build/amd64-subtools-host subtools SDK chroot.
182 """
183 cros_build_lib.AssertInsideChroot()
184 cros_build_lib.AssertRootUser()
185
Trent Aptedbd3cb412023-08-18 11:37:02 +1000186 sdk_subtools.setup_base_sdk(build_target, setup_chroot)
Trent Apted7d2777b2023-06-29 13:35:03 +1000187
188 if setup_chroot:
Trent Apted16007b82023-07-06 14:51:57 +1000189 _TEST_PACKAGE.write_text(_TEST_PACKAGE_CONTENTS, encoding="utf-8")
Trent Apted7d2777b2023-06-29 13:35:03 +1000190
191
Trent Apted16007b82023-07-06 14:51:57 +1000192def _run_inside_subtools_chroot(opts: Options) -> None:
193 """Steps that build_sdk_subtools performs once it is in its chroot."""
Trent Apted16007b82023-07-06 14:51:57 +1000194 if opts.update_packages:
Trent Aptedbd3cb412023-08-18 11:37:02 +1000195 try:
196 sdk_subtools.update_packages(opts.packages, opts.jobs)
197 except sysroot_lib.PackageInstallError as e:
198 cros_build_lib.Die(e)
Trent Apted16007b82023-07-06 14:51:57 +1000199
Trent Aptedde109222023-09-05 12:04:58 +1000200 installed = sdk_subtools.bundle_and_export(opts.production, opts.export)
201 if not installed.subtools:
202 logger.warn("No subtools available.")
203 elif not opts.export:
204 logger.notice(
205 "Use --export to export a package. Available:%s",
206 "".join(f"\n\t{x.summary}" for x in installed.subtools),
207 )
Trent Apted16007b82023-07-06 14:51:57 +1000208
209
Trent Apted7d2777b2023-06-29 13:35:03 +1000210def main(argv: Optional[List[str]] = None) -> Optional[int]:
211 opts = parse_args(argv)
212 return build_sdk_subtools(opts, argv if argv else [])
213
214
215def build_sdk_subtools(opts: Options, argv: List[str]) -> int:
216 """Executes SDK subtools builder steps according to `opts`."""
217 # BuildTarget needs a str, but opts.output_dir is osutils.ExpandPath.
218 custom_output_dir = str(opts.output_dir) if opts.output_dir else None
219 build_target = build_target_lib.BuildTarget(
220 name=SUBTOOLS_OUTPUT_DIR, build_root=custom_output_dir
221 )
222
223 # If the process is in the subtools chroot, we must assume it's already set
224 # up (we are in it). So start building.
Trent Aptedbd3cb412023-08-18 11:37:02 +1000225 if sdk_subtools.is_inside_subtools_chroot() and not opts.relaunch_for_setup:
Trent Apted16007b82023-07-06 14:51:57 +1000226 _run_inside_subtools_chroot(opts)
Trent Apted7d2777b2023-06-29 13:35:03 +1000227 return 0
228
229 # Otherwise, we have the option to set it up. Then restart inside it. The
230 # setup runs `cros_sdk` to get a base SDK, creates an SDK subprocess to set
231 # it up as a subtools SDK, then restarts inside the subtools SDK.
232 if cros_build_lib.IsInsideChroot():
233 if opts.relaunch_for_setup:
234 # This is the subprocess of the not-in-chroot path used to convert
235 # the base SDK to a subtools SDK (within the chroot).
236 _setup_base_sdk(build_target, opts.setup_chroot)
237 return 0
238 else:
239 cros_build_lib.Die(
240 "build_sdk_subtools must be run outside the chroot."
241 )
242
Trent Apted7c6b7742023-09-04 09:48:34 +1000243 subtools_chroot = constants.DEFAULT_OUT_PATH / build_target.root.lstrip("/")
Trent Apted7d2777b2023-06-29 13:35:03 +1000244 chroot_args = ["--chroot", subtools_chroot]
Trent Aptedde109222023-09-05 12:04:58 +1000245 logger.info("Initializing subtools builder in %s", subtools_chroot)
Trent Apted7d2777b2023-06-29 13:35:03 +1000246
247 if opts.setup_chroot:
248 # Get an SDK. TODO(b/277992359):
249 # - Fetch an SDK version pinned by pupr rather than the default.
250 # - Should this use cros_sdk_lib directly?
251
Trent Apted7c6b7742023-09-04 09:48:34 +1000252 # Ensure `cros_sdk` can check for a lock file on first use.
253 osutils.SafeMakedirs(subtools_chroot.parent)
254
Trent Apted7d2777b2023-06-29 13:35:03 +1000255 # Pass "--skip-chroot-upgrade": the SDK should initially be used
256 # "as-is", but later steps may upgrade packages in the subtools deptree.
257 cros_sdk_args = ["--create", "--skip-chroot-upgrade"]
Trent Apted7c6b7742023-09-04 09:48:34 +1000258 if opts.clean:
259 # Subtools SDKs go under out/build, so if --no-delete-out-dir isn't
260 # used, cros_sdk will try to delete the SDK it's also making.
261 cros_sdk_args += ["--delete", "--no-delete-out-dir"]
262
Trent Apted7d2777b2023-06-29 13:35:03 +1000263 cros_sdk = cros_build_lib.run(
264 ["cros_sdk"] + chroot_args + cros_sdk_args,
265 check=False,
266 cwd=constants.SOURCE_ROOT,
267 )
268 if cros_sdk.returncode != 0:
269 return cros_sdk.returncode
270
271 # Invoke `_setup_base_sdk()` inside the SDK.
272 setup_base_sdk = cros_build_lib.sudo_run(
273 ["build_sdk_subtools"] + argv + [_RELAUNCH_FOR_SETUP_FLAG],
274 check=False,
275 enter_chroot=True,
276 chroot_args=chroot_args,
277 cwd=constants.SOURCE_ROOT,
278 )
279 if setup_base_sdk.returncode != 0:
280 return setup_base_sdk.returncode
281
282 raise commandline.ChrootRequiredError(
283 ["build_sdk_subtools"] + argv, chroot_args=chroot_args
284 )