blob: 3b8bae789827f6bc01787cf7e6b1babcca053d26 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
Zdenek Behan508dcce2011-12-05 15:39:32 +01002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Mike Frysinger750c5f52014-09-16 16:16:57 -04005"""This script manages the installed toolchains in the chroot."""
Zdenek Behan508dcce2011-12-05 15:39:32 +01006
Mike Frysinger3ed47722017-08-08 14:59:08 -04007import errno
Mike Frysinger35247af2012-11-16 18:58:06 -05008import glob
Mike Frysinger3ed47722017-08-08 14:59:08 -04009import hashlib
Mike Frysinger7ccee992012-06-01 21:27:59 -040010import json
Chris McDonald59650c32021-07-20 15:29:28 -060011import logging
Zdenek Behan508dcce2011-12-05 15:39:32 +010012import os
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -070013import re
Tobias Boschddd16492019-08-14 09:29:54 -070014import shutil
Zdenek Behan508dcce2011-12-05 15:39:32 +010015
Chris McDonald59650c32021-07-20 15:29:28 -060016from chromite.third_party import lddtree
17
Mike Frysinger506e75f2012-12-17 14:21:13 -050018from chromite.lib import commandline
Mike Frysinger95452702021-01-23 00:07:22 -050019from chromite.lib import constants
Brian Harring503f3ab2012-03-09 21:39:41 -080020from chromite.lib import cros_build_lib
Brian Harringaf019fb2012-05-10 15:06:13 -070021from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050022from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080023from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040024from chromite.utils import key_value_store
Mike Frysinger35247af2012-11-16 18:58:06 -050025
Zdenek Behan508dcce2011-12-05 15:39:32 +010026
Mike Frysinger31596002012-12-03 23:54:24 -050027if cros_build_lib.IsInsideChroot():
Alex Klein1699fab2022-09-08 08:46:06 -060028 # Only import portage after we've checked that we're inside the chroot.
29 # Outside may not have portage, in which case the above may not happen.
30 # We'll check in main() if the operation needs portage.
31 # pylint: disable=import-error
32 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010033
34
Alex Klein1699fab2022-09-08 08:46:06 -060035EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, "parallel_emerge")
36PACKAGE_STABLE = "[stable]"
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010037
Mike Frysinger4cd80b62022-05-04 20:39:01 -040038CHROMIUMOS_OVERLAY = os.path.join(
Alex Klein1699fab2022-09-08 08:46:06 -060039 constants.CHROOT_SOURCE_ROOT, constants.CHROMIUMOS_OVERLAY_DIR
40)
Mike Frysinger4cd80b62022-05-04 20:39:01 -040041ECLASS_OVERLAY = os.path.join(
Alex Klein1699fab2022-09-08 08:46:06 -060042 constants.CHROOT_SOURCE_ROOT, constants.ECLASS_OVERLAY_DIR
43)
Mike Frysinger4cd80b62022-05-04 20:39:01 -040044STABLE_OVERLAY = os.path.join(
Alex Klein1699fab2022-09-08 08:46:06 -060045 constants.CHROOT_SOURCE_ROOT, constants.PORTAGE_STABLE_OVERLAY_DIR
46)
47CROSSDEV_OVERLAY = "/usr/local/portage/crossdev"
Zdenek Behan508dcce2011-12-05 15:39:32 +010048
49
Mike Frysinger66bfde52017-09-12 16:42:57 -040050# The exact list of host toolchain packages we care about. These are the
51# packages that bots/devs install only from binpkgs and rely on the SDK bot
52# (chromiumos-sdk) to validate+uprev.
53#
Mike Frysinger66bfde52017-09-12 16:42:57 -040054# We don't use crossdev to manage the host toolchain for us, especially since
55# we diverge significantly now (with llvm/clang/etc...), and we don't need or
56# want crossdev managing /etc/portage config files for the sdk
57HOST_PACKAGES = (
Alex Klein1699fab2022-09-08 08:46:06 -060058 "dev-lang/go",
59 "dev-lang/rust-bootstrap",
60 "dev-lang/rust-host",
61 "dev-libs/elfutils",
62 "sys-devel/binutils",
63 "sys-devel/gcc",
64 "sys-devel/llvm",
65 "sys-kernel/linux-headers",
66 "sys-libs/glibc",
67 "sys-libs/libcxx",
68 "sys-libs/llvm-libunwind",
Mike Frysinger66bfde52017-09-12 16:42:57 -040069)
70
Mike Frysinger785b0c32017-09-13 01:35:59 -040071# These packages are also installed into the host SDK. However, they require
72# the cross-compilers to be installed first (because they need them to actually
73# build), so we have to delay their installation.
74HOST_POST_CROSS_PACKAGES = (
Alex Klein1699fab2022-09-08 08:46:06 -060075 "dev-lang/rust",
76 "virtual/target-sdk-post-cross",
77 "dev-embedded/coreboot-sdk",
78 "dev-embedded/hps-sdk",
79 "dev-embedded/ti50-sdk",
Mike Frysinger785b0c32017-09-13 01:35:59 -040080)
81
82# New packages that we're in the process of adding to the SDK. Since the SDK
83# bot hasn't had a chance to run yet, there are no binary packages available,
84# so we have to list them here and wait. Once it completes, entries here can
85# be removed so they'll end up on bots & dev's systems.
George Burgess IV66e199c2021-05-05 15:38:40 -070086NEW_PACKAGES = ()
Mike Frysinger785b0c32017-09-13 01:35:59 -040087
Rahul Chaudhry4b803052015-05-13 15:25:56 -070088# Enable the Go compiler for these targets.
89TARGET_GO_ENABLED = (
Alex Klein1699fab2022-09-08 08:46:06 -060090 "x86_64-cros-linux-gnu",
91 "armv7a-cros-linux-gnueabi",
92 "armv7a-cros-linux-gnueabihf",
93 "aarch64-cros-linux-gnu",
Rahul Chaudhry4b803052015-05-13 15:25:56 -070094)
Alex Klein1699fab2022-09-08 08:46:06 -060095CROSSDEV_GO_ARGS = ["--ex-pkg", "dev-lang/go"]
Rahul Chaudhry4b803052015-05-13 15:25:56 -070096
Alex Klein1699fab2022-09-08 08:46:06 -060097CROSSDEV_LIBXCRYPT_ARGS = ["--ex-pkg", "sys-libs/libxcrypt"]
Adrian Ratiubf0b9af2022-05-02 14:48:15 +030098
Manoj Gupta1b5642e2017-03-08 16:44:12 -080099# Enable llvm's compiler-rt for these targets.
100TARGET_COMPILER_RT_ENABLED = (
Alex Klein1699fab2022-09-08 08:46:06 -0600101 "armv7a-cros-linux-gnueabi",
102 "armv7a-cros-linux-gnueabihf",
103 "aarch64-cros-linux-gnu",
104 "arm-none-eabi",
105 "armv7m-cros-eabi",
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800106)
Alex Klein1699fab2022-09-08 08:46:06 -0600107CROSSDEV_COMPILER_RT_ARGS = ["--ex-pkg", "sys-libs/compiler-rt"]
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800108
Manoj Gupta946abb42017-04-12 14:27:19 -0700109TARGET_LLVM_PKGS_ENABLED = (
Alex Klein1699fab2022-09-08 08:46:06 -0600110 "armv7m-cros-eabi",
111 "armv7a-cros-linux-gnueabi",
112 "armv7a-cros-linux-gnueabihf",
113 "aarch64-cros-linux-gnu",
114 "i686-cros-linux-gnu",
115 "x86_64-cros-linux-gnu",
Manoj Gupta946abb42017-04-12 14:27:19 -0700116)
117
118LLVM_PKGS_TABLE = {
Alex Klein1699fab2022-09-08 08:46:06 -0600119 "ex_llvm-libunwind": ["--ex-pkg", "sys-libs/llvm-libunwind"],
120 "ex_libcxx": ["--ex-pkg", "sys-libs/libcxx"],
Manoj Gupta946abb42017-04-12 14:27:19 -0700121}
122
Alex Klein1699fab2022-09-08 08:46:06 -0600123
David James66a09c42012-11-05 13:31:38 -0800124class Crossdev(object):
Alex Klein1699fab2022-09-08 08:46:06 -0600125 """Class for interacting with crossdev and caching its output."""
David James66a09c42012-11-05 13:31:38 -0800126
Alex Klein1699fab2022-09-08 08:46:06 -0600127 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, ".configured.json")
128 _CACHE = {}
129 # Packages that needs separate handling, in addition to what we have from
130 # crossdev.
131 MANUAL_PKGS = {
132 "rust": "dev-lang",
133 "llvm": "sys-devel",
134 "llvm-libunwind": "sys-libs",
135 "libcxx": "sys-libs",
136 "elfutils": "dev-libs",
Mike Frysinger3ed47722017-08-08 14:59:08 -0400137 }
138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 @classmethod
140 def Load(cls, reconfig):
141 """Load crossdev cache from disk.
Mike Frysinger3ed47722017-08-08 14:59:08 -0400142
Alex Klein1699fab2022-09-08 08:46:06 -0600143 We invalidate the cache when crossdev updates or this script changes.
144 """
145 crossdev_version = GetStablePackageVersion("sys-devel/crossdev", True)
146 # If we run the compiled/cached .pyc file, we'll read/hash that when we
147 # really always want to track the source .py file.
148 script = os.path.abspath(__file__)
149 if script.endswith(".pyc"):
150 script = script[:-1]
151 setup_toolchains_hash = hashlib.md5(
152 osutils.ReadFile(script, mode="rb")
153 ).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400154
Alex Klein1699fab2022-09-08 08:46:06 -0600155 cls._CACHE = {
156 "crossdev_version": crossdev_version,
157 "setup_toolchains_hash": setup_toolchains_hash,
Mike Frysinger66bfde52017-09-12 16:42:57 -0400158 }
Alex Klein1699fab2022-09-08 08:46:06 -0600159
160 logging.debug("cache: checking file: %s", cls._CACHE_FILE)
161 if reconfig:
162 logging.debug("cache: forcing regen due to reconfig")
163 return
164
165 try:
166 file_data = osutils.ReadFile(cls._CACHE_FILE)
167 except IOError as e:
168 if e.errno != errno.ENOENT:
169 logging.warning("cache: reading failed: %s", e)
170 osutils.SafeUnlink(cls._CACHE_FILE)
171 return
172
173 try:
174 data = json.loads(file_data)
175 except ValueError as e:
176 logging.warning("cache: ignoring invalid content: %s", e)
177 return
178
179 if crossdev_version != data.get("crossdev_version"):
180 logging.debug("cache: rebuilding after crossdev upgrade")
181 elif setup_toolchains_hash != data.get("setup_toolchains_hash"):
182 logging.debug(
183 "cache: rebuilding after cros_setup_toolchains change"
184 )
Mike Frysinger785b0c32017-09-13 01:35:59 -0400185 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600186 logging.debug("cache: content is up-to-date!")
187 cls._CACHE = data
Han Shene23782f2016-02-18 12:20:00 -0800188
Alex Klein1699fab2022-09-08 08:46:06 -0600189 @classmethod
190 def Save(cls):
191 """Store crossdev cache on disk."""
192 # Save the cache from the successful run.
193 with open(cls._CACHE_FILE, "w") as f:
194 json.dump(cls._CACHE, f)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400195
Alex Klein1699fab2022-09-08 08:46:06 -0600196 @classmethod
197 def GetConfig(cls, target):
198 """Returns a map of crossdev provided variables about a tuple."""
199 CACHE_ATTR = "_target_tuple_map"
Han Shene23782f2016-02-18 12:20:00 -0800200
Alex Klein1699fab2022-09-08 08:46:06 -0600201 val = cls._CACHE.setdefault(CACHE_ATTR, {})
202 if not target in val:
203 if target.startswith("host"):
204 conf = {
205 "crosspkgs": [],
206 "target": toolchain.GetHostTuple(),
207 }
208 if target == "host":
209 packages_list = HOST_PACKAGES
210 else:
211 packages_list = HOST_POST_CROSS_PACKAGES
212 manual_pkgs = dict(
213 (pkg, cat)
214 for cat, pkg in [x.split("/") for x in packages_list]
215 )
216 else:
217 # Build the crossdev command.
218 cmd = ["crossdev", "--show-target-cfg", "--ex-gdb"]
219 # Enable libxcrypt for all linux-gnu targets.
220 if "cros-linux-gnu" in target:
221 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
222 if target in TARGET_COMPILER_RT_ENABLED:
223 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
224 if target in TARGET_LLVM_PKGS_ENABLED:
225 for pkg in LLVM_PKGS_TABLE:
226 cmd.extend(LLVM_PKGS_TABLE[pkg])
227 if target in TARGET_GO_ENABLED:
228 cmd.extend(CROSSDEV_GO_ARGS)
229 cmd.extend(["-t", target])
230 # Catch output of crossdev.
231 out = cros_build_lib.run(
232 cmd, print_cmd=False, stdout=True, encoding="utf-8"
233 ).stdout.splitlines()
234 # List of tuples split at the first '=', converted into dict.
235 conf = dict(
236 (k, cros_build_lib.ShellUnquote(v))
237 for k, v in (x.split("=", 1) for x in out)
238 )
239 conf["crosspkgs"] = conf["crosspkgs"].split()
Han Shene23782f2016-02-18 12:20:00 -0800240
Alex Klein1699fab2022-09-08 08:46:06 -0600241 manual_pkgs = cls.MANUAL_PKGS
David James66a09c42012-11-05 13:31:38 -0800242
Alex Klein1699fab2022-09-08 08:46:06 -0600243 for pkg, cat in manual_pkgs.items():
244 conf[pkg + "_pn"] = pkg
245 conf[pkg + "_category"] = cat
246 if pkg not in conf["crosspkgs"]:
247 conf["crosspkgs"].append(pkg)
David James66a09c42012-11-05 13:31:38 -0800248
Alex Klein1699fab2022-09-08 08:46:06 -0600249 val[target] = conf
David James66a09c42012-11-05 13:31:38 -0800250
Alex Klein1699fab2022-09-08 08:46:06 -0600251 return val[target]
Manoj Gupta4d016f62021-10-19 16:39:34 -0700252
Alex Klein1699fab2022-09-08 08:46:06 -0600253 @classmethod
254 def UpdateTargets(cls, targets, usepkg, config_only=False):
255 """Calls crossdev to initialize a cross target.
Manoj Gupta4d016f62021-10-19 16:39:34 -0700256
Alex Klein1699fab2022-09-08 08:46:06 -0600257 Args:
258 targets: The dict of targets to initialize using crossdev.
259 usepkg: Copies the commandline opts.
260 config_only: Just update.
261 """
262 configured_targets = cls._CACHE.setdefault("configured_targets", [])
263 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 # Schedule all of the targets in parallel, and let them run.
266 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
267 for target_name in targets:
268 # We already started this target in this loop.
269 if target_name in started_targets:
270 continue
271 # The target is already configured.
272 if config_only and target_name in configured_targets:
273 continue
274 queue.put(
275 [target_name, targets[target_name], usepkg, config_only]
276 )
277 started_targets.add(target_name)
David James66a09c42012-11-05 13:31:38 -0800278
Alex Klein1699fab2022-09-08 08:46:06 -0600279 @classmethod
280 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
281 """Calls crossdev to initialize a cross target.
David James66a09c42012-11-05 13:31:38 -0800282
Alex Klein1699fab2022-09-08 08:46:06 -0600283 Args:
284 target_name: The name of the target to initialize.
285 target: The target info for initializing.
286 usepkg: Copies the commandline opts.
287 config_only: Just update.
288 """
289 configured_targets = cls._CACHE.setdefault("configured_targets", [])
290 cmdbase = ["crossdev", "--show-fail-log"]
291 cmdbase.extend(["--env", "FEATURES=splitdebug"])
292 # Pick stable by default, and override as necessary.
293 cmdbase.extend(["-P", "--oneshot"])
294 if usepkg:
295 cmdbase.extend(
296 ["-P", "--getbinpkg", "-P", "--usepkgonly", "--without-headers"]
297 )
David James66a09c42012-11-05 13:31:38 -0800298
Alex Klein1699fab2022-09-08 08:46:06 -0600299 overlays = " ".join(
300 (CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY)
301 )
302 cmdbase.extend(["--overlays", overlays])
303 cmdbase.extend(["--ov-output", CROSSDEV_OVERLAY])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700304
Alex Klein1699fab2022-09-08 08:46:06 -0600305 cmd = cmdbase + ["-t", target_name]
Manoj Gupta4d016f62021-10-19 16:39:34 -0700306
Alex Klein1699fab2022-09-08 08:46:06 -0600307 for pkg in GetTargetPackages(target_name):
308 if pkg == "gdb":
309 # Gdb does not have selectable versions.
310 cmd.append("--ex-gdb")
311 elif pkg == "ex_libxcrypt":
312 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
313 elif pkg == "ex_compiler-rt":
314 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
315 elif pkg == "ex_go":
316 # Go does not have selectable versions.
317 cmd.extend(CROSSDEV_GO_ARGS)
318 elif pkg in LLVM_PKGS_TABLE:
319 cmd.extend(LLVM_PKGS_TABLE[pkg])
320 elif pkg in cls.MANUAL_PKGS:
321 pass
322 else:
323 # The first of the desired versions is the "primary" one.
324 version = GetDesiredPackageVersions(target_name, pkg)[0]
325 cmd.extend(["--%s" % pkg, version])
326
327 cmd.extend(target["crossdev"].split())
328
329 if config_only:
330 # In this case we want to just quietly reinit
331 cmd.append("--init-target")
332 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
333 else:
334 cros_build_lib.run(cmd)
335
336 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800337
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100338
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100339def GetTargetPackages(target):
Alex Klein1699fab2022-09-08 08:46:06 -0600340 """Returns a list of packages for a given target."""
341 conf = Crossdev.GetConfig(target)
342 # Undesired packages are denoted by empty ${pkg}_pn variable.
343 return [x for x in conf["crosspkgs"] if conf.get(x + "_pn")]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100344
345
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100346# Portage helper functions:
347def GetPortagePackage(target, package):
Alex Klein1699fab2022-09-08 08:46:06 -0600348 """Returns a package name for the given target."""
349 conf = Crossdev.GetConfig(target)
350 # Portage category:
351 if target.startswith("host") or package in Crossdev.MANUAL_PKGS:
352 category = conf[package + "_category"]
353 else:
354 category = conf["category"]
355 # Portage package:
356 pn = conf[package + "_pn"]
357 # Final package name:
358 assert category
359 assert pn
360 return "%s/%s" % (category, pn)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100361
362
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700363def PortageTrees(root):
Alex Klein1699fab2022-09-08 08:46:06 -0600364 """Return the portage trees for a given root."""
365 if root == "/":
366 return portage.db["/"]
367 # The portage logic requires the path always end in a slash.
368 root = root.rstrip("/") + "/"
369 return portage.create_trees(target_root=root, config_root=root)[root]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700370
371
Alex Klein1699fab2022-09-08 08:46:06 -0600372def GetInstalledPackageVersions(atom, root="/"):
373 """Extracts the list of current versions of a target, package pair.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100374
Alex Klein1699fab2022-09-08 08:46:06 -0600375 Args:
376 atom: The atom to operate on (e.g. sys-devel/gcc)
377 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378
Alex Klein1699fab2022-09-08 08:46:06 -0600379 Returns:
380 The list of versions of the package currently installed.
381 """
382 versions = []
383 for pkg in PortageTrees(root)["vartree"].dbapi.match(atom, use_cache=0):
384 version = portage.versions.cpv_getversion(pkg)
385 versions.append(version)
386 return versions
Zdenek Behan508dcce2011-12-05 15:39:32 +0100387
388
Alex Klein1699fab2022-09-08 08:46:06 -0600389def GetStablePackageVersion(atom, installed, root="/"):
390 """Extracts the current stable version for a given package.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100391
Alex Klein1699fab2022-09-08 08:46:06 -0600392 Args:
393 atom: The target/package to operate on e.g. i686-cros-linux-gnu/gcc
394 installed: Whether we want installed packages or ebuilds
395 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100396
Alex Klein1699fab2022-09-08 08:46:06 -0600397 Returns:
398 A string containing the latest version.
399 """
400 pkgtype = "vartree" if installed else "porttree"
401 cpv = portage.best(
402 PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0)
403 )
404 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100405
406
Alex Klein1699fab2022-09-08 08:46:06 -0600407def VersionListToNumeric(target, package, versions, installed, root="/"):
408 """Resolves keywords in a given version list for a particular package.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100409
Alex Klein1699fab2022-09-08 08:46:06 -0600410 Resolving means replacing PACKAGE_STABLE with the actual number.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100411
Alex Klein1699fab2022-09-08 08:46:06 -0600412 Args:
413 target: The target to operate on (e.g. i686-cros-linux-gnu)
414 package: The target/package to operate on (e.g. gcc)
415 versions: List of versions to resolve
416 installed: Query installed packages
417 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100418
Alex Klein1699fab2022-09-08 08:46:06 -0600419 Returns:
420 List of purely numeric versions equivalent to argument
421 """
422 resolved = []
423 atom = GetPortagePackage(target, package)
424 if not installed:
425 root = "/"
426 for version in versions:
427 if version == PACKAGE_STABLE:
428 resolved.append(GetStablePackageVersion(atom, installed, root=root))
429 else:
430 resolved.append(version)
431 return resolved
Zdenek Behan508dcce2011-12-05 15:39:32 +0100432
433
434def GetDesiredPackageVersions(target, package):
Alex Klein1699fab2022-09-08 08:46:06 -0600435 """Produces the list of desired versions for each target, package pair.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 The first version in the list is implicitly treated as primary, ie.
438 the version that will be initialized by crossdev and selected.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439
Alex Klein1699fab2022-09-08 08:46:06 -0600440 If the version is PACKAGE_STABLE, it really means the current version which
441 is emerged by using the package atom with no particular version key.
442 Since crossdev unmasks all packages by default, this will actually
443 mean 'unstable' in most cases.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 Args:
446 target: The target to operate on (e.g. i686-cros-linux-gnu)
447 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100448
Alex Klein1699fab2022-09-08 08:46:06 -0600449 Returns:
450 A list composed of either a version string, PACKAGE_STABLE
451 """
452 if package in GetTargetPackages(target):
453 return [PACKAGE_STABLE]
454 else:
455 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100456
457
458def TargetIsInitialized(target):
Alex Klein1699fab2022-09-08 08:46:06 -0600459 """Verifies if the given list of targets has been correctly initialized.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460
Alex Klein1699fab2022-09-08 08:46:06 -0600461 This determines whether we have to call crossdev while emerging
462 toolchain packages or can do it using emerge. Emerge is naturally
463 preferred, because all packages can be updated in a single pass.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100464
Alex Klein1699fab2022-09-08 08:46:06 -0600465 Args:
466 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 Returns:
469 True if |target| is completely initialized, else False
470 """
471 # Check if packages for the given target all have a proper version.
472 try:
473 for package in GetTargetPackages(target):
474 atom = GetPortagePackage(target, package)
475 # Do we even want this package && is it initialized?
476 if not (
477 GetStablePackageVersion(atom, True)
478 and GetStablePackageVersion(atom, False)
479 ):
480 return False
481 return True
482 except cros_build_lib.RunCommandError:
483 # Fails - The target has likely never been initialized before.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100484 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100485
486
487def RemovePackageMask(target):
Alex Klein1699fab2022-09-08 08:46:06 -0600488 """Removes a package.mask file for the given platform.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100489
Alex Klein1699fab2022-09-08 08:46:06 -0600490 The pre-existing package.mask files can mess with the keywords.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100491
Alex Klein1699fab2022-09-08 08:46:06 -0600492 Args:
493 target: The target to operate on (e.g. i686-cros-linux-gnu)
494 """
495 maskfile = os.path.join("/etc/portage/package.mask", "cross-" + target)
496 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100497
498
Zdenek Behan508dcce2011-12-05 15:39:32 +0100499# Main functions performing the actual update steps.
Alex Klein1699fab2022-09-08 08:46:06 -0600500def RebuildLibtool(root="/"):
501 """Rebuild libtool as needed
Mike Frysingerc880a962013-11-08 13:59:06 -0500502
Alex Klein1699fab2022-09-08 08:46:06 -0600503 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
504 gcc, libtool will break. We can't use binary packages either as those will
505 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700506
Alex Klein1699fab2022-09-08 08:46:06 -0600507 Args:
508 root: The install root where we want libtool rebuilt.
509 """
510 needs_update = False
511 with open(os.path.join(root, "usr/bin/libtool")) as f:
512 for line in f:
513 # Look for a line like:
514 # sys_lib_search_path_spec="..."
515 # It'll be a list of paths and gcc will be one of them.
516 if line.startswith("sys_lib_search_path_spec="):
517 line = line.rstrip()
518 for path in line.split("=", 1)[1].strip('"').split():
519 root_path = os.path.join(root, path.lstrip(os.path.sep))
520 logging.debug("Libtool: checking %s", root_path)
521 if not os.path.exists(root_path):
522 logging.info("Rebuilding libtool after gcc upgrade")
523 logging.info(" %s", line)
524 logging.info(" missing path: %s", path)
525 needs_update = True
526 break
Mike Frysingerc880a962013-11-08 13:59:06 -0500527
Alex Klein1699fab2022-09-08 08:46:06 -0600528 if needs_update:
529 break
Mike Frysingerc880a962013-11-08 13:59:06 -0500530
Alex Klein1699fab2022-09-08 08:46:06 -0600531 if needs_update:
532 cmd = [EMERGE_CMD, "--oneshot"]
533 if root != "/":
534 cmd.extend(["--sysroot=%s" % root, "--root=%s" % root])
535 cmd.append("sys-devel/libtool")
536 cros_build_lib.run(cmd)
537 else:
538 logging.debug("Libtool is up-to-date; no need to rebuild")
Mike Frysingerc880a962013-11-08 13:59:06 -0500539
540
Alex Klein1699fab2022-09-08 08:46:06 -0600541def UpdateTargets(targets, usepkg, root="/"):
542 """Determines which packages need update/unmerge and defers to portage.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543
Alex Klein1699fab2022-09-08 08:46:06 -0600544 Args:
545 targets: The list of targets to update
546 usepkg: Copies the commandline option
547 root: The install root in which we want packages updated.
548 """
549 # For each target, we do two things. Figure out the list of updates,
550 # and figure out the appropriate keywords/masks. Crossdev will initialize
551 # these, but they need to be regenerated on every update.
552 logging.info("Determining required toolchain updates...")
553 mergemap = {}
554 # Used to keep track of post-cross packages. These are allowed to have
555 # implicit dependencies on toolchain packages, and therefore need to
556 # be built last.
557 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558 for target in targets:
Alex Klein1699fab2022-09-08 08:46:06 -0600559 is_post_cross_target = target.endswith("-post-cross")
560 logging.debug("Updating target %s", target)
561 # Record the highest needed version for each target, for masking purposes.
562 RemovePackageMask(target)
563 for package in GetTargetPackages(target):
564 # Portage name for the package
565 logging.debug(" Checking package %s", package)
566 pkg = GetPortagePackage(target, package)
567 current = GetInstalledPackageVersions(pkg, root=root)
568 desired = GetDesiredPackageVersions(target, package)
569 desired_num = VersionListToNumeric(target, package, desired, False)
570 if pkg in NEW_PACKAGES and usepkg:
571 # Skip this binary package (for now).
572 continue
573 mergemap[pkg] = set(desired_num).difference(current)
574 logging.debug(" %s -> %s", current, desired_num)
575 if is_post_cross_target:
576 post_cross_pkgs.add(pkg)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400577
Alex Klein1699fab2022-09-08 08:46:06 -0600578 packages = [pkg for pkg, vers in mergemap.items() if vers]
579 if not packages:
580 logging.info("Nothing to update!")
581 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100582
Alex Klein1699fab2022-09-08 08:46:06 -0600583 logging.info("Updating packages:")
584 logging.info("%s", packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100585
Alex Klein1699fab2022-09-08 08:46:06 -0600586 cmd = [EMERGE_CMD, "--oneshot", "--update"]
587 if usepkg:
588 cmd.extend(["--getbinpkg", "--usepkgonly"])
589 if root != "/":
590 cmd.extend(["--sysroot=%s" % root, "--root=%s" % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100591
Alex Klein1699fab2022-09-08 08:46:06 -0600592 if usepkg:
593 # Since we are not building from source, we can handle
594 # all packages in one go.
595 cmd.extend(packages)
596 cros_build_lib.run(cmd)
597 else:
598 pre_cross_items = [
599 pkg for pkg in packages if pkg not in post_cross_pkgs
600 ]
601 if pre_cross_items:
602 cros_build_lib.run(cmd + pre_cross_items)
603 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
604 if post_cross_items:
605 cros_build_lib.run(cmd + post_cross_items)
606 return True
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700607
Alex Klein1699fab2022-09-08 08:46:06 -0600608
609def CleanTargets(targets, root="/"):
610 """Unmerges old packages that are assumed unnecessary.
611
612 Args:
613 targets: The list of targets to clean up.
614 root: The install root in which we want packages cleaned up.
615 """
616 unmergemap = {}
617 for target in targets:
618 logging.debug("Cleaning target %s", target)
619 for package in GetTargetPackages(target):
620 logging.debug(" Cleaning package %s", package)
621 pkg = GetPortagePackage(target, package)
622 current = GetInstalledPackageVersions(pkg, root=root)
623 desired = GetDesiredPackageVersions(target, package)
624 # NOTE: This refers to installed packages (vartree) rather than the
625 # Portage version (porttree and/or bintree) when determining the current
626 # version. While this isn't the most accurate thing to do, it is probably
627 # a good simple compromise, which should have the desired result of
628 # uninstalling everything but the latest installed version. In
629 # particular, using the bintree (--usebinpkg) requires a non-trivial
630 # binhost sync and is probably more complex than useful.
631 desired_num = VersionListToNumeric(target, package, desired, True)
632 if not set(desired_num).issubset(current):
633 logging.warning(
634 "Error detecting stable version for %s, " "skipping clean!",
635 pkg,
636 )
637 return
638 unmergemap[pkg] = set(current).difference(desired_num)
639
640 # Cleaning doesn't care about consistency and rebuilding package.* files.
641 packages = []
642 for pkg, vers in unmergemap.items():
643 packages.extend("=%s-%s" % (pkg, ver) for ver in vers if ver != "9999")
644
645 if packages:
646 logging.info("Cleaning packages:")
647 logging.info("%s", packages)
648 cmd = [EMERGE_CMD, "--unmerge"]
649 if root != "/":
650 cmd.extend(["--sysroot=%s" % root, "--root=%s" % root])
651 cmd.extend(packages)
652 cros_build_lib.run(cmd)
653 else:
654 logging.info("Nothing to clean!")
655
656
657def SelectActiveToolchains(targets, root="/"):
658 """Runs gcc-config and binutils-config to select the desired.
659
660 Args:
661 targets: The targets to select
662 root: The root where we want to select toolchain versions.
663 """
664 for package in ["gcc", "binutils"]:
665 for target in targets:
666 # See if this package is part of this target.
667 if package not in GetTargetPackages(target):
668 logging.debug("%s: %s is not used", target, package)
669 continue
670
671 # Pick the first version in the numbered list as the selected one.
672 desired = GetDesiredPackageVersions(target, package)
673 desired_num = VersionListToNumeric(
674 target, package, desired, True, root=root
675 )
676 desired = desired_num[0]
677 # *-config does not play revisions, strip them, keep just PV.
678 desired = portage.versions.pkgsplit("%s-%s" % (package, desired))[1]
679
680 if target.startswith("host"):
681 # *-config is the only tool treating host identically (by tuple).
682 target = toolchain.GetHostTuple()
683
684 # And finally, attach target to it.
685 desired = "%s-%s" % (target, desired)
686
687 extra_env = {"CHOST": target}
688 if root != "/":
689 extra_env["ROOT"] = root
690 cmd = ["%s-config" % package, "-c", target]
691 result = cros_build_lib.run(
692 cmd,
693 print_cmd=False,
694 stdout=True,
695 encoding="utf-8",
696 extra_env=extra_env,
697 )
698 current = result.stdout.splitlines()[0]
699
700 # Do not reconfig when the current is live or nothing needs to be done.
701 extra_env = {"ROOT": root} if root != "/" else None
702 if current != desired and current != "9999":
703 cmd = [package + "-config", desired]
704 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100705
706
Mike Frysinger35247af2012-11-16 18:58:06 -0500707def ExpandTargets(targets_wanted):
Alex Klein1699fab2022-09-08 08:46:06 -0600708 """Expand any possible toolchain aliases into full targets
Mike Frysinger35247af2012-11-16 18:58:06 -0500709
Alex Klein1699fab2022-09-08 08:46:06 -0600710 This will expand 'all' and 'sdk' into the respective toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500711
Alex Klein1699fab2022-09-08 08:46:06 -0600712 Args:
713 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500714
Alex Klein1699fab2022-09-08 08:46:06 -0600715 Returns:
716 Dictionary of concrete targets and their toolchain tuples.
717 """
718 targets_wanted = set(targets_wanted)
719 if targets_wanted == set(["boards"]):
720 # Only pull targets from the included boards.
721 return {}
Gilad Arnold8195b532015-04-07 10:56:30 +0300722
Alex Klein1699fab2022-09-08 08:46:06 -0600723 all_targets = toolchain.GetAllTargets()
724 if targets_wanted == set(["all"]):
725 return all_targets
726 if targets_wanted == set(["sdk"]):
727 # Filter out all the non-sdk toolchains as we don't want to mess
728 # with those in all of our builds.
729 return toolchain.FilterToolchains(all_targets, "sdk", True)
Gilad Arnold8195b532015-04-07 10:56:30 +0300730
Alex Klein1699fab2022-09-08 08:46:06 -0600731 # Verify user input.
732 nonexistent = targets_wanted.difference(all_targets)
733 if nonexistent:
734 raise ValueError("Invalid targets: %s" % (",".join(nonexistent),))
735 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500736
737
Alex Klein1699fab2022-09-08 08:46:06 -0600738def UpdateToolchains(
739 usepkg,
740 deleteold,
741 hostonly,
742 reconfig,
743 targets_wanted,
744 boards_wanted,
745 root="/",
746):
747 """Performs all steps to create a synchronized toolchain enviroment.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100748
Alex Klein1699fab2022-09-08 08:46:06 -0600749 Args:
750 usepkg: Use prebuilt packages
751 deleteold: Unmerge deprecated packages
752 hostonly: Only setup the host toolchain
753 reconfig: Reload crossdev config and reselect toolchains
754 targets_wanted: All the targets to update
755 boards_wanted: Load targets from these boards
756 root: The root in which to install the toolchains.
757 """
758 targets, crossdev_targets, reconfig_targets = {}, {}, {}
759 if not hostonly:
760 # For hostonly, we can skip most of the below logic, much of which won't
761 # work on bare systems where this is useful.
762 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400763
Alex Klein1699fab2022-09-08 08:46:06 -0600764 # Filter out toolchains that don't (yet) have a binpkg available.
765 if usepkg:
766 for target in list(targets.keys()):
767 if not targets[target]["have-binpkg"]:
768 del targets[target]
Mike Frysingerd246fb92021-10-26 16:08:39 -0400769
Alex Klein1699fab2022-09-08 08:46:06 -0600770 # Now re-add any targets that might be from this board. This is to
771 # allow unofficial boards to declare their own toolchains.
772 for board in boards_wanted:
773 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100774
Alex Klein1699fab2022-09-08 08:46:06 -0600775 # First check and initialize all cross targets that need to be.
776 for target in targets:
777 if TargetIsInitialized(target):
778 reconfig_targets[target] = targets[target]
779 else:
780 crossdev_targets[target] = targets[target]
781 if crossdev_targets:
782 logging.info("The following targets need to be re-initialized:")
783 logging.info("%s", crossdev_targets)
784 Crossdev.UpdateTargets(crossdev_targets, usepkg)
785 # Those that were not initialized may need a config update.
786 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100787
Alex Klein1699fab2022-09-08 08:46:06 -0600788 # If we're building a subset of toolchains for a board, we might not have
789 # all the tuples that the packages expect. We don't define the "full" set
790 # of tuples currently other than "whatever the full sdk has normally".
791 if usepkg or set(("all", "sdk")) & targets_wanted:
792 # Since we have cross-compilers now, we can update these packages.
793 targets["host-post-cross"] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400794
Alex Klein1699fab2022-09-08 08:46:06 -0600795 # We want host updated.
796 targets["host"] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100797
Alex Klein1699fab2022-09-08 08:46:06 -0600798 # Now update all packages.
799 if (
800 UpdateTargets(targets, usepkg, root=root)
801 or crossdev_targets
802 or reconfig
803 ):
804 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800805
Alex Klein1699fab2022-09-08 08:46:06 -0600806 if deleteold:
807 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100808
Alex Klein1699fab2022-09-08 08:46:06 -0600809 # Now that we've cleared out old versions, see if we need to rebuild
810 # anything. Can't do this earlier as it might not be broken.
811 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500812
Zdenek Behan508dcce2011-12-05 15:39:32 +0100813
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700814def ShowConfig(name):
Alex Klein1699fab2022-09-08 08:46:06 -0600815 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500816
Alex Klein1699fab2022-09-08 08:46:06 -0600817 Args:
818 name: The board name to query.
819 """
820 toolchains = toolchain.GetToolchainsForBoard(name)
821 # Make sure we display the default toolchain first.
822 # Note: Do not use logging here as this is meant to be used by other tools.
823 print(
824 ",".join(
825 list(toolchain.FilterToolchains(toolchains, "default", True))
826 + list(toolchain.FilterToolchains(toolchains, "default", False))
827 )
828 )
Mike Frysinger35247af2012-11-16 18:58:06 -0500829
830
Mike Frysinger35247af2012-11-16 18:58:06 -0500831def GeneratePathWrapper(root, wrappath, path):
Alex Klein1699fab2022-09-08 08:46:06 -0600832 """Generate a shell script to execute another shell script
Mike Frysinger35247af2012-11-16 18:58:06 -0500833
Alex Klein1699fab2022-09-08 08:46:06 -0600834 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
835 argv[0] won't be pointing to the correct path, generate a shell script that
836 just executes another program with its full path.
Mike Frysinger35247af2012-11-16 18:58:06 -0500837
Alex Klein1699fab2022-09-08 08:46:06 -0600838 Args:
839 root: The root tree to generate scripts inside of
840 wrappath: The full path (inside |root|) to create the wrapper
841 path: The target program which this wrapper will execute
842 """
843 replacements = {
844 "path": path,
845 "relroot": os.path.relpath("/", os.path.dirname(wrappath)),
846 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900847
Alex Klein1699fab2022-09-08 08:46:06 -0600848 # Do not use exec here, because exec invokes script with absolute path in
849 # argv0. Keeping relativeness allows us to remove abs path from compile result
850 # and leads directory independent build cache sharing in some distributed
851 # build system.
852 wrapper = (
853 """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900854basedir=$(dirname "$0")
855"${basedir}/%(relroot)s%(path)s" "$@"
856exit "$?"
Alex Klein1699fab2022-09-08 08:46:06 -0600857"""
858 % replacements
859 )
860 root_wrapper = root + wrappath
861 if os.path.islink(root_wrapper):
862 os.unlink(root_wrapper)
863 else:
864 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
865 osutils.WriteFile(root_wrapper, wrapper)
866 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500867
868
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700869def FixClangXXWrapper(root, path):
Alex Klein1699fab2022-09-08 08:46:06 -0600870 """Fix wrapper shell scripts and symlinks for invoking clang++
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700871
Alex Klein1699fab2022-09-08 08:46:06 -0600872 In a typical installation, clang++ symlinks to clang, which symlinks to the
873 elf executable. The executable distinguishes between clang and clang++ based
874 on argv[0].
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700875
Alex Klein1699fab2022-09-08 08:46:06 -0600876 When invoked through the LdsoWrapper, argv[0] always contains the path to the
877 executable elf file, making clang/clang++ invocations indistinguishable.
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700878
Alex Klein1699fab2022-09-08 08:46:06 -0600879 This function detects if the elf executable being wrapped is clang-X.Y, and
880 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700881
Alex Klein1699fab2022-09-08 08:46:06 -0600882 The calling sequence now becomes:
883 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
884 the Ldsowrapper).
885 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
886 to the original clang-3.9 elf.
887 -) The difference this time is that inside the elf file execution, $0 is
888 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700889
Alex Klein1699fab2022-09-08 08:46:06 -0600890 Update: Starting since clang 7, the clang and clang++ are symlinks to
891 clang-7 binary, not clang-7.0. The pattern match is extended to handle
892 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
Manoj Guptaae268142018-04-27 23:28:36 -0700893
Alex Klein1699fab2022-09-08 08:46:06 -0600894 Args:
895 root: The root tree to generate scripts / symlinks inside of
896 path: The target elf for which LdsoWrapper was created
897 """
898 if re.match(r"/usr/bin/clang-\d+(\.\d+)*$", path):
899 logging.info("fixing clang++ invocation for %s", path)
900 clangdir = os.path.dirname(root + path)
901 clang = os.path.basename(path)
902 clangxx = clang.replace("clang", "clang++")
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700903
Alex Klein1699fab2022-09-08 08:46:06 -0600904 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
905 os.symlink(clang + ".elf", os.path.join(clangdir, clangxx + ".elf"))
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700906
Alex Klein1699fab2022-09-08 08:46:06 -0600907 # Create a hardlink clang++-X.Y pointing to clang-X.Y
908 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700909
Alex Klein1699fab2022-09-08 08:46:06 -0600910 # Adjust the clang++ symlink to point to clang++-X.Y
911 os.unlink(os.path.join(clangdir, "clang++"))
912 os.symlink(clangxx, os.path.join(clangdir, "clang++"))
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700913
914
Mike Frysinger35247af2012-11-16 18:58:06 -0500915def FileIsCrosSdkElf(elf):
Alex Klein1699fab2022-09-08 08:46:06 -0600916 """Determine if |elf| is an ELF that we execute in the cros_sdk
Mike Frysinger35247af2012-11-16 18:58:06 -0500917
Alex Klein1699fab2022-09-08 08:46:06 -0600918 We don't need this to be perfect, just quick. It makes sure the ELF
919 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
Mike Frysinger35247af2012-11-16 18:58:06 -0500920
Alex Klein1699fab2022-09-08 08:46:06 -0600921 Args:
922 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500923
Alex Klein1699fab2022-09-08 08:46:06 -0600924 Returns:
925 True if we think |elf| is a native ELF
926 """
927 with open(elf, "rb") as f:
928 data = f.read(20)
929 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
930 return (
931 data[0:4] == b"\x7fELF"
932 and data[4:5] == b"\x02"
933 and data[5:6] == b"\x01"
934 and data[18:19] == b"\x3e"
935 )
Mike Frysinger35247af2012-11-16 18:58:06 -0500936
937
938def IsPathPackagable(ptype, path):
Alex Klein1699fab2022-09-08 08:46:06 -0600939 """Should the specified file be included in a toolchain package?
Mike Frysinger35247af2012-11-16 18:58:06 -0500940
Alex Klein1699fab2022-09-08 08:46:06 -0600941 We only need to handle files as we'll create dirs as we need them.
Mike Frysinger35247af2012-11-16 18:58:06 -0500942
Alex Klein1699fab2022-09-08 08:46:06 -0600943 Further, trim files that won't be useful:
944 - non-english translations (.mo) since it'd require env vars
945 - debug files since these are for the host compiler itself
946 - info/man pages as they're big, and docs are online, and the
947 native docs should work fine for the most part (`man gcc`)
Mike Frysinger35247af2012-11-16 18:58:06 -0500948
Alex Klein1699fab2022-09-08 08:46:06 -0600949 Args:
950 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
951 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500952
Alex Klein1699fab2022-09-08 08:46:06 -0600953 Returns:
954 True if we want to include this path in the package
955 """
956 return not (
957 ptype in ("dir",)
958 or path.startswith("/usr/lib/debug/")
959 or os.path.splitext(path)[1] == ".mo"
960 or ("/man/" in path or "/info/" in path)
961 )
Mike Frysinger35247af2012-11-16 18:58:06 -0500962
963
964def ReadlinkRoot(path, root):
Alex Klein1699fab2022-09-08 08:46:06 -0600965 """Like os.readlink(), but relative to a |root|
Mike Frysinger35247af2012-11-16 18:58:06 -0500966
Alex Klein1699fab2022-09-08 08:46:06 -0600967 Args:
968 path: The symlink to read
969 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500970
Alex Klein1699fab2022-09-08 08:46:06 -0600971 Returns:
972 A fully resolved symlink path
973 """
974 while os.path.islink(root + path):
975 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
976 return path
Mike Frysinger35247af2012-11-16 18:58:06 -0500977
978
Alex Klein1699fab2022-09-08 08:46:06 -0600979def _GetFilesForTarget(target, root="/"):
980 """Locate all the files to package for |target|
Mike Frysinger35247af2012-11-16 18:58:06 -0500981
Alex Klein1699fab2022-09-08 08:46:06 -0600982 This does not cover ELF dependencies.
Mike Frysinger35247af2012-11-16 18:58:06 -0500983
Alex Klein1699fab2022-09-08 08:46:06 -0600984 Args:
985 target: The toolchain target name
986 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500987
Alex Klein1699fab2022-09-08 08:46:06 -0600988 Returns:
989 A tuple of a set of all packable paths, and a set of all paths which
990 are also native ELFs
991 """
992 paths = set()
993 elfs = set()
Mike Frysinger35247af2012-11-16 18:58:06 -0500994
Alex Klein1699fab2022-09-08 08:46:06 -0600995 # Find all the files owned by the packages for this target.
996 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500997
Alex Klein1699fab2022-09-08 08:46:06 -0600998 # Skip Go compiler from redistributable packages.
999 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
1000 # into it. Due to this, the toolchain cannot be unpacked anywhere
1001 # else and be readily useful. To enable packaging Go, we need to:
1002 # -) Tweak the wrappers/environment to override GOROOT
1003 # automatically based on the unpack location.
1004 # -) Make sure the ELF dependency checking and wrapping logic
1005 # below skips the Go toolchain executables and libraries.
1006 # -) Make sure the packaging process maintains the relative
1007 # timestamps of precompiled standard library packages.
1008 # (see dev-lang/go ebuild for details).
1009 if pkg == "ex_go":
1010 continue
Rahul Chaudhry4b803052015-05-13 15:25:56 -07001011
Alex Klein1699fab2022-09-08 08:46:06 -06001012 # Use armv7a-cros-linux-gnueabi/compiler-rt for
1013 # armv7a-cros-linux-gnueabihf/compiler-rt.
1014 # Currently the armv7a-cros-linux-gnueabi is actually
1015 # the same as armv7a-cros-linux-gnueabihf with different names.
1016 # Because of that, for compiler-rt, it generates the same binary in
1017 # the same location. To avoid the installation conflict, we do not
1018 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
1019 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
1020 # toolchain.
1021 if "compiler-rt" in pkg and "armv7a-cros-linux-gnueabi" in target:
1022 atom = GetPortagePackage(target, pkg)
1023 cat, pn = atom.split("/")
1024 ver = GetInstalledPackageVersions(atom, root=root)[0]
1025 dblink = portage.dblink(
1026 cat, pn + "-" + ver, myroot=root, settings=portage.settings
1027 )
1028 contents = dblink.getcontents()
1029 if not contents:
1030 if "hf" in target:
1031 new_target = "armv7a-cros-linux-gnueabi"
1032 else:
1033 new_target = "armv7a-cros-linux-gnueabihf"
1034 atom = GetPortagePackage(new_target, pkg)
Yunlian Jiang36f35242018-04-27 10:18:40 -07001035 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001036 atom = GetPortagePackage(target, pkg)
Yunlian Jiang36f35242018-04-27 10:18:40 -07001037
Alex Klein1699fab2022-09-08 08:46:06 -06001038 cat, pn = atom.split("/")
1039 ver = GetInstalledPackageVersions(atom, root=root)[0]
1040 logging.info("packaging %s-%s", atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -05001041
Alex Klein1699fab2022-09-08 08:46:06 -06001042 dblink = portage.dblink(
1043 cat, pn + "-" + ver, myroot=root, settings=portage.settings
1044 )
1045 contents = dblink.getcontents()
1046 for obj in contents:
1047 ptype = contents[obj][0]
1048 if not IsPathPackagable(ptype, obj):
1049 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001050
Alex Klein1699fab2022-09-08 08:46:06 -06001051 if ptype == "obj":
1052 # For native ELFs, we need to pull in their dependencies too.
1053 if FileIsCrosSdkElf(obj):
1054 logging.debug("Adding ELF %s", obj)
1055 elfs.add(obj)
1056 logging.debug("Adding path %s", obj)
1057 paths.add(obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001058
Alex Klein1699fab2022-09-08 08:46:06 -06001059 return paths, elfs
Mike Frysinger35247af2012-11-16 18:58:06 -05001060
1061
Alex Klein1699fab2022-09-08 08:46:06 -06001062def _BuildInitialPackageRoot(
1063 output_dir, paths, elfs, ldpaths, path_rewrite_func=lambda x: x, root="/"
1064):
1065 """Link in all packable files and their runtime dependencies
Mike Frysinger35247af2012-11-16 18:58:06 -05001066
Alex Klein1699fab2022-09-08 08:46:06 -06001067 This also wraps up executable ELFs with helper scripts.
Mike Frysinger35247af2012-11-16 18:58:06 -05001068
Alex Klein1699fab2022-09-08 08:46:06 -06001069 Args:
1070 output_dir: The output directory to store files
1071 paths: All the files to include
1072 elfs: All the files which are ELFs (a subset of |paths|)
1073 ldpaths: A dict of static ldpath information
1074 path_rewrite_func: User callback to rewrite paths in output_dir
1075 root: The root path to pull all packages/files from
1076 """
1077 # Link in all the files.
1078 sym_paths = {}
1079 for path in paths:
1080 new_path = path_rewrite_func(path)
1081 logging.debug("Transformed %s to %s", path, new_path)
1082 dst = output_dir + new_path
1083 osutils.SafeMakedirs(os.path.dirname(dst))
Mike Frysinger35247af2012-11-16 18:58:06 -05001084
Alex Klein1699fab2022-09-08 08:46:06 -06001085 # Is this a symlink which we have to rewrite or wrap?
1086 # Delay wrap check until after we have created all paths.
1087 src = root + path
1088 if os.path.islink(src):
1089 tgt = os.readlink(src)
1090 if os.path.sep in tgt:
1091 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001092
Alex Klein1699fab2022-09-08 08:46:06 -06001093 # Rewrite absolute links to relative and then generate the symlink
1094 # ourselves. All other symlinks can be hardlinked below.
1095 if tgt[0] == "/":
1096 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1097 os.symlink(tgt, dst)
1098 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001099
Alex Klein1699fab2022-09-08 08:46:06 -06001100 logging.debug("Linking path %s -> %s", src, dst)
1101 os.link(src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001102
Alex Klein1699fab2022-09-08 08:46:06 -06001103 # Locate all the dependencies for all the ELFs. Stick them all in the
1104 # top level "lib" dir to make the wrapper simpler. This exact path does
1105 # not matter since we execute ldso directly, and we tell the ldso the
1106 # exact path to search for its libraries.
1107 libdir = os.path.join(output_dir, "lib")
1108 osutils.SafeMakedirs(libdir)
1109 donelibs = set()
1110 basenamelibs = set()
Manoj Gupta98e674d2022-10-05 00:31:41 +00001111 glibc_re = re.compile(r"/lib(c|pthread)[0-9.-]*\.so[0-9.-]*")
Alex Klein1699fab2022-09-08 08:46:06 -06001112 for elf in elfs:
1113 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
1114 logging.debug("Parsed elf %s data: %s", elf, e)
1115 interp = e["interp"]
Mike Frysinger221bd822017-09-29 02:51:47 -04001116
Alex Klein1699fab2022-09-08 08:46:06 -06001117 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1118 if os.path.basename(elf).startswith("libopcodes-"):
1119 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001120
Alex Klein1699fab2022-09-08 08:46:06 -06001121 # Copy all the dependencies before we copy the program & generate wrappers.
1122 for lib, lib_data in e["libs"].items():
1123 src = path = lib_data["path"]
1124 if path is None:
1125 logging.warning("%s: could not locate %s", elf, lib)
1126 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001127
Alex Klein1699fab2022-09-08 08:46:06 -06001128 # No need to try and copy the same source lib multiple times.
1129 if path in donelibs:
1130 continue
1131 donelibs.add(path)
Mike Frysinger9fe02342019-12-12 17:52:53 -05001132
Alex Klein1699fab2022-09-08 08:46:06 -06001133 # Die if we try to normalize different source libs with the same basename.
1134 if lib in basenamelibs:
1135 logging.error(
1136 "Multiple sources detected for %s:\n new: %s\n old: %s",
1137 os.path.join("/lib", lib),
1138 path,
1139 " ".join(
1140 x
1141 for x in donelibs
1142 if x != path and os.path.basename(x) == lib
1143 ),
1144 )
1145 # TODO(crbug.com/917193): Make this fatal.
1146 # cros_build_lib.Die('Unable to resolve lib conflicts')
1147 continue
1148 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001149
Alex Klein1699fab2022-09-08 08:46:06 -06001150 # Needed libs are the SONAME, but that is usually a symlink, not a
1151 # real file. So link in the target rather than the symlink itself.
1152 # We have to walk all the possible symlinks (SONAME could point to a
1153 # symlink which points to a symlink), and we have to handle absolute
1154 # ourselves (since we have a "root" argument).
1155 dst = os.path.join(libdir, os.path.basename(path))
1156 src = ReadlinkRoot(src, root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001157
Alex Klein1699fab2022-09-08 08:46:06 -06001158 logging.debug("Linking lib %s -> %s", root + src, dst)
1159 os.link(root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001160
Alex Klein1699fab2022-09-08 08:46:06 -06001161 # Do not create wrapper for libc. crbug.com/766827
1162 if interp and not glibc_re.search(elf):
1163 # Generate a wrapper if it is executable.
1164 interp = os.path.join("/lib", os.path.basename(interp))
1165 lddtree.GenerateLdsoWrapper(
1166 output_dir,
1167 path_rewrite_func(elf),
1168 interp,
1169 libpaths=e["rpath"] + e["runpath"],
1170 )
1171 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger00b129f2021-04-21 18:11:48 -04001172
Alex Klein1699fab2022-09-08 08:46:06 -06001173 # Wrap any symlinks to the wrapper.
1174 if elf in sym_paths:
1175 link = sym_paths[elf]
1176 GeneratePathWrapper(output_dir, link, elf)
Mike Frysinger00b129f2021-04-21 18:11:48 -04001177
Mike Frysinger35247af2012-11-16 18:58:06 -05001178
1179def _EnvdGetVar(envd, var):
Alex Klein1699fab2022-09-08 08:46:06 -06001180 """Given a Gentoo env.d file, extract a var from it
Mike Frysinger35247af2012-11-16 18:58:06 -05001181
Alex Klein1699fab2022-09-08 08:46:06 -06001182 Args:
1183 envd: The env.d file to load (may be a glob path)
1184 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001185
Alex Klein1699fab2022-09-08 08:46:06 -06001186 Returns:
1187 The value of |var|
1188 """
1189 envds = glob.glob(envd)
1190 assert len(envds) == 1, "%s: should have exactly 1 env.d file" % envd
1191 envd = envds[0]
1192 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001193
1194
1195def _ProcessBinutilsConfig(target, output_dir):
Alex Klein1699fab2022-09-08 08:46:06 -06001196 """Do what binutils-config would have done"""
1197 binpath = os.path.join("/bin", target + "-")
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001198
Alex Klein1699fab2022-09-08 08:46:06 -06001199 # Locate the bin dir holding the linker and perform some confidence checks
1200 binutils_bin_path = os.path.join(
1201 output_dir, "usr", toolchain.GetHostTuple(), target, "binutils-bin"
1202 )
1203 globpath = os.path.join(binutils_bin_path, "*")
1204 srcpath = glob.glob(globpath)
1205 assert len(srcpath) == 1, (
1206 "%s: matched more than one path. Is Gold enabled?" % globpath
1207 )
1208 srcpath = srcpath[0]
1209 ld_path = os.path.join(srcpath, "ld")
1210 assert os.path.exists(ld_path), "%s: linker is missing!" % ld_path
1211 ld_path = os.path.join(srcpath, "ld.bfd")
1212 assert os.path.exists(ld_path), "%s: linker is missing!" % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001213
Alex Klein1699fab2022-09-08 08:46:06 -06001214 srcpath = srcpath[len(output_dir) :]
1215 gccpath = os.path.join("/usr", "libexec", "gcc")
1216 for prog in os.listdir(output_dir + srcpath):
1217 # Skip binaries already wrapped.
1218 if not prog.endswith(".real"):
1219 GeneratePathWrapper(
1220 output_dir, binpath + prog, os.path.join(srcpath, prog)
1221 )
1222 GeneratePathWrapper(
1223 output_dir,
1224 os.path.join(gccpath, prog),
1225 os.path.join(srcpath, prog),
1226 )
Mike Frysinger35247af2012-11-16 18:58:06 -05001227
Alex Klein1699fab2022-09-08 08:46:06 -06001228 libpath = os.path.join("/usr", toolchain.GetHostTuple(), target, "lib")
1229 envd = os.path.join(output_dir, "etc", "env.d", "binutils", "*")
1230 srcpath = _EnvdGetVar(envd, "LIBPATH")
1231 os.symlink(
1232 os.path.relpath(srcpath, os.path.dirname(libpath)), output_dir + libpath
1233 )
Mike Frysinger35247af2012-11-16 18:58:06 -05001234
1235
1236def _ProcessGccConfig(target, output_dir):
Alex Klein1699fab2022-09-08 08:46:06 -06001237 """Do what gcc-config would have done"""
1238 binpath = "/bin"
1239 envd = os.path.join(output_dir, "etc", "env.d", "gcc", "*")
1240 srcpath = _EnvdGetVar(envd, "GCC_PATH")
1241 for prog in os.listdir(output_dir + srcpath):
1242 # Skip binaries already wrapped.
1243 if (
1244 not prog.endswith(".real")
1245 and not prog.endswith(".elf")
1246 and prog.startswith(target)
1247 ):
1248 GeneratePathWrapper(
1249 output_dir,
1250 os.path.join(binpath, prog),
1251 os.path.join(srcpath, prog),
1252 )
1253 return srcpath
Mike Frysinger35247af2012-11-16 18:58:06 -05001254
1255
Frank Henigman179ec7c2015-02-06 03:01:09 -05001256def _ProcessSysrootWrappers(_target, output_dir, srcpath):
Alex Klein1699fab2022-09-08 08:46:06 -06001257 """Remove chroot-specific things from our sysroot wrappers"""
1258 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001259
Alex Klein1699fab2022-09-08 08:46:06 -06001260 # Use the version of the wrapper that does not use ccache.
1261 for sysroot_wrapper in glob.glob(
1262 os.path.join(output_dir + srcpath, "sysroot_wrapper*.ccache")
1263 ):
1264 # Can't update the wrapper in place to not affect the chroot,
1265 # but only the extracted toolchain.
1266 os.unlink(sysroot_wrapper)
1267 shutil.copy(sysroot_wrapper[:-6] + "noccache", sysroot_wrapper)
1268 shutil.copy(
1269 sysroot_wrapper[:-6] + "noccache.elf", sysroot_wrapper + ".elf"
1270 )
Mike Frysinger35247af2012-11-16 18:58:06 -05001271
1272
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001273def _ProcessClangWrappers(target, output_dir):
Alex Klein1699fab2022-09-08 08:46:06 -06001274 """Remove chroot-specific things from our sysroot wrappers"""
1275 clang_bin_path = "/usr/bin"
1276 # Disable ccache from clang wrappers.
1277 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1278 GeneratePathWrapper(
1279 output_dir, f"/bin/{target}-clang", f"/usr/bin/{target}-clang"
1280 )
1281 GeneratePathWrapper(
1282 output_dir, f"/bin/{target}-clang++", f"/usr/bin/{target}-clang++"
1283 )
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001284
1285
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001286def _CreateMainLibDir(target, output_dir):
Alex Klein1699fab2022-09-08 08:46:06 -06001287 """Create some lib dirs so that compiler can get the right Gcc paths"""
1288 osutils.SafeMakedirs(os.path.join(output_dir, "usr", target, "lib"))
1289 osutils.SafeMakedirs(os.path.join(output_dir, "usr", target, "usr/lib"))
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001290
1291
Manoj Guptadf8b3872022-01-13 11:57:36 -08001292def _CreateRemoteToolchainFile(output_dir):
Alex Klein1699fab2022-09-08 08:46:06 -06001293 """Create a remote_toolchain_inputs file for reclient/RBE"""
1294 # The inputs file lists all files/shared libraries needed to run clang.
1295 # All inputs are relative to location of clang binary and one input
1296 # location per line of file e.g.
1297 # clang-13.elf
1298 # clang++-13.elf
1299 # relative/path/to/clang/resource/directory
Manoj Guptadf8b3872022-01-13 11:57:36 -08001300
Alex Klein1699fab2022-09-08 08:46:06 -06001301 clang_path = os.path.join(output_dir, "usr/bin")
1302 # Add needed shared libraries and internal files e.g. allowlists.
1303 toolchain_inputs = ["../../lib"]
1304 clang_shared_dirs = glob.glob(
1305 os.path.join(output_dir, "usr/lib64/clang/*/share")
1306 )
1307 for clang_dir in clang_shared_dirs:
1308 toolchain_inputs.append(os.path.relpath(clang_dir, clang_path))
Manoj Guptadf8b3872022-01-13 11:57:36 -08001309
Alex Klein1699fab2022-09-08 08:46:06 -06001310 # Add actual clang binaries/wrappers.
1311 for clang_files in glob.glob(os.path.join(clang_path, "clang*-[0-9]*")):
1312 toolchain_inputs.append(os.path.basename(clang_files))
Manoj Guptadf8b3872022-01-13 11:57:36 -08001313
Alex Klein1699fab2022-09-08 08:46:06 -06001314 with open(os.path.join(clang_path, "remote_toolchain_inputs"), "w") as f:
1315 f.writelines("%s\n" % line for line in toolchain_inputs)
Manoj Guptadf8b3872022-01-13 11:57:36 -08001316
1317
Mike Frysinger35247af2012-11-16 18:58:06 -05001318def _ProcessDistroCleanups(target, output_dir):
Alex Klein1699fab2022-09-08 08:46:06 -06001319 """Clean up the tree and remove all distro-specific requirements
Mike Frysinger35247af2012-11-16 18:58:06 -05001320
Alex Klein1699fab2022-09-08 08:46:06 -06001321 Args:
1322 target: The toolchain target name
1323 output_dir: The output directory to clean up
Han Shen699ea192016-03-02 10:42:47 -08001324 """
Alex Klein1699fab2022-09-08 08:46:06 -06001325 _ProcessBinutilsConfig(target, output_dir)
1326 gcc_path = _ProcessGccConfig(target, output_dir)
1327 _ProcessSysrootWrappers(target, output_dir, gcc_path)
1328 _ProcessClangWrappers(target, output_dir)
1329 _CreateMainLibDir(target, output_dir)
1330 _CreateRemoteToolchainFile(output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001331
Alex Klein1699fab2022-09-08 08:46:06 -06001332 osutils.RmDir(os.path.join(output_dir, "etc"))
Mike Frysinger35247af2012-11-16 18:58:06 -05001333
1334
Alex Klein1699fab2022-09-08 08:46:06 -06001335def CreatePackagableRoot(target, output_dir, ldpaths, root="/"):
1336 """Setup a tree from the packages for the specified target
Mike Frysinger35247af2012-11-16 18:58:06 -05001337
Alex Klein1699fab2022-09-08 08:46:06 -06001338 This populates a path with all the files from toolchain packages so that
1339 a tarball can easily be generated from the result.
Mike Frysinger35247af2012-11-16 18:58:06 -05001340
Alex Klein1699fab2022-09-08 08:46:06 -06001341 Args:
1342 target: The target to create a packagable root from
1343 output_dir: The output directory to place all the files
1344 ldpaths: A dict of static ldpath information
1345 root: The root path to pull all packages/files from
1346 """
1347 # Find all the files owned by the packages for this target.
1348 paths, elfs = _GetFilesForTarget(target, root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001349
Alex Klein1699fab2022-09-08 08:46:06 -06001350 # Link in all the package's files, any ELF dependencies, and wrap any
1351 # executable ELFs with helper scripts.
1352 def MoveUsrBinToBin(path):
1353 """Move /usr/bin to /bin so people can just use that toplevel dir
Mike Frysinger35247af2012-11-16 18:58:06 -05001354
Alex Klein1699fab2022-09-08 08:46:06 -06001355 Note we do not apply this to clang or rust - there is correlation between
1356 clang's search path for libraries / inclusion and its installation path.
1357 """
1358 NO_MOVE_PATTERNS = ("clang", "rust", "cargo", "sysroot_wrapper")
1359 if path.startswith("/usr/bin/") and not any(
1360 x in path for x in NO_MOVE_PATTERNS
1361 ):
1362 return path[4:]
1363 return path
Mike Frysinger221bd822017-09-29 02:51:47 -04001364
Alex Klein1699fab2022-09-08 08:46:06 -06001365 _BuildInitialPackageRoot(
1366 output_dir,
1367 paths,
1368 elfs,
1369 ldpaths,
1370 path_rewrite_func=MoveUsrBinToBin,
1371 root=root,
1372 )
Mike Frysinger35247af2012-11-16 18:58:06 -05001373
Alex Klein1699fab2022-09-08 08:46:06 -06001374 # The packages, when part of the normal distro, have helper scripts
1375 # that setup paths and such. Since we are making this standalone, we
1376 # need to preprocess all that ourselves.
1377 _ProcessDistroCleanups(target, output_dir)
1378
1379
1380def CreatePackages(targets_wanted, output_dir, root="/"):
1381 """Create redistributable cross-compiler packages for the specified targets
1382
1383 This creates toolchain packages that should be usable in conjunction with
1384 a downloaded sysroot (created elsewhere).
1385
1386 Tarballs (one per target) will be created in $PWD.
1387
1388 Args:
1389 targets_wanted: The targets to package up.
1390 output_dir: The directory to put the packages in.
1391 root: The root path to pull all packages/files from.
1392 """
1393 logging.info("Writing tarballs to %s", output_dir)
1394 osutils.SafeMakedirs(output_dir)
1395 ldpaths = lddtree.LoadLdpaths(root)
1396 targets = ExpandTargets(targets_wanted)
1397
1398 with osutils.TempDir(prefix="create-packages") as tempdir:
1399 logging.debug("Using tempdir: %s", tempdir)
1400
1401 # We have to split the root generation from the compression stages. This is
1402 # because we hardlink in all the files (to avoid overhead of reading/writing
1403 # the copies multiple times). But tar gets angry if a file's hardlink count
1404 # changes from when it starts reading a file to when it finishes.
1405 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1406 for target in targets:
1407 output_target_dir = os.path.join(tempdir, target)
1408 queue.put([target, output_target_dir, ldpaths, root])
1409
1410 # Build the tarball.
1411 with parallel.BackgroundTaskRunner(
1412 cros_build_lib.CreateTarball
1413 ) as queue:
1414 for target in targets:
1415 tar_file = os.path.join(output_dir, target + ".tar.xz")
1416 queue.put([tar_file, os.path.join(tempdir, target)])
Mike Frysinger35247af2012-11-16 18:58:06 -05001417
1418
Mike Frysinger07534cf2017-09-12 17:40:21 -04001419def GetParser():
Alex Klein1699fab2022-09-08 08:46:06 -06001420 """Return a command line parser."""
1421 parser = commandline.ArgumentParser(description=__doc__)
1422 parser.add_argument(
1423 "-u",
1424 "--nousepkg",
1425 action="store_false",
1426 dest="usepkg",
1427 default=True,
1428 help="Do not use prebuilt packages",
1429 )
1430 parser.add_argument(
1431 "-d",
1432 "--deleteold",
1433 action="store_true",
1434 dest="deleteold",
1435 default=False,
1436 help="Unmerge deprecated packages",
1437 )
1438 parser.add_argument(
1439 "-t",
1440 "--targets",
1441 dest="targets",
1442 default="sdk",
1443 help="Comma separated list of tuples. Special keywords "
1444 "'host', 'sdk', 'boards', and 'all' are "
1445 "allowed. Defaults to 'sdk'.",
1446 )
1447 parser.add_argument(
1448 "--include-boards",
1449 default="",
1450 metavar="BOARDS",
1451 help="Comma separated list of boards whose toolchains we "
1452 "will always include. Default: none",
1453 )
1454 parser.add_argument(
1455 "--hostonly",
1456 dest="hostonly",
1457 default=False,
1458 action="store_true",
1459 help="Only setup the host toolchain. "
1460 "Useful for bootstrapping chroot",
1461 )
1462 parser.add_argument(
1463 "--show-board-cfg",
1464 "--show-cfg",
1465 dest="cfg_name",
1466 default=None,
1467 help="Board to list toolchains tuples for",
1468 )
1469 parser.add_argument(
1470 "--show-packages",
1471 default=None,
1472 help="List all packages the specified target uses",
1473 )
1474 parser.add_argument(
1475 "--create-packages",
1476 action="store_true",
1477 default=False,
1478 help="Build redistributable packages",
1479 )
1480 parser.add_argument(
1481 "--output-dir",
1482 default=os.getcwd(),
1483 type="path",
1484 help="Output directory",
1485 )
1486 parser.add_argument(
1487 "--reconfig",
1488 default=False,
1489 action="store_true",
1490 help="Reload crossdev config and reselect toolchains",
1491 )
1492 parser.add_argument(
1493 "--sysroot",
1494 type="path",
1495 help="The sysroot in which to install the toolchains",
1496 )
1497 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001498
Mike Frysinger07534cf2017-09-12 17:40:21 -04001499
1500def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -06001501 parser = GetParser()
1502 options = parser.parse_args(argv)
1503 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001504
Alex Klein1699fab2022-09-08 08:46:06 -06001505 # Figure out what we're supposed to do and reject conflicting options.
1506 conflicting_options = (
1507 options.cfg_name,
1508 options.show_packages,
1509 options.create_packages,
1510 )
1511 if sum(bool(x) for x in conflicting_options) > 1:
1512 parser.error(
1513 "conflicting options: create-packages & show-packages & "
1514 "show-board-cfg"
1515 )
Mike Frysinger984d0622012-06-01 16:08:44 -04001516
Alex Klein1699fab2022-09-08 08:46:06 -06001517 targets_wanted = set(options.targets.split(","))
1518 boards_wanted = (
1519 set(options.include_boards.split(","))
1520 if options.include_boards
1521 else set()
1522 )
Mike Frysinger35247af2012-11-16 18:58:06 -05001523
Alex Klein1699fab2022-09-08 08:46:06 -06001524 if options.cfg_name:
1525 ShowConfig(options.cfg_name)
1526 elif options.show_packages is not None:
1527 cros_build_lib.AssertInsideChroot()
1528 target = options.show_packages
1529 Crossdev.Load(False)
1530 for package in GetTargetPackages(target):
1531 print(GetPortagePackage(target, package))
1532 elif options.create_packages:
1533 cros_build_lib.AssertInsideChroot()
1534 Crossdev.Load(False)
1535 CreatePackages(targets_wanted, options.output_dir)
1536 else:
1537 cros_build_lib.AssertInsideChroot()
1538 # This has to be always run as root.
1539 if osutils.IsNonRootUser():
1540 cros_build_lib.Die("this script must be run as root")
Mike Frysinger35247af2012-11-16 18:58:06 -05001541
Alex Klein1699fab2022-09-08 08:46:06 -06001542 Crossdev.Load(options.reconfig)
1543 root = options.sysroot or "/"
1544 UpdateToolchains(
1545 options.usepkg,
1546 options.deleteold,
1547 options.hostonly,
1548 options.reconfig,
1549 targets_wanted,
1550 boards_wanted,
1551 root=root,
1552 )
1553 Crossdev.Save()
Mike Frysinger35247af2012-11-16 18:58:06 -05001554
Alex Klein1699fab2022-09-08 08:46:06 -06001555 return 0