blob: 712394aa442acfc1b6dc6db77c999c931f41dbca [file] [log] [blame]
Zdenek Behan508dcce2011-12-05 15:39:32 +01001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# 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():
28 # 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.
Mike Frysinger27e21b72018-07-12 14:20:21 -040031 # pylint: disable=import-error
Mike Frysinger31596002012-12-03 23:54:24 -050032 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010033
34
Matt Tennantf1e30972012-03-02 16:30:07 -080035EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010036PACKAGE_STABLE = '[stable]'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010037
Mike Frysinger4cd80b62022-05-04 20:39:01 -040038CHROMIUMOS_OVERLAY = os.path.join(
39 constants.CHROOT_SOURCE_ROOT, constants.CHROMIUMOS_OVERLAY_DIR)
40ECLASS_OVERLAY = os.path.join(
41 constants.CHROOT_SOURCE_ROOT, constants.ECLASS_OVERLAY_DIR)
42STABLE_OVERLAY = os.path.join(
43 constants.CHROOT_SOURCE_ROOT, constants.PORTAGE_STABLE_OVERLAY_DIR)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010044CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010045
46
Mike Frysinger66bfde52017-09-12 16:42:57 -040047# The exact list of host toolchain packages we care about. These are the
48# packages that bots/devs install only from binpkgs and rely on the SDK bot
49# (chromiumos-sdk) to validate+uprev.
50#
Mike Frysinger66bfde52017-09-12 16:42:57 -040051# We don't use crossdev to manage the host toolchain for us, especially since
52# we diverge significantly now (with llvm/clang/etc...), and we don't need or
53# want crossdev managing /etc/portage config files for the sdk
54HOST_PACKAGES = (
55 'dev-lang/go',
George Burgess IV95882172022-06-01 11:10:11 -070056 'dev-lang/rust-bootstrap',
57 'dev-lang/rust-host',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080058 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040059 'sys-devel/binutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040060 'sys-devel/gcc',
61 'sys-devel/llvm',
62 'sys-kernel/linux-headers',
63 'sys-libs/glibc',
64 'sys-libs/libcxx',
Manoj Guptade64cb22019-03-31 18:48:58 -070065 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040066)
67
Mike Frysinger785b0c32017-09-13 01:35:59 -040068# These packages are also installed into the host SDK. However, they require
69# the cross-compilers to be installed first (because they need them to actually
70# build), so we have to delay their installation.
71HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070072 'dev-lang/rust',
Mike Frysinger61a24392017-10-17 17:14:27 -040073 'virtual/target-sdk-post-cross',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010074 'dev-embedded/coreboot-sdk',
Dan Callaghan7cc58ff2022-02-16 08:38:13 +110075 'dev-embedded/hps-sdk',
George Burgess IV288ecc82021-04-21 07:52:45 -070076 'dev-embedded/ti50-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040077)
78
79# New packages that we're in the process of adding to the SDK. Since the SDK
80# bot hasn't had a chance to run yet, there are no binary packages available,
81# so we have to list them here and wait. Once it completes, entries here can
82# be removed so they'll end up on bots & dev's systems.
George Burgess IV66e199c2021-05-05 15:38:40 -070083NEW_PACKAGES = ()
Mike Frysinger785b0c32017-09-13 01:35:59 -040084
Rahul Chaudhry4b803052015-05-13 15:25:56 -070085# Enable the Go compiler for these targets.
86TARGET_GO_ENABLED = (
87 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070088 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070089 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070090 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070091)
92CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
93
Adrian Ratiubf0b9af2022-05-02 14:48:15 +030094CROSSDEV_LIBXCRYPT_ARGS = ['--ex-pkg', 'sys-libs/libxcrypt']
95
Manoj Gupta1b5642e2017-03-08 16:44:12 -080096# Enable llvm's compiler-rt for these targets.
97TARGET_COMPILER_RT_ENABLED = (
98 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070099 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700100 'aarch64-cros-linux-gnu',
Manoj Guptabf1b6422021-11-08 09:50:20 -0800101 'arm-none-eabi',
Manoj Gupta21f3a082018-03-06 21:25:39 -0800102 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800103)
104CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
105
Manoj Gupta946abb42017-04-12 14:27:19 -0700106TARGET_LLVM_PKGS_ENABLED = (
Manoj Gupta847b6092022-06-01 10:26:34 -0700107 'armv7m-cros-eabi',
Manoj Gupta946abb42017-04-12 14:27:19 -0700108 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700109 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700110 'aarch64-cros-linux-gnu',
Mike Frysingerd96d5442021-11-09 05:12:34 -0500111 'i686-cros-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700112 'x86_64-cros-linux-gnu',
113)
114
115LLVM_PKGS_TABLE = {
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700116 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700117 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700118}
119
David James66a09c42012-11-05 13:31:38 -0800120class Crossdev(object):
121 """Class for interacting with crossdev and caching its output."""
122
123 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
124 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800125 # Packages that needs separate handling, in addition to what we have from
126 # crossdev.
127 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700128 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800129 'llvm': 'sys-devel',
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700130 'llvm-libunwind': 'sys-libs',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700131 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700132 'elfutils': 'dev-libs',
Han Shene23782f2016-02-18 12:20:00 -0800133 }
David James66a09c42012-11-05 13:31:38 -0800134
135 @classmethod
136 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400137 """Load crossdev cache from disk.
138
139 We invalidate the cache when crossdev updates or this script changes.
140 """
David James90239b92012-11-05 15:31:34 -0800141 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400142 # If we run the compiled/cached .pyc file, we'll read/hash that when we
143 # really always want to track the source .py file.
144 script = os.path.abspath(__file__)
145 if script.endswith('.pyc'):
146 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500147 setup_toolchains_hash = hashlib.md5(
148 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400149
150 cls._CACHE = {
151 'crossdev_version': crossdev_version,
152 'setup_toolchains_hash': setup_toolchains_hash,
153 }
154
155 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
156 if reconfig:
157 logging.debug('cache: forcing regen due to reconfig')
158 return
159
160 try:
161 file_data = osutils.ReadFile(cls._CACHE_FILE)
162 except IOError as e:
163 if e.errno != errno.ENOENT:
164 logging.warning('cache: reading failed: %s', e)
165 osutils.SafeUnlink(cls._CACHE_FILE)
166 return
167
168 try:
169 data = json.loads(file_data)
170 except ValueError as e:
171 logging.warning('cache: ignoring invalid content: %s', e)
172 return
173
174 if crossdev_version != data.get('crossdev_version'):
175 logging.debug('cache: rebuilding after crossdev upgrade')
176 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
177 logging.debug('cache: rebuilding after cros_setup_toolchains change')
178 else:
179 logging.debug('cache: content is up-to-date!')
180 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800181
182 @classmethod
183 def Save(cls):
184 """Store crossdev cache on disk."""
185 # Save the cache from the successful run.
186 with open(cls._CACHE_FILE, 'w') as f:
187 json.dump(cls._CACHE, f)
188
189 @classmethod
190 def GetConfig(cls, target):
191 """Returns a map of crossdev provided variables about a tuple."""
192 CACHE_ATTR = '_target_tuple_map'
193
194 val = cls._CACHE.setdefault(CACHE_ATTR, {})
195 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400196 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400197 conf = {
198 'crosspkgs': [],
199 'target': toolchain.GetHostTuple(),
200 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400201 if target == 'host':
202 packages_list = HOST_PACKAGES
203 else:
204 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400205 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400206 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400207 else:
208 # Build the crossdev command.
209 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
Adrian Ratiubf0b9af2022-05-02 14:48:15 +0300210 # Enable libxcrypt for all linux-gnu targets.
211 if 'cros-linux-gnu' in target:
212 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400213 if target in TARGET_COMPILER_RT_ENABLED:
214 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400215 if target in TARGET_LLVM_PKGS_ENABLED:
216 for pkg in LLVM_PKGS_TABLE:
217 cmd.extend(LLVM_PKGS_TABLE[pkg])
Manoj Gupta78607282021-05-24 16:58:13 +0000218 if target in TARGET_GO_ENABLED:
219 cmd.extend(CROSSDEV_GO_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400220 cmd.extend(['-t', target])
221 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400222 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500223 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500224 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400225 # List of tuples split at the first '=', converted into dict.
226 conf = dict((k, cros_build_lib.ShellUnquote(v))
227 for k, v in (x.split('=', 1) for x in out))
228 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800229
Mike Frysinger66bfde52017-09-12 16:42:57 -0400230 manual_pkgs = cls.MANUAL_PKGS
231
232 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400233 conf[pkg + '_pn'] = pkg
234 conf[pkg + '_category'] = cat
235 if pkg not in conf['crosspkgs']:
236 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800237
238 val[target] = conf
239
David James66a09c42012-11-05 13:31:38 -0800240 return val[target]
241
242 @classmethod
243 def UpdateTargets(cls, targets, usepkg, config_only=False):
244 """Calls crossdev to initialize a cross target.
245
246 Args:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700247 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700248 usepkg: Copies the commandline opts.
249 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800250 """
251 configured_targets = cls._CACHE.setdefault('configured_targets', [])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700252 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800253
Manoj Gupta4d016f62021-10-19 16:39:34 -0700254 # Schedule all of the targets in parallel, and let them run.
255 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
256 for target_name in targets:
257 # We already started this target in this loop.
258 if target_name in started_targets:
259 continue
260 # The target is already configured.
261 if config_only and target_name in configured_targets:
262 continue
263 queue.put([target_name, targets[target_name], usepkg, config_only])
264 started_targets.add(target_name)
265
266 @classmethod
267 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
268 """Calls crossdev to initialize a cross target.
269
270 Args:
271 target_name: The name of the target to initialize.
272 target: The target info for initializing.
273 usepkg: Copies the commandline opts.
274 config_only: Just update.
275 """
276 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800277 cmdbase = ['crossdev', '--show-fail-log']
278 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
279 # Pick stable by default, and override as necessary.
280 cmdbase.extend(['-P', '--oneshot'])
281 if usepkg:
282 cmdbase.extend(['-P', '--getbinpkg',
283 '-P', '--usepkgonly',
284 '--without-headers'])
285
Christopher Wileyb22c0712015-06-02 10:37:03 -0700286 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800287 cmdbase.extend(['--overlays', overlays])
288 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
289
Manoj Gupta4d016f62021-10-19 16:39:34 -0700290 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800291
Manoj Gupta4d016f62021-10-19 16:39:34 -0700292 for pkg in GetTargetPackages(target_name):
293 if pkg == 'gdb':
294 # Gdb does not have selectable versions.
295 cmd.append('--ex-gdb')
Adrian Ratiubf0b9af2022-05-02 14:48:15 +0300296 elif pkg == 'ex_libxcrypt':
297 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
Manoj Gupta4d016f62021-10-19 16:39:34 -0700298 elif pkg == 'ex_compiler-rt':
299 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
300 elif pkg == 'ex_go':
301 # Go does not have selectable versions.
302 cmd.extend(CROSSDEV_GO_ARGS)
303 elif pkg in LLVM_PKGS_TABLE:
304 cmd.extend(LLVM_PKGS_TABLE[pkg])
305 elif pkg in cls.MANUAL_PKGS:
306 pass
David James66a09c42012-11-05 13:31:38 -0800307 else:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700308 # The first of the desired versions is the "primary" one.
309 version = GetDesiredPackageVersions(target_name, pkg)[0]
310 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800311
Manoj Gupta4d016f62021-10-19 16:39:34 -0700312 cmd.extend(target['crossdev'].split())
313
314 if config_only:
315 # In this case we want to just quietly reinit
316 cmd.append('--init-target')
317 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
318 else:
319 cros_build_lib.run(cmd)
320
321 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800322
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100323
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100324def GetTargetPackages(target):
325 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800326 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100327 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800328 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100329
330
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100331# Portage helper functions:
332def GetPortagePackage(target, package):
333 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800334 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100335 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400336 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100337 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100338 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100339 category = conf['category']
340 # Portage package:
341 pn = conf[package + '_pn']
342 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500343 assert category
344 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100345 return '%s/%s' % (category, pn)
346
347
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700348def PortageTrees(root):
349 """Return the portage trees for a given root."""
350 if root == '/':
351 return portage.db['/']
352 # The portage logic requires the path always end in a slash.
353 root = root.rstrip('/') + '/'
354 return portage.create_trees(target_root=root, config_root=root)[root]
355
356
357def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100358 """Extracts the list of current versions of a target, package pair.
359
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500360 Args:
361 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700362 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100363
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500364 Returns:
365 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100366 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100367 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700368 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369 version = portage.versions.cpv_getversion(pkg)
370 versions.append(version)
371 return versions
372
373
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700374def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100375 """Extracts the current stable version for a given package.
376
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500377 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500378 atom: The target/package to operate on e.g. i686-cros-linux-gnu/gcc
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500379 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700380 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100381
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500382 Returns:
383 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100384 """
David James90239b92012-11-05 15:31:34 -0800385 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700386 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800387 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100388
389
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700390def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100391 """Resolves keywords in a given version list for a particular package.
392
393 Resolving means replacing PACKAGE_STABLE with the actual number.
394
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500395 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500396 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500397 package: The target/package to operate on (e.g. gcc)
398 versions: List of versions to resolve
399 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700400 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100401
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500402 Returns:
403 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100404 """
405 resolved = []
David James90239b92012-11-05 15:31:34 -0800406 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700407 if not installed:
408 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100409 for version in versions:
410 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700411 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400412 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100413 resolved.append(version)
414 return resolved
415
416
417def GetDesiredPackageVersions(target, package):
418 """Produces the list of desired versions for each target, package pair.
419
420 The first version in the list is implicitly treated as primary, ie.
421 the version that will be initialized by crossdev and selected.
422
423 If the version is PACKAGE_STABLE, it really means the current version which
424 is emerged by using the package atom with no particular version key.
425 Since crossdev unmasks all packages by default, this will actually
426 mean 'unstable' in most cases.
427
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500428 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500429 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500430 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100431
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500432 Returns:
433 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100434 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400435 if package in GetTargetPackages(target):
436 return [PACKAGE_STABLE]
437 else:
438 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439
440
441def TargetIsInitialized(target):
442 """Verifies if the given list of targets has been correctly initialized.
443
444 This determines whether we have to call crossdev while emerging
445 toolchain packages or can do it using emerge. Emerge is naturally
446 preferred, because all packages can be updated in a single pass.
447
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500448 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500449 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100450
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500451 Returns:
452 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100453 """
454 # Check if packages for the given target all have a proper version.
455 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100456 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800457 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100458 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400459 if not (GetStablePackageVersion(atom, True) and
460 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100461 return False
462 return True
463 except cros_build_lib.RunCommandError:
464 # Fails - The target has likely never been initialized before.
465 return False
466
467
468def RemovePackageMask(target):
469 """Removes a package.mask file for the given platform.
470
471 The pre-existing package.mask files can mess with the keywords.
472
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500473 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500474 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100475 """
476 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700477 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100478
479
Zdenek Behan508dcce2011-12-05 15:39:32 +0100480# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700481def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500482 """Rebuild libtool as needed
483
484 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
485 gcc, libtool will break. We can't use binary packages either as those will
486 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700487
488 Args:
489 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500490 """
491 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700492 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500493 for line in f:
494 # Look for a line like:
495 # sys_lib_search_path_spec="..."
496 # It'll be a list of paths and gcc will be one of them.
497 if line.startswith('sys_lib_search_path_spec='):
498 line = line.rstrip()
499 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400500 root_path = os.path.join(root, path.lstrip(os.path.sep))
501 logging.debug('Libtool: checking %s', root_path)
502 if not os.path.exists(root_path):
503 logging.info('Rebuilding libtool after gcc upgrade')
504 logging.info(' %s', line)
505 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500506 needs_update = True
507 break
508
509 if needs_update:
510 break
511
512 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700513 cmd = [EMERGE_CMD, '--oneshot']
514 if root != '/':
515 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
516 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400517 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400518 else:
519 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500520
521
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700522def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100523 """Determines which packages need update/unmerge and defers to portage.
524
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500525 Args:
526 targets: The list of targets to update
527 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700528 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100530 # For each target, we do two things. Figure out the list of updates,
531 # and figure out the appropriate keywords/masks. Crossdev will initialize
532 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400533 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800534 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000535 # Used to keep track of post-cross packages. These are allowed to have
536 # implicit dependencies on toolchain packages, and therefore need to
537 # be built last.
538 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000540 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400541 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100542 # Record the highest needed version for each target, for masking purposes.
543 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100544 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100545 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400546 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100547 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700548 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100549 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200550 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400551 if pkg in NEW_PACKAGES and usepkg:
552 # Skip this binary package (for now).
553 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100554 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400555 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000556 if is_post_cross_target:
557 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400559 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100560 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400561 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800562 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100563
Mike Frysinger3bba5032016-09-20 14:15:04 -0400564 logging.info('Updating packages:')
565 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100566
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100567 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100568 if usepkg:
569 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700570 if root != '/':
571 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100572
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000573 if usepkg:
574 # Since we are not building from source, we can handle
575 # all packages in one go.
576 cmd.extend(packages)
577 cros_build_lib.run(cmd)
578 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700579 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
580 if pre_cross_items:
581 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000582 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700583 if post_cross_items:
584 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800585 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100586
587
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700588def CleanTargets(targets, root='/'):
589 """Unmerges old packages that are assumed unnecessary.
590
591 Args:
592 targets: The list of targets to clean up.
593 root: The install root in which we want packages cleaned up.
594 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595 unmergemap = {}
596 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400597 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100598 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400599 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100600 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700601 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100602 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700603 # NOTE: This refers to installed packages (vartree) rather than the
604 # Portage version (porttree and/or bintree) when determining the current
605 # version. While this isn't the most accurate thing to do, it is probably
606 # a good simple compromise, which should have the desired result of
607 # uninstalling everything but the latest installed version. In
608 # particular, using the bintree (--usebinpkg) requires a non-trivial
609 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200610 desired_num = VersionListToNumeric(target, package, desired, True)
611 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400612 logging.warning('Error detecting stable version for %s, '
613 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200614 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100615 unmergemap[pkg] = set(current).difference(desired_num)
616
617 # Cleaning doesn't care about consistency and rebuilding package.* files.
618 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400619 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100620 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
621
622 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400623 logging.info('Cleaning packages:')
624 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100625 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700626 if root != '/':
627 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100628 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400629 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100630 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400631 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100632
633
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200634def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100635 """Runs gcc-config and binutils-config to select the desired.
636
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500637 Args:
638 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700639 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100640 """
641 for package in ['gcc', 'binutils']:
642 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400643 # See if this package is part of this target.
644 if package not in GetTargetPackages(target):
645 logging.debug('%s: %s is not used', target, package)
646 continue
647
Zdenek Behan508dcce2011-12-05 15:39:32 +0100648 # Pick the first version in the numbered list as the selected one.
649 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700650 desired_num = VersionListToNumeric(target, package, desired, True,
651 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100652 desired = desired_num[0]
653 # *-config does not play revisions, strip them, keep just PV.
654 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
655
Mike Frysinger785b0c32017-09-13 01:35:59 -0400656 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100657 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800658 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100659
660 # And finally, attach target to it.
661 desired = '%s-%s' % (target, desired)
662
David James7ec5efc2012-11-06 09:39:49 -0800663 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700664 if root != '/':
665 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800666 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400667 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500668 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500669 extra_env=extra_env)
Mike Frysinger876a8e52022-06-23 18:07:30 -0400670 current = result.stdout.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700671
672 # Do not reconfig when the current is live or nothing needs to be done.
673 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100674 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500675 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400676 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100677
678
Mike Frysinger35247af2012-11-16 18:58:06 -0500679def ExpandTargets(targets_wanted):
680 """Expand any possible toolchain aliases into full targets
681
682 This will expand 'all' and 'sdk' into the respective toolchain tuples.
683
684 Args:
685 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500686
Mike Frysinger35247af2012-11-16 18:58:06 -0500687 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300688 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500689 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500690 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700691 if targets_wanted == set(['boards']):
692 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300693 return {}
694
695 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500696 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300697 return all_targets
698 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500699 # Filter out all the non-sdk toolchains as we don't want to mess
700 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300701 return toolchain.FilterToolchains(all_targets, 'sdk', True)
702
703 # Verify user input.
704 nonexistent = targets_wanted.difference(all_targets)
705 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400706 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300707 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500708
709
David Jamesf8c672f2012-11-06 13:38:11 -0800710def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700711 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100712 """Performs all steps to create a synchronized toolchain enviroment.
713
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500714 Args:
715 usepkg: Use prebuilt packages
716 deleteold: Unmerge deprecated packages
717 hostonly: Only setup the host toolchain
718 reconfig: Reload crossdev config and reselect toolchains
719 targets_wanted: All the targets to update
720 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700721 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100722 """
David Jamesf8c672f2012-11-06 13:38:11 -0800723 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100724 if not hostonly:
725 # For hostonly, we can skip most of the below logic, much of which won't
726 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500727 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400728
Mike Frysingerd246fb92021-10-26 16:08:39 -0400729 # Filter out toolchains that don't (yet) have a binpkg available.
730 if usepkg:
731 for target in list(targets.keys()):
732 if not targets[target]['have-binpkg']:
733 del targets[target]
734
Don Garrettc0c74002015-10-09 12:58:19 -0700735 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300736 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400737 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800738 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100739
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100740 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400741 for target in targets:
742 if TargetIsInitialized(target):
743 reconfig_targets[target] = targets[target]
744 else:
745 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100746 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400747 logging.info('The following targets need to be re-initialized:')
748 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800749 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200750 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800751 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100752
Mike Frysinger66814c32017-10-09 18:11:46 -0400753 # If we're building a subset of toolchains for a board, we might not have
754 # all the tuples that the packages expect. We don't define the "full" set
755 # of tuples currently other than "whatever the full sdk has normally".
756 if usepkg or set(('all', 'sdk')) & targets_wanted:
757 # Since we have cross-compilers now, we can update these packages.
758 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400759
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100760 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400761 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100762
763 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700764 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200765 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800766
767 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700768 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100769
Mike Frysingerc880a962013-11-08 13:59:06 -0500770 # Now that we've cleared out old versions, see if we need to rebuild
771 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700772 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500773
Zdenek Behan508dcce2011-12-05 15:39:32 +0100774
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700775def ShowConfig(name):
776 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500777
778 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700779 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500780 """
Don Garrettc0c74002015-10-09 12:58:19 -0700781 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500782 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400783 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400784 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400785 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
786 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500787
788
Mike Frysinger35247af2012-11-16 18:58:06 -0500789def GeneratePathWrapper(root, wrappath, path):
790 """Generate a shell script to execute another shell script
791
792 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
793 argv[0] won't be pointing to the correct path, generate a shell script that
794 just executes another program with its full path.
795
796 Args:
797 root: The root tree to generate scripts inside of
798 wrappath: The full path (inside |root|) to create the wrapper
799 path: The target program which this wrapper will execute
800 """
801 replacements = {
802 'path': path,
803 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
804 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900805
806 # Do not use exec here, because exec invokes script with absolute path in
807 # argv0. Keeping relativeness allows us to remove abs path from compile result
808 # and leads directory independent build cache sharing in some distributed
809 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500810 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900811basedir=$(dirname "$0")
812"${basedir}/%(relroot)s%(path)s" "$@"
813exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500814""" % replacements
815 root_wrapper = root + wrappath
816 if os.path.islink(root_wrapper):
817 os.unlink(root_wrapper)
818 else:
819 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
820 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400821 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500822
823
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700824def FixClangXXWrapper(root, path):
825 """Fix wrapper shell scripts and symlinks for invoking clang++
826
827 In a typical installation, clang++ symlinks to clang, which symlinks to the
828 elf executable. The executable distinguishes between clang and clang++ based
829 on argv[0].
830
831 When invoked through the LdsoWrapper, argv[0] always contains the path to the
832 executable elf file, making clang/clang++ invocations indistinguishable.
833
834 This function detects if the elf executable being wrapped is clang-X.Y, and
835 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
836
837 The calling sequence now becomes:
838 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
839 the Ldsowrapper).
840 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
841 to the original clang-3.9 elf.
842 -) The difference this time is that inside the elf file execution, $0 is
843 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
844
Manoj Guptaae268142018-04-27 23:28:36 -0700845 Update: Starting since clang 7, the clang and clang++ are symlinks to
846 clang-7 binary, not clang-7.0. The pattern match is extended to handle
847 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
848
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700849 Args:
850 root: The root tree to generate scripts / symlinks inside of
851 path: The target elf for which LdsoWrapper was created
852 """
Manoj Guptaae268142018-04-27 23:28:36 -0700853 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700854 logging.info('fixing clang++ invocation for %s', path)
855 clangdir = os.path.dirname(root + path)
856 clang = os.path.basename(path)
857 clangxx = clang.replace('clang', 'clang++')
858
859 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
860 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
861
862 # Create a hardlink clang++-X.Y pointing to clang-X.Y
863 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
864
865 # Adjust the clang++ symlink to point to clang++-X.Y
866 os.unlink(os.path.join(clangdir, 'clang++'))
867 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
868
869
Mike Frysinger35247af2012-11-16 18:58:06 -0500870def FileIsCrosSdkElf(elf):
871 """Determine if |elf| is an ELF that we execute in the cros_sdk
872
873 We don't need this to be perfect, just quick. It makes sure the ELF
874 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
875
876 Args:
877 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500878
Mike Frysinger35247af2012-11-16 18:58:06 -0500879 Returns:
880 True if we think |elf| is a native ELF
881 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500882 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500883 data = f.read(20)
884 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500885 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500886 data[4:5] == b'\x02' and
887 data[5:6] == b'\x01' and
888 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500889
890
891def IsPathPackagable(ptype, path):
892 """Should the specified file be included in a toolchain package?
893
894 We only need to handle files as we'll create dirs as we need them.
895
896 Further, trim files that won't be useful:
897 - non-english translations (.mo) since it'd require env vars
898 - debug files since these are for the host compiler itself
899 - info/man pages as they're big, and docs are online, and the
900 native docs should work fine for the most part (`man gcc`)
901
902 Args:
903 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
904 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500905
Mike Frysinger35247af2012-11-16 18:58:06 -0500906 Returns:
907 True if we want to include this path in the package
908 """
909 return not (ptype in ('dir',) or
910 path.startswith('/usr/lib/debug/') or
911 os.path.splitext(path)[1] == '.mo' or
912 ('/man/' in path or '/info/' in path))
913
914
915def ReadlinkRoot(path, root):
916 """Like os.readlink(), but relative to a |root|
917
918 Args:
919 path: The symlink to read
920 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500921
Mike Frysinger35247af2012-11-16 18:58:06 -0500922 Returns:
923 A fully resolved symlink path
924 """
925 while os.path.islink(root + path):
926 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
927 return path
928
929
930def _GetFilesForTarget(target, root='/'):
931 """Locate all the files to package for |target|
932
933 This does not cover ELF dependencies.
934
935 Args:
936 target: The toolchain target name
937 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500938
Mike Frysinger35247af2012-11-16 18:58:06 -0500939 Returns:
940 A tuple of a set of all packable paths, and a set of all paths which
941 are also native ELFs
942 """
943 paths = set()
944 elfs = set()
945
946 # Find all the files owned by the packages for this target.
947 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500948
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700949 # Skip Go compiler from redistributable packages.
950 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
951 # into it. Due to this, the toolchain cannot be unpacked anywhere
952 # else and be readily useful. To enable packaging Go, we need to:
953 # -) Tweak the wrappers/environment to override GOROOT
954 # automatically based on the unpack location.
955 # -) Make sure the ELF dependency checking and wrapping logic
956 # below skips the Go toolchain executables and libraries.
957 # -) Make sure the packaging process maintains the relative
958 # timestamps of precompiled standard library packages.
959 # (see dev-lang/go ebuild for details).
960 if pkg == 'ex_go':
961 continue
962
Yunlian Jiang36f35242018-04-27 10:18:40 -0700963 # Use armv7a-cros-linux-gnueabi/compiler-rt for
964 # armv7a-cros-linux-gnueabihf/compiler-rt.
965 # Currently the armv7a-cros-linux-gnueabi is actually
966 # the same as armv7a-cros-linux-gnueabihf with different names.
967 # Because of that, for compiler-rt, it generates the same binary in
968 # the same location. To avoid the installation conflict, we do not
969 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
970 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
971 # toolchain.
972 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
973 atom = GetPortagePackage(target, pkg)
974 cat, pn = atom.split('/')
975 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700976 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
977 settings=portage.settings)
978 contents = dblink.getcontents()
979 if not contents:
980 if 'hf' in target:
981 new_target = 'armv7a-cros-linux-gnueabi'
982 else:
983 new_target = 'armv7a-cros-linux-gnueabihf'
984 atom = GetPortagePackage(new_target, pkg)
985 else:
986 atom = GetPortagePackage(target, pkg)
987
Mike Frysinger35247af2012-11-16 18:58:06 -0500988 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700989 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700990 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500991
Mike Frysinger35247af2012-11-16 18:58:06 -0500992 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
993 settings=portage.settings)
994 contents = dblink.getcontents()
995 for obj in contents:
996 ptype = contents[obj][0]
997 if not IsPathPackagable(ptype, obj):
998 continue
999
1000 if ptype == 'obj':
1001 # For native ELFs, we need to pull in their dependencies too.
1002 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001003 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001004 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001005 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001006 paths.add(obj)
1007
1008 return paths, elfs
1009
1010
1011def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -05001012 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -05001013 """Link in all packable files and their runtime dependencies
1014
1015 This also wraps up executable ELFs with helper scripts.
1016
1017 Args:
1018 output_dir: The output directory to store files
1019 paths: All the files to include
1020 elfs: All the files which are ELFs (a subset of |paths|)
1021 ldpaths: A dict of static ldpath information
1022 path_rewrite_func: User callback to rewrite paths in output_dir
1023 root: The root path to pull all packages/files from
1024 """
1025 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001026 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001027 for path in paths:
1028 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001029 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001030 dst = output_dir + new_path
1031 osutils.SafeMakedirs(os.path.dirname(dst))
1032
1033 # Is this a symlink which we have to rewrite or wrap?
1034 # Delay wrap check until after we have created all paths.
1035 src = root + path
1036 if os.path.islink(src):
1037 tgt = os.readlink(src)
1038 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001039 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001040
1041 # Rewrite absolute links to relative and then generate the symlink
1042 # ourselves. All other symlinks can be hardlinked below.
1043 if tgt[0] == '/':
1044 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1045 os.symlink(tgt, dst)
1046 continue
1047
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001048 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001049 os.link(src, dst)
1050
Mike Frysinger35247af2012-11-16 18:58:06 -05001051 # Locate all the dependencies for all the ELFs. Stick them all in the
1052 # top level "lib" dir to make the wrapper simpler. This exact path does
1053 # not matter since we execute ldso directly, and we tell the ldso the
1054 # exact path to search for its libraries.
1055 libdir = os.path.join(output_dir, 'lib')
1056 osutils.SafeMakedirs(libdir)
1057 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001058 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001059 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001060 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001061 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001062 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001063 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001064
Mike Frysinger9fe02342019-12-12 17:52:53 -05001065 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1066 if os.path.basename(elf).startswith('libopcodes-'):
1067 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001068
Mike Frysinger00b129f2021-04-21 18:11:48 -04001069 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001070 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001071 src = path = lib_data['path']
1072 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001073 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001074 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001075
1076 # No need to try and copy the same source lib multiple times.
1077 if path in donelibs:
1078 continue
1079 donelibs.add(path)
1080
1081 # Die if we try to normalize different source libs with the same basename.
1082 if lib in basenamelibs:
1083 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1084 os.path.join('/lib', lib), path,
1085 ' '.join(x for x in donelibs
1086 if x != path and os.path.basename(x) == lib))
1087 # TODO(crbug.com/917193): Make this fatal.
1088 # cros_build_lib.Die('Unable to resolve lib conflicts')
1089 continue
1090 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001091
1092 # Needed libs are the SONAME, but that is usually a symlink, not a
1093 # real file. So link in the target rather than the symlink itself.
1094 # We have to walk all the possible symlinks (SONAME could point to a
1095 # symlink which points to a symlink), and we have to handle absolute
1096 # ourselves (since we have a "root" argument).
1097 dst = os.path.join(libdir, os.path.basename(path))
1098 src = ReadlinkRoot(src, root)
1099
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001100 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001101 os.link(root + src, dst)
1102
Mike Frysinger00b129f2021-04-21 18:11:48 -04001103 # Do not create wrapper for libc. crbug.com/766827
1104 if interp and not glibc_re.search(elf):
1105 # Generate a wrapper if it is executable.
1106 interp = os.path.join('/lib', os.path.basename(interp))
1107 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1108 libpaths=e['rpath'] + e['runpath'])
1109 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1110
1111 # Wrap any symlinks to the wrapper.
1112 if elf in sym_paths:
1113 link = sym_paths[elf]
1114 GeneratePathWrapper(output_dir, link, elf)
1115
Mike Frysinger35247af2012-11-16 18:58:06 -05001116
1117def _EnvdGetVar(envd, var):
1118 """Given a Gentoo env.d file, extract a var from it
1119
1120 Args:
1121 envd: The env.d file to load (may be a glob path)
1122 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001123
Mike Frysinger35247af2012-11-16 18:58:06 -05001124 Returns:
1125 The value of |var|
1126 """
1127 envds = glob.glob(envd)
1128 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1129 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001130 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001131
1132
1133def _ProcessBinutilsConfig(target, output_dir):
1134 """Do what binutils-config would have done"""
1135 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001136
Sloan Johnsonc59e9262022-06-10 20:51:53 +00001137 # Locate the bin dir holding the linker and perform some confidence checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001138 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1139 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001140 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001141 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001142 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1143 % globpath)
1144 srcpath = srcpath[0]
1145 ld_path = os.path.join(srcpath, 'ld')
1146 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1147 ld_path = os.path.join(srcpath, 'ld.bfd')
1148 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001149
Mike Frysinger78b7a812014-11-26 19:45:23 -05001150 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001151 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1152 for prog in os.listdir(output_dir + srcpath):
1153 # Skip binaries already wrapped.
1154 if not prog.endswith('.real'):
1155 GeneratePathWrapper(output_dir, binpath + prog,
1156 os.path.join(srcpath, prog))
1157 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1158 os.path.join(srcpath, prog))
1159
David James27ac4ae2012-12-03 23:16:15 -08001160 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001161 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001162 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1163 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1164 output_dir + libpath)
1165
1166
1167def _ProcessGccConfig(target, output_dir):
1168 """Do what gcc-config would have done"""
1169 binpath = '/bin'
1170 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1171 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1172 for prog in os.listdir(output_dir + srcpath):
1173 # Skip binaries already wrapped.
1174 if (not prog.endswith('.real') and
1175 not prog.endswith('.elf') and
1176 prog.startswith(target)):
1177 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1178 os.path.join(srcpath, prog))
1179 return srcpath
1180
1181
Frank Henigman179ec7c2015-02-06 03:01:09 -05001182def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1183 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001184 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001185
Tobias Boschddd16492019-08-14 09:29:54 -07001186 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001187 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001188 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1189 # Can't update the wrapper in place to not affect the chroot,
1190 # but only the extracted toolchain.
1191 os.unlink(sysroot_wrapper)
1192 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001193 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001194
1195
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001196def _ProcessClangWrappers(target, output_dir):
1197 """Remove chroot-specific things from our sysroot wrappers"""
1198 clang_bin_path = '/usr/bin'
1199 # Disable ccache from clang wrappers.
1200 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1201 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1202 f'/usr/bin/{target}-clang')
1203 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1204 f'/usr/bin/{target}-clang++')
1205
1206
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001207def _CreateMainLibDir(target, output_dir):
1208 """Create some lib dirs so that compiler can get the right Gcc paths"""
1209 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1210 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1211
1212
Manoj Guptadf8b3872022-01-13 11:57:36 -08001213def _CreateRemoteToolchainFile(output_dir):
1214 """Create a remote_toolchain_inputs file for reclient/RBE"""
1215 # The inputs file lists all files/shared libraries needed to run clang.
1216 # All inputs are relative to location of clang binary and one input
1217 # location per line of file e.g.
1218 # clang-13.elf
1219 # clang++-13.elf
1220 # relative/path/to/clang/resource/directory
1221
1222 clang_path = os.path.join(output_dir, 'usr/bin')
1223 # Add needed shared libraries and internal files e.g. allowlists.
1224 toolchain_inputs = ['../../lib']
1225 clang_shared_dirs = glob.glob(
1226 os.path.join(output_dir, 'usr/lib64/clang/*/share'))
1227 for clang_dir in clang_shared_dirs:
1228 toolchain_inputs.append(os.path.relpath(clang_dir, clang_path))
1229
1230 # Add actual clang binaries/wrappers.
1231 for clang_files in glob.glob(os.path.join(clang_path, 'clang*-[0-9]*')):
1232 toolchain_inputs.append(os.path.basename(clang_files))
1233
1234 with open(os.path.join(clang_path, 'remote_toolchain_inputs'), 'w') as f:
1235 f.writelines('%s\n' % line for line in toolchain_inputs)
1236
1237
Mike Frysinger35247af2012-11-16 18:58:06 -05001238def _ProcessDistroCleanups(target, output_dir):
1239 """Clean up the tree and remove all distro-specific requirements
1240
1241 Args:
1242 target: The toolchain target name
1243 output_dir: The output directory to clean up
1244 """
1245 _ProcessBinutilsConfig(target, output_dir)
1246 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001247 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001248 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001249 _CreateMainLibDir(target, output_dir)
Manoj Guptadf8b3872022-01-13 11:57:36 -08001250 _CreateRemoteToolchainFile(output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001251
1252 osutils.RmDir(os.path.join(output_dir, 'etc'))
1253
1254
1255def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1256 """Setup a tree from the packages for the specified target
1257
1258 This populates a path with all the files from toolchain packages so that
1259 a tarball can easily be generated from the result.
1260
1261 Args:
1262 target: The target to create a packagable root from
1263 output_dir: The output directory to place all the files
1264 ldpaths: A dict of static ldpath information
1265 root: The root path to pull all packages/files from
1266 """
1267 # Find all the files owned by the packages for this target.
1268 paths, elfs = _GetFilesForTarget(target, root=root)
1269
1270 # Link in all the package's files, any ELF dependencies, and wrap any
1271 # executable ELFs with helper scripts.
1272 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001273 """Move /usr/bin to /bin so people can just use that toplevel dir
1274
George Burgess IVca1d7612020-10-01 00:38:32 -07001275 Note we do not apply this to clang or rust - there is correlation between
1276 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001277 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001278 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001279 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001280 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001281 return path[4:]
1282 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001283 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1284 path_rewrite_func=MoveUsrBinToBin, root=root)
1285
1286 # The packages, when part of the normal distro, have helper scripts
1287 # that setup paths and such. Since we are making this standalone, we
1288 # need to preprocess all that ourselves.
1289 _ProcessDistroCleanups(target, output_dir)
1290
1291
1292def CreatePackages(targets_wanted, output_dir, root='/'):
1293 """Create redistributable cross-compiler packages for the specified targets
1294
1295 This creates toolchain packages that should be usable in conjunction with
1296 a downloaded sysroot (created elsewhere).
1297
1298 Tarballs (one per target) will be created in $PWD.
1299
1300 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001301 targets_wanted: The targets to package up.
1302 output_dir: The directory to put the packages in.
1303 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001304 """
Ralph Nathan03047282015-03-23 11:09:32 -07001305 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001306 osutils.SafeMakedirs(output_dir)
1307 ldpaths = lddtree.LoadLdpaths(root)
1308 targets = ExpandTargets(targets_wanted)
1309
Mike Frysinger221bd822017-09-29 02:51:47 -04001310 with osutils.TempDir(prefix='create-packages') as tempdir:
1311 logging.debug('Using tempdir: %s', tempdir)
1312
Mike Frysinger35247af2012-11-16 18:58:06 -05001313 # We have to split the root generation from the compression stages. This is
1314 # because we hardlink in all the files (to avoid overhead of reading/writing
1315 # the copies multiple times). But tar gets angry if a file's hardlink count
1316 # changes from when it starts reading a file to when it finishes.
1317 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1318 for target in targets:
1319 output_target_dir = os.path.join(tempdir, target)
1320 queue.put([target, output_target_dir, ldpaths, root])
1321
1322 # Build the tarball.
1323 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1324 for target in targets:
1325 tar_file = os.path.join(output_dir, target + '.tar.xz')
1326 queue.put([tar_file, os.path.join(tempdir, target)])
1327
1328
Mike Frysinger07534cf2017-09-12 17:40:21 -04001329def GetParser():
1330 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001331 parser = commandline.ArgumentParser(description=__doc__)
1332 parser.add_argument('-u', '--nousepkg',
1333 action='store_false', dest='usepkg', default=True,
Tom Hughesaa41d192022-06-07 16:44:11 -07001334 help='Do not use prebuilt packages')
Mike Frysinger0c808452014-11-06 17:30:23 -05001335 parser.add_argument('-d', '--deleteold',
1336 action='store_true', dest='deleteold', default=False,
1337 help='Unmerge deprecated packages')
1338 parser.add_argument('-t', '--targets',
1339 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001340 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001341 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001342 "allowed. Defaults to 'sdk'.")
1343 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1344 help='Comma separated list of boards whose toolchains we '
1345 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001346 parser.add_argument('--hostonly',
1347 dest='hostonly', default=False, action='store_true',
1348 help='Only setup the host toolchain. '
1349 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001350 parser.add_argument('--show-board-cfg', '--show-cfg',
1351 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001352 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001353 parser.add_argument('--show-packages', default=None,
1354 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001355 parser.add_argument('--create-packages',
1356 action='store_true', default=False,
1357 help='Build redistributable packages')
1358 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1359 help='Output directory')
1360 parser.add_argument('--reconfig', default=False, action='store_true',
1361 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001362 parser.add_argument('--sysroot', type='path',
1363 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001364 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001365
Mike Frysinger07534cf2017-09-12 17:40:21 -04001366
1367def main(argv):
1368 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001369 options = parser.parse_args(argv)
1370 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001371
Mike Frysinger35247af2012-11-16 18:58:06 -05001372 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001373 conflicting_options = (
1374 options.cfg_name,
1375 options.show_packages,
1376 options.create_packages,
1377 )
1378 if sum(bool(x) for x in conflicting_options) > 1:
1379 parser.error('conflicting options: create-packages & show-packages & '
1380 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001381
Gilad Arnold8195b532015-04-07 10:56:30 +03001382 targets_wanted = set(options.targets.split(','))
1383 boards_wanted = (set(options.include_boards.split(','))
1384 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001385
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001386 if options.cfg_name:
1387 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001388 elif options.show_packages is not None:
1389 cros_build_lib.AssertInsideChroot()
1390 target = options.show_packages
1391 Crossdev.Load(False)
1392 for package in GetTargetPackages(target):
1393 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001394 elif options.create_packages:
1395 cros_build_lib.AssertInsideChroot()
1396 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001397 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001398 else:
1399 cros_build_lib.AssertInsideChroot()
1400 # This has to be always run as root.
Ram Chandrasekar69751282022-02-25 21:07:36 +00001401 if osutils.IsNonRootUser():
Mike Frysinger35247af2012-11-16 18:58:06 -05001402 cros_build_lib.Die('this script must be run as root')
1403
1404 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001405 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001406 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001407 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001408 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001409 Crossdev.Save()
1410
1411 return 0