blob: eaead5add896efaa74cea846e9352914c8e77c6d [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
38CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070039ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010040STABLE_OVERLAY = '/usr/local/portage/stable'
41CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010042
43
Mike Frysinger66bfde52017-09-12 16:42:57 -040044# The exact list of host toolchain packages we care about. These are the
45# packages that bots/devs install only from binpkgs and rely on the SDK bot
46# (chromiumos-sdk) to validate+uprev.
47#
Mike Frysinger66bfde52017-09-12 16:42:57 -040048# We don't use crossdev to manage the host toolchain for us, especially since
49# we diverge significantly now (with llvm/clang/etc...), and we don't need or
50# want crossdev managing /etc/portage config files for the sdk
51HOST_PACKAGES = (
52 'dev-lang/go',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080053 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040054 'sys-devel/binutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040055 'sys-devel/gcc',
56 'sys-devel/llvm',
57 'sys-kernel/linux-headers',
58 'sys-libs/glibc',
59 'sys-libs/libcxx',
60 'sys-libs/libcxxabi',
Manoj Guptade64cb22019-03-31 18:48:58 -070061 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040062)
63
Mike Frysinger785b0c32017-09-13 01:35:59 -040064# These packages are also installed into the host SDK. However, they require
65# the cross-compilers to be installed first (because they need them to actually
66# build), so we have to delay their installation.
67HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070068 'dev-lang/rust',
George Burgess IV9d1e2322021-04-30 09:54:45 -070069 'dev-lang/rust-bootstrap',
Mike Frysinger61a24392017-10-17 17:14:27 -040070 'virtual/target-sdk-post-cross',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010071 'dev-embedded/coreboot-sdk',
Dan Callaghan7cc58ff2022-02-16 08:38:13 +110072 'dev-embedded/hps-sdk',
George Burgess IV288ecc82021-04-21 07:52:45 -070073 'dev-embedded/ti50-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040074)
75
76# New packages that we're in the process of adding to the SDK. Since the SDK
77# bot hasn't had a chance to run yet, there are no binary packages available,
78# so we have to list them here and wait. Once it completes, entries here can
79# be removed so they'll end up on bots & dev's systems.
George Burgess IV66e199c2021-05-05 15:38:40 -070080NEW_PACKAGES = ()
Mike Frysinger785b0c32017-09-13 01:35:59 -040081
Rahul Chaudhry4b803052015-05-13 15:25:56 -070082# Enable the Go compiler for these targets.
83TARGET_GO_ENABLED = (
84 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070085 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070086 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070087 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070088)
89CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
90
Manoj Gupta1b5642e2017-03-08 16:44:12 -080091# Enable llvm's compiler-rt for these targets.
92TARGET_COMPILER_RT_ENABLED = (
93 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070094 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070095 'aarch64-cros-linux-gnu',
Manoj Guptabf1b6422021-11-08 09:50:20 -080096 'arm-none-eabi',
Manoj Gupta21f3a082018-03-06 21:25:39 -080097 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080098)
99CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
100
Manoj Gupta946abb42017-04-12 14:27:19 -0700101TARGET_LLVM_PKGS_ENABLED = (
102 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700103 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700104 'aarch64-cros-linux-gnu',
Mike Frysingerd96d5442021-11-09 05:12:34 -0500105 'i686-cros-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700106 'x86_64-cros-linux-gnu',
107)
108
109LLVM_PKGS_TABLE = {
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700110 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700111 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
112 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700113}
114
David James66a09c42012-11-05 13:31:38 -0800115class Crossdev(object):
116 """Class for interacting with crossdev and caching its output."""
117
118 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
119 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800120 # Packages that needs separate handling, in addition to what we have from
121 # crossdev.
122 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700123 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800124 'llvm': 'sys-devel',
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700125 'llvm-libunwind': 'sys-libs',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700126 'libcxxabi': 'sys-libs',
127 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700128 'elfutils': 'dev-libs',
Han Shene23782f2016-02-18 12:20:00 -0800129 }
David James66a09c42012-11-05 13:31:38 -0800130
131 @classmethod
132 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400133 """Load crossdev cache from disk.
134
135 We invalidate the cache when crossdev updates or this script changes.
136 """
David James90239b92012-11-05 15:31:34 -0800137 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400138 # If we run the compiled/cached .pyc file, we'll read/hash that when we
139 # really always want to track the source .py file.
140 script = os.path.abspath(__file__)
141 if script.endswith('.pyc'):
142 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500143 setup_toolchains_hash = hashlib.md5(
144 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400145
146 cls._CACHE = {
147 'crossdev_version': crossdev_version,
148 'setup_toolchains_hash': setup_toolchains_hash,
149 }
150
151 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
152 if reconfig:
153 logging.debug('cache: forcing regen due to reconfig')
154 return
155
156 try:
157 file_data = osutils.ReadFile(cls._CACHE_FILE)
158 except IOError as e:
159 if e.errno != errno.ENOENT:
160 logging.warning('cache: reading failed: %s', e)
161 osutils.SafeUnlink(cls._CACHE_FILE)
162 return
163
164 try:
165 data = json.loads(file_data)
166 except ValueError as e:
167 logging.warning('cache: ignoring invalid content: %s', e)
168 return
169
170 if crossdev_version != data.get('crossdev_version'):
171 logging.debug('cache: rebuilding after crossdev upgrade')
172 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
173 logging.debug('cache: rebuilding after cros_setup_toolchains change')
174 else:
175 logging.debug('cache: content is up-to-date!')
176 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800177
178 @classmethod
179 def Save(cls):
180 """Store crossdev cache on disk."""
181 # Save the cache from the successful run.
182 with open(cls._CACHE_FILE, 'w') as f:
183 json.dump(cls._CACHE, f)
184
185 @classmethod
186 def GetConfig(cls, target):
187 """Returns a map of crossdev provided variables about a tuple."""
188 CACHE_ATTR = '_target_tuple_map'
189
190 val = cls._CACHE.setdefault(CACHE_ATTR, {})
191 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400192 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400193 conf = {
194 'crosspkgs': [],
195 'target': toolchain.GetHostTuple(),
196 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400197 if target == 'host':
198 packages_list = HOST_PACKAGES
199 else:
200 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400201 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400202 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400203 else:
204 # Build the crossdev command.
205 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
206 if target in TARGET_COMPILER_RT_ENABLED:
207 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400208 if target in TARGET_LLVM_PKGS_ENABLED:
209 for pkg in LLVM_PKGS_TABLE:
210 cmd.extend(LLVM_PKGS_TABLE[pkg])
Manoj Gupta78607282021-05-24 16:58:13 +0000211 if target in TARGET_GO_ENABLED:
212 cmd.extend(CROSSDEV_GO_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400213 cmd.extend(['-t', target])
214 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400215 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500216 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500217 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400218 # List of tuples split at the first '=', converted into dict.
219 conf = dict((k, cros_build_lib.ShellUnquote(v))
220 for k, v in (x.split('=', 1) for x in out))
221 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800222
Mike Frysinger66bfde52017-09-12 16:42:57 -0400223 manual_pkgs = cls.MANUAL_PKGS
224
225 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400226 conf[pkg + '_pn'] = pkg
227 conf[pkg + '_category'] = cat
228 if pkg not in conf['crosspkgs']:
229 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800230
231 val[target] = conf
232
David James66a09c42012-11-05 13:31:38 -0800233 return val[target]
234
235 @classmethod
236 def UpdateTargets(cls, targets, usepkg, config_only=False):
237 """Calls crossdev to initialize a cross target.
238
239 Args:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700240 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700241 usepkg: Copies the commandline opts.
242 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800243 """
244 configured_targets = cls._CACHE.setdefault('configured_targets', [])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700245 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800246
Manoj Gupta4d016f62021-10-19 16:39:34 -0700247 # Schedule all of the targets in parallel, and let them run.
248 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
249 for target_name in targets:
250 # We already started this target in this loop.
251 if target_name in started_targets:
252 continue
253 # The target is already configured.
254 if config_only and target_name in configured_targets:
255 continue
256 queue.put([target_name, targets[target_name], usepkg, config_only])
257 started_targets.add(target_name)
258
259 @classmethod
260 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
261 """Calls crossdev to initialize a cross target.
262
263 Args:
264 target_name: The name of the target to initialize.
265 target: The target info for initializing.
266 usepkg: Copies the commandline opts.
267 config_only: Just update.
268 """
269 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800270 cmdbase = ['crossdev', '--show-fail-log']
271 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
272 # Pick stable by default, and override as necessary.
273 cmdbase.extend(['-P', '--oneshot'])
274 if usepkg:
275 cmdbase.extend(['-P', '--getbinpkg',
276 '-P', '--usepkgonly',
277 '--without-headers'])
278
Christopher Wileyb22c0712015-06-02 10:37:03 -0700279 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800280 cmdbase.extend(['--overlays', overlays])
281 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
282
Manoj Gupta4d016f62021-10-19 16:39:34 -0700283 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800284
Manoj Gupta4d016f62021-10-19 16:39:34 -0700285 for pkg in GetTargetPackages(target_name):
286 if pkg == 'gdb':
287 # Gdb does not have selectable versions.
288 cmd.append('--ex-gdb')
289 elif pkg == 'ex_compiler-rt':
290 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
291 elif pkg == 'ex_go':
292 # Go does not have selectable versions.
293 cmd.extend(CROSSDEV_GO_ARGS)
294 elif pkg in LLVM_PKGS_TABLE:
295 cmd.extend(LLVM_PKGS_TABLE[pkg])
296 elif pkg in cls.MANUAL_PKGS:
297 pass
David James66a09c42012-11-05 13:31:38 -0800298 else:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700299 # The first of the desired versions is the "primary" one.
300 version = GetDesiredPackageVersions(target_name, pkg)[0]
301 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800302
Manoj Gupta4d016f62021-10-19 16:39:34 -0700303 cmd.extend(target['crossdev'].split())
304
305 if config_only:
306 # In this case we want to just quietly reinit
307 cmd.append('--init-target')
308 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
309 else:
310 cros_build_lib.run(cmd)
311
312 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800313
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100314
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100315def GetTargetPackages(target):
316 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800317 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100318 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800319 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100320
321
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100322# Portage helper functions:
323def GetPortagePackage(target, package):
324 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800325 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100326 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400327 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100328 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100329 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100330 category = conf['category']
331 # Portage package:
332 pn = conf[package + '_pn']
333 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500334 assert category
335 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100336 return '%s/%s' % (category, pn)
337
338
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700339def PortageTrees(root):
340 """Return the portage trees for a given root."""
341 if root == '/':
342 return portage.db['/']
343 # The portage logic requires the path always end in a slash.
344 root = root.rstrip('/') + '/'
345 return portage.create_trees(target_root=root, config_root=root)[root]
346
347
348def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100349 """Extracts the list of current versions of a target, package pair.
350
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500351 Args:
352 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700353 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100354
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500355 Returns:
356 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100357 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100358 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700359 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360 version = portage.versions.cpv_getversion(pkg)
361 versions.append(version)
362 return versions
363
364
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700365def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100366 """Extracts the current stable version for a given package.
367
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500368 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500369 atom: The target/package to operate on e.g. i686-cros-linux-gnu/gcc
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500370 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700371 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100372
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500373 Returns:
374 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100375 """
David James90239b92012-11-05 15:31:34 -0800376 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700377 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800378 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100379
380
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700381def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100382 """Resolves keywords in a given version list for a particular package.
383
384 Resolving means replacing PACKAGE_STABLE with the actual number.
385
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500386 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500387 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500388 package: The target/package to operate on (e.g. gcc)
389 versions: List of versions to resolve
390 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700391 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100392
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500393 Returns:
394 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100395 """
396 resolved = []
David James90239b92012-11-05 15:31:34 -0800397 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700398 if not installed:
399 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100400 for version in versions:
401 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700402 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400403 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100404 resolved.append(version)
405 return resolved
406
407
408def GetDesiredPackageVersions(target, package):
409 """Produces the list of desired versions for each target, package pair.
410
411 The first version in the list is implicitly treated as primary, ie.
412 the version that will be initialized by crossdev and selected.
413
414 If the version is PACKAGE_STABLE, it really means the current version which
415 is emerged by using the package atom with no particular version key.
416 Since crossdev unmasks all packages by default, this will actually
417 mean 'unstable' in most cases.
418
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500419 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500420 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500421 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100422
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500423 Returns:
424 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100425 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400426 if package in GetTargetPackages(target):
427 return [PACKAGE_STABLE]
428 else:
429 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100430
431
432def TargetIsInitialized(target):
433 """Verifies if the given list of targets has been correctly initialized.
434
435 This determines whether we have to call crossdev while emerging
436 toolchain packages or can do it using emerge. Emerge is naturally
437 preferred, because all packages can be updated in a single pass.
438
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500439 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500440 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100441
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500442 Returns:
443 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100444 """
445 # Check if packages for the given target all have a proper version.
446 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100447 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800448 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100449 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400450 if not (GetStablePackageVersion(atom, True) and
451 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100452 return False
453 return True
454 except cros_build_lib.RunCommandError:
455 # Fails - The target has likely never been initialized before.
456 return False
457
458
459def RemovePackageMask(target):
460 """Removes a package.mask file for the given platform.
461
462 The pre-existing package.mask files can mess with the keywords.
463
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500464 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500465 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100466 """
467 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700468 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100469
470
Zdenek Behan508dcce2011-12-05 15:39:32 +0100471# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700472def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500473 """Rebuild libtool as needed
474
475 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
476 gcc, libtool will break. We can't use binary packages either as those will
477 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700478
479 Args:
480 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500481 """
482 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700483 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500484 for line in f:
485 # Look for a line like:
486 # sys_lib_search_path_spec="..."
487 # It'll be a list of paths and gcc will be one of them.
488 if line.startswith('sys_lib_search_path_spec='):
489 line = line.rstrip()
490 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400491 root_path = os.path.join(root, path.lstrip(os.path.sep))
492 logging.debug('Libtool: checking %s', root_path)
493 if not os.path.exists(root_path):
494 logging.info('Rebuilding libtool after gcc upgrade')
495 logging.info(' %s', line)
496 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500497 needs_update = True
498 break
499
500 if needs_update:
501 break
502
503 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700504 cmd = [EMERGE_CMD, '--oneshot']
505 if root != '/':
506 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
507 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400508 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400509 else:
510 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500511
512
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700513def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100514 """Determines which packages need update/unmerge and defers to portage.
515
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500516 Args:
517 targets: The list of targets to update
518 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700519 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100521 # For each target, we do two things. Figure out the list of updates,
522 # and figure out the appropriate keywords/masks. Crossdev will initialize
523 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400524 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800525 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000526 # Used to keep track of post-cross packages. These are allowed to have
527 # implicit dependencies on toolchain packages, and therefore need to
528 # be built last.
529 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100530 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000531 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400532 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100533 # Record the highest needed version for each target, for masking purposes.
534 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100535 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400537 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100538 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700539 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200541 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400542 if pkg in NEW_PACKAGES and usepkg:
543 # Skip this binary package (for now).
544 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100545 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400546 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000547 if is_post_cross_target:
548 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100549
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400550 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100551 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400552 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800553 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100554
Mike Frysinger3bba5032016-09-20 14:15:04 -0400555 logging.info('Updating packages:')
556 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100557
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100558 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100559 if usepkg:
560 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700561 if root != '/':
562 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100563
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000564 if usepkg:
565 # Since we are not building from source, we can handle
566 # all packages in one go.
567 cmd.extend(packages)
568 cros_build_lib.run(cmd)
569 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700570 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
571 if pre_cross_items:
572 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000573 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700574 if post_cross_items:
575 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800576 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100577
578
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700579def CleanTargets(targets, root='/'):
580 """Unmerges old packages that are assumed unnecessary.
581
582 Args:
583 targets: The list of targets to clean up.
584 root: The install root in which we want packages cleaned up.
585 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100586 unmergemap = {}
587 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400588 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100589 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400590 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100591 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700592 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700594 # NOTE: This refers to installed packages (vartree) rather than the
595 # Portage version (porttree and/or bintree) when determining the current
596 # version. While this isn't the most accurate thing to do, it is probably
597 # a good simple compromise, which should have the desired result of
598 # uninstalling everything but the latest installed version. In
599 # particular, using the bintree (--usebinpkg) requires a non-trivial
600 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200601 desired_num = VersionListToNumeric(target, package, desired, True)
602 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400603 logging.warning('Error detecting stable version for %s, '
604 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200605 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100606 unmergemap[pkg] = set(current).difference(desired_num)
607
608 # Cleaning doesn't care about consistency and rebuilding package.* files.
609 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400610 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100611 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
612
613 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400614 logging.info('Cleaning packages:')
615 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100616 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700617 if root != '/':
618 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100619 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400620 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100621 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400622 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100623
624
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200625def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100626 """Runs gcc-config and binutils-config to select the desired.
627
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500628 Args:
629 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700630 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100631 """
632 for package in ['gcc', 'binutils']:
633 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400634 # See if this package is part of this target.
635 if package not in GetTargetPackages(target):
636 logging.debug('%s: %s is not used', target, package)
637 continue
638
Zdenek Behan508dcce2011-12-05 15:39:32 +0100639 # Pick the first version in the numbered list as the selected one.
640 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700641 desired_num = VersionListToNumeric(target, package, desired, True,
642 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100643 desired = desired_num[0]
644 # *-config does not play revisions, strip them, keep just PV.
645 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
646
Mike Frysinger785b0c32017-09-13 01:35:59 -0400647 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100648 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800649 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100650
651 # And finally, attach target to it.
652 desired = '%s-%s' % (target, desired)
653
David James7ec5efc2012-11-06 09:39:49 -0800654 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700655 if root != '/':
656 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800657 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400658 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500659 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500660 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500661 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700662
663 # Do not reconfig when the current is live or nothing needs to be done.
664 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100665 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500666 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400667 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100668
669
Mike Frysinger35247af2012-11-16 18:58:06 -0500670def ExpandTargets(targets_wanted):
671 """Expand any possible toolchain aliases into full targets
672
673 This will expand 'all' and 'sdk' into the respective toolchain tuples.
674
675 Args:
676 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500677
Mike Frysinger35247af2012-11-16 18:58:06 -0500678 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300679 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500680 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500681 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700682 if targets_wanted == set(['boards']):
683 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300684 return {}
685
686 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500687 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300688 return all_targets
689 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500690 # Filter out all the non-sdk toolchains as we don't want to mess
691 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300692 return toolchain.FilterToolchains(all_targets, 'sdk', True)
693
694 # Verify user input.
695 nonexistent = targets_wanted.difference(all_targets)
696 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400697 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300698 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500699
700
David Jamesf8c672f2012-11-06 13:38:11 -0800701def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700702 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100703 """Performs all steps to create a synchronized toolchain enviroment.
704
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500705 Args:
706 usepkg: Use prebuilt packages
707 deleteold: Unmerge deprecated packages
708 hostonly: Only setup the host toolchain
709 reconfig: Reload crossdev config and reselect toolchains
710 targets_wanted: All the targets to update
711 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700712 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100713 """
David Jamesf8c672f2012-11-06 13:38:11 -0800714 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100715 if not hostonly:
716 # For hostonly, we can skip most of the below logic, much of which won't
717 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500718 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400719
Mike Frysingerd246fb92021-10-26 16:08:39 -0400720 # Filter out toolchains that don't (yet) have a binpkg available.
721 if usepkg:
722 for target in list(targets.keys()):
723 if not targets[target]['have-binpkg']:
724 del targets[target]
725
Don Garrettc0c74002015-10-09 12:58:19 -0700726 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300727 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400728 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800729 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100730
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100731 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400732 for target in targets:
733 if TargetIsInitialized(target):
734 reconfig_targets[target] = targets[target]
735 else:
736 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100737 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400738 logging.info('The following targets need to be re-initialized:')
739 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800740 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200741 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800742 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100743
Mike Frysinger66814c32017-10-09 18:11:46 -0400744 # If we're building a subset of toolchains for a board, we might not have
745 # all the tuples that the packages expect. We don't define the "full" set
746 # of tuples currently other than "whatever the full sdk has normally".
747 if usepkg or set(('all', 'sdk')) & targets_wanted:
748 # Since we have cross-compilers now, we can update these packages.
749 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400750
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100751 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400752 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100753
754 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700755 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200756 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800757
758 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700759 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100760
Mike Frysingerc880a962013-11-08 13:59:06 -0500761 # Now that we've cleared out old versions, see if we need to rebuild
762 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700763 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500764
Zdenek Behan508dcce2011-12-05 15:39:32 +0100765
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700766def ShowConfig(name):
767 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500768
769 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700770 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500771 """
Don Garrettc0c74002015-10-09 12:58:19 -0700772 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500773 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400774 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400775 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400776 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
777 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500778
779
Mike Frysinger35247af2012-11-16 18:58:06 -0500780def GeneratePathWrapper(root, wrappath, path):
781 """Generate a shell script to execute another shell script
782
783 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
784 argv[0] won't be pointing to the correct path, generate a shell script that
785 just executes another program with its full path.
786
787 Args:
788 root: The root tree to generate scripts inside of
789 wrappath: The full path (inside |root|) to create the wrapper
790 path: The target program which this wrapper will execute
791 """
792 replacements = {
793 'path': path,
794 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
795 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900796
797 # Do not use exec here, because exec invokes script with absolute path in
798 # argv0. Keeping relativeness allows us to remove abs path from compile result
799 # and leads directory independent build cache sharing in some distributed
800 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500801 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900802basedir=$(dirname "$0")
803"${basedir}/%(relroot)s%(path)s" "$@"
804exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500805""" % replacements
806 root_wrapper = root + wrappath
807 if os.path.islink(root_wrapper):
808 os.unlink(root_wrapper)
809 else:
810 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
811 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400812 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500813
814
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700815def FixClangXXWrapper(root, path):
816 """Fix wrapper shell scripts and symlinks for invoking clang++
817
818 In a typical installation, clang++ symlinks to clang, which symlinks to the
819 elf executable. The executable distinguishes between clang and clang++ based
820 on argv[0].
821
822 When invoked through the LdsoWrapper, argv[0] always contains the path to the
823 executable elf file, making clang/clang++ invocations indistinguishable.
824
825 This function detects if the elf executable being wrapped is clang-X.Y, and
826 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
827
828 The calling sequence now becomes:
829 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
830 the Ldsowrapper).
831 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
832 to the original clang-3.9 elf.
833 -) The difference this time is that inside the elf file execution, $0 is
834 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
835
Manoj Guptaae268142018-04-27 23:28:36 -0700836 Update: Starting since clang 7, the clang and clang++ are symlinks to
837 clang-7 binary, not clang-7.0. The pattern match is extended to handle
838 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
839
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700840 Args:
841 root: The root tree to generate scripts / symlinks inside of
842 path: The target elf for which LdsoWrapper was created
843 """
Manoj Guptaae268142018-04-27 23:28:36 -0700844 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700845 logging.info('fixing clang++ invocation for %s', path)
846 clangdir = os.path.dirname(root + path)
847 clang = os.path.basename(path)
848 clangxx = clang.replace('clang', 'clang++')
849
850 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
851 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
852
853 # Create a hardlink clang++-X.Y pointing to clang-X.Y
854 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
855
856 # Adjust the clang++ symlink to point to clang++-X.Y
857 os.unlink(os.path.join(clangdir, 'clang++'))
858 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
859
860
Mike Frysinger35247af2012-11-16 18:58:06 -0500861def FileIsCrosSdkElf(elf):
862 """Determine if |elf| is an ELF that we execute in the cros_sdk
863
864 We don't need this to be perfect, just quick. It makes sure the ELF
865 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
866
867 Args:
868 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500869
Mike Frysinger35247af2012-11-16 18:58:06 -0500870 Returns:
871 True if we think |elf| is a native ELF
872 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500873 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500874 data = f.read(20)
875 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500876 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500877 data[4:5] == b'\x02' and
878 data[5:6] == b'\x01' and
879 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500880
881
882def IsPathPackagable(ptype, path):
883 """Should the specified file be included in a toolchain package?
884
885 We only need to handle files as we'll create dirs as we need them.
886
887 Further, trim files that won't be useful:
888 - non-english translations (.mo) since it'd require env vars
889 - debug files since these are for the host compiler itself
890 - info/man pages as they're big, and docs are online, and the
891 native docs should work fine for the most part (`man gcc`)
892
893 Args:
894 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
895 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500896
Mike Frysinger35247af2012-11-16 18:58:06 -0500897 Returns:
898 True if we want to include this path in the package
899 """
900 return not (ptype in ('dir',) or
901 path.startswith('/usr/lib/debug/') or
902 os.path.splitext(path)[1] == '.mo' or
903 ('/man/' in path or '/info/' in path))
904
905
906def ReadlinkRoot(path, root):
907 """Like os.readlink(), but relative to a |root|
908
909 Args:
910 path: The symlink to read
911 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500912
Mike Frysinger35247af2012-11-16 18:58:06 -0500913 Returns:
914 A fully resolved symlink path
915 """
916 while os.path.islink(root + path):
917 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
918 return path
919
920
921def _GetFilesForTarget(target, root='/'):
922 """Locate all the files to package for |target|
923
924 This does not cover ELF dependencies.
925
926 Args:
927 target: The toolchain target name
928 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500929
Mike Frysinger35247af2012-11-16 18:58:06 -0500930 Returns:
931 A tuple of a set of all packable paths, and a set of all paths which
932 are also native ELFs
933 """
934 paths = set()
935 elfs = set()
936
937 # Find all the files owned by the packages for this target.
938 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500939
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700940 # Skip Go compiler from redistributable packages.
941 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
942 # into it. Due to this, the toolchain cannot be unpacked anywhere
943 # else and be readily useful. To enable packaging Go, we need to:
944 # -) Tweak the wrappers/environment to override GOROOT
945 # automatically based on the unpack location.
946 # -) Make sure the ELF dependency checking and wrapping logic
947 # below skips the Go toolchain executables and libraries.
948 # -) Make sure the packaging process maintains the relative
949 # timestamps of precompiled standard library packages.
950 # (see dev-lang/go ebuild for details).
951 if pkg == 'ex_go':
952 continue
953
Yunlian Jiang36f35242018-04-27 10:18:40 -0700954 # Use armv7a-cros-linux-gnueabi/compiler-rt for
955 # armv7a-cros-linux-gnueabihf/compiler-rt.
956 # Currently the armv7a-cros-linux-gnueabi is actually
957 # the same as armv7a-cros-linux-gnueabihf with different names.
958 # Because of that, for compiler-rt, it generates the same binary in
959 # the same location. To avoid the installation conflict, we do not
960 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
961 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
962 # toolchain.
963 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
964 atom = GetPortagePackage(target, pkg)
965 cat, pn = atom.split('/')
966 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700967 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
968 settings=portage.settings)
969 contents = dblink.getcontents()
970 if not contents:
971 if 'hf' in target:
972 new_target = 'armv7a-cros-linux-gnueabi'
973 else:
974 new_target = 'armv7a-cros-linux-gnueabihf'
975 atom = GetPortagePackage(new_target, pkg)
976 else:
977 atom = GetPortagePackage(target, pkg)
978
Mike Frysinger35247af2012-11-16 18:58:06 -0500979 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700980 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700981 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500982
Mike Frysinger35247af2012-11-16 18:58:06 -0500983 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
984 settings=portage.settings)
985 contents = dblink.getcontents()
986 for obj in contents:
987 ptype = contents[obj][0]
988 if not IsPathPackagable(ptype, obj):
989 continue
990
991 if ptype == 'obj':
992 # For native ELFs, we need to pull in their dependencies too.
993 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500994 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500995 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500996 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500997 paths.add(obj)
998
999 return paths, elfs
1000
1001
1002def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -05001003 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -05001004 """Link in all packable files and their runtime dependencies
1005
1006 This also wraps up executable ELFs with helper scripts.
1007
1008 Args:
1009 output_dir: The output directory to store files
1010 paths: All the files to include
1011 elfs: All the files which are ELFs (a subset of |paths|)
1012 ldpaths: A dict of static ldpath information
1013 path_rewrite_func: User callback to rewrite paths in output_dir
1014 root: The root path to pull all packages/files from
1015 """
1016 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001017 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001018 for path in paths:
1019 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001020 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001021 dst = output_dir + new_path
1022 osutils.SafeMakedirs(os.path.dirname(dst))
1023
1024 # Is this a symlink which we have to rewrite or wrap?
1025 # Delay wrap check until after we have created all paths.
1026 src = root + path
1027 if os.path.islink(src):
1028 tgt = os.readlink(src)
1029 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001030 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001031
1032 # Rewrite absolute links to relative and then generate the symlink
1033 # ourselves. All other symlinks can be hardlinked below.
1034 if tgt[0] == '/':
1035 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1036 os.symlink(tgt, dst)
1037 continue
1038
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001039 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001040 os.link(src, dst)
1041
Mike Frysinger35247af2012-11-16 18:58:06 -05001042 # Locate all the dependencies for all the ELFs. Stick them all in the
1043 # top level "lib" dir to make the wrapper simpler. This exact path does
1044 # not matter since we execute ldso directly, and we tell the ldso the
1045 # exact path to search for its libraries.
1046 libdir = os.path.join(output_dir, 'lib')
1047 osutils.SafeMakedirs(libdir)
1048 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001049 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001050 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001051 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001052 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001053 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001054 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001055
Mike Frysinger9fe02342019-12-12 17:52:53 -05001056 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1057 if os.path.basename(elf).startswith('libopcodes-'):
1058 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001059
Mike Frysinger00b129f2021-04-21 18:11:48 -04001060 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001061 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001062 src = path = lib_data['path']
1063 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001064 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001065 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001066
1067 # No need to try and copy the same source lib multiple times.
1068 if path in donelibs:
1069 continue
1070 donelibs.add(path)
1071
1072 # Die if we try to normalize different source libs with the same basename.
1073 if lib in basenamelibs:
1074 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1075 os.path.join('/lib', lib), path,
1076 ' '.join(x for x in donelibs
1077 if x != path and os.path.basename(x) == lib))
1078 # TODO(crbug.com/917193): Make this fatal.
1079 # cros_build_lib.Die('Unable to resolve lib conflicts')
1080 continue
1081 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001082
1083 # Needed libs are the SONAME, but that is usually a symlink, not a
1084 # real file. So link in the target rather than the symlink itself.
1085 # We have to walk all the possible symlinks (SONAME could point to a
1086 # symlink which points to a symlink), and we have to handle absolute
1087 # ourselves (since we have a "root" argument).
1088 dst = os.path.join(libdir, os.path.basename(path))
1089 src = ReadlinkRoot(src, root)
1090
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001091 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001092 os.link(root + src, dst)
1093
Mike Frysinger00b129f2021-04-21 18:11:48 -04001094 # Do not create wrapper for libc. crbug.com/766827
1095 if interp and not glibc_re.search(elf):
1096 # Generate a wrapper if it is executable.
1097 interp = os.path.join('/lib', os.path.basename(interp))
1098 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1099 libpaths=e['rpath'] + e['runpath'])
1100 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1101
1102 # Wrap any symlinks to the wrapper.
1103 if elf in sym_paths:
1104 link = sym_paths[elf]
1105 GeneratePathWrapper(output_dir, link, elf)
1106
Mike Frysinger35247af2012-11-16 18:58:06 -05001107
1108def _EnvdGetVar(envd, var):
1109 """Given a Gentoo env.d file, extract a var from it
1110
1111 Args:
1112 envd: The env.d file to load (may be a glob path)
1113 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001114
Mike Frysinger35247af2012-11-16 18:58:06 -05001115 Returns:
1116 The value of |var|
1117 """
1118 envds = glob.glob(envd)
1119 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1120 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001121 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001122
1123
1124def _ProcessBinutilsConfig(target, output_dir):
1125 """Do what binutils-config would have done"""
1126 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001127
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001128 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001129 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1130 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001131 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001132 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001133 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1134 % globpath)
1135 srcpath = srcpath[0]
1136 ld_path = os.path.join(srcpath, 'ld')
1137 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1138 ld_path = os.path.join(srcpath, 'ld.bfd')
1139 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001140
Mike Frysinger78b7a812014-11-26 19:45:23 -05001141 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001142 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1143 for prog in os.listdir(output_dir + srcpath):
1144 # Skip binaries already wrapped.
1145 if not prog.endswith('.real'):
1146 GeneratePathWrapper(output_dir, binpath + prog,
1147 os.path.join(srcpath, prog))
1148 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1149 os.path.join(srcpath, prog))
1150
David James27ac4ae2012-12-03 23:16:15 -08001151 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001152 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001153 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1154 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1155 output_dir + libpath)
1156
1157
1158def _ProcessGccConfig(target, output_dir):
1159 """Do what gcc-config would have done"""
1160 binpath = '/bin'
1161 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1162 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1163 for prog in os.listdir(output_dir + srcpath):
1164 # Skip binaries already wrapped.
1165 if (not prog.endswith('.real') and
1166 not prog.endswith('.elf') and
1167 prog.startswith(target)):
1168 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1169 os.path.join(srcpath, prog))
1170 return srcpath
1171
1172
Frank Henigman179ec7c2015-02-06 03:01:09 -05001173def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1174 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001175 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001176
Tobias Boschddd16492019-08-14 09:29:54 -07001177 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001178 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001179 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1180 # Can't update the wrapper in place to not affect the chroot,
1181 # but only the extracted toolchain.
1182 os.unlink(sysroot_wrapper)
1183 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001184 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001185
1186
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001187def _ProcessClangWrappers(target, output_dir):
1188 """Remove chroot-specific things from our sysroot wrappers"""
1189 clang_bin_path = '/usr/bin'
1190 # Disable ccache from clang wrappers.
1191 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1192 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1193 f'/usr/bin/{target}-clang')
1194 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1195 f'/usr/bin/{target}-clang++')
1196
1197
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001198def _CreateMainLibDir(target, output_dir):
1199 """Create some lib dirs so that compiler can get the right Gcc paths"""
1200 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1201 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1202
1203
Manoj Guptadf8b3872022-01-13 11:57:36 -08001204def _CreateRemoteToolchainFile(output_dir):
1205 """Create a remote_toolchain_inputs file for reclient/RBE"""
1206 # The inputs file lists all files/shared libraries needed to run clang.
1207 # All inputs are relative to location of clang binary and one input
1208 # location per line of file e.g.
1209 # clang-13.elf
1210 # clang++-13.elf
1211 # relative/path/to/clang/resource/directory
1212
1213 clang_path = os.path.join(output_dir, 'usr/bin')
1214 # Add needed shared libraries and internal files e.g. allowlists.
1215 toolchain_inputs = ['../../lib']
1216 clang_shared_dirs = glob.glob(
1217 os.path.join(output_dir, 'usr/lib64/clang/*/share'))
1218 for clang_dir in clang_shared_dirs:
1219 toolchain_inputs.append(os.path.relpath(clang_dir, clang_path))
1220
1221 # Add actual clang binaries/wrappers.
1222 for clang_files in glob.glob(os.path.join(clang_path, 'clang*-[0-9]*')):
1223 toolchain_inputs.append(os.path.basename(clang_files))
1224
1225 with open(os.path.join(clang_path, 'remote_toolchain_inputs'), 'w') as f:
1226 f.writelines('%s\n' % line for line in toolchain_inputs)
1227
1228
Mike Frysinger35247af2012-11-16 18:58:06 -05001229def _ProcessDistroCleanups(target, output_dir):
1230 """Clean up the tree and remove all distro-specific requirements
1231
1232 Args:
1233 target: The toolchain target name
1234 output_dir: The output directory to clean up
1235 """
1236 _ProcessBinutilsConfig(target, output_dir)
1237 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001238 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001239 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001240 _CreateMainLibDir(target, output_dir)
Manoj Guptadf8b3872022-01-13 11:57:36 -08001241 _CreateRemoteToolchainFile(output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001242
1243 osutils.RmDir(os.path.join(output_dir, 'etc'))
1244
1245
1246def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1247 """Setup a tree from the packages for the specified target
1248
1249 This populates a path with all the files from toolchain packages so that
1250 a tarball can easily be generated from the result.
1251
1252 Args:
1253 target: The target to create a packagable root from
1254 output_dir: The output directory to place all the files
1255 ldpaths: A dict of static ldpath information
1256 root: The root path to pull all packages/files from
1257 """
1258 # Find all the files owned by the packages for this target.
1259 paths, elfs = _GetFilesForTarget(target, root=root)
1260
1261 # Link in all the package's files, any ELF dependencies, and wrap any
1262 # executable ELFs with helper scripts.
1263 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001264 """Move /usr/bin to /bin so people can just use that toplevel dir
1265
George Burgess IVca1d7612020-10-01 00:38:32 -07001266 Note we do not apply this to clang or rust - there is correlation between
1267 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001268 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001269 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001270 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001271 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001272 return path[4:]
1273 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001274 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1275 path_rewrite_func=MoveUsrBinToBin, root=root)
1276
1277 # The packages, when part of the normal distro, have helper scripts
1278 # that setup paths and such. Since we are making this standalone, we
1279 # need to preprocess all that ourselves.
1280 _ProcessDistroCleanups(target, output_dir)
1281
1282
1283def CreatePackages(targets_wanted, output_dir, root='/'):
1284 """Create redistributable cross-compiler packages for the specified targets
1285
1286 This creates toolchain packages that should be usable in conjunction with
1287 a downloaded sysroot (created elsewhere).
1288
1289 Tarballs (one per target) will be created in $PWD.
1290
1291 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001292 targets_wanted: The targets to package up.
1293 output_dir: The directory to put the packages in.
1294 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001295 """
Ralph Nathan03047282015-03-23 11:09:32 -07001296 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001297 osutils.SafeMakedirs(output_dir)
1298 ldpaths = lddtree.LoadLdpaths(root)
1299 targets = ExpandTargets(targets_wanted)
1300
Mike Frysinger221bd822017-09-29 02:51:47 -04001301 with osutils.TempDir(prefix='create-packages') as tempdir:
1302 logging.debug('Using tempdir: %s', tempdir)
1303
Mike Frysinger35247af2012-11-16 18:58:06 -05001304 # We have to split the root generation from the compression stages. This is
1305 # because we hardlink in all the files (to avoid overhead of reading/writing
1306 # the copies multiple times). But tar gets angry if a file's hardlink count
1307 # changes from when it starts reading a file to when it finishes.
1308 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1309 for target in targets:
1310 output_target_dir = os.path.join(tempdir, target)
1311 queue.put([target, output_target_dir, ldpaths, root])
1312
1313 # Build the tarball.
1314 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1315 for target in targets:
1316 tar_file = os.path.join(output_dir, target + '.tar.xz')
1317 queue.put([tar_file, os.path.join(tempdir, target)])
1318
1319
Mike Frysinger07534cf2017-09-12 17:40:21 -04001320def GetParser():
1321 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001322 parser = commandline.ArgumentParser(description=__doc__)
1323 parser.add_argument('-u', '--nousepkg',
1324 action='store_false', dest='usepkg', default=True,
1325 help='Use prebuilt packages if possible')
1326 parser.add_argument('-d', '--deleteold',
1327 action='store_true', dest='deleteold', default=False,
1328 help='Unmerge deprecated packages')
1329 parser.add_argument('-t', '--targets',
1330 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001331 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001332 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001333 "allowed. Defaults to 'sdk'.")
1334 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1335 help='Comma separated list of boards whose toolchains we '
1336 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001337 parser.add_argument('--hostonly',
1338 dest='hostonly', default=False, action='store_true',
1339 help='Only setup the host toolchain. '
1340 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001341 parser.add_argument('--show-board-cfg', '--show-cfg',
1342 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001343 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001344 parser.add_argument('--show-packages', default=None,
1345 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001346 parser.add_argument('--create-packages',
1347 action='store_true', default=False,
1348 help='Build redistributable packages')
1349 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1350 help='Output directory')
1351 parser.add_argument('--reconfig', default=False, action='store_true',
1352 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001353 parser.add_argument('--sysroot', type='path',
1354 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001355 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001356
Mike Frysinger07534cf2017-09-12 17:40:21 -04001357
1358def main(argv):
1359 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001360 options = parser.parse_args(argv)
1361 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001362
Mike Frysinger35247af2012-11-16 18:58:06 -05001363 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001364 conflicting_options = (
1365 options.cfg_name,
1366 options.show_packages,
1367 options.create_packages,
1368 )
1369 if sum(bool(x) for x in conflicting_options) > 1:
1370 parser.error('conflicting options: create-packages & show-packages & '
1371 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001372
Gilad Arnold8195b532015-04-07 10:56:30 +03001373 targets_wanted = set(options.targets.split(','))
1374 boards_wanted = (set(options.include_boards.split(','))
1375 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001376
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001377 if options.cfg_name:
1378 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001379 elif options.show_packages is not None:
1380 cros_build_lib.AssertInsideChroot()
1381 target = options.show_packages
1382 Crossdev.Load(False)
1383 for package in GetTargetPackages(target):
1384 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001385 elif options.create_packages:
1386 cros_build_lib.AssertInsideChroot()
1387 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001388 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001389 else:
1390 cros_build_lib.AssertInsideChroot()
1391 # This has to be always run as root.
Ram Chandrasekar69751282022-02-25 21:07:36 +00001392 if osutils.IsNonRootUser():
Mike Frysinger35247af2012-11-16 18:58:06 -05001393 cros_build_lib.Die('this script must be run as root')
1394
1395 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001396 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001397 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001398 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001399 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001400 Crossdev.Save()
1401
1402 return 0