blob: e71bdcae7518d7f1febc89a8a43d44ce04eb9900 [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',
Manoj Gupta93713122020-10-29 17:52:16 -0700103 'i686-pc-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700104 'x86_64-cros-linux-gnu',
105)
106
107LLVM_PKGS_TABLE = {
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700108 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700109 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
110 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700111}
112
David James66a09c42012-11-05 13:31:38 -0800113class Crossdev(object):
114 """Class for interacting with crossdev and caching its output."""
115
116 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
117 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800118 # Packages that needs separate handling, in addition to what we have from
119 # crossdev.
120 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700121 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800122 'llvm': 'sys-devel',
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700123 'llvm-libunwind': 'sys-libs',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700124 'libcxxabi': 'sys-libs',
125 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700126 'elfutils': 'dev-libs',
Han Shene23782f2016-02-18 12:20:00 -0800127 }
David James66a09c42012-11-05 13:31:38 -0800128
129 @classmethod
130 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400131 """Load crossdev cache from disk.
132
133 We invalidate the cache when crossdev updates or this script changes.
134 """
David James90239b92012-11-05 15:31:34 -0800135 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400136 # If we run the compiled/cached .pyc file, we'll read/hash that when we
137 # really always want to track the source .py file.
138 script = os.path.abspath(__file__)
139 if script.endswith('.pyc'):
140 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500141 setup_toolchains_hash = hashlib.md5(
142 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400143
144 cls._CACHE = {
145 'crossdev_version': crossdev_version,
146 'setup_toolchains_hash': setup_toolchains_hash,
147 }
148
149 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
150 if reconfig:
151 logging.debug('cache: forcing regen due to reconfig')
152 return
153
154 try:
155 file_data = osutils.ReadFile(cls._CACHE_FILE)
156 except IOError as e:
157 if e.errno != errno.ENOENT:
158 logging.warning('cache: reading failed: %s', e)
159 osutils.SafeUnlink(cls._CACHE_FILE)
160 return
161
162 try:
163 data = json.loads(file_data)
164 except ValueError as e:
165 logging.warning('cache: ignoring invalid content: %s', e)
166 return
167
168 if crossdev_version != data.get('crossdev_version'):
169 logging.debug('cache: rebuilding after crossdev upgrade')
170 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
171 logging.debug('cache: rebuilding after cros_setup_toolchains change')
172 else:
173 logging.debug('cache: content is up-to-date!')
174 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800175
176 @classmethod
177 def Save(cls):
178 """Store crossdev cache on disk."""
179 # Save the cache from the successful run.
180 with open(cls._CACHE_FILE, 'w') as f:
181 json.dump(cls._CACHE, f)
182
183 @classmethod
184 def GetConfig(cls, target):
185 """Returns a map of crossdev provided variables about a tuple."""
186 CACHE_ATTR = '_target_tuple_map'
187
188 val = cls._CACHE.setdefault(CACHE_ATTR, {})
189 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400190 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400191 conf = {
192 'crosspkgs': [],
193 'target': toolchain.GetHostTuple(),
194 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400195 if target == 'host':
196 packages_list = HOST_PACKAGES
197 else:
198 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400199 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400200 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400201 else:
202 # Build the crossdev command.
203 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
204 if target in TARGET_COMPILER_RT_ENABLED:
205 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400206 if target in TARGET_LLVM_PKGS_ENABLED:
207 for pkg in LLVM_PKGS_TABLE:
208 cmd.extend(LLVM_PKGS_TABLE[pkg])
Manoj Gupta78607282021-05-24 16:58:13 +0000209 if target in TARGET_GO_ENABLED:
210 cmd.extend(CROSSDEV_GO_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400211 cmd.extend(['-t', target])
212 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400213 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500214 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500215 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400216 # List of tuples split at the first '=', converted into dict.
217 conf = dict((k, cros_build_lib.ShellUnquote(v))
218 for k, v in (x.split('=', 1) for x in out))
219 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800220
Mike Frysinger66bfde52017-09-12 16:42:57 -0400221 manual_pkgs = cls.MANUAL_PKGS
222
223 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400224 conf[pkg + '_pn'] = pkg
225 conf[pkg + '_category'] = cat
226 if pkg not in conf['crosspkgs']:
227 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800228
229 val[target] = conf
230
David James66a09c42012-11-05 13:31:38 -0800231 return val[target]
232
233 @classmethod
234 def UpdateTargets(cls, targets, usepkg, config_only=False):
235 """Calls crossdev to initialize a cross target.
236
237 Args:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700238 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700239 usepkg: Copies the commandline opts.
240 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800241 """
242 configured_targets = cls._CACHE.setdefault('configured_targets', [])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700243 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800244
Manoj Gupta4d016f62021-10-19 16:39:34 -0700245 # Schedule all of the targets in parallel, and let them run.
246 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
247 for target_name in targets:
248 # We already started this target in this loop.
249 if target_name in started_targets:
250 continue
251 # The target is already configured.
252 if config_only and target_name in configured_targets:
253 continue
254 queue.put([target_name, targets[target_name], usepkg, config_only])
255 started_targets.add(target_name)
256
257 @classmethod
258 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
259 """Calls crossdev to initialize a cross target.
260
261 Args:
262 target_name: The name of the target to initialize.
263 target: The target info for initializing.
264 usepkg: Copies the commandline opts.
265 config_only: Just update.
266 """
267 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800268 cmdbase = ['crossdev', '--show-fail-log']
269 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
270 # Pick stable by default, and override as necessary.
271 cmdbase.extend(['-P', '--oneshot'])
272 if usepkg:
273 cmdbase.extend(['-P', '--getbinpkg',
274 '-P', '--usepkgonly',
275 '--without-headers'])
276
Christopher Wileyb22c0712015-06-02 10:37:03 -0700277 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800278 cmdbase.extend(['--overlays', overlays])
279 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
280
Manoj Gupta4d016f62021-10-19 16:39:34 -0700281 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800282
Manoj Gupta4d016f62021-10-19 16:39:34 -0700283 for pkg in GetTargetPackages(target_name):
284 if pkg == 'gdb':
285 # Gdb does not have selectable versions.
286 cmd.append('--ex-gdb')
287 elif pkg == 'ex_compiler-rt':
288 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
289 elif pkg == 'ex_go':
290 # Go does not have selectable versions.
291 cmd.extend(CROSSDEV_GO_ARGS)
292 elif pkg in LLVM_PKGS_TABLE:
293 cmd.extend(LLVM_PKGS_TABLE[pkg])
294 elif pkg in cls.MANUAL_PKGS:
295 pass
David James66a09c42012-11-05 13:31:38 -0800296 else:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700297 # The first of the desired versions is the "primary" one.
298 version = GetDesiredPackageVersions(target_name, pkg)[0]
299 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800300
Manoj Gupta4d016f62021-10-19 16:39:34 -0700301 cmd.extend(target['crossdev'].split())
302
303 if config_only:
304 # In this case we want to just quietly reinit
305 cmd.append('--init-target')
306 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
307 else:
308 cros_build_lib.run(cmd)
309
310 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800311
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100312
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100313def GetTargetPackages(target):
314 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800315 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100316 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800317 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100318
319
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100320# Portage helper functions:
321def GetPortagePackage(target, package):
322 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800323 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100324 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400325 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100326 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100327 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100328 category = conf['category']
329 # Portage package:
330 pn = conf[package + '_pn']
331 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500332 assert category
333 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100334 return '%s/%s' % (category, pn)
335
336
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700337def PortageTrees(root):
338 """Return the portage trees for a given root."""
339 if root == '/':
340 return portage.db['/']
341 # The portage logic requires the path always end in a slash.
342 root = root.rstrip('/') + '/'
343 return portage.create_trees(target_root=root, config_root=root)[root]
344
345
346def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347 """Extracts the list of current versions of a target, package pair.
348
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500349 Args:
350 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700351 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100352
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500353 Returns:
354 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100355 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100356 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700357 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100358 version = portage.versions.cpv_getversion(pkg)
359 versions.append(version)
360 return versions
361
362
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700363def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100364 """Extracts the current stable version for a given package.
365
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500366 Args:
367 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
368 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700369 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100370
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500371 Returns:
372 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100373 """
David James90239b92012-11-05 15:31:34 -0800374 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700375 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800376 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100377
378
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700379def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100380 """Resolves keywords in a given version list for a particular package.
381
382 Resolving means replacing PACKAGE_STABLE with the actual number.
383
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500384 Args:
385 target: The target to operate on (e.g. i686-pc-linux-gnu)
386 package: The target/package to operate on (e.g. gcc)
387 versions: List of versions to resolve
388 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700389 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100390
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500391 Returns:
392 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100393 """
394 resolved = []
David James90239b92012-11-05 15:31:34 -0800395 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700396 if not installed:
397 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100398 for version in versions:
399 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700400 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400401 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100402 resolved.append(version)
403 return resolved
404
405
406def GetDesiredPackageVersions(target, package):
407 """Produces the list of desired versions for each target, package pair.
408
409 The first version in the list is implicitly treated as primary, ie.
410 the version that will be initialized by crossdev and selected.
411
412 If the version is PACKAGE_STABLE, it really means the current version which
413 is emerged by using the package atom with no particular version key.
414 Since crossdev unmasks all packages by default, this will actually
415 mean 'unstable' in most cases.
416
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500417 Args:
418 target: The target to operate on (e.g. i686-pc-linux-gnu)
419 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100420
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500421 Returns:
422 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100423 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400424 if package in GetTargetPackages(target):
425 return [PACKAGE_STABLE]
426 else:
427 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100428
429
430def TargetIsInitialized(target):
431 """Verifies if the given list of targets has been correctly initialized.
432
433 This determines whether we have to call crossdev while emerging
434 toolchain packages or can do it using emerge. Emerge is naturally
435 preferred, because all packages can be updated in a single pass.
436
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500437 Args:
438 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500440 Returns:
441 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442 """
443 # Check if packages for the given target all have a proper version.
444 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100445 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800446 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100447 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400448 if not (GetStablePackageVersion(atom, True) and
449 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100450 return False
451 return True
452 except cros_build_lib.RunCommandError:
453 # Fails - The target has likely never been initialized before.
454 return False
455
456
457def RemovePackageMask(target):
458 """Removes a package.mask file for the given platform.
459
460 The pre-existing package.mask files can mess with the keywords.
461
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500462 Args:
463 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100464 """
465 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700466 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100467
468
Zdenek Behan508dcce2011-12-05 15:39:32 +0100469# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700470def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500471 """Rebuild libtool as needed
472
473 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
474 gcc, libtool will break. We can't use binary packages either as those will
475 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700476
477 Args:
478 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500479 """
480 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700481 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500482 for line in f:
483 # Look for a line like:
484 # sys_lib_search_path_spec="..."
485 # It'll be a list of paths and gcc will be one of them.
486 if line.startswith('sys_lib_search_path_spec='):
487 line = line.rstrip()
488 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400489 root_path = os.path.join(root, path.lstrip(os.path.sep))
490 logging.debug('Libtool: checking %s', root_path)
491 if not os.path.exists(root_path):
492 logging.info('Rebuilding libtool after gcc upgrade')
493 logging.info(' %s', line)
494 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500495 needs_update = True
496 break
497
498 if needs_update:
499 break
500
501 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700502 cmd = [EMERGE_CMD, '--oneshot']
503 if root != '/':
504 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
505 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400506 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400507 else:
508 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500509
510
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700511def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100512 """Determines which packages need update/unmerge and defers to portage.
513
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500514 Args:
515 targets: The list of targets to update
516 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700517 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100518 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100519 # For each target, we do two things. Figure out the list of updates,
520 # and figure out the appropriate keywords/masks. Crossdev will initialize
521 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400522 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800523 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000524 # Used to keep track of post-cross packages. These are allowed to have
525 # implicit dependencies on toolchain packages, and therefore need to
526 # be built last.
527 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000529 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400530 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100531 # Record the highest needed version for each target, for masking purposes.
532 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100533 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400535 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100536 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700537 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200539 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400540 if pkg in NEW_PACKAGES and usepkg:
541 # Skip this binary package (for now).
542 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100543 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400544 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000545 if is_post_cross_target:
546 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100547
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400548 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100549 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400550 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800551 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100552
Mike Frysinger3bba5032016-09-20 14:15:04 -0400553 logging.info('Updating packages:')
554 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100555
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100556 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100557 if usepkg:
558 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700559 if root != '/':
560 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000562 if usepkg:
563 # Since we are not building from source, we can handle
564 # all packages in one go.
565 cmd.extend(packages)
566 cros_build_lib.run(cmd)
567 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700568 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
569 if pre_cross_items:
570 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000571 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700572 if post_cross_items:
573 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800574 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100575
576
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700577def CleanTargets(targets, root='/'):
578 """Unmerges old packages that are assumed unnecessary.
579
580 Args:
581 targets: The list of targets to clean up.
582 root: The install root in which we want packages cleaned up.
583 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100584 unmergemap = {}
585 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400586 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100587 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400588 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100589 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700590 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100591 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700592 # NOTE: This refers to installed packages (vartree) rather than the
593 # Portage version (porttree and/or bintree) when determining the current
594 # version. While this isn't the most accurate thing to do, it is probably
595 # a good simple compromise, which should have the desired result of
596 # uninstalling everything but the latest installed version. In
597 # particular, using the bintree (--usebinpkg) requires a non-trivial
598 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200599 desired_num = VersionListToNumeric(target, package, desired, True)
600 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400601 logging.warning('Error detecting stable version for %s, '
602 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200603 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100604 unmergemap[pkg] = set(current).difference(desired_num)
605
606 # Cleaning doesn't care about consistency and rebuilding package.* files.
607 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400608 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100609 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
610
611 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400612 logging.info('Cleaning packages:')
613 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100614 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700615 if root != '/':
616 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100617 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400618 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100619 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400620 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100621
622
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200623def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100624 """Runs gcc-config and binutils-config to select the desired.
625
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500626 Args:
627 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700628 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100629 """
630 for package in ['gcc', 'binutils']:
631 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400632 # See if this package is part of this target.
633 if package not in GetTargetPackages(target):
634 logging.debug('%s: %s is not used', target, package)
635 continue
636
Zdenek Behan508dcce2011-12-05 15:39:32 +0100637 # Pick the first version in the numbered list as the selected one.
638 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700639 desired_num = VersionListToNumeric(target, package, desired, True,
640 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100641 desired = desired_num[0]
642 # *-config does not play revisions, strip them, keep just PV.
643 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
644
Mike Frysinger785b0c32017-09-13 01:35:59 -0400645 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100646 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800647 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100648
649 # And finally, attach target to it.
650 desired = '%s-%s' % (target, desired)
651
David James7ec5efc2012-11-06 09:39:49 -0800652 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700653 if root != '/':
654 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800655 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400656 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500657 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500658 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500659 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700660
661 # Do not reconfig when the current is live or nothing needs to be done.
662 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100663 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500664 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400665 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100666
667
Mike Frysinger35247af2012-11-16 18:58:06 -0500668def ExpandTargets(targets_wanted):
669 """Expand any possible toolchain aliases into full targets
670
671 This will expand 'all' and 'sdk' into the respective toolchain tuples.
672
673 Args:
674 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500675
Mike Frysinger35247af2012-11-16 18:58:06 -0500676 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300677 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500678 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500679 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700680 if targets_wanted == set(['boards']):
681 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300682 return {}
683
684 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500685 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300686 return all_targets
687 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500688 # Filter out all the non-sdk toolchains as we don't want to mess
689 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300690 return toolchain.FilterToolchains(all_targets, 'sdk', True)
691
692 # Verify user input.
693 nonexistent = targets_wanted.difference(all_targets)
694 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400695 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300696 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500697
698
David Jamesf8c672f2012-11-06 13:38:11 -0800699def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700700 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100701 """Performs all steps to create a synchronized toolchain enviroment.
702
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500703 Args:
704 usepkg: Use prebuilt packages
705 deleteold: Unmerge deprecated packages
706 hostonly: Only setup the host toolchain
707 reconfig: Reload crossdev config and reselect toolchains
708 targets_wanted: All the targets to update
709 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700710 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100711 """
David Jamesf8c672f2012-11-06 13:38:11 -0800712 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100713 if not hostonly:
714 # For hostonly, we can skip most of the below logic, much of which won't
715 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500716 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400717
Don Garrettc0c74002015-10-09 12:58:19 -0700718 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300719 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400720 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800721 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100722
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100723 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400724 for target in targets:
725 if TargetIsInitialized(target):
726 reconfig_targets[target] = targets[target]
727 else:
728 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100729 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400730 logging.info('The following targets need to be re-initialized:')
731 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800732 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200733 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800734 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100735
Mike Frysinger66814c32017-10-09 18:11:46 -0400736 # If we're building a subset of toolchains for a board, we might not have
737 # all the tuples that the packages expect. We don't define the "full" set
738 # of tuples currently other than "whatever the full sdk has normally".
739 if usepkg or set(('all', 'sdk')) & targets_wanted:
740 # Since we have cross-compilers now, we can update these packages.
741 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400742
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100743 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400744 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100745
746 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700747 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200748 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800749
750 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700751 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100752
Mike Frysingerc880a962013-11-08 13:59:06 -0500753 # Now that we've cleared out old versions, see if we need to rebuild
754 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700755 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500756
Zdenek Behan508dcce2011-12-05 15:39:32 +0100757
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700758def ShowConfig(name):
759 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500760
761 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700762 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500763 """
Don Garrettc0c74002015-10-09 12:58:19 -0700764 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500765 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400766 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400767 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400768 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
769 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500770
771
Mike Frysinger35247af2012-11-16 18:58:06 -0500772def GeneratePathWrapper(root, wrappath, path):
773 """Generate a shell script to execute another shell script
774
775 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
776 argv[0] won't be pointing to the correct path, generate a shell script that
777 just executes another program with its full path.
778
779 Args:
780 root: The root tree to generate scripts inside of
781 wrappath: The full path (inside |root|) to create the wrapper
782 path: The target program which this wrapper will execute
783 """
784 replacements = {
785 'path': path,
786 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
787 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900788
789 # Do not use exec here, because exec invokes script with absolute path in
790 # argv0. Keeping relativeness allows us to remove abs path from compile result
791 # and leads directory independent build cache sharing in some distributed
792 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500793 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900794basedir=$(dirname "$0")
795"${basedir}/%(relroot)s%(path)s" "$@"
796exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500797""" % replacements
798 root_wrapper = root + wrappath
799 if os.path.islink(root_wrapper):
800 os.unlink(root_wrapper)
801 else:
802 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
803 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400804 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500805
806
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700807def FixClangXXWrapper(root, path):
808 """Fix wrapper shell scripts and symlinks for invoking clang++
809
810 In a typical installation, clang++ symlinks to clang, which symlinks to the
811 elf executable. The executable distinguishes between clang and clang++ based
812 on argv[0].
813
814 When invoked through the LdsoWrapper, argv[0] always contains the path to the
815 executable elf file, making clang/clang++ invocations indistinguishable.
816
817 This function detects if the elf executable being wrapped is clang-X.Y, and
818 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
819
820 The calling sequence now becomes:
821 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
822 the Ldsowrapper).
823 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
824 to the original clang-3.9 elf.
825 -) The difference this time is that inside the elf file execution, $0 is
826 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
827
Manoj Guptaae268142018-04-27 23:28:36 -0700828 Update: Starting since clang 7, the clang and clang++ are symlinks to
829 clang-7 binary, not clang-7.0. The pattern match is extended to handle
830 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
831
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700832 Args:
833 root: The root tree to generate scripts / symlinks inside of
834 path: The target elf for which LdsoWrapper was created
835 """
Manoj Guptaae268142018-04-27 23:28:36 -0700836 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700837 logging.info('fixing clang++ invocation for %s', path)
838 clangdir = os.path.dirname(root + path)
839 clang = os.path.basename(path)
840 clangxx = clang.replace('clang', 'clang++')
841
842 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
843 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
844
845 # Create a hardlink clang++-X.Y pointing to clang-X.Y
846 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
847
848 # Adjust the clang++ symlink to point to clang++-X.Y
849 os.unlink(os.path.join(clangdir, 'clang++'))
850 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
851
852
Mike Frysinger35247af2012-11-16 18:58:06 -0500853def FileIsCrosSdkElf(elf):
854 """Determine if |elf| is an ELF that we execute in the cros_sdk
855
856 We don't need this to be perfect, just quick. It makes sure the ELF
857 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
858
859 Args:
860 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500861
Mike Frysinger35247af2012-11-16 18:58:06 -0500862 Returns:
863 True if we think |elf| is a native ELF
864 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500865 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500866 data = f.read(20)
867 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500868 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500869 data[4:5] == b'\x02' and
870 data[5:6] == b'\x01' and
871 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500872
873
874def IsPathPackagable(ptype, path):
875 """Should the specified file be included in a toolchain package?
876
877 We only need to handle files as we'll create dirs as we need them.
878
879 Further, trim files that won't be useful:
880 - non-english translations (.mo) since it'd require env vars
881 - debug files since these are for the host compiler itself
882 - info/man pages as they're big, and docs are online, and the
883 native docs should work fine for the most part (`man gcc`)
884
885 Args:
886 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
887 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500888
Mike Frysinger35247af2012-11-16 18:58:06 -0500889 Returns:
890 True if we want to include this path in the package
891 """
892 return not (ptype in ('dir',) or
893 path.startswith('/usr/lib/debug/') or
894 os.path.splitext(path)[1] == '.mo' or
895 ('/man/' in path or '/info/' in path))
896
897
898def ReadlinkRoot(path, root):
899 """Like os.readlink(), but relative to a |root|
900
901 Args:
902 path: The symlink to read
903 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500904
Mike Frysinger35247af2012-11-16 18:58:06 -0500905 Returns:
906 A fully resolved symlink path
907 """
908 while os.path.islink(root + path):
909 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
910 return path
911
912
913def _GetFilesForTarget(target, root='/'):
914 """Locate all the files to package for |target|
915
916 This does not cover ELF dependencies.
917
918 Args:
919 target: The toolchain target name
920 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500921
Mike Frysinger35247af2012-11-16 18:58:06 -0500922 Returns:
923 A tuple of a set of all packable paths, and a set of all paths which
924 are also native ELFs
925 """
926 paths = set()
927 elfs = set()
928
929 # Find all the files owned by the packages for this target.
930 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500931
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700932 # Skip Go compiler from redistributable packages.
933 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
934 # into it. Due to this, the toolchain cannot be unpacked anywhere
935 # else and be readily useful. To enable packaging Go, we need to:
936 # -) Tweak the wrappers/environment to override GOROOT
937 # automatically based on the unpack location.
938 # -) Make sure the ELF dependency checking and wrapping logic
939 # below skips the Go toolchain executables and libraries.
940 # -) Make sure the packaging process maintains the relative
941 # timestamps of precompiled standard library packages.
942 # (see dev-lang/go ebuild for details).
943 if pkg == 'ex_go':
944 continue
945
Yunlian Jiang36f35242018-04-27 10:18:40 -0700946 # Use armv7a-cros-linux-gnueabi/compiler-rt for
947 # armv7a-cros-linux-gnueabihf/compiler-rt.
948 # Currently the armv7a-cros-linux-gnueabi is actually
949 # the same as armv7a-cros-linux-gnueabihf with different names.
950 # Because of that, for compiler-rt, it generates the same binary in
951 # the same location. To avoid the installation conflict, we do not
952 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
953 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
954 # toolchain.
955 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
956 atom = GetPortagePackage(target, pkg)
957 cat, pn = atom.split('/')
958 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700959 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
960 settings=portage.settings)
961 contents = dblink.getcontents()
962 if not contents:
963 if 'hf' in target:
964 new_target = 'armv7a-cros-linux-gnueabi'
965 else:
966 new_target = 'armv7a-cros-linux-gnueabihf'
967 atom = GetPortagePackage(new_target, pkg)
968 else:
969 atom = GetPortagePackage(target, pkg)
970
Mike Frysinger35247af2012-11-16 18:58:06 -0500971 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700972 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700973 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500974
Mike Frysinger35247af2012-11-16 18:58:06 -0500975 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
976 settings=portage.settings)
977 contents = dblink.getcontents()
978 for obj in contents:
979 ptype = contents[obj][0]
980 if not IsPathPackagable(ptype, obj):
981 continue
982
983 if ptype == 'obj':
984 # For native ELFs, we need to pull in their dependencies too.
985 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500986 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500987 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500988 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500989 paths.add(obj)
990
991 return paths, elfs
992
993
994def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500995 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500996 """Link in all packable files and their runtime dependencies
997
998 This also wraps up executable ELFs with helper scripts.
999
1000 Args:
1001 output_dir: The output directory to store files
1002 paths: All the files to include
1003 elfs: All the files which are ELFs (a subset of |paths|)
1004 ldpaths: A dict of static ldpath information
1005 path_rewrite_func: User callback to rewrite paths in output_dir
1006 root: The root path to pull all packages/files from
1007 """
1008 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001009 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001010 for path in paths:
1011 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001012 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001013 dst = output_dir + new_path
1014 osutils.SafeMakedirs(os.path.dirname(dst))
1015
1016 # Is this a symlink which we have to rewrite or wrap?
1017 # Delay wrap check until after we have created all paths.
1018 src = root + path
1019 if os.path.islink(src):
1020 tgt = os.readlink(src)
1021 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001022 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001023
1024 # Rewrite absolute links to relative and then generate the symlink
1025 # ourselves. All other symlinks can be hardlinked below.
1026 if tgt[0] == '/':
1027 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1028 os.symlink(tgt, dst)
1029 continue
1030
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001031 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001032 os.link(src, dst)
1033
Mike Frysinger35247af2012-11-16 18:58:06 -05001034 # Locate all the dependencies for all the ELFs. Stick them all in the
1035 # top level "lib" dir to make the wrapper simpler. This exact path does
1036 # not matter since we execute ldso directly, and we tell the ldso the
1037 # exact path to search for its libraries.
1038 libdir = os.path.join(output_dir, 'lib')
1039 osutils.SafeMakedirs(libdir)
1040 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001041 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001042 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001043 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001044 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001045 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001046 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001047
Mike Frysinger9fe02342019-12-12 17:52:53 -05001048 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1049 if os.path.basename(elf).startswith('libopcodes-'):
1050 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001051
Mike Frysinger00b129f2021-04-21 18:11:48 -04001052 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001053 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001054 src = path = lib_data['path']
1055 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001056 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001057 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001058
1059 # No need to try and copy the same source lib multiple times.
1060 if path in donelibs:
1061 continue
1062 donelibs.add(path)
1063
1064 # Die if we try to normalize different source libs with the same basename.
1065 if lib in basenamelibs:
1066 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1067 os.path.join('/lib', lib), path,
1068 ' '.join(x for x in donelibs
1069 if x != path and os.path.basename(x) == lib))
1070 # TODO(crbug.com/917193): Make this fatal.
1071 # cros_build_lib.Die('Unable to resolve lib conflicts')
1072 continue
1073 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001074
1075 # Needed libs are the SONAME, but that is usually a symlink, not a
1076 # real file. So link in the target rather than the symlink itself.
1077 # We have to walk all the possible symlinks (SONAME could point to a
1078 # symlink which points to a symlink), and we have to handle absolute
1079 # ourselves (since we have a "root" argument).
1080 dst = os.path.join(libdir, os.path.basename(path))
1081 src = ReadlinkRoot(src, root)
1082
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001083 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001084 os.link(root + src, dst)
1085
Mike Frysinger00b129f2021-04-21 18:11:48 -04001086 # Do not create wrapper for libc. crbug.com/766827
1087 if interp and not glibc_re.search(elf):
1088 # Generate a wrapper if it is executable.
1089 interp = os.path.join('/lib', os.path.basename(interp))
1090 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1091 libpaths=e['rpath'] + e['runpath'])
1092 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1093
1094 # Wrap any symlinks to the wrapper.
1095 if elf in sym_paths:
1096 link = sym_paths[elf]
1097 GeneratePathWrapper(output_dir, link, elf)
1098
Mike Frysinger35247af2012-11-16 18:58:06 -05001099
1100def _EnvdGetVar(envd, var):
1101 """Given a Gentoo env.d file, extract a var from it
1102
1103 Args:
1104 envd: The env.d file to load (may be a glob path)
1105 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001106
Mike Frysinger35247af2012-11-16 18:58:06 -05001107 Returns:
1108 The value of |var|
1109 """
1110 envds = glob.glob(envd)
1111 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1112 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001113 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001114
1115
1116def _ProcessBinutilsConfig(target, output_dir):
1117 """Do what binutils-config would have done"""
1118 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001119
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001120 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001121 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1122 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001123 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001124 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001125 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1126 % globpath)
1127 srcpath = srcpath[0]
1128 ld_path = os.path.join(srcpath, 'ld')
1129 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1130 ld_path = os.path.join(srcpath, 'ld.bfd')
1131 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001132
Mike Frysinger78b7a812014-11-26 19:45:23 -05001133 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001134 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1135 for prog in os.listdir(output_dir + srcpath):
1136 # Skip binaries already wrapped.
1137 if not prog.endswith('.real'):
1138 GeneratePathWrapper(output_dir, binpath + prog,
1139 os.path.join(srcpath, prog))
1140 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1141 os.path.join(srcpath, prog))
1142
David James27ac4ae2012-12-03 23:16:15 -08001143 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001144 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001145 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1146 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1147 output_dir + libpath)
1148
1149
1150def _ProcessGccConfig(target, output_dir):
1151 """Do what gcc-config would have done"""
1152 binpath = '/bin'
1153 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1154 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1155 for prog in os.listdir(output_dir + srcpath):
1156 # Skip binaries already wrapped.
1157 if (not prog.endswith('.real') and
1158 not prog.endswith('.elf') and
1159 prog.startswith(target)):
1160 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1161 os.path.join(srcpath, prog))
1162 return srcpath
1163
1164
Frank Henigman179ec7c2015-02-06 03:01:09 -05001165def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1166 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001167 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001168
Tobias Boschddd16492019-08-14 09:29:54 -07001169 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001170 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001171 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1172 # Can't update the wrapper in place to not affect the chroot,
1173 # but only the extracted toolchain.
1174 os.unlink(sysroot_wrapper)
1175 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001176 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001177
1178
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001179def _ProcessClangWrappers(target, output_dir):
1180 """Remove chroot-specific things from our sysroot wrappers"""
1181 clang_bin_path = '/usr/bin'
1182 # Disable ccache from clang wrappers.
1183 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1184 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1185 f'/usr/bin/{target}-clang')
1186 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1187 f'/usr/bin/{target}-clang++')
1188
1189
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001190def _CreateMainLibDir(target, output_dir):
1191 """Create some lib dirs so that compiler can get the right Gcc paths"""
1192 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1193 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1194
1195
Mike Frysinger35247af2012-11-16 18:58:06 -05001196def _ProcessDistroCleanups(target, output_dir):
1197 """Clean up the tree and remove all distro-specific requirements
1198
1199 Args:
1200 target: The toolchain target name
1201 output_dir: The output directory to clean up
1202 """
1203 _ProcessBinutilsConfig(target, output_dir)
1204 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001205 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001206 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001207 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001208
1209 osutils.RmDir(os.path.join(output_dir, 'etc'))
1210
1211
1212def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1213 """Setup a tree from the packages for the specified target
1214
1215 This populates a path with all the files from toolchain packages so that
1216 a tarball can easily be generated from the result.
1217
1218 Args:
1219 target: The target to create a packagable root from
1220 output_dir: The output directory to place all the files
1221 ldpaths: A dict of static ldpath information
1222 root: The root path to pull all packages/files from
1223 """
1224 # Find all the files owned by the packages for this target.
1225 paths, elfs = _GetFilesForTarget(target, root=root)
1226
1227 # Link in all the package's files, any ELF dependencies, and wrap any
1228 # executable ELFs with helper scripts.
1229 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001230 """Move /usr/bin to /bin so people can just use that toplevel dir
1231
George Burgess IVca1d7612020-10-01 00:38:32 -07001232 Note we do not apply this to clang or rust - there is correlation between
1233 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001234 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001235 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001236 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001237 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001238 return path[4:]
1239 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001240 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1241 path_rewrite_func=MoveUsrBinToBin, root=root)
1242
1243 # The packages, when part of the normal distro, have helper scripts
1244 # that setup paths and such. Since we are making this standalone, we
1245 # need to preprocess all that ourselves.
1246 _ProcessDistroCleanups(target, output_dir)
1247
1248
1249def CreatePackages(targets_wanted, output_dir, root='/'):
1250 """Create redistributable cross-compiler packages for the specified targets
1251
1252 This creates toolchain packages that should be usable in conjunction with
1253 a downloaded sysroot (created elsewhere).
1254
1255 Tarballs (one per target) will be created in $PWD.
1256
1257 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001258 targets_wanted: The targets to package up.
1259 output_dir: The directory to put the packages in.
1260 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001261 """
Ralph Nathan03047282015-03-23 11:09:32 -07001262 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001263 osutils.SafeMakedirs(output_dir)
1264 ldpaths = lddtree.LoadLdpaths(root)
1265 targets = ExpandTargets(targets_wanted)
1266
Mike Frysinger221bd822017-09-29 02:51:47 -04001267 with osutils.TempDir(prefix='create-packages') as tempdir:
1268 logging.debug('Using tempdir: %s', tempdir)
1269
Mike Frysinger35247af2012-11-16 18:58:06 -05001270 # We have to split the root generation from the compression stages. This is
1271 # because we hardlink in all the files (to avoid overhead of reading/writing
1272 # the copies multiple times). But tar gets angry if a file's hardlink count
1273 # changes from when it starts reading a file to when it finishes.
1274 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1275 for target in targets:
1276 output_target_dir = os.path.join(tempdir, target)
1277 queue.put([target, output_target_dir, ldpaths, root])
1278
1279 # Build the tarball.
1280 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1281 for target in targets:
1282 tar_file = os.path.join(output_dir, target + '.tar.xz')
1283 queue.put([tar_file, os.path.join(tempdir, target)])
1284
1285
Mike Frysinger07534cf2017-09-12 17:40:21 -04001286def GetParser():
1287 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001288 parser = commandline.ArgumentParser(description=__doc__)
1289 parser.add_argument('-u', '--nousepkg',
1290 action='store_false', dest='usepkg', default=True,
1291 help='Use prebuilt packages if possible')
1292 parser.add_argument('-d', '--deleteold',
1293 action='store_true', dest='deleteold', default=False,
1294 help='Unmerge deprecated packages')
1295 parser.add_argument('-t', '--targets',
1296 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001297 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001298 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001299 "allowed. Defaults to 'sdk'.")
1300 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1301 help='Comma separated list of boards whose toolchains we '
1302 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001303 parser.add_argument('--hostonly',
1304 dest='hostonly', default=False, action='store_true',
1305 help='Only setup the host toolchain. '
1306 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001307 parser.add_argument('--show-board-cfg', '--show-cfg',
1308 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001309 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001310 parser.add_argument('--show-packages', default=None,
1311 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001312 parser.add_argument('--create-packages',
1313 action='store_true', default=False,
1314 help='Build redistributable packages')
1315 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1316 help='Output directory')
1317 parser.add_argument('--reconfig', default=False, action='store_true',
1318 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001319 parser.add_argument('--sysroot', type='path',
1320 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001321 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001322
Mike Frysinger07534cf2017-09-12 17:40:21 -04001323
1324def main(argv):
1325 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001326 options = parser.parse_args(argv)
1327 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001328
Mike Frysinger35247af2012-11-16 18:58:06 -05001329 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001330 conflicting_options = (
1331 options.cfg_name,
1332 options.show_packages,
1333 options.create_packages,
1334 )
1335 if sum(bool(x) for x in conflicting_options) > 1:
1336 parser.error('conflicting options: create-packages & show-packages & '
1337 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001338
Gilad Arnold8195b532015-04-07 10:56:30 +03001339 targets_wanted = set(options.targets.split(','))
1340 boards_wanted = (set(options.include_boards.split(','))
1341 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001342
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001343 if options.cfg_name:
1344 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001345 elif options.show_packages is not None:
1346 cros_build_lib.AssertInsideChroot()
1347 target = options.show_packages
1348 Crossdev.Load(False)
1349 for package in GetTargetPackages(target):
1350 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001351 elif options.create_packages:
1352 cros_build_lib.AssertInsideChroot()
1353 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001354 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001355 else:
1356 cros_build_lib.AssertInsideChroot()
1357 # This has to be always run as root.
1358 if os.geteuid() != 0:
1359 cros_build_lib.Die('this script must be run as root')
1360
1361 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001362 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001363 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001364 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001365 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001366 Crossdev.Save()
1367
1368 return 0