blob: 6678e97de7316b7ba7b19d5838687392e8919659 [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',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080056 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040057 'sys-devel/binutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040058 'sys-devel/gcc',
59 'sys-devel/llvm',
60 'sys-kernel/linux-headers',
61 'sys-libs/glibc',
62 'sys-libs/libcxx',
63 'sys-libs/libcxxabi',
Manoj Guptade64cb22019-03-31 18:48:58 -070064 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040065)
66
Mike Frysinger785b0c32017-09-13 01:35:59 -040067# These packages are also installed into the host SDK. However, they require
68# the cross-compilers to be installed first (because they need them to actually
69# build), so we have to delay their installation.
70HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070071 'dev-lang/rust',
George Burgess IV9d1e2322021-04-30 09:54:45 -070072 'dev-lang/rust-bootstrap',
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 = (
107 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700108 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700109 'aarch64-cros-linux-gnu',
Mike Frysingerd96d5442021-11-09 05:12:34 -0500110 'i686-cros-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700111 'x86_64-cros-linux-gnu',
112)
113
114LLVM_PKGS_TABLE = {
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700115 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700116 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
117 '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 'libcxxabi': 'sys-libs',
132 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700133 'elfutils': 'dev-libs',
Han Shene23782f2016-02-18 12:20:00 -0800134 }
David James66a09c42012-11-05 13:31:38 -0800135
136 @classmethod
137 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400138 """Load crossdev cache from disk.
139
140 We invalidate the cache when crossdev updates or this script changes.
141 """
David James90239b92012-11-05 15:31:34 -0800142 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400143 # If we run the compiled/cached .pyc file, we'll read/hash that when we
144 # really always want to track the source .py file.
145 script = os.path.abspath(__file__)
146 if script.endswith('.pyc'):
147 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500148 setup_toolchains_hash = hashlib.md5(
149 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400150
151 cls._CACHE = {
152 'crossdev_version': crossdev_version,
153 'setup_toolchains_hash': setup_toolchains_hash,
154 }
155
156 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
157 if reconfig:
158 logging.debug('cache: forcing regen due to reconfig')
159 return
160
161 try:
162 file_data = osutils.ReadFile(cls._CACHE_FILE)
163 except IOError as e:
164 if e.errno != errno.ENOENT:
165 logging.warning('cache: reading failed: %s', e)
166 osutils.SafeUnlink(cls._CACHE_FILE)
167 return
168
169 try:
170 data = json.loads(file_data)
171 except ValueError as e:
172 logging.warning('cache: ignoring invalid content: %s', e)
173 return
174
175 if crossdev_version != data.get('crossdev_version'):
176 logging.debug('cache: rebuilding after crossdev upgrade')
177 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
178 logging.debug('cache: rebuilding after cros_setup_toolchains change')
179 else:
180 logging.debug('cache: content is up-to-date!')
181 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800182
183 @classmethod
184 def Save(cls):
185 """Store crossdev cache on disk."""
186 # Save the cache from the successful run.
187 with open(cls._CACHE_FILE, 'w') as f:
188 json.dump(cls._CACHE, f)
189
190 @classmethod
191 def GetConfig(cls, target):
192 """Returns a map of crossdev provided variables about a tuple."""
193 CACHE_ATTR = '_target_tuple_map'
194
195 val = cls._CACHE.setdefault(CACHE_ATTR, {})
196 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400197 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400198 conf = {
199 'crosspkgs': [],
200 'target': toolchain.GetHostTuple(),
201 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400202 if target == 'host':
203 packages_list = HOST_PACKAGES
204 else:
205 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400206 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400207 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400208 else:
209 # Build the crossdev command.
210 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
Adrian Ratiubf0b9af2022-05-02 14:48:15 +0300211 # Enable libxcrypt for all linux-gnu targets.
212 if 'cros-linux-gnu' in target:
213 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400214 if target in TARGET_COMPILER_RT_ENABLED:
215 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400216 if target in TARGET_LLVM_PKGS_ENABLED:
217 for pkg in LLVM_PKGS_TABLE:
218 cmd.extend(LLVM_PKGS_TABLE[pkg])
Manoj Gupta78607282021-05-24 16:58:13 +0000219 if target in TARGET_GO_ENABLED:
220 cmd.extend(CROSSDEV_GO_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400221 cmd.extend(['-t', target])
222 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400223 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500224 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500225 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400226 # List of tuples split at the first '=', converted into dict.
227 conf = dict((k, cros_build_lib.ShellUnquote(v))
228 for k, v in (x.split('=', 1) for x in out))
229 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800230
Mike Frysinger66bfde52017-09-12 16:42:57 -0400231 manual_pkgs = cls.MANUAL_PKGS
232
233 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400234 conf[pkg + '_pn'] = pkg
235 conf[pkg + '_category'] = cat
236 if pkg not in conf['crosspkgs']:
237 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800238
239 val[target] = conf
240
David James66a09c42012-11-05 13:31:38 -0800241 return val[target]
242
243 @classmethod
244 def UpdateTargets(cls, targets, usepkg, config_only=False):
245 """Calls crossdev to initialize a cross target.
246
247 Args:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700248 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700249 usepkg: Copies the commandline opts.
250 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800251 """
252 configured_targets = cls._CACHE.setdefault('configured_targets', [])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700253 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800254
Manoj Gupta4d016f62021-10-19 16:39:34 -0700255 # Schedule all of the targets in parallel, and let them run.
256 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
257 for target_name in targets:
258 # We already started this target in this loop.
259 if target_name in started_targets:
260 continue
261 # The target is already configured.
262 if config_only and target_name in configured_targets:
263 continue
264 queue.put([target_name, targets[target_name], usepkg, config_only])
265 started_targets.add(target_name)
266
267 @classmethod
268 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
269 """Calls crossdev to initialize a cross target.
270
271 Args:
272 target_name: The name of the target to initialize.
273 target: The target info for initializing.
274 usepkg: Copies the commandline opts.
275 config_only: Just update.
276 """
277 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800278 cmdbase = ['crossdev', '--show-fail-log']
279 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
280 # Pick stable by default, and override as necessary.
281 cmdbase.extend(['-P', '--oneshot'])
282 if usepkg:
283 cmdbase.extend(['-P', '--getbinpkg',
284 '-P', '--usepkgonly',
285 '--without-headers'])
286
Christopher Wileyb22c0712015-06-02 10:37:03 -0700287 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800288 cmdbase.extend(['--overlays', overlays])
289 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
290
Manoj Gupta4d016f62021-10-19 16:39:34 -0700291 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800292
Manoj Gupta4d016f62021-10-19 16:39:34 -0700293 for pkg in GetTargetPackages(target_name):
294 if pkg == 'gdb':
295 # Gdb does not have selectable versions.
296 cmd.append('--ex-gdb')
Adrian Ratiubf0b9af2022-05-02 14:48:15 +0300297 elif pkg == 'ex_libxcrypt':
298 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
Manoj Gupta4d016f62021-10-19 16:39:34 -0700299 elif pkg == 'ex_compiler-rt':
300 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
301 elif pkg == 'ex_go':
302 # Go does not have selectable versions.
303 cmd.extend(CROSSDEV_GO_ARGS)
304 elif pkg in LLVM_PKGS_TABLE:
305 cmd.extend(LLVM_PKGS_TABLE[pkg])
306 elif pkg in cls.MANUAL_PKGS:
307 pass
David James66a09c42012-11-05 13:31:38 -0800308 else:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700309 # The first of the desired versions is the "primary" one.
310 version = GetDesiredPackageVersions(target_name, pkg)[0]
311 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800312
Manoj Gupta4d016f62021-10-19 16:39:34 -0700313 cmd.extend(target['crossdev'].split())
314
315 if config_only:
316 # In this case we want to just quietly reinit
317 cmd.append('--init-target')
318 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
319 else:
320 cros_build_lib.run(cmd)
321
322 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800323
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100324
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100325def GetTargetPackages(target):
326 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800327 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100328 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800329 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100330
331
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100332# Portage helper functions:
333def GetPortagePackage(target, package):
334 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800335 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100336 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400337 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100338 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100339 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100340 category = conf['category']
341 # Portage package:
342 pn = conf[package + '_pn']
343 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500344 assert category
345 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100346 return '%s/%s' % (category, pn)
347
348
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700349def PortageTrees(root):
350 """Return the portage trees for a given root."""
351 if root == '/':
352 return portage.db['/']
353 # The portage logic requires the path always end in a slash.
354 root = root.rstrip('/') + '/'
355 return portage.create_trees(target_root=root, config_root=root)[root]
356
357
358def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100359 """Extracts the list of current versions of a target, package pair.
360
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500361 Args:
362 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700363 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100364
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500365 Returns:
366 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100367 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100368 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700369 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100370 version = portage.versions.cpv_getversion(pkg)
371 versions.append(version)
372 return versions
373
374
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700375def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100376 """Extracts the current stable version for a given package.
377
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500378 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500379 atom: The target/package to operate on e.g. i686-cros-linux-gnu/gcc
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500380 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700381 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100382
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500383 Returns:
384 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100385 """
David James90239b92012-11-05 15:31:34 -0800386 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700387 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800388 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100389
390
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700391def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100392 """Resolves keywords in a given version list for a particular package.
393
394 Resolving means replacing PACKAGE_STABLE with the actual number.
395
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500396 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500397 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500398 package: The target/package to operate on (e.g. gcc)
399 versions: List of versions to resolve
400 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700401 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100402
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500403 Returns:
404 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100405 """
406 resolved = []
David James90239b92012-11-05 15:31:34 -0800407 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700408 if not installed:
409 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100410 for version in versions:
411 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700412 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400413 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100414 resolved.append(version)
415 return resolved
416
417
418def GetDesiredPackageVersions(target, package):
419 """Produces the list of desired versions for each target, package pair.
420
421 The first version in the list is implicitly treated as primary, ie.
422 the version that will be initialized by crossdev and selected.
423
424 If the version is PACKAGE_STABLE, it really means the current version which
425 is emerged by using the package atom with no particular version key.
426 Since crossdev unmasks all packages by default, this will actually
427 mean 'unstable' in most cases.
428
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500429 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500430 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500431 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100432
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500433 Returns:
434 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100435 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400436 if package in GetTargetPackages(target):
437 return [PACKAGE_STABLE]
438 else:
439 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100440
441
442def TargetIsInitialized(target):
443 """Verifies if the given list of targets has been correctly initialized.
444
445 This determines whether we have to call crossdev while emerging
446 toolchain packages or can do it using emerge. Emerge is naturally
447 preferred, because all packages can be updated in a single pass.
448
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500449 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500450 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100451
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500452 Returns:
453 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100454 """
455 # Check if packages for the given target all have a proper version.
456 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100457 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800458 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100459 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400460 if not (GetStablePackageVersion(atom, True) and
461 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100462 return False
463 return True
464 except cros_build_lib.RunCommandError:
465 # Fails - The target has likely never been initialized before.
466 return False
467
468
469def RemovePackageMask(target):
470 """Removes a package.mask file for the given platform.
471
472 The pre-existing package.mask files can mess with the keywords.
473
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500474 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500475 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100476 """
477 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700478 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100479
480
Zdenek Behan508dcce2011-12-05 15:39:32 +0100481# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700482def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500483 """Rebuild libtool as needed
484
485 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
486 gcc, libtool will break. We can't use binary packages either as those will
487 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700488
489 Args:
490 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500491 """
492 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700493 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500494 for line in f:
495 # Look for a line like:
496 # sys_lib_search_path_spec="..."
497 # It'll be a list of paths and gcc will be one of them.
498 if line.startswith('sys_lib_search_path_spec='):
499 line = line.rstrip()
500 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400501 root_path = os.path.join(root, path.lstrip(os.path.sep))
502 logging.debug('Libtool: checking %s', root_path)
503 if not os.path.exists(root_path):
504 logging.info('Rebuilding libtool after gcc upgrade')
505 logging.info(' %s', line)
506 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500507 needs_update = True
508 break
509
510 if needs_update:
511 break
512
513 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700514 cmd = [EMERGE_CMD, '--oneshot']
515 if root != '/':
516 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
517 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400518 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400519 else:
520 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500521
522
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700523def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100524 """Determines which packages need update/unmerge and defers to portage.
525
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500526 Args:
527 targets: The list of targets to update
528 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700529 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100530 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100531 # For each target, we do two things. Figure out the list of updates,
532 # and figure out the appropriate keywords/masks. Crossdev will initialize
533 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400534 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800535 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000536 # Used to keep track of post-cross packages. These are allowed to have
537 # implicit dependencies on toolchain packages, and therefore need to
538 # be built last.
539 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000541 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400542 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543 # Record the highest needed version for each target, for masking purposes.
544 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100545 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100546 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400547 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100548 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700549 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100550 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200551 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400552 if pkg in NEW_PACKAGES and usepkg:
553 # Skip this binary package (for now).
554 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100555 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400556 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000557 if is_post_cross_target:
558 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100559
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400560 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400562 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800563 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100564
Mike Frysinger3bba5032016-09-20 14:15:04 -0400565 logging.info('Updating packages:')
566 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100567
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100568 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100569 if usepkg:
570 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700571 if root != '/':
572 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100573
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000574 if usepkg:
575 # Since we are not building from source, we can handle
576 # all packages in one go.
577 cmd.extend(packages)
578 cros_build_lib.run(cmd)
579 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700580 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
581 if pre_cross_items:
582 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000583 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700584 if post_cross_items:
585 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800586 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100587
588
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700589def CleanTargets(targets, root='/'):
590 """Unmerges old packages that are assumed unnecessary.
591
592 Args:
593 targets: The list of targets to clean up.
594 root: The install root in which we want packages cleaned up.
595 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100596 unmergemap = {}
597 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400598 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100599 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400600 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100601 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700602 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700604 # NOTE: This refers to installed packages (vartree) rather than the
605 # Portage version (porttree and/or bintree) when determining the current
606 # version. While this isn't the most accurate thing to do, it is probably
607 # a good simple compromise, which should have the desired result of
608 # uninstalling everything but the latest installed version. In
609 # particular, using the bintree (--usebinpkg) requires a non-trivial
610 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200611 desired_num = VersionListToNumeric(target, package, desired, True)
612 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400613 logging.warning('Error detecting stable version for %s, '
614 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200615 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100616 unmergemap[pkg] = set(current).difference(desired_num)
617
618 # Cleaning doesn't care about consistency and rebuilding package.* files.
619 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400620 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100621 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
622
623 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400624 logging.info('Cleaning packages:')
625 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100626 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700627 if root != '/':
628 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100629 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400630 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100631 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400632 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100633
634
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200635def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100636 """Runs gcc-config and binutils-config to select the desired.
637
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500638 Args:
639 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700640 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100641 """
642 for package in ['gcc', 'binutils']:
643 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400644 # See if this package is part of this target.
645 if package not in GetTargetPackages(target):
646 logging.debug('%s: %s is not used', target, package)
647 continue
648
Zdenek Behan508dcce2011-12-05 15:39:32 +0100649 # Pick the first version in the numbered list as the selected one.
650 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700651 desired_num = VersionListToNumeric(target, package, desired, True,
652 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100653 desired = desired_num[0]
654 # *-config does not play revisions, strip them, keep just PV.
655 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
656
Mike Frysinger785b0c32017-09-13 01:35:59 -0400657 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100658 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800659 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100660
661 # And finally, attach target to it.
662 desired = '%s-%s' % (target, desired)
663
David James7ec5efc2012-11-06 09:39:49 -0800664 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700665 if root != '/':
666 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800667 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400668 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500669 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500670 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500671 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700672
673 # Do not reconfig when the current is live or nothing needs to be done.
674 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100675 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500676 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400677 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100678
679
Mike Frysinger35247af2012-11-16 18:58:06 -0500680def ExpandTargets(targets_wanted):
681 """Expand any possible toolchain aliases into full targets
682
683 This will expand 'all' and 'sdk' into the respective toolchain tuples.
684
685 Args:
686 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500687
Mike Frysinger35247af2012-11-16 18:58:06 -0500688 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300689 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500690 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500691 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700692 if targets_wanted == set(['boards']):
693 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300694 return {}
695
696 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500697 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300698 return all_targets
699 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500700 # Filter out all the non-sdk toolchains as we don't want to mess
701 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300702 return toolchain.FilterToolchains(all_targets, 'sdk', True)
703
704 # Verify user input.
705 nonexistent = targets_wanted.difference(all_targets)
706 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400707 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300708 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500709
710
David Jamesf8c672f2012-11-06 13:38:11 -0800711def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700712 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100713 """Performs all steps to create a synchronized toolchain enviroment.
714
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500715 Args:
716 usepkg: Use prebuilt packages
717 deleteold: Unmerge deprecated packages
718 hostonly: Only setup the host toolchain
719 reconfig: Reload crossdev config and reselect toolchains
720 targets_wanted: All the targets to update
721 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700722 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100723 """
David Jamesf8c672f2012-11-06 13:38:11 -0800724 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100725 if not hostonly:
726 # For hostonly, we can skip most of the below logic, much of which won't
727 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500728 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400729
Mike Frysingerd246fb92021-10-26 16:08:39 -0400730 # Filter out toolchains that don't (yet) have a binpkg available.
731 if usepkg:
732 for target in list(targets.keys()):
733 if not targets[target]['have-binpkg']:
734 del targets[target]
735
Don Garrettc0c74002015-10-09 12:58:19 -0700736 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300737 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400738 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800739 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100740
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100741 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400742 for target in targets:
743 if TargetIsInitialized(target):
744 reconfig_targets[target] = targets[target]
745 else:
746 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100747 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400748 logging.info('The following targets need to be re-initialized:')
749 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800750 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200751 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800752 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100753
Mike Frysinger66814c32017-10-09 18:11:46 -0400754 # If we're building a subset of toolchains for a board, we might not have
755 # all the tuples that the packages expect. We don't define the "full" set
756 # of tuples currently other than "whatever the full sdk has normally".
757 if usepkg or set(('all', 'sdk')) & targets_wanted:
758 # Since we have cross-compilers now, we can update these packages.
759 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400760
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100761 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400762 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100763
764 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700765 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200766 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800767
768 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700769 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100770
Mike Frysingerc880a962013-11-08 13:59:06 -0500771 # Now that we've cleared out old versions, see if we need to rebuild
772 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700773 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500774
Zdenek Behan508dcce2011-12-05 15:39:32 +0100775
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700776def ShowConfig(name):
777 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500778
779 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700780 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500781 """
Don Garrettc0c74002015-10-09 12:58:19 -0700782 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500783 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400784 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400785 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400786 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
787 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500788
789
Mike Frysinger35247af2012-11-16 18:58:06 -0500790def GeneratePathWrapper(root, wrappath, path):
791 """Generate a shell script to execute another shell script
792
793 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
794 argv[0] won't be pointing to the correct path, generate a shell script that
795 just executes another program with its full path.
796
797 Args:
798 root: The root tree to generate scripts inside of
799 wrappath: The full path (inside |root|) to create the wrapper
800 path: The target program which this wrapper will execute
801 """
802 replacements = {
803 'path': path,
804 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
805 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900806
807 # Do not use exec here, because exec invokes script with absolute path in
808 # argv0. Keeping relativeness allows us to remove abs path from compile result
809 # and leads directory independent build cache sharing in some distributed
810 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500811 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900812basedir=$(dirname "$0")
813"${basedir}/%(relroot)s%(path)s" "$@"
814exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500815""" % replacements
816 root_wrapper = root + wrappath
817 if os.path.islink(root_wrapper):
818 os.unlink(root_wrapper)
819 else:
820 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
821 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400822 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500823
824
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700825def FixClangXXWrapper(root, path):
826 """Fix wrapper shell scripts and symlinks for invoking clang++
827
828 In a typical installation, clang++ symlinks to clang, which symlinks to the
829 elf executable. The executable distinguishes between clang and clang++ based
830 on argv[0].
831
832 When invoked through the LdsoWrapper, argv[0] always contains the path to the
833 executable elf file, making clang/clang++ invocations indistinguishable.
834
835 This function detects if the elf executable being wrapped is clang-X.Y, and
836 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
837
838 The calling sequence now becomes:
839 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
840 the Ldsowrapper).
841 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
842 to the original clang-3.9 elf.
843 -) The difference this time is that inside the elf file execution, $0 is
844 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
845
Manoj Guptaae268142018-04-27 23:28:36 -0700846 Update: Starting since clang 7, the clang and clang++ are symlinks to
847 clang-7 binary, not clang-7.0. The pattern match is extended to handle
848 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
849
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700850 Args:
851 root: The root tree to generate scripts / symlinks inside of
852 path: The target elf for which LdsoWrapper was created
853 """
Manoj Guptaae268142018-04-27 23:28:36 -0700854 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700855 logging.info('fixing clang++ invocation for %s', path)
856 clangdir = os.path.dirname(root + path)
857 clang = os.path.basename(path)
858 clangxx = clang.replace('clang', 'clang++')
859
860 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
861 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
862
863 # Create a hardlink clang++-X.Y pointing to clang-X.Y
864 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
865
866 # Adjust the clang++ symlink to point to clang++-X.Y
867 os.unlink(os.path.join(clangdir, 'clang++'))
868 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
869
870
Mike Frysinger35247af2012-11-16 18:58:06 -0500871def FileIsCrosSdkElf(elf):
872 """Determine if |elf| is an ELF that we execute in the cros_sdk
873
874 We don't need this to be perfect, just quick. It makes sure the ELF
875 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
876
877 Args:
878 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500879
Mike Frysinger35247af2012-11-16 18:58:06 -0500880 Returns:
881 True if we think |elf| is a native ELF
882 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500883 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500884 data = f.read(20)
885 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500886 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500887 data[4:5] == b'\x02' and
888 data[5:6] == b'\x01' and
889 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500890
891
892def IsPathPackagable(ptype, path):
893 """Should the specified file be included in a toolchain package?
894
895 We only need to handle files as we'll create dirs as we need them.
896
897 Further, trim files that won't be useful:
898 - non-english translations (.mo) since it'd require env vars
899 - debug files since these are for the host compiler itself
900 - info/man pages as they're big, and docs are online, and the
901 native docs should work fine for the most part (`man gcc`)
902
903 Args:
904 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
905 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500906
Mike Frysinger35247af2012-11-16 18:58:06 -0500907 Returns:
908 True if we want to include this path in the package
909 """
910 return not (ptype in ('dir',) or
911 path.startswith('/usr/lib/debug/') or
912 os.path.splitext(path)[1] == '.mo' or
913 ('/man/' in path or '/info/' in path))
914
915
916def ReadlinkRoot(path, root):
917 """Like os.readlink(), but relative to a |root|
918
919 Args:
920 path: The symlink to read
921 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500922
Mike Frysinger35247af2012-11-16 18:58:06 -0500923 Returns:
924 A fully resolved symlink path
925 """
926 while os.path.islink(root + path):
927 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
928 return path
929
930
931def _GetFilesForTarget(target, root='/'):
932 """Locate all the files to package for |target|
933
934 This does not cover ELF dependencies.
935
936 Args:
937 target: The toolchain target name
938 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500939
Mike Frysinger35247af2012-11-16 18:58:06 -0500940 Returns:
941 A tuple of a set of all packable paths, and a set of all paths which
942 are also native ELFs
943 """
944 paths = set()
945 elfs = set()
946
947 # Find all the files owned by the packages for this target.
948 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500949
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700950 # Skip Go compiler from redistributable packages.
951 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
952 # into it. Due to this, the toolchain cannot be unpacked anywhere
953 # else and be readily useful. To enable packaging Go, we need to:
954 # -) Tweak the wrappers/environment to override GOROOT
955 # automatically based on the unpack location.
956 # -) Make sure the ELF dependency checking and wrapping logic
957 # below skips the Go toolchain executables and libraries.
958 # -) Make sure the packaging process maintains the relative
959 # timestamps of precompiled standard library packages.
960 # (see dev-lang/go ebuild for details).
961 if pkg == 'ex_go':
962 continue
963
Yunlian Jiang36f35242018-04-27 10:18:40 -0700964 # Use armv7a-cros-linux-gnueabi/compiler-rt for
965 # armv7a-cros-linux-gnueabihf/compiler-rt.
966 # Currently the armv7a-cros-linux-gnueabi is actually
967 # the same as armv7a-cros-linux-gnueabihf with different names.
968 # Because of that, for compiler-rt, it generates the same binary in
969 # the same location. To avoid the installation conflict, we do not
970 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
971 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
972 # toolchain.
973 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
974 atom = GetPortagePackage(target, pkg)
975 cat, pn = atom.split('/')
976 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700977 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
978 settings=portage.settings)
979 contents = dblink.getcontents()
980 if not contents:
981 if 'hf' in target:
982 new_target = 'armv7a-cros-linux-gnueabi'
983 else:
984 new_target = 'armv7a-cros-linux-gnueabihf'
985 atom = GetPortagePackage(new_target, pkg)
986 else:
987 atom = GetPortagePackage(target, pkg)
988
Mike Frysinger35247af2012-11-16 18:58:06 -0500989 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700990 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700991 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500992
Mike Frysinger35247af2012-11-16 18:58:06 -0500993 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
994 settings=portage.settings)
995 contents = dblink.getcontents()
996 for obj in contents:
997 ptype = contents[obj][0]
998 if not IsPathPackagable(ptype, obj):
999 continue
1000
1001 if ptype == 'obj':
1002 # For native ELFs, we need to pull in their dependencies too.
1003 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001004 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001005 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001006 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001007 paths.add(obj)
1008
1009 return paths, elfs
1010
1011
1012def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -05001013 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -05001014 """Link in all packable files and their runtime dependencies
1015
1016 This also wraps up executable ELFs with helper scripts.
1017
1018 Args:
1019 output_dir: The output directory to store files
1020 paths: All the files to include
1021 elfs: All the files which are ELFs (a subset of |paths|)
1022 ldpaths: A dict of static ldpath information
1023 path_rewrite_func: User callback to rewrite paths in output_dir
1024 root: The root path to pull all packages/files from
1025 """
1026 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001027 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001028 for path in paths:
1029 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001030 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001031 dst = output_dir + new_path
1032 osutils.SafeMakedirs(os.path.dirname(dst))
1033
1034 # Is this a symlink which we have to rewrite or wrap?
1035 # Delay wrap check until after we have created all paths.
1036 src = root + path
1037 if os.path.islink(src):
1038 tgt = os.readlink(src)
1039 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001040 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001041
1042 # Rewrite absolute links to relative and then generate the symlink
1043 # ourselves. All other symlinks can be hardlinked below.
1044 if tgt[0] == '/':
1045 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1046 os.symlink(tgt, dst)
1047 continue
1048
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001049 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001050 os.link(src, dst)
1051
Mike Frysinger35247af2012-11-16 18:58:06 -05001052 # Locate all the dependencies for all the ELFs. Stick them all in the
1053 # top level "lib" dir to make the wrapper simpler. This exact path does
1054 # not matter since we execute ldso directly, and we tell the ldso the
1055 # exact path to search for its libraries.
1056 libdir = os.path.join(output_dir, 'lib')
1057 osutils.SafeMakedirs(libdir)
1058 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001059 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001060 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001061 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001062 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001063 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001064 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001065
Mike Frysinger9fe02342019-12-12 17:52:53 -05001066 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1067 if os.path.basename(elf).startswith('libopcodes-'):
1068 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001069
Mike Frysinger00b129f2021-04-21 18:11:48 -04001070 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001071 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001072 src = path = lib_data['path']
1073 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001074 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001075 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001076
1077 # No need to try and copy the same source lib multiple times.
1078 if path in donelibs:
1079 continue
1080 donelibs.add(path)
1081
1082 # Die if we try to normalize different source libs with the same basename.
1083 if lib in basenamelibs:
1084 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1085 os.path.join('/lib', lib), path,
1086 ' '.join(x for x in donelibs
1087 if x != path and os.path.basename(x) == lib))
1088 # TODO(crbug.com/917193): Make this fatal.
1089 # cros_build_lib.Die('Unable to resolve lib conflicts')
1090 continue
1091 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001092
1093 # Needed libs are the SONAME, but that is usually a symlink, not a
1094 # real file. So link in the target rather than the symlink itself.
1095 # We have to walk all the possible symlinks (SONAME could point to a
1096 # symlink which points to a symlink), and we have to handle absolute
1097 # ourselves (since we have a "root" argument).
1098 dst = os.path.join(libdir, os.path.basename(path))
1099 src = ReadlinkRoot(src, root)
1100
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001101 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001102 os.link(root + src, dst)
1103
Mike Frysinger00b129f2021-04-21 18:11:48 -04001104 # Do not create wrapper for libc. crbug.com/766827
1105 if interp and not glibc_re.search(elf):
1106 # Generate a wrapper if it is executable.
1107 interp = os.path.join('/lib', os.path.basename(interp))
1108 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1109 libpaths=e['rpath'] + e['runpath'])
1110 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1111
1112 # Wrap any symlinks to the wrapper.
1113 if elf in sym_paths:
1114 link = sym_paths[elf]
1115 GeneratePathWrapper(output_dir, link, elf)
1116
Mike Frysinger35247af2012-11-16 18:58:06 -05001117
1118def _EnvdGetVar(envd, var):
1119 """Given a Gentoo env.d file, extract a var from it
1120
1121 Args:
1122 envd: The env.d file to load (may be a glob path)
1123 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001124
Mike Frysinger35247af2012-11-16 18:58:06 -05001125 Returns:
1126 The value of |var|
1127 """
1128 envds = glob.glob(envd)
1129 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1130 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001131 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001132
1133
1134def _ProcessBinutilsConfig(target, output_dir):
1135 """Do what binutils-config would have done"""
1136 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001137
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001138 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001139 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1140 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001141 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001142 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001143 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1144 % globpath)
1145 srcpath = srcpath[0]
1146 ld_path = os.path.join(srcpath, 'ld')
1147 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1148 ld_path = os.path.join(srcpath, 'ld.bfd')
1149 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001150
Mike Frysinger78b7a812014-11-26 19:45:23 -05001151 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001152 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1153 for prog in os.listdir(output_dir + srcpath):
1154 # Skip binaries already wrapped.
1155 if not prog.endswith('.real'):
1156 GeneratePathWrapper(output_dir, binpath + prog,
1157 os.path.join(srcpath, prog))
1158 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1159 os.path.join(srcpath, prog))
1160
David James27ac4ae2012-12-03 23:16:15 -08001161 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001162 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001163 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1164 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1165 output_dir + libpath)
1166
1167
1168def _ProcessGccConfig(target, output_dir):
1169 """Do what gcc-config would have done"""
1170 binpath = '/bin'
1171 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1172 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1173 for prog in os.listdir(output_dir + srcpath):
1174 # Skip binaries already wrapped.
1175 if (not prog.endswith('.real') and
1176 not prog.endswith('.elf') and
1177 prog.startswith(target)):
1178 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1179 os.path.join(srcpath, prog))
1180 return srcpath
1181
1182
Frank Henigman179ec7c2015-02-06 03:01:09 -05001183def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1184 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001185 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001186
Tobias Boschddd16492019-08-14 09:29:54 -07001187 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001188 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001189 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1190 # Can't update the wrapper in place to not affect the chroot,
1191 # but only the extracted toolchain.
1192 os.unlink(sysroot_wrapper)
1193 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001194 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001195
1196
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001197def _ProcessClangWrappers(target, output_dir):
1198 """Remove chroot-specific things from our sysroot wrappers"""
1199 clang_bin_path = '/usr/bin'
1200 # Disable ccache from clang wrappers.
1201 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1202 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1203 f'/usr/bin/{target}-clang')
1204 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1205 f'/usr/bin/{target}-clang++')
1206
1207
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001208def _CreateMainLibDir(target, output_dir):
1209 """Create some lib dirs so that compiler can get the right Gcc paths"""
1210 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1211 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1212
1213
Manoj Guptadf8b3872022-01-13 11:57:36 -08001214def _CreateRemoteToolchainFile(output_dir):
1215 """Create a remote_toolchain_inputs file for reclient/RBE"""
1216 # The inputs file lists all files/shared libraries needed to run clang.
1217 # All inputs are relative to location of clang binary and one input
1218 # location per line of file e.g.
1219 # clang-13.elf
1220 # clang++-13.elf
1221 # relative/path/to/clang/resource/directory
1222
1223 clang_path = os.path.join(output_dir, 'usr/bin')
1224 # Add needed shared libraries and internal files e.g. allowlists.
1225 toolchain_inputs = ['../../lib']
1226 clang_shared_dirs = glob.glob(
1227 os.path.join(output_dir, 'usr/lib64/clang/*/share'))
1228 for clang_dir in clang_shared_dirs:
1229 toolchain_inputs.append(os.path.relpath(clang_dir, clang_path))
1230
1231 # Add actual clang binaries/wrappers.
1232 for clang_files in glob.glob(os.path.join(clang_path, 'clang*-[0-9]*')):
1233 toolchain_inputs.append(os.path.basename(clang_files))
1234
1235 with open(os.path.join(clang_path, 'remote_toolchain_inputs'), 'w') as f:
1236 f.writelines('%s\n' % line for line in toolchain_inputs)
1237
1238
Mike Frysinger35247af2012-11-16 18:58:06 -05001239def _ProcessDistroCleanups(target, output_dir):
1240 """Clean up the tree and remove all distro-specific requirements
1241
1242 Args:
1243 target: The toolchain target name
1244 output_dir: The output directory to clean up
1245 """
1246 _ProcessBinutilsConfig(target, output_dir)
1247 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001248 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001249 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001250 _CreateMainLibDir(target, output_dir)
Manoj Guptadf8b3872022-01-13 11:57:36 -08001251 _CreateRemoteToolchainFile(output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001252
1253 osutils.RmDir(os.path.join(output_dir, 'etc'))
1254
1255
1256def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1257 """Setup a tree from the packages for the specified target
1258
1259 This populates a path with all the files from toolchain packages so that
1260 a tarball can easily be generated from the result.
1261
1262 Args:
1263 target: The target to create a packagable root from
1264 output_dir: The output directory to place all the files
1265 ldpaths: A dict of static ldpath information
1266 root: The root path to pull all packages/files from
1267 """
1268 # Find all the files owned by the packages for this target.
1269 paths, elfs = _GetFilesForTarget(target, root=root)
1270
1271 # Link in all the package's files, any ELF dependencies, and wrap any
1272 # executable ELFs with helper scripts.
1273 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001274 """Move /usr/bin to /bin so people can just use that toplevel dir
1275
George Burgess IVca1d7612020-10-01 00:38:32 -07001276 Note we do not apply this to clang or rust - there is correlation between
1277 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001278 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001279 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001280 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001281 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001282 return path[4:]
1283 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001284 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1285 path_rewrite_func=MoveUsrBinToBin, root=root)
1286
1287 # The packages, when part of the normal distro, have helper scripts
1288 # that setup paths and such. Since we are making this standalone, we
1289 # need to preprocess all that ourselves.
1290 _ProcessDistroCleanups(target, output_dir)
1291
1292
1293def CreatePackages(targets_wanted, output_dir, root='/'):
1294 """Create redistributable cross-compiler packages for the specified targets
1295
1296 This creates toolchain packages that should be usable in conjunction with
1297 a downloaded sysroot (created elsewhere).
1298
1299 Tarballs (one per target) will be created in $PWD.
1300
1301 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001302 targets_wanted: The targets to package up.
1303 output_dir: The directory to put the packages in.
1304 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001305 """
Ralph Nathan03047282015-03-23 11:09:32 -07001306 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001307 osutils.SafeMakedirs(output_dir)
1308 ldpaths = lddtree.LoadLdpaths(root)
1309 targets = ExpandTargets(targets_wanted)
1310
Mike Frysinger221bd822017-09-29 02:51:47 -04001311 with osutils.TempDir(prefix='create-packages') as tempdir:
1312 logging.debug('Using tempdir: %s', tempdir)
1313
Mike Frysinger35247af2012-11-16 18:58:06 -05001314 # We have to split the root generation from the compression stages. This is
1315 # because we hardlink in all the files (to avoid overhead of reading/writing
1316 # the copies multiple times). But tar gets angry if a file's hardlink count
1317 # changes from when it starts reading a file to when it finishes.
1318 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1319 for target in targets:
1320 output_target_dir = os.path.join(tempdir, target)
1321 queue.put([target, output_target_dir, ldpaths, root])
1322
1323 # Build the tarball.
1324 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1325 for target in targets:
1326 tar_file = os.path.join(output_dir, target + '.tar.xz')
1327 queue.put([tar_file, os.path.join(tempdir, target)])
1328
1329
Mike Frysinger07534cf2017-09-12 17:40:21 -04001330def GetParser():
1331 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001332 parser = commandline.ArgumentParser(description=__doc__)
1333 parser.add_argument('-u', '--nousepkg',
1334 action='store_false', dest='usepkg', default=True,
1335 help='Use prebuilt packages if possible')
1336 parser.add_argument('-d', '--deleteold',
1337 action='store_true', dest='deleteold', default=False,
1338 help='Unmerge deprecated packages')
1339 parser.add_argument('-t', '--targets',
1340 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001341 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001342 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001343 "allowed. Defaults to 'sdk'.")
1344 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1345 help='Comma separated list of boards whose toolchains we '
1346 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001347 parser.add_argument('--hostonly',
1348 dest='hostonly', default=False, action='store_true',
1349 help='Only setup the host toolchain. '
1350 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001351 parser.add_argument('--show-board-cfg', '--show-cfg',
1352 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001353 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001354 parser.add_argument('--show-packages', default=None,
1355 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001356 parser.add_argument('--create-packages',
1357 action='store_true', default=False,
1358 help='Build redistributable packages')
1359 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1360 help='Output directory')
1361 parser.add_argument('--reconfig', default=False, action='store_true',
1362 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001363 parser.add_argument('--sysroot', type='path',
1364 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001365 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001366
Mike Frysinger07534cf2017-09-12 17:40:21 -04001367
1368def main(argv):
1369 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001370 options = parser.parse_args(argv)
1371 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001372
Mike Frysinger35247af2012-11-16 18:58:06 -05001373 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001374 conflicting_options = (
1375 options.cfg_name,
1376 options.show_packages,
1377 options.create_packages,
1378 )
1379 if sum(bool(x) for x in conflicting_options) > 1:
1380 parser.error('conflicting options: create-packages & show-packages & '
1381 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001382
Gilad Arnold8195b532015-04-07 10:56:30 +03001383 targets_wanted = set(options.targets.split(','))
1384 boards_wanted = (set(options.include_boards.split(','))
1385 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001386
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001387 if options.cfg_name:
1388 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001389 elif options.show_packages is not None:
1390 cros_build_lib.AssertInsideChroot()
1391 target = options.show_packages
1392 Crossdev.Load(False)
1393 for package in GetTargetPackages(target):
1394 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001395 elif options.create_packages:
1396 cros_build_lib.AssertInsideChroot()
1397 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001398 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001399 else:
1400 cros_build_lib.AssertInsideChroot()
1401 # This has to be always run as root.
Ram Chandrasekar69751282022-02-25 21:07:36 +00001402 if osutils.IsNonRootUser():
Mike Frysinger35247af2012-11-16 18:58:06 -05001403 cros_build_lib.Die('this script must be run as root')
1404
1405 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001406 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001407 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001408 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001409 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001410 Crossdev.Save()
1411
1412 return 0