blob: e4b56c2f285c65a1d0ed7a7e50800c81f4c2eb50 [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',
George Burgess IV288ecc82021-04-21 07:52:45 -070072 'dev-embedded/ti50-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040073)
74
75# New packages that we're in the process of adding to the SDK. Since the SDK
76# bot hasn't had a chance to run yet, there are no binary packages available,
77# so we have to list them here and wait. Once it completes, entries here can
78# be removed so they'll end up on bots & dev's systems.
George Burgess IV66e199c2021-05-05 15:38:40 -070079NEW_PACKAGES = ()
Mike Frysinger785b0c32017-09-13 01:35:59 -040080
Rahul Chaudhry4b803052015-05-13 15:25:56 -070081# Enable the Go compiler for these targets.
82TARGET_GO_ENABLED = (
83 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070084 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070085 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070086 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070087)
88CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
89
Manoj Gupta1b5642e2017-03-08 16:44:12 -080090# Enable llvm's compiler-rt for these targets.
91TARGET_COMPILER_RT_ENABLED = (
92 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070093 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070094 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -080095 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080096)
97CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
98
Manoj Gupta946abb42017-04-12 14:27:19 -070099TARGET_LLVM_PKGS_ENABLED = (
100 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700101 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700102 'aarch64-cros-linux-gnu',
Mike Frysingerd96d5442021-11-09 05:12:34 -0500103 'i686-cros-linux-gnu',
Manoj Gupta93713122020-10-29 17:52:16 -0700104 'i686-pc-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700105 'x86_64-cros-linux-gnu',
106)
107
108LLVM_PKGS_TABLE = {
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700109 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700110 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
111 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700112}
113
David James66a09c42012-11-05 13:31:38 -0800114class Crossdev(object):
115 """Class for interacting with crossdev and caching its output."""
116
117 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
118 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800119 # Packages that needs separate handling, in addition to what we have from
120 # crossdev.
121 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700122 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800123 'llvm': 'sys-devel',
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700124 'llvm-libunwind': 'sys-libs',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700125 'libcxxabi': 'sys-libs',
126 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700127 'elfutils': 'dev-libs',
Han Shene23782f2016-02-18 12:20:00 -0800128 }
David James66a09c42012-11-05 13:31:38 -0800129
130 @classmethod
131 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400132 """Load crossdev cache from disk.
133
134 We invalidate the cache when crossdev updates or this script changes.
135 """
David James90239b92012-11-05 15:31:34 -0800136 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400137 # If we run the compiled/cached .pyc file, we'll read/hash that when we
138 # really always want to track the source .py file.
139 script = os.path.abspath(__file__)
140 if script.endswith('.pyc'):
141 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500142 setup_toolchains_hash = hashlib.md5(
143 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400144
145 cls._CACHE = {
146 'crossdev_version': crossdev_version,
147 'setup_toolchains_hash': setup_toolchains_hash,
148 }
149
150 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
151 if reconfig:
152 logging.debug('cache: forcing regen due to reconfig')
153 return
154
155 try:
156 file_data = osutils.ReadFile(cls._CACHE_FILE)
157 except IOError as e:
158 if e.errno != errno.ENOENT:
159 logging.warning('cache: reading failed: %s', e)
160 osutils.SafeUnlink(cls._CACHE_FILE)
161 return
162
163 try:
164 data = json.loads(file_data)
165 except ValueError as e:
166 logging.warning('cache: ignoring invalid content: %s', e)
167 return
168
169 if crossdev_version != data.get('crossdev_version'):
170 logging.debug('cache: rebuilding after crossdev upgrade')
171 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
172 logging.debug('cache: rebuilding after cros_setup_toolchains change')
173 else:
174 logging.debug('cache: content is up-to-date!')
175 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800176
177 @classmethod
178 def Save(cls):
179 """Store crossdev cache on disk."""
180 # Save the cache from the successful run.
181 with open(cls._CACHE_FILE, 'w') as f:
182 json.dump(cls._CACHE, f)
183
184 @classmethod
185 def GetConfig(cls, target):
186 """Returns a map of crossdev provided variables about a tuple."""
187 CACHE_ATTR = '_target_tuple_map'
188
189 val = cls._CACHE.setdefault(CACHE_ATTR, {})
190 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400191 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400192 conf = {
193 'crosspkgs': [],
194 'target': toolchain.GetHostTuple(),
195 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400196 if target == 'host':
197 packages_list = HOST_PACKAGES
198 else:
199 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400200 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400201 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400202 else:
203 # Build the crossdev command.
204 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
205 if target in TARGET_COMPILER_RT_ENABLED:
206 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400207 if target in TARGET_LLVM_PKGS_ENABLED:
208 for pkg in LLVM_PKGS_TABLE:
209 cmd.extend(LLVM_PKGS_TABLE[pkg])
Manoj Gupta78607282021-05-24 16:58:13 +0000210 if target in TARGET_GO_ENABLED:
211 cmd.extend(CROSSDEV_GO_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400212 cmd.extend(['-t', target])
213 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400214 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500215 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500216 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400217 # List of tuples split at the first '=', converted into dict.
218 conf = dict((k, cros_build_lib.ShellUnquote(v))
219 for k, v in (x.split('=', 1) for x in out))
220 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800221
Mike Frysinger66bfde52017-09-12 16:42:57 -0400222 manual_pkgs = cls.MANUAL_PKGS
223
224 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400225 conf[pkg + '_pn'] = pkg
226 conf[pkg + '_category'] = cat
227 if pkg not in conf['crosspkgs']:
228 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800229
230 val[target] = conf
231
David James66a09c42012-11-05 13:31:38 -0800232 return val[target]
233
234 @classmethod
235 def UpdateTargets(cls, targets, usepkg, config_only=False):
236 """Calls crossdev to initialize a cross target.
237
238 Args:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700239 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700240 usepkg: Copies the commandline opts.
241 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800242 """
243 configured_targets = cls._CACHE.setdefault('configured_targets', [])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700244 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800245
Manoj Gupta4d016f62021-10-19 16:39:34 -0700246 # Schedule all of the targets in parallel, and let them run.
247 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
248 for target_name in targets:
249 # We already started this target in this loop.
250 if target_name in started_targets:
251 continue
252 # The target is already configured.
253 if config_only and target_name in configured_targets:
254 continue
255 queue.put([target_name, targets[target_name], usepkg, config_only])
256 started_targets.add(target_name)
257
258 @classmethod
259 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
260 """Calls crossdev to initialize a cross target.
261
262 Args:
263 target_name: The name of the target to initialize.
264 target: The target info for initializing.
265 usepkg: Copies the commandline opts.
266 config_only: Just update.
267 """
268 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800269 cmdbase = ['crossdev', '--show-fail-log']
270 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
271 # Pick stable by default, and override as necessary.
272 cmdbase.extend(['-P', '--oneshot'])
273 if usepkg:
274 cmdbase.extend(['-P', '--getbinpkg',
275 '-P', '--usepkgonly',
276 '--without-headers'])
277
Christopher Wileyb22c0712015-06-02 10:37:03 -0700278 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800279 cmdbase.extend(['--overlays', overlays])
280 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
281
Manoj Gupta4d016f62021-10-19 16:39:34 -0700282 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800283
Manoj Gupta4d016f62021-10-19 16:39:34 -0700284 for pkg in GetTargetPackages(target_name):
285 if pkg == 'gdb':
286 # Gdb does not have selectable versions.
287 cmd.append('--ex-gdb')
288 elif pkg == 'ex_compiler-rt':
289 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
290 elif pkg == 'ex_go':
291 # Go does not have selectable versions.
292 cmd.extend(CROSSDEV_GO_ARGS)
293 elif pkg in LLVM_PKGS_TABLE:
294 cmd.extend(LLVM_PKGS_TABLE[pkg])
295 elif pkg in cls.MANUAL_PKGS:
296 pass
David James66a09c42012-11-05 13:31:38 -0800297 else:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700298 # The first of the desired versions is the "primary" one.
299 version = GetDesiredPackageVersions(target_name, pkg)[0]
300 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800301
Manoj Gupta4d016f62021-10-19 16:39:34 -0700302 cmd.extend(target['crossdev'].split())
303
304 if config_only:
305 # In this case we want to just quietly reinit
306 cmd.append('--init-target')
307 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
308 else:
309 cros_build_lib.run(cmd)
310
311 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800312
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100313
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100314def GetTargetPackages(target):
315 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800316 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100317 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800318 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100319
320
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100321# Portage helper functions:
322def GetPortagePackage(target, package):
323 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800324 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100325 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400326 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100327 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100328 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100329 category = conf['category']
330 # Portage package:
331 pn = conf[package + '_pn']
332 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500333 assert category
334 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100335 return '%s/%s' % (category, pn)
336
337
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700338def PortageTrees(root):
339 """Return the portage trees for a given root."""
340 if root == '/':
341 return portage.db['/']
342 # The portage logic requires the path always end in a slash.
343 root = root.rstrip('/') + '/'
344 return portage.create_trees(target_root=root, config_root=root)[root]
345
346
347def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100348 """Extracts the list of current versions of a target, package pair.
349
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500350 Args:
351 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700352 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100353
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500354 Returns:
355 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100356 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100357 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700358 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100359 version = portage.versions.cpv_getversion(pkg)
360 versions.append(version)
361 return versions
362
363
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700364def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100365 """Extracts the current stable version for a given package.
366
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500367 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500368 atom: The target/package to operate on e.g. i686-cros-linux-gnu/gcc
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500369 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700370 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100371
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500372 Returns:
373 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100374 """
David James90239b92012-11-05 15:31:34 -0800375 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700376 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800377 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378
379
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700380def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100381 """Resolves keywords in a given version list for a particular package.
382
383 Resolving means replacing PACKAGE_STABLE with the actual number.
384
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500385 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500386 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500387 package: The target/package to operate on (e.g. gcc)
388 versions: List of versions to resolve
389 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700390 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100391
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500392 Returns:
393 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100394 """
395 resolved = []
David James90239b92012-11-05 15:31:34 -0800396 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700397 if not installed:
398 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100399 for version in versions:
400 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700401 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400402 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100403 resolved.append(version)
404 return resolved
405
406
407def GetDesiredPackageVersions(target, package):
408 """Produces the list of desired versions for each target, package pair.
409
410 The first version in the list is implicitly treated as primary, ie.
411 the version that will be initialized by crossdev and selected.
412
413 If the version is PACKAGE_STABLE, it really means the current version which
414 is emerged by using the package atom with no particular version key.
415 Since crossdev unmasks all packages by default, this will actually
416 mean 'unstable' in most cases.
417
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500418 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500419 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500420 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100421
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500422 Returns:
423 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100424 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400425 if package in GetTargetPackages(target):
426 return [PACKAGE_STABLE]
427 else:
428 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100429
430
431def TargetIsInitialized(target):
432 """Verifies if the given list of targets has been correctly initialized.
433
434 This determines whether we have to call crossdev while emerging
435 toolchain packages or can do it using emerge. Emerge is naturally
436 preferred, because all packages can be updated in a single pass.
437
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500438 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500439 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100440
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500441 Returns:
442 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100443 """
444 # Check if packages for the given target all have a proper version.
445 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100446 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800447 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100448 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400449 if not (GetStablePackageVersion(atom, True) and
450 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100451 return False
452 return True
453 except cros_build_lib.RunCommandError:
454 # Fails - The target has likely never been initialized before.
455 return False
456
457
458def RemovePackageMask(target):
459 """Removes a package.mask file for the given platform.
460
461 The pre-existing package.mask files can mess with the keywords.
462
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500463 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500464 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100465 """
466 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700467 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100468
469
Zdenek Behan508dcce2011-12-05 15:39:32 +0100470# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700471def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500472 """Rebuild libtool as needed
473
474 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
475 gcc, libtool will break. We can't use binary packages either as those will
476 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700477
478 Args:
479 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500480 """
481 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700482 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500483 for line in f:
484 # Look for a line like:
485 # sys_lib_search_path_spec="..."
486 # It'll be a list of paths and gcc will be one of them.
487 if line.startswith('sys_lib_search_path_spec='):
488 line = line.rstrip()
489 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400490 root_path = os.path.join(root, path.lstrip(os.path.sep))
491 logging.debug('Libtool: checking %s', root_path)
492 if not os.path.exists(root_path):
493 logging.info('Rebuilding libtool after gcc upgrade')
494 logging.info(' %s', line)
495 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500496 needs_update = True
497 break
498
499 if needs_update:
500 break
501
502 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700503 cmd = [EMERGE_CMD, '--oneshot']
504 if root != '/':
505 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
506 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400507 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400508 else:
509 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500510
511
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700512def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100513 """Determines which packages need update/unmerge and defers to portage.
514
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500515 Args:
516 targets: The list of targets to update
517 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700518 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100519 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520 # For each target, we do two things. Figure out the list of updates,
521 # and figure out the appropriate keywords/masks. Crossdev will initialize
522 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400523 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800524 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000525 # Used to keep track of post-cross packages. These are allowed to have
526 # implicit dependencies on toolchain packages, and therefore need to
527 # be built last.
528 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000530 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400531 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532 # Record the highest needed version for each target, for masking purposes.
533 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100534 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400536 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100537 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700538 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200540 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400541 if pkg in NEW_PACKAGES and usepkg:
542 # Skip this binary package (for now).
543 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100544 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400545 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000546 if is_post_cross_target:
547 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100548
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400549 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100550 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400551 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800552 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100553
Mike Frysinger3bba5032016-09-20 14:15:04 -0400554 logging.info('Updating packages:')
555 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100556
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100557 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558 if usepkg:
559 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700560 if root != '/':
561 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100562
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000563 if usepkg:
564 # Since we are not building from source, we can handle
565 # all packages in one go.
566 cmd.extend(packages)
567 cros_build_lib.run(cmd)
568 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700569 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
570 if pre_cross_items:
571 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000572 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700573 if post_cross_items:
574 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800575 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100576
577
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700578def CleanTargets(targets, root='/'):
579 """Unmerges old packages that are assumed unnecessary.
580
581 Args:
582 targets: The list of targets to clean up.
583 root: The install root in which we want packages cleaned up.
584 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100585 unmergemap = {}
586 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400587 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100588 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400589 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100590 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700591 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100592 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700593 # NOTE: This refers to installed packages (vartree) rather than the
594 # Portage version (porttree and/or bintree) when determining the current
595 # version. While this isn't the most accurate thing to do, it is probably
596 # a good simple compromise, which should have the desired result of
597 # uninstalling everything but the latest installed version. In
598 # particular, using the bintree (--usebinpkg) requires a non-trivial
599 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200600 desired_num = VersionListToNumeric(target, package, desired, True)
601 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400602 logging.warning('Error detecting stable version for %s, '
603 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200604 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100605 unmergemap[pkg] = set(current).difference(desired_num)
606
607 # Cleaning doesn't care about consistency and rebuilding package.* files.
608 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400609 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100610 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
611
612 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400613 logging.info('Cleaning packages:')
614 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100615 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700616 if root != '/':
617 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100618 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400619 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100620 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400621 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100622
623
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200624def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100625 """Runs gcc-config and binutils-config to select the desired.
626
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500627 Args:
628 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700629 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100630 """
631 for package in ['gcc', 'binutils']:
632 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400633 # See if this package is part of this target.
634 if package not in GetTargetPackages(target):
635 logging.debug('%s: %s is not used', target, package)
636 continue
637
Zdenek Behan508dcce2011-12-05 15:39:32 +0100638 # Pick the first version in the numbered list as the selected one.
639 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700640 desired_num = VersionListToNumeric(target, package, desired, True,
641 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100642 desired = desired_num[0]
643 # *-config does not play revisions, strip them, keep just PV.
644 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
645
Mike Frysinger785b0c32017-09-13 01:35:59 -0400646 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100647 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800648 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100649
650 # And finally, attach target to it.
651 desired = '%s-%s' % (target, desired)
652
David James7ec5efc2012-11-06 09:39:49 -0800653 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700654 if root != '/':
655 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800656 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400657 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500658 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500659 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500660 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700661
662 # Do not reconfig when the current is live or nothing needs to be done.
663 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100664 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500665 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400666 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100667
668
Mike Frysinger35247af2012-11-16 18:58:06 -0500669def ExpandTargets(targets_wanted):
670 """Expand any possible toolchain aliases into full targets
671
672 This will expand 'all' and 'sdk' into the respective toolchain tuples.
673
674 Args:
675 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500676
Mike Frysinger35247af2012-11-16 18:58:06 -0500677 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300678 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500679 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500680 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700681 if targets_wanted == set(['boards']):
682 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300683 return {}
684
685 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500686 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300687 return all_targets
688 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500689 # Filter out all the non-sdk toolchains as we don't want to mess
690 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300691 return toolchain.FilterToolchains(all_targets, 'sdk', True)
692
693 # Verify user input.
694 nonexistent = targets_wanted.difference(all_targets)
695 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400696 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300697 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500698
699
David Jamesf8c672f2012-11-06 13:38:11 -0800700def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700701 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100702 """Performs all steps to create a synchronized toolchain enviroment.
703
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500704 Args:
705 usepkg: Use prebuilt packages
706 deleteold: Unmerge deprecated packages
707 hostonly: Only setup the host toolchain
708 reconfig: Reload crossdev config and reselect toolchains
709 targets_wanted: All the targets to update
710 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700711 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100712 """
David Jamesf8c672f2012-11-06 13:38:11 -0800713 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100714 if not hostonly:
715 # For hostonly, we can skip most of the below logic, much of which won't
716 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500717 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400718
Mike Frysingerd246fb92021-10-26 16:08:39 -0400719 # Filter out toolchains that don't (yet) have a binpkg available.
720 if usepkg:
721 for target in list(targets.keys()):
722 if not targets[target]['have-binpkg']:
723 del targets[target]
724
Don Garrettc0c74002015-10-09 12:58:19 -0700725 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300726 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400727 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800728 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100729
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100730 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400731 for target in targets:
732 if TargetIsInitialized(target):
733 reconfig_targets[target] = targets[target]
734 else:
735 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100736 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400737 logging.info('The following targets need to be re-initialized:')
738 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800739 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200740 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800741 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100742
Mike Frysinger66814c32017-10-09 18:11:46 -0400743 # If we're building a subset of toolchains for a board, we might not have
744 # all the tuples that the packages expect. We don't define the "full" set
745 # of tuples currently other than "whatever the full sdk has normally".
746 if usepkg or set(('all', 'sdk')) & targets_wanted:
747 # Since we have cross-compilers now, we can update these packages.
748 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400749
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100750 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400751 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100752
753 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700754 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200755 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800756
757 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700758 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100759
Mike Frysingerc880a962013-11-08 13:59:06 -0500760 # Now that we've cleared out old versions, see if we need to rebuild
761 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700762 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500763
Zdenek Behan508dcce2011-12-05 15:39:32 +0100764
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700765def ShowConfig(name):
766 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500767
768 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700769 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500770 """
Don Garrettc0c74002015-10-09 12:58:19 -0700771 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500772 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400773 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400774 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400775 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
776 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500777
778
Mike Frysinger35247af2012-11-16 18:58:06 -0500779def GeneratePathWrapper(root, wrappath, path):
780 """Generate a shell script to execute another shell script
781
782 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
783 argv[0] won't be pointing to the correct path, generate a shell script that
784 just executes another program with its full path.
785
786 Args:
787 root: The root tree to generate scripts inside of
788 wrappath: The full path (inside |root|) to create the wrapper
789 path: The target program which this wrapper will execute
790 """
791 replacements = {
792 'path': path,
793 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
794 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900795
796 # Do not use exec here, because exec invokes script with absolute path in
797 # argv0. Keeping relativeness allows us to remove abs path from compile result
798 # and leads directory independent build cache sharing in some distributed
799 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500800 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900801basedir=$(dirname "$0")
802"${basedir}/%(relroot)s%(path)s" "$@"
803exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500804""" % replacements
805 root_wrapper = root + wrappath
806 if os.path.islink(root_wrapper):
807 os.unlink(root_wrapper)
808 else:
809 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
810 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400811 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500812
813
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700814def FixClangXXWrapper(root, path):
815 """Fix wrapper shell scripts and symlinks for invoking clang++
816
817 In a typical installation, clang++ symlinks to clang, which symlinks to the
818 elf executable. The executable distinguishes between clang and clang++ based
819 on argv[0].
820
821 When invoked through the LdsoWrapper, argv[0] always contains the path to the
822 executable elf file, making clang/clang++ invocations indistinguishable.
823
824 This function detects if the elf executable being wrapped is clang-X.Y, and
825 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
826
827 The calling sequence now becomes:
828 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
829 the Ldsowrapper).
830 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
831 to the original clang-3.9 elf.
832 -) The difference this time is that inside the elf file execution, $0 is
833 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
834
Manoj Guptaae268142018-04-27 23:28:36 -0700835 Update: Starting since clang 7, the clang and clang++ are symlinks to
836 clang-7 binary, not clang-7.0. The pattern match is extended to handle
837 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
838
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700839 Args:
840 root: The root tree to generate scripts / symlinks inside of
841 path: The target elf for which LdsoWrapper was created
842 """
Manoj Guptaae268142018-04-27 23:28:36 -0700843 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700844 logging.info('fixing clang++ invocation for %s', path)
845 clangdir = os.path.dirname(root + path)
846 clang = os.path.basename(path)
847 clangxx = clang.replace('clang', 'clang++')
848
849 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
850 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
851
852 # Create a hardlink clang++-X.Y pointing to clang-X.Y
853 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
854
855 # Adjust the clang++ symlink to point to clang++-X.Y
856 os.unlink(os.path.join(clangdir, 'clang++'))
857 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
858
859
Mike Frysinger35247af2012-11-16 18:58:06 -0500860def FileIsCrosSdkElf(elf):
861 """Determine if |elf| is an ELF that we execute in the cros_sdk
862
863 We don't need this to be perfect, just quick. It makes sure the ELF
864 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
865
866 Args:
867 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500868
Mike Frysinger35247af2012-11-16 18:58:06 -0500869 Returns:
870 True if we think |elf| is a native ELF
871 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500872 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500873 data = f.read(20)
874 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500875 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500876 data[4:5] == b'\x02' and
877 data[5:6] == b'\x01' and
878 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500879
880
881def IsPathPackagable(ptype, path):
882 """Should the specified file be included in a toolchain package?
883
884 We only need to handle files as we'll create dirs as we need them.
885
886 Further, trim files that won't be useful:
887 - non-english translations (.mo) since it'd require env vars
888 - debug files since these are for the host compiler itself
889 - info/man pages as they're big, and docs are online, and the
890 native docs should work fine for the most part (`man gcc`)
891
892 Args:
893 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
894 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500895
Mike Frysinger35247af2012-11-16 18:58:06 -0500896 Returns:
897 True if we want to include this path in the package
898 """
899 return not (ptype in ('dir',) or
900 path.startswith('/usr/lib/debug/') or
901 os.path.splitext(path)[1] == '.mo' or
902 ('/man/' in path or '/info/' in path))
903
904
905def ReadlinkRoot(path, root):
906 """Like os.readlink(), but relative to a |root|
907
908 Args:
909 path: The symlink to read
910 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500911
Mike Frysinger35247af2012-11-16 18:58:06 -0500912 Returns:
913 A fully resolved symlink path
914 """
915 while os.path.islink(root + path):
916 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
917 return path
918
919
920def _GetFilesForTarget(target, root='/'):
921 """Locate all the files to package for |target|
922
923 This does not cover ELF dependencies.
924
925 Args:
926 target: The toolchain target name
927 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500928
Mike Frysinger35247af2012-11-16 18:58:06 -0500929 Returns:
930 A tuple of a set of all packable paths, and a set of all paths which
931 are also native ELFs
932 """
933 paths = set()
934 elfs = set()
935
936 # Find all the files owned by the packages for this target.
937 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500938
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700939 # Skip Go compiler from redistributable packages.
940 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
941 # into it. Due to this, the toolchain cannot be unpacked anywhere
942 # else and be readily useful. To enable packaging Go, we need to:
943 # -) Tweak the wrappers/environment to override GOROOT
944 # automatically based on the unpack location.
945 # -) Make sure the ELF dependency checking and wrapping logic
946 # below skips the Go toolchain executables and libraries.
947 # -) Make sure the packaging process maintains the relative
948 # timestamps of precompiled standard library packages.
949 # (see dev-lang/go ebuild for details).
950 if pkg == 'ex_go':
951 continue
952
Yunlian Jiang36f35242018-04-27 10:18:40 -0700953 # Use armv7a-cros-linux-gnueabi/compiler-rt for
954 # armv7a-cros-linux-gnueabihf/compiler-rt.
955 # Currently the armv7a-cros-linux-gnueabi is actually
956 # the same as armv7a-cros-linux-gnueabihf with different names.
957 # Because of that, for compiler-rt, it generates the same binary in
958 # the same location. To avoid the installation conflict, we do not
959 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
960 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
961 # toolchain.
962 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
963 atom = GetPortagePackage(target, pkg)
964 cat, pn = atom.split('/')
965 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700966 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
967 settings=portage.settings)
968 contents = dblink.getcontents()
969 if not contents:
970 if 'hf' in target:
971 new_target = 'armv7a-cros-linux-gnueabi'
972 else:
973 new_target = 'armv7a-cros-linux-gnueabihf'
974 atom = GetPortagePackage(new_target, pkg)
975 else:
976 atom = GetPortagePackage(target, pkg)
977
Mike Frysinger35247af2012-11-16 18:58:06 -0500978 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700979 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700980 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500981
Mike Frysinger35247af2012-11-16 18:58:06 -0500982 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
983 settings=portage.settings)
984 contents = dblink.getcontents()
985 for obj in contents:
986 ptype = contents[obj][0]
987 if not IsPathPackagable(ptype, obj):
988 continue
989
990 if ptype == 'obj':
991 # For native ELFs, we need to pull in their dependencies too.
992 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500993 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500994 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500995 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500996 paths.add(obj)
997
998 return paths, elfs
999
1000
1001def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -05001002 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -05001003 """Link in all packable files and their runtime dependencies
1004
1005 This also wraps up executable ELFs with helper scripts.
1006
1007 Args:
1008 output_dir: The output directory to store files
1009 paths: All the files to include
1010 elfs: All the files which are ELFs (a subset of |paths|)
1011 ldpaths: A dict of static ldpath information
1012 path_rewrite_func: User callback to rewrite paths in output_dir
1013 root: The root path to pull all packages/files from
1014 """
1015 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001016 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001017 for path in paths:
1018 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001019 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001020 dst = output_dir + new_path
1021 osutils.SafeMakedirs(os.path.dirname(dst))
1022
1023 # Is this a symlink which we have to rewrite or wrap?
1024 # Delay wrap check until after we have created all paths.
1025 src = root + path
1026 if os.path.islink(src):
1027 tgt = os.readlink(src)
1028 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001029 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001030
1031 # Rewrite absolute links to relative and then generate the symlink
1032 # ourselves. All other symlinks can be hardlinked below.
1033 if tgt[0] == '/':
1034 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1035 os.symlink(tgt, dst)
1036 continue
1037
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001038 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001039 os.link(src, dst)
1040
Mike Frysinger35247af2012-11-16 18:58:06 -05001041 # Locate all the dependencies for all the ELFs. Stick them all in the
1042 # top level "lib" dir to make the wrapper simpler. This exact path does
1043 # not matter since we execute ldso directly, and we tell the ldso the
1044 # exact path to search for its libraries.
1045 libdir = os.path.join(output_dir, 'lib')
1046 osutils.SafeMakedirs(libdir)
1047 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001048 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001049 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001050 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001051 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001052 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001053 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001054
Mike Frysinger9fe02342019-12-12 17:52:53 -05001055 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1056 if os.path.basename(elf).startswith('libopcodes-'):
1057 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001058
Mike Frysinger00b129f2021-04-21 18:11:48 -04001059 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001060 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001061 src = path = lib_data['path']
1062 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001063 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001064 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001065
1066 # No need to try and copy the same source lib multiple times.
1067 if path in donelibs:
1068 continue
1069 donelibs.add(path)
1070
1071 # Die if we try to normalize different source libs with the same basename.
1072 if lib in basenamelibs:
1073 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1074 os.path.join('/lib', lib), path,
1075 ' '.join(x for x in donelibs
1076 if x != path and os.path.basename(x) == lib))
1077 # TODO(crbug.com/917193): Make this fatal.
1078 # cros_build_lib.Die('Unable to resolve lib conflicts')
1079 continue
1080 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001081
1082 # Needed libs are the SONAME, but that is usually a symlink, not a
1083 # real file. So link in the target rather than the symlink itself.
1084 # We have to walk all the possible symlinks (SONAME could point to a
1085 # symlink which points to a symlink), and we have to handle absolute
1086 # ourselves (since we have a "root" argument).
1087 dst = os.path.join(libdir, os.path.basename(path))
1088 src = ReadlinkRoot(src, root)
1089
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001090 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001091 os.link(root + src, dst)
1092
Mike Frysinger00b129f2021-04-21 18:11:48 -04001093 # Do not create wrapper for libc. crbug.com/766827
1094 if interp and not glibc_re.search(elf):
1095 # Generate a wrapper if it is executable.
1096 interp = os.path.join('/lib', os.path.basename(interp))
1097 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1098 libpaths=e['rpath'] + e['runpath'])
1099 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1100
1101 # Wrap any symlinks to the wrapper.
1102 if elf in sym_paths:
1103 link = sym_paths[elf]
1104 GeneratePathWrapper(output_dir, link, elf)
1105
Mike Frysinger35247af2012-11-16 18:58:06 -05001106
1107def _EnvdGetVar(envd, var):
1108 """Given a Gentoo env.d file, extract a var from it
1109
1110 Args:
1111 envd: The env.d file to load (may be a glob path)
1112 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001113
Mike Frysinger35247af2012-11-16 18:58:06 -05001114 Returns:
1115 The value of |var|
1116 """
1117 envds = glob.glob(envd)
1118 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1119 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001120 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001121
1122
1123def _ProcessBinutilsConfig(target, output_dir):
1124 """Do what binutils-config would have done"""
1125 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001126
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001127 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001128 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1129 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001130 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001131 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001132 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1133 % globpath)
1134 srcpath = srcpath[0]
1135 ld_path = os.path.join(srcpath, 'ld')
1136 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1137 ld_path = os.path.join(srcpath, 'ld.bfd')
1138 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001139
Mike Frysinger78b7a812014-11-26 19:45:23 -05001140 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001141 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1142 for prog in os.listdir(output_dir + srcpath):
1143 # Skip binaries already wrapped.
1144 if not prog.endswith('.real'):
1145 GeneratePathWrapper(output_dir, binpath + prog,
1146 os.path.join(srcpath, prog))
1147 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1148 os.path.join(srcpath, prog))
1149
David James27ac4ae2012-12-03 23:16:15 -08001150 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001151 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001152 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1153 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1154 output_dir + libpath)
1155
1156
1157def _ProcessGccConfig(target, output_dir):
1158 """Do what gcc-config would have done"""
1159 binpath = '/bin'
1160 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1161 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1162 for prog in os.listdir(output_dir + srcpath):
1163 # Skip binaries already wrapped.
1164 if (not prog.endswith('.real') and
1165 not prog.endswith('.elf') and
1166 prog.startswith(target)):
1167 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1168 os.path.join(srcpath, prog))
1169 return srcpath
1170
1171
Frank Henigman179ec7c2015-02-06 03:01:09 -05001172def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1173 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001174 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001175
Tobias Boschddd16492019-08-14 09:29:54 -07001176 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001177 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001178 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1179 # Can't update the wrapper in place to not affect the chroot,
1180 # but only the extracted toolchain.
1181 os.unlink(sysroot_wrapper)
1182 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001183 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001184
1185
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001186def _ProcessClangWrappers(target, output_dir):
1187 """Remove chroot-specific things from our sysroot wrappers"""
1188 clang_bin_path = '/usr/bin'
1189 # Disable ccache from clang wrappers.
1190 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1191 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1192 f'/usr/bin/{target}-clang')
1193 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1194 f'/usr/bin/{target}-clang++')
1195
1196
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001197def _CreateMainLibDir(target, output_dir):
1198 """Create some lib dirs so that compiler can get the right Gcc paths"""
1199 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1200 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1201
1202
Manoj Guptadf8b3872022-01-13 11:57:36 -08001203def _CreateRemoteToolchainFile(output_dir):
1204 """Create a remote_toolchain_inputs file for reclient/RBE"""
1205 # The inputs file lists all files/shared libraries needed to run clang.
1206 # All inputs are relative to location of clang binary and one input
1207 # location per line of file e.g.
1208 # clang-13.elf
1209 # clang++-13.elf
1210 # relative/path/to/clang/resource/directory
1211
1212 clang_path = os.path.join(output_dir, 'usr/bin')
1213 # Add needed shared libraries and internal files e.g. allowlists.
1214 toolchain_inputs = ['../../lib']
1215 clang_shared_dirs = glob.glob(
1216 os.path.join(output_dir, 'usr/lib64/clang/*/share'))
1217 for clang_dir in clang_shared_dirs:
1218 toolchain_inputs.append(os.path.relpath(clang_dir, clang_path))
1219
1220 # Add actual clang binaries/wrappers.
1221 for clang_files in glob.glob(os.path.join(clang_path, 'clang*-[0-9]*')):
1222 toolchain_inputs.append(os.path.basename(clang_files))
1223
1224 with open(os.path.join(clang_path, 'remote_toolchain_inputs'), 'w') as f:
1225 f.writelines('%s\n' % line for line in toolchain_inputs)
1226
1227
Mike Frysinger35247af2012-11-16 18:58:06 -05001228def _ProcessDistroCleanups(target, output_dir):
1229 """Clean up the tree and remove all distro-specific requirements
1230
1231 Args:
1232 target: The toolchain target name
1233 output_dir: The output directory to clean up
1234 """
1235 _ProcessBinutilsConfig(target, output_dir)
1236 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001237 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001238 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001239 _CreateMainLibDir(target, output_dir)
Manoj Guptadf8b3872022-01-13 11:57:36 -08001240 _CreateRemoteToolchainFile(output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001241
1242 osutils.RmDir(os.path.join(output_dir, 'etc'))
1243
1244
1245def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1246 """Setup a tree from the packages for the specified target
1247
1248 This populates a path with all the files from toolchain packages so that
1249 a tarball can easily be generated from the result.
1250
1251 Args:
1252 target: The target to create a packagable root from
1253 output_dir: The output directory to place all the files
1254 ldpaths: A dict of static ldpath information
1255 root: The root path to pull all packages/files from
1256 """
1257 # Find all the files owned by the packages for this target.
1258 paths, elfs = _GetFilesForTarget(target, root=root)
1259
1260 # Link in all the package's files, any ELF dependencies, and wrap any
1261 # executable ELFs with helper scripts.
1262 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001263 """Move /usr/bin to /bin so people can just use that toplevel dir
1264
George Burgess IVca1d7612020-10-01 00:38:32 -07001265 Note we do not apply this to clang or rust - there is correlation between
1266 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001267 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001268 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001269 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001270 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001271 return path[4:]
1272 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001273 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1274 path_rewrite_func=MoveUsrBinToBin, root=root)
1275
1276 # The packages, when part of the normal distro, have helper scripts
1277 # that setup paths and such. Since we are making this standalone, we
1278 # need to preprocess all that ourselves.
1279 _ProcessDistroCleanups(target, output_dir)
1280
1281
1282def CreatePackages(targets_wanted, output_dir, root='/'):
1283 """Create redistributable cross-compiler packages for the specified targets
1284
1285 This creates toolchain packages that should be usable in conjunction with
1286 a downloaded sysroot (created elsewhere).
1287
1288 Tarballs (one per target) will be created in $PWD.
1289
1290 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001291 targets_wanted: The targets to package up.
1292 output_dir: The directory to put the packages in.
1293 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001294 """
Ralph Nathan03047282015-03-23 11:09:32 -07001295 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001296 osutils.SafeMakedirs(output_dir)
1297 ldpaths = lddtree.LoadLdpaths(root)
1298 targets = ExpandTargets(targets_wanted)
1299
Mike Frysinger221bd822017-09-29 02:51:47 -04001300 with osutils.TempDir(prefix='create-packages') as tempdir:
1301 logging.debug('Using tempdir: %s', tempdir)
1302
Mike Frysinger35247af2012-11-16 18:58:06 -05001303 # We have to split the root generation from the compression stages. This is
1304 # because we hardlink in all the files (to avoid overhead of reading/writing
1305 # the copies multiple times). But tar gets angry if a file's hardlink count
1306 # changes from when it starts reading a file to when it finishes.
1307 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1308 for target in targets:
1309 output_target_dir = os.path.join(tempdir, target)
1310 queue.put([target, output_target_dir, ldpaths, root])
1311
1312 # Build the tarball.
1313 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1314 for target in targets:
1315 tar_file = os.path.join(output_dir, target + '.tar.xz')
1316 queue.put([tar_file, os.path.join(tempdir, target)])
1317
1318
Mike Frysinger07534cf2017-09-12 17:40:21 -04001319def GetParser():
1320 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001321 parser = commandline.ArgumentParser(description=__doc__)
1322 parser.add_argument('-u', '--nousepkg',
1323 action='store_false', dest='usepkg', default=True,
1324 help='Use prebuilt packages if possible')
1325 parser.add_argument('-d', '--deleteold',
1326 action='store_true', dest='deleteold', default=False,
1327 help='Unmerge deprecated packages')
1328 parser.add_argument('-t', '--targets',
1329 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001330 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001331 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001332 "allowed. Defaults to 'sdk'.")
1333 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1334 help='Comma separated list of boards whose toolchains we '
1335 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001336 parser.add_argument('--hostonly',
1337 dest='hostonly', default=False, action='store_true',
1338 help='Only setup the host toolchain. '
1339 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001340 parser.add_argument('--show-board-cfg', '--show-cfg',
1341 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001342 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001343 parser.add_argument('--show-packages', default=None,
1344 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001345 parser.add_argument('--create-packages',
1346 action='store_true', default=False,
1347 help='Build redistributable packages')
1348 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1349 help='Output directory')
1350 parser.add_argument('--reconfig', default=False, action='store_true',
1351 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001352 parser.add_argument('--sysroot', type='path',
1353 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001354 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001355
Mike Frysinger07534cf2017-09-12 17:40:21 -04001356
1357def main(argv):
1358 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001359 options = parser.parse_args(argv)
1360 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001361
Mike Frysinger35247af2012-11-16 18:58:06 -05001362 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001363 conflicting_options = (
1364 options.cfg_name,
1365 options.show_packages,
1366 options.create_packages,
1367 )
1368 if sum(bool(x) for x in conflicting_options) > 1:
1369 parser.error('conflicting options: create-packages & show-packages & '
1370 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001371
Gilad Arnold8195b532015-04-07 10:56:30 +03001372 targets_wanted = set(options.targets.split(','))
1373 boards_wanted = (set(options.include_boards.split(','))
1374 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001375
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001376 if options.cfg_name:
1377 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001378 elif options.show_packages is not None:
1379 cros_build_lib.AssertInsideChroot()
1380 target = options.show_packages
1381 Crossdev.Load(False)
1382 for package in GetTargetPackages(target):
1383 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001384 elif options.create_packages:
1385 cros_build_lib.AssertInsideChroot()
1386 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001387 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001388 else:
1389 cros_build_lib.AssertInsideChroot()
1390 # This has to be always run as root.
1391 if os.geteuid() != 0:
1392 cros_build_lib.Die('this script must be run as root')
1393
1394 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001395 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001396 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001397 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001398 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001399 Crossdev.Save()
1400
1401 return 0