blob: 520e76a0f6980cbbc52aff97faaeafb20157c686 [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 = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700108 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
109 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700110 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
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 Gupta20cfc6c2017-07-19 15:49:55 -0700123 'libcxxabi': 'sys-libs',
124 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700125 'elfutils': 'dev-libs',
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700126 'llvm-libunwind': 'sys-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:
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000238 targets: The list 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', [])
243
244 cmdbase = ['crossdev', '--show-fail-log']
245 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
246 # Pick stable by default, and override as necessary.
247 cmdbase.extend(['-P', '--oneshot'])
248 if usepkg:
249 cmdbase.extend(['-P', '--getbinpkg',
250 '-P', '--usepkgonly',
251 '--without-headers'])
252
Christopher Wileyb22c0712015-06-02 10:37:03 -0700253 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800254 cmdbase.extend(['--overlays', overlays])
255 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
256
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000257 # Build target by the reversed alphabetical order to make sure
258 # armv7a-cros-linux-gnueabihf builds before armv7a-cros-linux-gnueabi
259 # because some dependency issue. This can be reverted once we
260 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
261 for target in sorted(targets, reverse=True):
262 if config_only and target in configured_targets:
263 continue
David James66a09c42012-11-05 13:31:38 -0800264
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000265 cmd = cmdbase + ['-t', target]
266
267 for pkg in GetTargetPackages(target):
268 if pkg == 'gdb':
269 # Gdb does not have selectable versions.
270 cmd.append('--ex-gdb')
271 elif pkg == 'ex_compiler-rt':
272 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
273 elif pkg == 'ex_go':
274 # Go does not have selectable versions.
275 cmd.extend(CROSSDEV_GO_ARGS)
276 elif pkg in LLVM_PKGS_TABLE:
277 cmd.extend(LLVM_PKGS_TABLE[pkg])
278 elif pkg in cls.MANUAL_PKGS:
279 pass
280 else:
281 # The first of the desired versions is the "primary" one.
282 version = GetDesiredPackageVersions(target, pkg)[0]
283 cmd.extend(['--%s' % pkg, version])
284
285 cmd.extend(targets[target]['crossdev'].split())
286 if config_only:
287 # In this case we want to just quietly reinit
288 cmd.append('--init-target')
Mike Frysinger0282d222019-12-17 17:15:48 -0500289 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
David James66a09c42012-11-05 13:31:38 -0800290 else:
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000291 cros_build_lib.run(cmd)
David James66a09c42012-11-05 13:31:38 -0800292
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000293 configured_targets.append(target)
David James66a09c42012-11-05 13:31:38 -0800294
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100295
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100296def GetTargetPackages(target):
297 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800298 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100299 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800300 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100301
302
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100303# Portage helper functions:
304def GetPortagePackage(target, package):
305 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800306 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100307 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400308 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100309 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100310 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100311 category = conf['category']
312 # Portage package:
313 pn = conf[package + '_pn']
314 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500315 assert category
316 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100317 return '%s/%s' % (category, pn)
318
319
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700320def PortageTrees(root):
321 """Return the portage trees for a given root."""
322 if root == '/':
323 return portage.db['/']
324 # The portage logic requires the path always end in a slash.
325 root = root.rstrip('/') + '/'
326 return portage.create_trees(target_root=root, config_root=root)[root]
327
328
329def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100330 """Extracts the list of current versions of a target, package pair.
331
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500332 Args:
333 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700334 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100335
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500336 Returns:
337 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100338 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100339 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700340 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341 version = portage.versions.cpv_getversion(pkg)
342 versions.append(version)
343 return versions
344
345
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700346def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347 """Extracts the current stable version for a given package.
348
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500349 Args:
350 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
351 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700352 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100353
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500354 Returns:
355 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100356 """
David James90239b92012-11-05 15:31:34 -0800357 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700358 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800359 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360
361
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700362def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100363 """Resolves keywords in a given version list for a particular package.
364
365 Resolving means replacing PACKAGE_STABLE with the actual number.
366
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500367 Args:
368 target: The target to operate on (e.g. i686-pc-linux-gnu)
369 package: The target/package to operate on (e.g. gcc)
370 versions: List of versions to resolve
371 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700372 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100373
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500374 Returns:
375 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100376 """
377 resolved = []
David James90239b92012-11-05 15:31:34 -0800378 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700379 if not installed:
380 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100381 for version in versions:
382 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700383 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400384 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100385 resolved.append(version)
386 return resolved
387
388
389def GetDesiredPackageVersions(target, package):
390 """Produces the list of desired versions for each target, package pair.
391
392 The first version in the list is implicitly treated as primary, ie.
393 the version that will be initialized by crossdev and selected.
394
395 If the version is PACKAGE_STABLE, it really means the current version which
396 is emerged by using the package atom with no particular version key.
397 Since crossdev unmasks all packages by default, this will actually
398 mean 'unstable' in most cases.
399
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500400 Args:
401 target: The target to operate on (e.g. i686-pc-linux-gnu)
402 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100403
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500404 Returns:
405 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100406 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400407 if package in GetTargetPackages(target):
408 return [PACKAGE_STABLE]
409 else:
410 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100411
412
413def TargetIsInitialized(target):
414 """Verifies if the given list of targets has been correctly initialized.
415
416 This determines whether we have to call crossdev while emerging
417 toolchain packages or can do it using emerge. Emerge is naturally
418 preferred, because all packages can be updated in a single pass.
419
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500420 Args:
421 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100422
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500423 Returns:
424 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100425 """
426 # Check if packages for the given target all have a proper version.
427 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100428 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800429 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100430 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400431 if not (GetStablePackageVersion(atom, True) and
432 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433 return False
434 return True
435 except cros_build_lib.RunCommandError:
436 # Fails - The target has likely never been initialized before.
437 return False
438
439
440def RemovePackageMask(target):
441 """Removes a package.mask file for the given platform.
442
443 The pre-existing package.mask files can mess with the keywords.
444
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500445 Args:
446 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100447 """
448 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700449 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100450
451
Zdenek Behan508dcce2011-12-05 15:39:32 +0100452# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700453def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500454 """Rebuild libtool as needed
455
456 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
457 gcc, libtool will break. We can't use binary packages either as those will
458 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700459
460 Args:
461 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500462 """
463 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700464 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500465 for line in f:
466 # Look for a line like:
467 # sys_lib_search_path_spec="..."
468 # It'll be a list of paths and gcc will be one of them.
469 if line.startswith('sys_lib_search_path_spec='):
470 line = line.rstrip()
471 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400472 root_path = os.path.join(root, path.lstrip(os.path.sep))
473 logging.debug('Libtool: checking %s', root_path)
474 if not os.path.exists(root_path):
475 logging.info('Rebuilding libtool after gcc upgrade')
476 logging.info(' %s', line)
477 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500478 needs_update = True
479 break
480
481 if needs_update:
482 break
483
484 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700485 cmd = [EMERGE_CMD, '--oneshot']
486 if root != '/':
487 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
488 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400489 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400490 else:
491 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500492
493
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700494def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100495 """Determines which packages need update/unmerge and defers to portage.
496
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500497 Args:
498 targets: The list of targets to update
499 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700500 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100501 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100502 # For each target, we do two things. Figure out the list of updates,
503 # and figure out the appropriate keywords/masks. Crossdev will initialize
504 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400505 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800506 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000507 # Used to keep track of post-cross packages. These are allowed to have
508 # implicit dependencies on toolchain packages, and therefore need to
509 # be built last.
510 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100511 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000512 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400513 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100514 # Record the highest needed version for each target, for masking purposes.
515 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100516 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400518 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100519 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700520 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100521 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200522 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400523 if pkg in NEW_PACKAGES and usepkg:
524 # Skip this binary package (for now).
525 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100526 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400527 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000528 if is_post_cross_target:
529 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100530
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400531 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400533 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800534 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535
Mike Frysinger3bba5032016-09-20 14:15:04 -0400536 logging.info('Updating packages:')
537 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100539 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540 if usepkg:
541 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700542 if root != '/':
543 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100544
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000545 if usepkg:
546 # Since we are not building from source, we can handle
547 # all packages in one go.
548 cmd.extend(packages)
549 cros_build_lib.run(cmd)
550 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700551 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
552 if pre_cross_items:
553 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000554 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700555 if post_cross_items:
556 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800557 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558
559
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700560def CleanTargets(targets, root='/'):
561 """Unmerges old packages that are assumed unnecessary.
562
563 Args:
564 targets: The list of targets to clean up.
565 root: The install root in which we want packages cleaned up.
566 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100567 unmergemap = {}
568 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400569 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100570 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400571 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100572 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700573 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100574 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700575 # NOTE: This refers to installed packages (vartree) rather than the
576 # Portage version (porttree and/or bintree) when determining the current
577 # version. While this isn't the most accurate thing to do, it is probably
578 # a good simple compromise, which should have the desired result of
579 # uninstalling everything but the latest installed version. In
580 # particular, using the bintree (--usebinpkg) requires a non-trivial
581 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200582 desired_num = VersionListToNumeric(target, package, desired, True)
583 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400584 logging.warning('Error detecting stable version for %s, '
585 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200586 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100587 unmergemap[pkg] = set(current).difference(desired_num)
588
589 # Cleaning doesn't care about consistency and rebuilding package.* files.
590 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400591 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100592 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
593
594 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400595 logging.info('Cleaning packages:')
596 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100597 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700598 if root != '/':
599 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100600 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400601 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100602 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400603 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100604
605
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200606def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100607 """Runs gcc-config and binutils-config to select the desired.
608
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500609 Args:
610 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700611 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100612 """
613 for package in ['gcc', 'binutils']:
614 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400615 # See if this package is part of this target.
616 if package not in GetTargetPackages(target):
617 logging.debug('%s: %s is not used', target, package)
618 continue
619
Zdenek Behan508dcce2011-12-05 15:39:32 +0100620 # Pick the first version in the numbered list as the selected one.
621 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700622 desired_num = VersionListToNumeric(target, package, desired, True,
623 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100624 desired = desired_num[0]
625 # *-config does not play revisions, strip them, keep just PV.
626 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
627
Mike Frysinger785b0c32017-09-13 01:35:59 -0400628 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100629 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800630 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100631
632 # And finally, attach target to it.
633 desired = '%s-%s' % (target, desired)
634
David James7ec5efc2012-11-06 09:39:49 -0800635 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700636 if root != '/':
637 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800638 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400639 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500640 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500641 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500642 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700643
644 # Do not reconfig when the current is live or nothing needs to be done.
645 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100646 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500647 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400648 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100649
650
Mike Frysinger35247af2012-11-16 18:58:06 -0500651def ExpandTargets(targets_wanted):
652 """Expand any possible toolchain aliases into full targets
653
654 This will expand 'all' and 'sdk' into the respective toolchain tuples.
655
656 Args:
657 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500658
Mike Frysinger35247af2012-11-16 18:58:06 -0500659 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300660 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500661 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500662 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700663 if targets_wanted == set(['boards']):
664 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300665 return {}
666
667 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500668 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300669 return all_targets
670 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500671 # Filter out all the non-sdk toolchains as we don't want to mess
672 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300673 return toolchain.FilterToolchains(all_targets, 'sdk', True)
674
675 # Verify user input.
676 nonexistent = targets_wanted.difference(all_targets)
677 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400678 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300679 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500680
681
David Jamesf8c672f2012-11-06 13:38:11 -0800682def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700683 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100684 """Performs all steps to create a synchronized toolchain enviroment.
685
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500686 Args:
687 usepkg: Use prebuilt packages
688 deleteold: Unmerge deprecated packages
689 hostonly: Only setup the host toolchain
690 reconfig: Reload crossdev config and reselect toolchains
691 targets_wanted: All the targets to update
692 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700693 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100694 """
David Jamesf8c672f2012-11-06 13:38:11 -0800695 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100696 if not hostonly:
697 # For hostonly, we can skip most of the below logic, much of which won't
698 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500699 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400700
Don Garrettc0c74002015-10-09 12:58:19 -0700701 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300702 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400703 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800704 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100705
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100706 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400707 for target in targets:
708 if TargetIsInitialized(target):
709 reconfig_targets[target] = targets[target]
710 else:
711 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100712 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400713 logging.info('The following targets need to be re-initialized:')
714 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800715 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200716 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800717 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100718
Mike Frysinger66814c32017-10-09 18:11:46 -0400719 # If we're building a subset of toolchains for a board, we might not have
720 # all the tuples that the packages expect. We don't define the "full" set
721 # of tuples currently other than "whatever the full sdk has normally".
722 if usepkg or set(('all', 'sdk')) & targets_wanted:
723 # Since we have cross-compilers now, we can update these packages.
724 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400725
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100726 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400727 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100728
729 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700730 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200731 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800732
733 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700734 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100735
Mike Frysingerc880a962013-11-08 13:59:06 -0500736 # Now that we've cleared out old versions, see if we need to rebuild
737 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700738 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500739
Zdenek Behan508dcce2011-12-05 15:39:32 +0100740
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700741def ShowConfig(name):
742 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500743
744 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700745 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500746 """
Don Garrettc0c74002015-10-09 12:58:19 -0700747 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500748 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400749 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400750 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400751 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
752 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500753
754
Mike Frysinger35247af2012-11-16 18:58:06 -0500755def GeneratePathWrapper(root, wrappath, path):
756 """Generate a shell script to execute another shell script
757
758 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
759 argv[0] won't be pointing to the correct path, generate a shell script that
760 just executes another program with its full path.
761
762 Args:
763 root: The root tree to generate scripts inside of
764 wrappath: The full path (inside |root|) to create the wrapper
765 path: The target program which this wrapper will execute
766 """
767 replacements = {
768 'path': path,
769 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
770 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900771
772 # Do not use exec here, because exec invokes script with absolute path in
773 # argv0. Keeping relativeness allows us to remove abs path from compile result
774 # and leads directory independent build cache sharing in some distributed
775 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500776 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900777basedir=$(dirname "$0")
778"${basedir}/%(relroot)s%(path)s" "$@"
779exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500780""" % replacements
781 root_wrapper = root + wrappath
782 if os.path.islink(root_wrapper):
783 os.unlink(root_wrapper)
784 else:
785 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
786 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400787 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500788
789
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700790def FixClangXXWrapper(root, path):
791 """Fix wrapper shell scripts and symlinks for invoking clang++
792
793 In a typical installation, clang++ symlinks to clang, which symlinks to the
794 elf executable. The executable distinguishes between clang and clang++ based
795 on argv[0].
796
797 When invoked through the LdsoWrapper, argv[0] always contains the path to the
798 executable elf file, making clang/clang++ invocations indistinguishable.
799
800 This function detects if the elf executable being wrapped is clang-X.Y, and
801 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
802
803 The calling sequence now becomes:
804 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
805 the Ldsowrapper).
806 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
807 to the original clang-3.9 elf.
808 -) The difference this time is that inside the elf file execution, $0 is
809 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
810
Manoj Guptaae268142018-04-27 23:28:36 -0700811 Update: Starting since clang 7, the clang and clang++ are symlinks to
812 clang-7 binary, not clang-7.0. The pattern match is extended to handle
813 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
814
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700815 Args:
816 root: The root tree to generate scripts / symlinks inside of
817 path: The target elf for which LdsoWrapper was created
818 """
Manoj Guptaae268142018-04-27 23:28:36 -0700819 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700820 logging.info('fixing clang++ invocation for %s', path)
821 clangdir = os.path.dirname(root + path)
822 clang = os.path.basename(path)
823 clangxx = clang.replace('clang', 'clang++')
824
825 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
826 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
827
828 # Create a hardlink clang++-X.Y pointing to clang-X.Y
829 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
830
831 # Adjust the clang++ symlink to point to clang++-X.Y
832 os.unlink(os.path.join(clangdir, 'clang++'))
833 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
834
835
Mike Frysinger35247af2012-11-16 18:58:06 -0500836def FileIsCrosSdkElf(elf):
837 """Determine if |elf| is an ELF that we execute in the cros_sdk
838
839 We don't need this to be perfect, just quick. It makes sure the ELF
840 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
841
842 Args:
843 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500844
Mike Frysinger35247af2012-11-16 18:58:06 -0500845 Returns:
846 True if we think |elf| is a native ELF
847 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500848 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500849 data = f.read(20)
850 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500851 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500852 data[4:5] == b'\x02' and
853 data[5:6] == b'\x01' and
854 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500855
856
857def IsPathPackagable(ptype, path):
858 """Should the specified file be included in a toolchain package?
859
860 We only need to handle files as we'll create dirs as we need them.
861
862 Further, trim files that won't be useful:
863 - non-english translations (.mo) since it'd require env vars
864 - debug files since these are for the host compiler itself
865 - info/man pages as they're big, and docs are online, and the
866 native docs should work fine for the most part (`man gcc`)
867
868 Args:
869 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
870 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500871
Mike Frysinger35247af2012-11-16 18:58:06 -0500872 Returns:
873 True if we want to include this path in the package
874 """
875 return not (ptype in ('dir',) or
876 path.startswith('/usr/lib/debug/') or
877 os.path.splitext(path)[1] == '.mo' or
878 ('/man/' in path or '/info/' in path))
879
880
881def ReadlinkRoot(path, root):
882 """Like os.readlink(), but relative to a |root|
883
884 Args:
885 path: The symlink to read
886 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500887
Mike Frysinger35247af2012-11-16 18:58:06 -0500888 Returns:
889 A fully resolved symlink path
890 """
891 while os.path.islink(root + path):
892 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
893 return path
894
895
896def _GetFilesForTarget(target, root='/'):
897 """Locate all the files to package for |target|
898
899 This does not cover ELF dependencies.
900
901 Args:
902 target: The toolchain target name
903 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500904
Mike Frysinger35247af2012-11-16 18:58:06 -0500905 Returns:
906 A tuple of a set of all packable paths, and a set of all paths which
907 are also native ELFs
908 """
909 paths = set()
910 elfs = set()
911
912 # Find all the files owned by the packages for this target.
913 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500914
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700915 # Skip Go compiler from redistributable packages.
916 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
917 # into it. Due to this, the toolchain cannot be unpacked anywhere
918 # else and be readily useful. To enable packaging Go, we need to:
919 # -) Tweak the wrappers/environment to override GOROOT
920 # automatically based on the unpack location.
921 # -) Make sure the ELF dependency checking and wrapping logic
922 # below skips the Go toolchain executables and libraries.
923 # -) Make sure the packaging process maintains the relative
924 # timestamps of precompiled standard library packages.
925 # (see dev-lang/go ebuild for details).
926 if pkg == 'ex_go':
927 continue
928
Yunlian Jiang36f35242018-04-27 10:18:40 -0700929 # Use armv7a-cros-linux-gnueabi/compiler-rt for
930 # armv7a-cros-linux-gnueabihf/compiler-rt.
931 # Currently the armv7a-cros-linux-gnueabi is actually
932 # the same as armv7a-cros-linux-gnueabihf with different names.
933 # Because of that, for compiler-rt, it generates the same binary in
934 # the same location. To avoid the installation conflict, we do not
935 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
936 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
937 # toolchain.
938 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
939 atom = GetPortagePackage(target, pkg)
940 cat, pn = atom.split('/')
941 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700942 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
943 settings=portage.settings)
944 contents = dblink.getcontents()
945 if not contents:
946 if 'hf' in target:
947 new_target = 'armv7a-cros-linux-gnueabi'
948 else:
949 new_target = 'armv7a-cros-linux-gnueabihf'
950 atom = GetPortagePackage(new_target, pkg)
951 else:
952 atom = GetPortagePackage(target, pkg)
953
Mike Frysinger35247af2012-11-16 18:58:06 -0500954 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700955 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700956 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500957
Mike Frysinger35247af2012-11-16 18:58:06 -0500958 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
959 settings=portage.settings)
960 contents = dblink.getcontents()
961 for obj in contents:
962 ptype = contents[obj][0]
963 if not IsPathPackagable(ptype, obj):
964 continue
965
966 if ptype == 'obj':
967 # For native ELFs, we need to pull in their dependencies too.
968 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500969 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500970 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500971 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500972 paths.add(obj)
973
974 return paths, elfs
975
976
977def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500978 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500979 """Link in all packable files and their runtime dependencies
980
981 This also wraps up executable ELFs with helper scripts.
982
983 Args:
984 output_dir: The output directory to store files
985 paths: All the files to include
986 elfs: All the files which are ELFs (a subset of |paths|)
987 ldpaths: A dict of static ldpath information
988 path_rewrite_func: User callback to rewrite paths in output_dir
989 root: The root path to pull all packages/files from
990 """
991 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400992 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500993 for path in paths:
994 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500995 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -0500996 dst = output_dir + new_path
997 osutils.SafeMakedirs(os.path.dirname(dst))
998
999 # Is this a symlink which we have to rewrite or wrap?
1000 # Delay wrap check until after we have created all paths.
1001 src = root + path
1002 if os.path.islink(src):
1003 tgt = os.readlink(src)
1004 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001005 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001006
1007 # Rewrite absolute links to relative and then generate the symlink
1008 # ourselves. All other symlinks can be hardlinked below.
1009 if tgt[0] == '/':
1010 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1011 os.symlink(tgt, dst)
1012 continue
1013
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001014 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001015 os.link(src, dst)
1016
Mike Frysinger35247af2012-11-16 18:58:06 -05001017 # Locate all the dependencies for all the ELFs. Stick them all in the
1018 # top level "lib" dir to make the wrapper simpler. This exact path does
1019 # not matter since we execute ldso directly, and we tell the ldso the
1020 # exact path to search for its libraries.
1021 libdir = os.path.join(output_dir, 'lib')
1022 osutils.SafeMakedirs(libdir)
1023 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001024 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001025 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001026 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001027 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001028 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001029 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001030
Mike Frysinger9fe02342019-12-12 17:52:53 -05001031 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1032 if os.path.basename(elf).startswith('libopcodes-'):
1033 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001034
Mike Frysinger00b129f2021-04-21 18:11:48 -04001035 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001036 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001037 src = path = lib_data['path']
1038 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001039 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001040 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001041
1042 # No need to try and copy the same source lib multiple times.
1043 if path in donelibs:
1044 continue
1045 donelibs.add(path)
1046
1047 # Die if we try to normalize different source libs with the same basename.
1048 if lib in basenamelibs:
1049 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1050 os.path.join('/lib', lib), path,
1051 ' '.join(x for x in donelibs
1052 if x != path and os.path.basename(x) == lib))
1053 # TODO(crbug.com/917193): Make this fatal.
1054 # cros_build_lib.Die('Unable to resolve lib conflicts')
1055 continue
1056 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001057
1058 # Needed libs are the SONAME, but that is usually a symlink, not a
1059 # real file. So link in the target rather than the symlink itself.
1060 # We have to walk all the possible symlinks (SONAME could point to a
1061 # symlink which points to a symlink), and we have to handle absolute
1062 # ourselves (since we have a "root" argument).
1063 dst = os.path.join(libdir, os.path.basename(path))
1064 src = ReadlinkRoot(src, root)
1065
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001066 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001067 os.link(root + src, dst)
1068
Mike Frysinger00b129f2021-04-21 18:11:48 -04001069 # Do not create wrapper for libc. crbug.com/766827
1070 if interp and not glibc_re.search(elf):
1071 # Generate a wrapper if it is executable.
1072 interp = os.path.join('/lib', os.path.basename(interp))
1073 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1074 libpaths=e['rpath'] + e['runpath'])
1075 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1076
1077 # Wrap any symlinks to the wrapper.
1078 if elf in sym_paths:
1079 link = sym_paths[elf]
1080 GeneratePathWrapper(output_dir, link, elf)
1081
Mike Frysinger35247af2012-11-16 18:58:06 -05001082
1083def _EnvdGetVar(envd, var):
1084 """Given a Gentoo env.d file, extract a var from it
1085
1086 Args:
1087 envd: The env.d file to load (may be a glob path)
1088 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001089
Mike Frysinger35247af2012-11-16 18:58:06 -05001090 Returns:
1091 The value of |var|
1092 """
1093 envds = glob.glob(envd)
1094 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1095 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001096 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001097
1098
1099def _ProcessBinutilsConfig(target, output_dir):
1100 """Do what binutils-config would have done"""
1101 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001102
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001103 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001104 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1105 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001106 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001107 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001108 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1109 % globpath)
1110 srcpath = srcpath[0]
1111 ld_path = os.path.join(srcpath, 'ld')
1112 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1113 ld_path = os.path.join(srcpath, 'ld.bfd')
1114 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001115
Mike Frysinger78b7a812014-11-26 19:45:23 -05001116 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001117 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1118 for prog in os.listdir(output_dir + srcpath):
1119 # Skip binaries already wrapped.
1120 if not prog.endswith('.real'):
1121 GeneratePathWrapper(output_dir, binpath + prog,
1122 os.path.join(srcpath, prog))
1123 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1124 os.path.join(srcpath, prog))
1125
David James27ac4ae2012-12-03 23:16:15 -08001126 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001127 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001128 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1129 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1130 output_dir + libpath)
1131
1132
1133def _ProcessGccConfig(target, output_dir):
1134 """Do what gcc-config would have done"""
1135 binpath = '/bin'
1136 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1137 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1138 for prog in os.listdir(output_dir + srcpath):
1139 # Skip binaries already wrapped.
1140 if (not prog.endswith('.real') and
1141 not prog.endswith('.elf') and
1142 prog.startswith(target)):
1143 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1144 os.path.join(srcpath, prog))
1145 return srcpath
1146
1147
Frank Henigman179ec7c2015-02-06 03:01:09 -05001148def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1149 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001150 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001151
Tobias Boschddd16492019-08-14 09:29:54 -07001152 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001153 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001154 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1155 # Can't update the wrapper in place to not affect the chroot,
1156 # but only the extracted toolchain.
1157 os.unlink(sysroot_wrapper)
1158 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001159 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001160
1161
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001162def _ProcessClangWrappers(target, output_dir):
1163 """Remove chroot-specific things from our sysroot wrappers"""
1164 clang_bin_path = '/usr/bin'
1165 # Disable ccache from clang wrappers.
1166 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1167 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1168 f'/usr/bin/{target}-clang')
1169 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1170 f'/usr/bin/{target}-clang++')
1171
1172
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001173def _CreateMainLibDir(target, output_dir):
1174 """Create some lib dirs so that compiler can get the right Gcc paths"""
1175 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1176 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1177
1178
Mike Frysinger35247af2012-11-16 18:58:06 -05001179def _ProcessDistroCleanups(target, output_dir):
1180 """Clean up the tree and remove all distro-specific requirements
1181
1182 Args:
1183 target: The toolchain target name
1184 output_dir: The output directory to clean up
1185 """
1186 _ProcessBinutilsConfig(target, output_dir)
1187 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001188 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001189 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001190 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001191
1192 osutils.RmDir(os.path.join(output_dir, 'etc'))
1193
1194
1195def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1196 """Setup a tree from the packages for the specified target
1197
1198 This populates a path with all the files from toolchain packages so that
1199 a tarball can easily be generated from the result.
1200
1201 Args:
1202 target: The target to create a packagable root from
1203 output_dir: The output directory to place all the files
1204 ldpaths: A dict of static ldpath information
1205 root: The root path to pull all packages/files from
1206 """
1207 # Find all the files owned by the packages for this target.
1208 paths, elfs = _GetFilesForTarget(target, root=root)
1209
1210 # Link in all the package's files, any ELF dependencies, and wrap any
1211 # executable ELFs with helper scripts.
1212 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001213 """Move /usr/bin to /bin so people can just use that toplevel dir
1214
George Burgess IVca1d7612020-10-01 00:38:32 -07001215 Note we do not apply this to clang or rust - there is correlation between
1216 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001217 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001218 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001219 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001220 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001221 return path[4:]
1222 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001223 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1224 path_rewrite_func=MoveUsrBinToBin, root=root)
1225
1226 # The packages, when part of the normal distro, have helper scripts
1227 # that setup paths and such. Since we are making this standalone, we
1228 # need to preprocess all that ourselves.
1229 _ProcessDistroCleanups(target, output_dir)
1230
1231
1232def CreatePackages(targets_wanted, output_dir, root='/'):
1233 """Create redistributable cross-compiler packages for the specified targets
1234
1235 This creates toolchain packages that should be usable in conjunction with
1236 a downloaded sysroot (created elsewhere).
1237
1238 Tarballs (one per target) will be created in $PWD.
1239
1240 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001241 targets_wanted: The targets to package up.
1242 output_dir: The directory to put the packages in.
1243 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001244 """
Ralph Nathan03047282015-03-23 11:09:32 -07001245 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001246 osutils.SafeMakedirs(output_dir)
1247 ldpaths = lddtree.LoadLdpaths(root)
1248 targets = ExpandTargets(targets_wanted)
1249
Mike Frysinger221bd822017-09-29 02:51:47 -04001250 with osutils.TempDir(prefix='create-packages') as tempdir:
1251 logging.debug('Using tempdir: %s', tempdir)
1252
Mike Frysinger35247af2012-11-16 18:58:06 -05001253 # We have to split the root generation from the compression stages. This is
1254 # because we hardlink in all the files (to avoid overhead of reading/writing
1255 # the copies multiple times). But tar gets angry if a file's hardlink count
1256 # changes from when it starts reading a file to when it finishes.
1257 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1258 for target in targets:
1259 output_target_dir = os.path.join(tempdir, target)
1260 queue.put([target, output_target_dir, ldpaths, root])
1261
1262 # Build the tarball.
1263 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1264 for target in targets:
1265 tar_file = os.path.join(output_dir, target + '.tar.xz')
1266 queue.put([tar_file, os.path.join(tempdir, target)])
1267
1268
Mike Frysinger07534cf2017-09-12 17:40:21 -04001269def GetParser():
1270 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001271 parser = commandline.ArgumentParser(description=__doc__)
1272 parser.add_argument('-u', '--nousepkg',
1273 action='store_false', dest='usepkg', default=True,
1274 help='Use prebuilt packages if possible')
1275 parser.add_argument('-d', '--deleteold',
1276 action='store_true', dest='deleteold', default=False,
1277 help='Unmerge deprecated packages')
1278 parser.add_argument('-t', '--targets',
1279 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001280 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001281 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001282 "allowed. Defaults to 'sdk'.")
1283 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1284 help='Comma separated list of boards whose toolchains we '
1285 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001286 parser.add_argument('--hostonly',
1287 dest='hostonly', default=False, action='store_true',
1288 help='Only setup the host toolchain. '
1289 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001290 parser.add_argument('--show-board-cfg', '--show-cfg',
1291 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001292 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001293 parser.add_argument('--show-packages', default=None,
1294 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001295 parser.add_argument('--create-packages',
1296 action='store_true', default=False,
1297 help='Build redistributable packages')
1298 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1299 help='Output directory')
1300 parser.add_argument('--reconfig', default=False, action='store_true',
1301 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001302 parser.add_argument('--sysroot', type='path',
1303 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001304 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001305
Mike Frysinger07534cf2017-09-12 17:40:21 -04001306
1307def main(argv):
1308 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001309 options = parser.parse_args(argv)
1310 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001311
Mike Frysinger35247af2012-11-16 18:58:06 -05001312 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001313 conflicting_options = (
1314 options.cfg_name,
1315 options.show_packages,
1316 options.create_packages,
1317 )
1318 if sum(bool(x) for x in conflicting_options) > 1:
1319 parser.error('conflicting options: create-packages & show-packages & '
1320 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001321
Gilad Arnold8195b532015-04-07 10:56:30 +03001322 targets_wanted = set(options.targets.split(','))
1323 boards_wanted = (set(options.include_boards.split(','))
1324 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001325
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001326 if options.cfg_name:
1327 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001328 elif options.show_packages is not None:
1329 cros_build_lib.AssertInsideChroot()
1330 target = options.show_packages
1331 Crossdev.Load(False)
1332 for package in GetTargetPackages(target):
1333 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001334 elif options.create_packages:
1335 cros_build_lib.AssertInsideChroot()
1336 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001337 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001338 else:
1339 cros_build_lib.AssertInsideChroot()
1340 # This has to be always run as root.
1341 if os.geteuid() != 0:
1342 cros_build_lib.Die('this script must be run as root')
1343
1344 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001345 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001346 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001347 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001348 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001349 Crossdev.Save()
1350
1351 return 0