blob: 1040563e88c7c8b2f343456e1b754df8088f2788 [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
Zdenek Behan508dcce2011-12-05 15:39:32 +010011import os
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -070012import re
Tobias Boschddd16492019-08-14 09:29:54 -070013import shutil
Zdenek Behan508dcce2011-12-05 15:39:32 +010014
Mike Frysinger506e75f2012-12-17 14:21:13 -050015from chromite.lib import commandline
Mike Frysinger95452702021-01-23 00:07:22 -050016from chromite.lib import constants
Brian Harring503f3ab2012-03-09 21:39:41 -080017from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070018from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070019from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050020from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080021from chromite.lib import toolchain
Mike Frysinger95452702021-01-23 00:07:22 -050022from chromite.third_party import lddtree
Mike Frysingere652ba12019-09-08 00:57:43 -040023from chromite.utils import key_value_store
Mike Frysinger35247af2012-11-16 18:58:06 -050024
Zdenek Behan508dcce2011-12-05 15:39:32 +010025
Mike Frysinger31596002012-12-03 23:54:24 -050026if cros_build_lib.IsInsideChroot():
27 # Only import portage after we've checked that we're inside the chroot.
28 # Outside may not have portage, in which case the above may not happen.
29 # We'll check in main() if the operation needs portage.
Mike Frysinger27e21b72018-07-12 14:20:21 -040030 # pylint: disable=import-error
Mike Frysinger31596002012-12-03 23:54:24 -050031 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010032
33
Matt Tennantf1e30972012-03-02 16:30:07 -080034EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010035PACKAGE_STABLE = '[stable]'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010036
37CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070038ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010039STABLE_OVERLAY = '/usr/local/portage/stable'
40CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010041
42
Mike Frysinger66bfde52017-09-12 16:42:57 -040043# The exact list of host toolchain packages we care about. These are the
44# packages that bots/devs install only from binpkgs and rely on the SDK bot
45# (chromiumos-sdk) to validate+uprev.
46#
Mike Frysinger66bfde52017-09-12 16:42:57 -040047# We don't use crossdev to manage the host toolchain for us, especially since
48# we diverge significantly now (with llvm/clang/etc...), and we don't need or
49# want crossdev managing /etc/portage config files for the sdk
50HOST_PACKAGES = (
51 'dev-lang/go',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080052 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040053 'sys-devel/binutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040054 'sys-devel/gcc',
55 'sys-devel/llvm',
56 'sys-kernel/linux-headers',
57 'sys-libs/glibc',
58 'sys-libs/libcxx',
59 'sys-libs/libcxxabi',
Manoj Guptade64cb22019-03-31 18:48:58 -070060 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040061)
62
Mike Frysinger785b0c32017-09-13 01:35:59 -040063# These packages are also installed into the host SDK. However, they require
64# the cross-compilers to be installed first (because they need them to actually
65# build), so we have to delay their installation.
66HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070067 'dev-lang/rust',
George Burgess IV9d1e2322021-04-30 09:54:45 -070068 'dev-lang/rust-bootstrap',
Mike Frysinger61a24392017-10-17 17:14:27 -040069 'virtual/target-sdk-post-cross',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010070 'dev-embedded/coreboot-sdk',
George Burgess IV288ecc82021-04-21 07:52:45 -070071 'dev-embedded/ti50-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040072)
73
74# New packages that we're in the process of adding to the SDK. Since the SDK
75# bot hasn't had a chance to run yet, there are no binary packages available,
76# so we have to list them here and wait. Once it completes, entries here can
77# be removed so they'll end up on bots & dev's systems.
George Burgess IV66e199c2021-05-05 15:38:40 -070078NEW_PACKAGES = ()
Mike Frysinger785b0c32017-09-13 01:35:59 -040079
Rahul Chaudhry4b803052015-05-13 15:25:56 -070080# Enable the Go compiler for these targets.
81TARGET_GO_ENABLED = (
82 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070083 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070084 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070085 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070086)
87CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
88
Manoj Gupta1b5642e2017-03-08 16:44:12 -080089# Enable llvm's compiler-rt for these targets.
90TARGET_COMPILER_RT_ENABLED = (
91 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070092 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070093 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -080094 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080095)
96CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
97
Manoj Gupta946abb42017-04-12 14:27:19 -070098TARGET_LLVM_PKGS_ENABLED = (
99 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700100 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700101 'aarch64-cros-linux-gnu',
Manoj Gupta93713122020-10-29 17:52:16 -0700102 'i686-pc-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700103 'x86_64-cros-linux-gnu',
104)
105
106LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700107 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
108 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700109 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700110}
111
David James66a09c42012-11-05 13:31:38 -0800112class Crossdev(object):
113 """Class for interacting with crossdev and caching its output."""
114
115 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
116 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800117 # Packages that needs separate handling, in addition to what we have from
118 # crossdev.
119 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700120 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800121 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700122 'libcxxabi': 'sys-libs',
123 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700124 'elfutils': 'dev-libs',
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700125 'llvm-libunwind': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800126 }
David James66a09c42012-11-05 13:31:38 -0800127
128 @classmethod
129 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400130 """Load crossdev cache from disk.
131
132 We invalidate the cache when crossdev updates or this script changes.
133 """
David James90239b92012-11-05 15:31:34 -0800134 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400135 # If we run the compiled/cached .pyc file, we'll read/hash that when we
136 # really always want to track the source .py file.
137 script = os.path.abspath(__file__)
138 if script.endswith('.pyc'):
139 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500140 setup_toolchains_hash = hashlib.md5(
141 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400142
143 cls._CACHE = {
144 'crossdev_version': crossdev_version,
145 'setup_toolchains_hash': setup_toolchains_hash,
146 }
147
148 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
149 if reconfig:
150 logging.debug('cache: forcing regen due to reconfig')
151 return
152
153 try:
154 file_data = osutils.ReadFile(cls._CACHE_FILE)
155 except IOError as e:
156 if e.errno != errno.ENOENT:
157 logging.warning('cache: reading failed: %s', e)
158 osutils.SafeUnlink(cls._CACHE_FILE)
159 return
160
161 try:
162 data = json.loads(file_data)
163 except ValueError as e:
164 logging.warning('cache: ignoring invalid content: %s', e)
165 return
166
167 if crossdev_version != data.get('crossdev_version'):
168 logging.debug('cache: rebuilding after crossdev upgrade')
169 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
170 logging.debug('cache: rebuilding after cros_setup_toolchains change')
171 else:
172 logging.debug('cache: content is up-to-date!')
173 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800174
175 @classmethod
176 def Save(cls):
177 """Store crossdev cache on disk."""
178 # Save the cache from the successful run.
179 with open(cls._CACHE_FILE, 'w') as f:
180 json.dump(cls._CACHE, f)
181
182 @classmethod
183 def GetConfig(cls, target):
184 """Returns a map of crossdev provided variables about a tuple."""
185 CACHE_ATTR = '_target_tuple_map'
186
187 val = cls._CACHE.setdefault(CACHE_ATTR, {})
188 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400189 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400190 conf = {
191 'crosspkgs': [],
192 'target': toolchain.GetHostTuple(),
193 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400194 if target == 'host':
195 packages_list = HOST_PACKAGES
196 else:
197 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400198 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400199 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400200 else:
201 # Build the crossdev command.
202 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
203 if target in TARGET_COMPILER_RT_ENABLED:
204 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
205 if target in TARGET_GO_ENABLED:
206 cmd.extend(CROSSDEV_GO_ARGS)
207 if target in TARGET_LLVM_PKGS_ENABLED:
208 for pkg in LLVM_PKGS_TABLE:
209 cmd.extend(LLVM_PKGS_TABLE[pkg])
210 cmd.extend(['-t', target])
211 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400212 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500213 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500214 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400215 # List of tuples split at the first '=', converted into dict.
216 conf = dict((k, cros_build_lib.ShellUnquote(v))
217 for k, v in (x.split('=', 1) for x in out))
218 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800219
Mike Frysinger66bfde52017-09-12 16:42:57 -0400220 manual_pkgs = cls.MANUAL_PKGS
221
222 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400223 conf[pkg + '_pn'] = pkg
224 conf[pkg + '_category'] = cat
225 if pkg not in conf['crosspkgs']:
226 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800227
228 val[target] = conf
229
David James66a09c42012-11-05 13:31:38 -0800230 return val[target]
231
232 @classmethod
233 def UpdateTargets(cls, targets, usepkg, config_only=False):
234 """Calls crossdev to initialize a cross target.
235
236 Args:
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000237 targets: The list of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700238 usepkg: Copies the commandline opts.
239 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800240 """
241 configured_targets = cls._CACHE.setdefault('configured_targets', [])
242
243 cmdbase = ['crossdev', '--show-fail-log']
244 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
245 # Pick stable by default, and override as necessary.
246 cmdbase.extend(['-P', '--oneshot'])
247 if usepkg:
248 cmdbase.extend(['-P', '--getbinpkg',
249 '-P', '--usepkgonly',
250 '--without-headers'])
251
Christopher Wileyb22c0712015-06-02 10:37:03 -0700252 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800253 cmdbase.extend(['--overlays', overlays])
254 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
255
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000256 # Build target by the reversed alphabetical order to make sure
257 # armv7a-cros-linux-gnueabihf builds before armv7a-cros-linux-gnueabi
258 # because some dependency issue. This can be reverted once we
259 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
260 for target in sorted(targets, reverse=True):
261 if config_only and target in configured_targets:
262 continue
David James66a09c42012-11-05 13:31:38 -0800263
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000264 cmd = cmdbase + ['-t', target]
265
266 for pkg in GetTargetPackages(target):
267 if pkg == 'gdb':
268 # Gdb does not have selectable versions.
269 cmd.append('--ex-gdb')
270 elif pkg == 'ex_compiler-rt':
271 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
272 elif pkg == 'ex_go':
273 # Go does not have selectable versions.
274 cmd.extend(CROSSDEV_GO_ARGS)
275 elif pkg in LLVM_PKGS_TABLE:
276 cmd.extend(LLVM_PKGS_TABLE[pkg])
277 elif pkg in cls.MANUAL_PKGS:
278 pass
279 else:
280 # The first of the desired versions is the "primary" one.
281 version = GetDesiredPackageVersions(target, pkg)[0]
282 cmd.extend(['--%s' % pkg, version])
283
284 cmd.extend(targets[target]['crossdev'].split())
285 if config_only:
286 # In this case we want to just quietly reinit
287 cmd.append('--init-target')
Mike Frysinger0282d222019-12-17 17:15:48 -0500288 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
David James66a09c42012-11-05 13:31:38 -0800289 else:
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000290 cros_build_lib.run(cmd)
David James66a09c42012-11-05 13:31:38 -0800291
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000292 configured_targets.append(target)
David James66a09c42012-11-05 13:31:38 -0800293
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100294
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100295def GetTargetPackages(target):
296 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800297 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100298 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800299 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100300
301
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100302# Portage helper functions:
303def GetPortagePackage(target, package):
304 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800305 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100306 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400307 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100308 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100309 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100310 category = conf['category']
311 # Portage package:
312 pn = conf[package + '_pn']
313 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500314 assert category
315 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100316 return '%s/%s' % (category, pn)
317
318
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700319def PortageTrees(root):
320 """Return the portage trees for a given root."""
321 if root == '/':
322 return portage.db['/']
323 # The portage logic requires the path always end in a slash.
324 root = root.rstrip('/') + '/'
325 return portage.create_trees(target_root=root, config_root=root)[root]
326
327
328def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100329 """Extracts the list of current versions of a target, package pair.
330
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500331 Args:
332 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700333 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100334
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500335 Returns:
336 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100337 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100338 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700339 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100340 version = portage.versions.cpv_getversion(pkg)
341 versions.append(version)
342 return versions
343
344
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700345def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100346 """Extracts the current stable version for a given package.
347
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500348 Args:
349 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
350 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700351 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100352
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500353 Returns:
354 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100355 """
David James90239b92012-11-05 15:31:34 -0800356 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700357 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800358 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100359
360
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700361def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100362 """Resolves keywords in a given version list for a particular package.
363
364 Resolving means replacing PACKAGE_STABLE with the actual number.
365
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500366 Args:
367 target: The target to operate on (e.g. i686-pc-linux-gnu)
368 package: The target/package to operate on (e.g. gcc)
369 versions: List of versions to resolve
370 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700371 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100372
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500373 Returns:
374 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100375 """
376 resolved = []
David James90239b92012-11-05 15:31:34 -0800377 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700378 if not installed:
379 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100380 for version in versions:
381 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700382 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400383 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100384 resolved.append(version)
385 return resolved
386
387
388def GetDesiredPackageVersions(target, package):
389 """Produces the list of desired versions for each target, package pair.
390
391 The first version in the list is implicitly treated as primary, ie.
392 the version that will be initialized by crossdev and selected.
393
394 If the version is PACKAGE_STABLE, it really means the current version which
395 is emerged by using the package atom with no particular version key.
396 Since crossdev unmasks all packages by default, this will actually
397 mean 'unstable' in most cases.
398
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500399 Args:
400 target: The target to operate on (e.g. i686-pc-linux-gnu)
401 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100402
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500403 Returns:
404 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100405 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400406 if package in GetTargetPackages(target):
407 return [PACKAGE_STABLE]
408 else:
409 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100410
411
412def TargetIsInitialized(target):
413 """Verifies if the given list of targets has been correctly initialized.
414
415 This determines whether we have to call crossdev while emerging
416 toolchain packages or can do it using emerge. Emerge is naturally
417 preferred, because all packages can be updated in a single pass.
418
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500419 Args:
420 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100421
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500422 Returns:
423 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100424 """
425 # Check if packages for the given target all have a proper version.
426 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100427 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800428 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100429 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400430 if not (GetStablePackageVersion(atom, True) and
431 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100432 return False
433 return True
434 except cros_build_lib.RunCommandError:
435 # Fails - The target has likely never been initialized before.
436 return False
437
438
439def RemovePackageMask(target):
440 """Removes a package.mask file for the given platform.
441
442 The pre-existing package.mask files can mess with the keywords.
443
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500444 Args:
445 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100446 """
447 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700448 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100449
450
Zdenek Behan508dcce2011-12-05 15:39:32 +0100451# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700452def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500453 """Rebuild libtool as needed
454
455 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
456 gcc, libtool will break. We can't use binary packages either as those will
457 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700458
459 Args:
460 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500461 """
462 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700463 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500464 for line in f:
465 # Look for a line like:
466 # sys_lib_search_path_spec="..."
467 # It'll be a list of paths and gcc will be one of them.
468 if line.startswith('sys_lib_search_path_spec='):
469 line = line.rstrip()
470 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400471 root_path = os.path.join(root, path.lstrip(os.path.sep))
472 logging.debug('Libtool: checking %s', root_path)
473 if not os.path.exists(root_path):
474 logging.info('Rebuilding libtool after gcc upgrade')
475 logging.info(' %s', line)
476 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500477 needs_update = True
478 break
479
480 if needs_update:
481 break
482
483 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700484 cmd = [EMERGE_CMD, '--oneshot']
485 if root != '/':
486 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
487 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400488 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400489 else:
490 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500491
492
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700493def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100494 """Determines which packages need update/unmerge and defers to portage.
495
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500496 Args:
497 targets: The list of targets to update
498 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700499 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100500 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100501 # For each target, we do two things. Figure out the list of updates,
502 # and figure out the appropriate keywords/masks. Crossdev will initialize
503 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400504 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800505 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000506 # Used to keep track of post-cross packages. These are allowed to have
507 # implicit dependencies on toolchain packages, and therefore need to
508 # be built last.
509 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000511 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400512 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100513 # Record the highest needed version for each target, for masking purposes.
514 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100515 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100516 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400517 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100518 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700519 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200521 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400522 if pkg in NEW_PACKAGES and usepkg:
523 # Skip this binary package (for now).
524 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100525 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400526 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000527 if is_post_cross_target:
528 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400530 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100531 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400532 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800533 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534
Mike Frysinger3bba5032016-09-20 14:15:04 -0400535 logging.info('Updating packages:')
536 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100537
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100538 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539 if usepkg:
540 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700541 if root != '/':
542 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000544 if usepkg:
545 # Since we are not building from source, we can handle
546 # all packages in one go.
547 cmd.extend(packages)
548 cros_build_lib.run(cmd)
549 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700550 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
551 if pre_cross_items:
552 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000553 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700554 if post_cross_items:
555 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800556 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100557
558
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700559def CleanTargets(targets, root='/'):
560 """Unmerges old packages that are assumed unnecessary.
561
562 Args:
563 targets: The list of targets to clean up.
564 root: The install root in which we want packages cleaned up.
565 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100566 unmergemap = {}
567 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400568 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100569 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400570 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100571 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700572 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100573 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700574 # NOTE: This refers to installed packages (vartree) rather than the
575 # Portage version (porttree and/or bintree) when determining the current
576 # version. While this isn't the most accurate thing to do, it is probably
577 # a good simple compromise, which should have the desired result of
578 # uninstalling everything but the latest installed version. In
579 # particular, using the bintree (--usebinpkg) requires a non-trivial
580 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200581 desired_num = VersionListToNumeric(target, package, desired, True)
582 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400583 logging.warning('Error detecting stable version for %s, '
584 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200585 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100586 unmergemap[pkg] = set(current).difference(desired_num)
587
588 # Cleaning doesn't care about consistency and rebuilding package.* files.
589 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400590 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100591 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
592
593 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400594 logging.info('Cleaning packages:')
595 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100596 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700597 if root != '/':
598 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100599 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400600 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100601 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400602 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603
604
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200605def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100606 """Runs gcc-config and binutils-config to select the desired.
607
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500608 Args:
609 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700610 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100611 """
612 for package in ['gcc', 'binutils']:
613 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400614 # See if this package is part of this target.
615 if package not in GetTargetPackages(target):
616 logging.debug('%s: %s is not used', target, package)
617 continue
618
Zdenek Behan508dcce2011-12-05 15:39:32 +0100619 # Pick the first version in the numbered list as the selected one.
620 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700621 desired_num = VersionListToNumeric(target, package, desired, True,
622 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100623 desired = desired_num[0]
624 # *-config does not play revisions, strip them, keep just PV.
625 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
626
Mike Frysinger785b0c32017-09-13 01:35:59 -0400627 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100628 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800629 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100630
631 # And finally, attach target to it.
632 desired = '%s-%s' % (target, desired)
633
David James7ec5efc2012-11-06 09:39:49 -0800634 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700635 if root != '/':
636 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800637 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400638 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500639 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500640 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500641 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700642
643 # Do not reconfig when the current is live or nothing needs to be done.
644 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100645 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500646 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400647 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100648
649
Mike Frysinger35247af2012-11-16 18:58:06 -0500650def ExpandTargets(targets_wanted):
651 """Expand any possible toolchain aliases into full targets
652
653 This will expand 'all' and 'sdk' into the respective toolchain tuples.
654
655 Args:
656 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500657
Mike Frysinger35247af2012-11-16 18:58:06 -0500658 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300659 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500660 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500661 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700662 if targets_wanted == set(['boards']):
663 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300664 return {}
665
666 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500667 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300668 return all_targets
669 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500670 # Filter out all the non-sdk toolchains as we don't want to mess
671 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300672 return toolchain.FilterToolchains(all_targets, 'sdk', True)
673
674 # Verify user input.
675 nonexistent = targets_wanted.difference(all_targets)
676 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400677 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300678 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500679
680
David Jamesf8c672f2012-11-06 13:38:11 -0800681def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700682 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100683 """Performs all steps to create a synchronized toolchain enviroment.
684
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500685 Args:
686 usepkg: Use prebuilt packages
687 deleteold: Unmerge deprecated packages
688 hostonly: Only setup the host toolchain
689 reconfig: Reload crossdev config and reselect toolchains
690 targets_wanted: All the targets to update
691 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700692 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100693 """
David Jamesf8c672f2012-11-06 13:38:11 -0800694 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100695 if not hostonly:
696 # For hostonly, we can skip most of the below logic, much of which won't
697 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500698 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400699
Don Garrettc0c74002015-10-09 12:58:19 -0700700 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300701 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400702 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800703 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100704
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100705 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400706 for target in targets:
707 if TargetIsInitialized(target):
708 reconfig_targets[target] = targets[target]
709 else:
710 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100711 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400712 logging.info('The following targets need to be re-initialized:')
713 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800714 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200715 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800716 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100717
Mike Frysinger66814c32017-10-09 18:11:46 -0400718 # If we're building a subset of toolchains for a board, we might not have
719 # all the tuples that the packages expect. We don't define the "full" set
720 # of tuples currently other than "whatever the full sdk has normally".
721 if usepkg or set(('all', 'sdk')) & targets_wanted:
722 # Since we have cross-compilers now, we can update these packages.
723 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400724
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100725 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400726 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100727
728 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700729 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200730 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800731
732 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700733 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100734
Mike Frysingerc880a962013-11-08 13:59:06 -0500735 # Now that we've cleared out old versions, see if we need to rebuild
736 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700737 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500738
Zdenek Behan508dcce2011-12-05 15:39:32 +0100739
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700740def ShowConfig(name):
741 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500742
743 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700744 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500745 """
Don Garrettc0c74002015-10-09 12:58:19 -0700746 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500747 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400748 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400749 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400750 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
751 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500752
753
Mike Frysinger35247af2012-11-16 18:58:06 -0500754def GeneratePathWrapper(root, wrappath, path):
755 """Generate a shell script to execute another shell script
756
757 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
758 argv[0] won't be pointing to the correct path, generate a shell script that
759 just executes another program with its full path.
760
761 Args:
762 root: The root tree to generate scripts inside of
763 wrappath: The full path (inside |root|) to create the wrapper
764 path: The target program which this wrapper will execute
765 """
766 replacements = {
767 'path': path,
768 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
769 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900770
771 # Do not use exec here, because exec invokes script with absolute path in
772 # argv0. Keeping relativeness allows us to remove abs path from compile result
773 # and leads directory independent build cache sharing in some distributed
774 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500775 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900776basedir=$(dirname "$0")
777"${basedir}/%(relroot)s%(path)s" "$@"
778exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500779""" % replacements
780 root_wrapper = root + wrappath
781 if os.path.islink(root_wrapper):
782 os.unlink(root_wrapper)
783 else:
784 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
785 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400786 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500787
788
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700789def FixClangXXWrapper(root, path):
790 """Fix wrapper shell scripts and symlinks for invoking clang++
791
792 In a typical installation, clang++ symlinks to clang, which symlinks to the
793 elf executable. The executable distinguishes between clang and clang++ based
794 on argv[0].
795
796 When invoked through the LdsoWrapper, argv[0] always contains the path to the
797 executable elf file, making clang/clang++ invocations indistinguishable.
798
799 This function detects if the elf executable being wrapped is clang-X.Y, and
800 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
801
802 The calling sequence now becomes:
803 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
804 the Ldsowrapper).
805 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
806 to the original clang-3.9 elf.
807 -) The difference this time is that inside the elf file execution, $0 is
808 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
809
Manoj Guptaae268142018-04-27 23:28:36 -0700810 Update: Starting since clang 7, the clang and clang++ are symlinks to
811 clang-7 binary, not clang-7.0. The pattern match is extended to handle
812 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
813
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700814 Args:
815 root: The root tree to generate scripts / symlinks inside of
816 path: The target elf for which LdsoWrapper was created
817 """
Manoj Guptaae268142018-04-27 23:28:36 -0700818 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700819 logging.info('fixing clang++ invocation for %s', path)
820 clangdir = os.path.dirname(root + path)
821 clang = os.path.basename(path)
822 clangxx = clang.replace('clang', 'clang++')
823
824 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
825 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
826
827 # Create a hardlink clang++-X.Y pointing to clang-X.Y
828 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
829
830 # Adjust the clang++ symlink to point to clang++-X.Y
831 os.unlink(os.path.join(clangdir, 'clang++'))
832 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
833
834
Mike Frysinger35247af2012-11-16 18:58:06 -0500835def FileIsCrosSdkElf(elf):
836 """Determine if |elf| is an ELF that we execute in the cros_sdk
837
838 We don't need this to be perfect, just quick. It makes sure the ELF
839 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
840
841 Args:
842 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500843
Mike Frysinger35247af2012-11-16 18:58:06 -0500844 Returns:
845 True if we think |elf| is a native ELF
846 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500847 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500848 data = f.read(20)
849 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500850 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500851 data[4:5] == b'\x02' and
852 data[5:6] == b'\x01' and
853 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500854
855
856def IsPathPackagable(ptype, path):
857 """Should the specified file be included in a toolchain package?
858
859 We only need to handle files as we'll create dirs as we need them.
860
861 Further, trim files that won't be useful:
862 - non-english translations (.mo) since it'd require env vars
863 - debug files since these are for the host compiler itself
864 - info/man pages as they're big, and docs are online, and the
865 native docs should work fine for the most part (`man gcc`)
866
867 Args:
868 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
869 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500870
Mike Frysinger35247af2012-11-16 18:58:06 -0500871 Returns:
872 True if we want to include this path in the package
873 """
874 return not (ptype in ('dir',) or
875 path.startswith('/usr/lib/debug/') or
876 os.path.splitext(path)[1] == '.mo' or
877 ('/man/' in path or '/info/' in path))
878
879
880def ReadlinkRoot(path, root):
881 """Like os.readlink(), but relative to a |root|
882
883 Args:
884 path: The symlink to read
885 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500886
Mike Frysinger35247af2012-11-16 18:58:06 -0500887 Returns:
888 A fully resolved symlink path
889 """
890 while os.path.islink(root + path):
891 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
892 return path
893
894
895def _GetFilesForTarget(target, root='/'):
896 """Locate all the files to package for |target|
897
898 This does not cover ELF dependencies.
899
900 Args:
901 target: The toolchain target name
902 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500903
Mike Frysinger35247af2012-11-16 18:58:06 -0500904 Returns:
905 A tuple of a set of all packable paths, and a set of all paths which
906 are also native ELFs
907 """
908 paths = set()
909 elfs = set()
910
911 # Find all the files owned by the packages for this target.
912 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500913
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700914 # Skip Go compiler from redistributable packages.
915 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
916 # into it. Due to this, the toolchain cannot be unpacked anywhere
917 # else and be readily useful. To enable packaging Go, we need to:
918 # -) Tweak the wrappers/environment to override GOROOT
919 # automatically based on the unpack location.
920 # -) Make sure the ELF dependency checking and wrapping logic
921 # below skips the Go toolchain executables and libraries.
922 # -) Make sure the packaging process maintains the relative
923 # timestamps of precompiled standard library packages.
924 # (see dev-lang/go ebuild for details).
925 if pkg == 'ex_go':
926 continue
927
Yunlian Jiang36f35242018-04-27 10:18:40 -0700928 # Use armv7a-cros-linux-gnueabi/compiler-rt for
929 # armv7a-cros-linux-gnueabihf/compiler-rt.
930 # Currently the armv7a-cros-linux-gnueabi is actually
931 # the same as armv7a-cros-linux-gnueabihf with different names.
932 # Because of that, for compiler-rt, it generates the same binary in
933 # the same location. To avoid the installation conflict, we do not
934 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
935 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
936 # toolchain.
937 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
938 atom = GetPortagePackage(target, pkg)
939 cat, pn = atom.split('/')
940 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700941 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
942 settings=portage.settings)
943 contents = dblink.getcontents()
944 if not contents:
945 if 'hf' in target:
946 new_target = 'armv7a-cros-linux-gnueabi'
947 else:
948 new_target = 'armv7a-cros-linux-gnueabihf'
949 atom = GetPortagePackage(new_target, pkg)
950 else:
951 atom = GetPortagePackage(target, pkg)
952
Mike Frysinger35247af2012-11-16 18:58:06 -0500953 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700954 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700955 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500956
Mike Frysinger35247af2012-11-16 18:58:06 -0500957 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
958 settings=portage.settings)
959 contents = dblink.getcontents()
960 for obj in contents:
961 ptype = contents[obj][0]
962 if not IsPathPackagable(ptype, obj):
963 continue
964
965 if ptype == 'obj':
966 # For native ELFs, we need to pull in their dependencies too.
967 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500968 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500969 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500970 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500971 paths.add(obj)
972
973 return paths, elfs
974
975
976def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500977 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500978 """Link in all packable files and their runtime dependencies
979
980 This also wraps up executable ELFs with helper scripts.
981
982 Args:
983 output_dir: The output directory to store files
984 paths: All the files to include
985 elfs: All the files which are ELFs (a subset of |paths|)
986 ldpaths: A dict of static ldpath information
987 path_rewrite_func: User callback to rewrite paths in output_dir
988 root: The root path to pull all packages/files from
989 """
990 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400991 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500992 for path in paths:
993 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500994 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -0500995 dst = output_dir + new_path
996 osutils.SafeMakedirs(os.path.dirname(dst))
997
998 # Is this a symlink which we have to rewrite or wrap?
999 # Delay wrap check until after we have created all paths.
1000 src = root + path
1001 if os.path.islink(src):
1002 tgt = os.readlink(src)
1003 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001004 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001005
1006 # Rewrite absolute links to relative and then generate the symlink
1007 # ourselves. All other symlinks can be hardlinked below.
1008 if tgt[0] == '/':
1009 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1010 os.symlink(tgt, dst)
1011 continue
1012
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001013 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001014 os.link(src, dst)
1015
Mike Frysinger35247af2012-11-16 18:58:06 -05001016 # Locate all the dependencies for all the ELFs. Stick them all in the
1017 # top level "lib" dir to make the wrapper simpler. This exact path does
1018 # not matter since we execute ldso directly, and we tell the ldso the
1019 # exact path to search for its libraries.
1020 libdir = os.path.join(output_dir, 'lib')
1021 osutils.SafeMakedirs(libdir)
1022 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001023 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001024 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001025 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001026 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001027 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001028 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001029
Mike Frysinger9fe02342019-12-12 17:52:53 -05001030 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1031 if os.path.basename(elf).startswith('libopcodes-'):
1032 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001033
Mike Frysinger00b129f2021-04-21 18:11:48 -04001034 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001035 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001036 src = path = lib_data['path']
1037 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001038 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001039 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001040
1041 # No need to try and copy the same source lib multiple times.
1042 if path in donelibs:
1043 continue
1044 donelibs.add(path)
1045
1046 # Die if we try to normalize different source libs with the same basename.
1047 if lib in basenamelibs:
1048 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1049 os.path.join('/lib', lib), path,
1050 ' '.join(x for x in donelibs
1051 if x != path and os.path.basename(x) == lib))
1052 # TODO(crbug.com/917193): Make this fatal.
1053 # cros_build_lib.Die('Unable to resolve lib conflicts')
1054 continue
1055 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001056
1057 # Needed libs are the SONAME, but that is usually a symlink, not a
1058 # real file. So link in the target rather than the symlink itself.
1059 # We have to walk all the possible symlinks (SONAME could point to a
1060 # symlink which points to a symlink), and we have to handle absolute
1061 # ourselves (since we have a "root" argument).
1062 dst = os.path.join(libdir, os.path.basename(path))
1063 src = ReadlinkRoot(src, root)
1064
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001065 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001066 os.link(root + src, dst)
1067
Mike Frysinger00b129f2021-04-21 18:11:48 -04001068 # Do not create wrapper for libc. crbug.com/766827
1069 if interp and not glibc_re.search(elf):
1070 # Generate a wrapper if it is executable.
1071 interp = os.path.join('/lib', os.path.basename(interp))
1072 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1073 libpaths=e['rpath'] + e['runpath'])
1074 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1075
1076 # Wrap any symlinks to the wrapper.
1077 if elf in sym_paths:
1078 link = sym_paths[elf]
1079 GeneratePathWrapper(output_dir, link, elf)
1080
Mike Frysinger35247af2012-11-16 18:58:06 -05001081
1082def _EnvdGetVar(envd, var):
1083 """Given a Gentoo env.d file, extract a var from it
1084
1085 Args:
1086 envd: The env.d file to load (may be a glob path)
1087 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001088
Mike Frysinger35247af2012-11-16 18:58:06 -05001089 Returns:
1090 The value of |var|
1091 """
1092 envds = glob.glob(envd)
1093 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1094 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001095 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001096
1097
1098def _ProcessBinutilsConfig(target, output_dir):
1099 """Do what binutils-config would have done"""
1100 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001101
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001102 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001103 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1104 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001105 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001106 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001107 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1108 % globpath)
1109 srcpath = srcpath[0]
1110 ld_path = os.path.join(srcpath, 'ld')
1111 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1112 ld_path = os.path.join(srcpath, 'ld.bfd')
1113 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001114
Mike Frysinger78b7a812014-11-26 19:45:23 -05001115 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001116 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1117 for prog in os.listdir(output_dir + srcpath):
1118 # Skip binaries already wrapped.
1119 if not prog.endswith('.real'):
1120 GeneratePathWrapper(output_dir, binpath + prog,
1121 os.path.join(srcpath, prog))
1122 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1123 os.path.join(srcpath, prog))
1124
David James27ac4ae2012-12-03 23:16:15 -08001125 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001126 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001127 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1128 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1129 output_dir + libpath)
1130
1131
1132def _ProcessGccConfig(target, output_dir):
1133 """Do what gcc-config would have done"""
1134 binpath = '/bin'
1135 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1136 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1137 for prog in os.listdir(output_dir + srcpath):
1138 # Skip binaries already wrapped.
1139 if (not prog.endswith('.real') and
1140 not prog.endswith('.elf') and
1141 prog.startswith(target)):
1142 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1143 os.path.join(srcpath, prog))
1144 return srcpath
1145
1146
Frank Henigman179ec7c2015-02-06 03:01:09 -05001147def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1148 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001149 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001150
Tobias Boschddd16492019-08-14 09:29:54 -07001151 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001152 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001153 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1154 # Can't update the wrapper in place to not affect the chroot,
1155 # but only the extracted toolchain.
1156 os.unlink(sysroot_wrapper)
1157 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001158 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001159
1160
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001161def _CreateMainLibDir(target, output_dir):
1162 """Create some lib dirs so that compiler can get the right Gcc paths"""
1163 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1164 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1165
1166
Mike Frysinger35247af2012-11-16 18:58:06 -05001167def _ProcessDistroCleanups(target, output_dir):
1168 """Clean up the tree and remove all distro-specific requirements
1169
1170 Args:
1171 target: The toolchain target name
1172 output_dir: The output directory to clean up
1173 """
1174 _ProcessBinutilsConfig(target, output_dir)
1175 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001176 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001177 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001178
1179 osutils.RmDir(os.path.join(output_dir, 'etc'))
1180
1181
1182def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1183 """Setup a tree from the packages for the specified target
1184
1185 This populates a path with all the files from toolchain packages so that
1186 a tarball can easily be generated from the result.
1187
1188 Args:
1189 target: The target to create a packagable root from
1190 output_dir: The output directory to place all the files
1191 ldpaths: A dict of static ldpath information
1192 root: The root path to pull all packages/files from
1193 """
1194 # Find all the files owned by the packages for this target.
1195 paths, elfs = _GetFilesForTarget(target, root=root)
1196
1197 # Link in all the package's files, any ELF dependencies, and wrap any
1198 # executable ELFs with helper scripts.
1199 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001200 """Move /usr/bin to /bin so people can just use that toplevel dir
1201
George Burgess IVca1d7612020-10-01 00:38:32 -07001202 Note we do not apply this to clang or rust - there is correlation between
1203 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001204 """
George Burgess IVca1d7612020-10-01 00:38:32 -07001205 if (path.startswith('/usr/bin/') and
1206 not any(x in path for x in ('clang', 'rust', 'cargo'))):
Han Shen699ea192016-03-02 10:42:47 -08001207 return path[4:]
1208 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001209 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1210 path_rewrite_func=MoveUsrBinToBin, root=root)
1211
1212 # The packages, when part of the normal distro, have helper scripts
1213 # that setup paths and such. Since we are making this standalone, we
1214 # need to preprocess all that ourselves.
1215 _ProcessDistroCleanups(target, output_dir)
1216
1217
1218def CreatePackages(targets_wanted, output_dir, root='/'):
1219 """Create redistributable cross-compiler packages for the specified targets
1220
1221 This creates toolchain packages that should be usable in conjunction with
1222 a downloaded sysroot (created elsewhere).
1223
1224 Tarballs (one per target) will be created in $PWD.
1225
1226 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001227 targets_wanted: The targets to package up.
1228 output_dir: The directory to put the packages in.
1229 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001230 """
Ralph Nathan03047282015-03-23 11:09:32 -07001231 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001232 osutils.SafeMakedirs(output_dir)
1233 ldpaths = lddtree.LoadLdpaths(root)
1234 targets = ExpandTargets(targets_wanted)
1235
Mike Frysinger221bd822017-09-29 02:51:47 -04001236 with osutils.TempDir(prefix='create-packages') as tempdir:
1237 logging.debug('Using tempdir: %s', tempdir)
1238
Mike Frysinger35247af2012-11-16 18:58:06 -05001239 # We have to split the root generation from the compression stages. This is
1240 # because we hardlink in all the files (to avoid overhead of reading/writing
1241 # the copies multiple times). But tar gets angry if a file's hardlink count
1242 # changes from when it starts reading a file to when it finishes.
1243 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1244 for target in targets:
1245 output_target_dir = os.path.join(tempdir, target)
1246 queue.put([target, output_target_dir, ldpaths, root])
1247
1248 # Build the tarball.
1249 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1250 for target in targets:
1251 tar_file = os.path.join(output_dir, target + '.tar.xz')
1252 queue.put([tar_file, os.path.join(tempdir, target)])
1253
1254
Mike Frysinger07534cf2017-09-12 17:40:21 -04001255def GetParser():
1256 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001257 parser = commandline.ArgumentParser(description=__doc__)
1258 parser.add_argument('-u', '--nousepkg',
1259 action='store_false', dest='usepkg', default=True,
1260 help='Use prebuilt packages if possible')
1261 parser.add_argument('-d', '--deleteold',
1262 action='store_true', dest='deleteold', default=False,
1263 help='Unmerge deprecated packages')
1264 parser.add_argument('-t', '--targets',
1265 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001266 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001267 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001268 "allowed. Defaults to 'sdk'.")
1269 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1270 help='Comma separated list of boards whose toolchains we '
1271 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001272 parser.add_argument('--hostonly',
1273 dest='hostonly', default=False, action='store_true',
1274 help='Only setup the host toolchain. '
1275 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001276 parser.add_argument('--show-board-cfg', '--show-cfg',
1277 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001278 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001279 parser.add_argument('--show-packages', default=None,
1280 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001281 parser.add_argument('--create-packages',
1282 action='store_true', default=False,
1283 help='Build redistributable packages')
1284 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1285 help='Output directory')
1286 parser.add_argument('--reconfig', default=False, action='store_true',
1287 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001288 parser.add_argument('--sysroot', type='path',
1289 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001290 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001291
Mike Frysinger07534cf2017-09-12 17:40:21 -04001292
1293def main(argv):
1294 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001295 options = parser.parse_args(argv)
1296 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001297
Mike Frysinger35247af2012-11-16 18:58:06 -05001298 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001299 conflicting_options = (
1300 options.cfg_name,
1301 options.show_packages,
1302 options.create_packages,
1303 )
1304 if sum(bool(x) for x in conflicting_options) > 1:
1305 parser.error('conflicting options: create-packages & show-packages & '
1306 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001307
Gilad Arnold8195b532015-04-07 10:56:30 +03001308 targets_wanted = set(options.targets.split(','))
1309 boards_wanted = (set(options.include_boards.split(','))
1310 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001311
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001312 if options.cfg_name:
1313 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001314 elif options.show_packages is not None:
1315 cros_build_lib.AssertInsideChroot()
1316 target = options.show_packages
1317 Crossdev.Load(False)
1318 for package in GetTargetPackages(target):
1319 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001320 elif options.create_packages:
1321 cros_build_lib.AssertInsideChroot()
1322 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001323 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001324 else:
1325 cros_build_lib.AssertInsideChroot()
1326 # This has to be always run as root.
1327 if os.geteuid() != 0:
1328 cros_build_lib.Die('this script must be run as root')
1329
1330 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001331 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001332 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001333 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001334 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001335 Crossdev.Save()
1336
1337 return 0