blob: 327a3c7c2948e6f460de28d04cbd6e477368be03 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Zdenek Behan508dcce2011-12-05 15:39:32 +01002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Mike Frysinger750c5f52014-09-16 16:16:57 -04006"""This script manages the installed toolchains in the chroot."""
Zdenek Behan508dcce2011-12-05 15:39:32 +01007
Mike Frysinger383367e2014-09-16 15:06:17 -04008from __future__ import print_function
9
Mike Frysinger3ed47722017-08-08 14:59:08 -040010import errno
Mike Frysinger35247af2012-11-16 18:58:06 -050011import glob
Mike Frysinger3ed47722017-08-08 14:59:08 -040012import hashlib
Mike Frysinger7ccee992012-06-01 21:27:59 -040013import json
Zdenek Behan508dcce2011-12-05 15:39:32 +010014import os
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -070015import re
Tobias Boschddd16492019-08-14 09:29:54 -070016import shutil
Mike Frysinger499ca392020-04-28 07:35:54 -040017import sys
Zdenek Behan508dcce2011-12-05 15:39:32 +010018
Mike Frysinger506e75f2012-12-17 14:21:13 -050019from chromite.lib import commandline
Mike Frysinger95452702021-01-23 00:07:22 -050020from chromite.lib import constants
Brian Harring503f3ab2012-03-09 21:39:41 -080021from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070022from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070023from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050024from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080025from chromite.lib import toolchain
Mike Frysinger95452702021-01-23 00:07:22 -050026from chromite.third_party import lddtree
Mike Frysingere652ba12019-09-08 00:57:43 -040027from chromite.utils import key_value_store
Mike Frysinger35247af2012-11-16 18:58:06 -050028
Zdenek Behan508dcce2011-12-05 15:39:32 +010029
Mike Frysinger31596002012-12-03 23:54:24 -050030if cros_build_lib.IsInsideChroot():
31 # Only import portage after we've checked that we're inside the chroot.
32 # Outside may not have portage, in which case the above may not happen.
33 # We'll check in main() if the operation needs portage.
Mike Frysinger27e21b72018-07-12 14:20:21 -040034 # pylint: disable=import-error
Mike Frysinger31596002012-12-03 23:54:24 -050035 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010036
37
Mike Frysinger499ca392020-04-28 07:35:54 -040038assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
39
40
Matt Tennantf1e30972012-03-02 16:30:07 -080041EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010042PACKAGE_STABLE = '[stable]'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010043
44CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070045ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010046STABLE_OVERLAY = '/usr/local/portage/stable'
47CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010048
49
Mike Frysinger66bfde52017-09-12 16:42:57 -040050# The exact list of host toolchain packages we care about. These are the
51# packages that bots/devs install only from binpkgs and rely on the SDK bot
52# (chromiumos-sdk) to validate+uprev.
53#
Mike Frysinger66bfde52017-09-12 16:42:57 -040054# We don't use crossdev to manage the host toolchain for us, especially since
55# we diverge significantly now (with llvm/clang/etc...), and we don't need or
56# want crossdev managing /etc/portage config files for the sdk
57HOST_PACKAGES = (
58 'dev-lang/go',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080059 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040060 'sys-devel/binutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040061 'sys-devel/gcc',
62 'sys-devel/llvm',
63 'sys-kernel/linux-headers',
64 'sys-libs/glibc',
65 'sys-libs/libcxx',
66 'sys-libs/libcxxabi',
Manoj Guptade64cb22019-03-31 18:48:58 -070067 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040068)
69
Mike Frysinger785b0c32017-09-13 01:35:59 -040070# These packages are also installed into the host SDK. However, they require
71# the cross-compilers to be installed first (because they need them to actually
72# build), so we have to delay their installation.
73HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070074 'dev-lang/rust',
George Burgess IV9d1e2322021-04-30 09:54:45 -070075 'dev-lang/rust-bootstrap',
Mike Frysinger61a24392017-10-17 17:14:27 -040076 'virtual/target-sdk-post-cross',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010077 'dev-embedded/coreboot-sdk',
George Burgess IV288ecc82021-04-21 07:52:45 -070078 'dev-embedded/ti50-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040079)
80
81# New packages that we're in the process of adding to the SDK. Since the SDK
82# bot hasn't had a chance to run yet, there are no binary packages available,
83# so we have to list them here and wait. Once it completes, entries here can
84# be removed so they'll end up on bots & dev's systems.
George Burgess IV66e199c2021-05-05 15:38:40 -070085NEW_PACKAGES = ()
Mike Frysinger785b0c32017-09-13 01:35:59 -040086
Rahul Chaudhry4b803052015-05-13 15:25:56 -070087# Enable the Go compiler for these targets.
88TARGET_GO_ENABLED = (
89 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070090 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070091 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070092 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070093)
94CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
95
Manoj Gupta1b5642e2017-03-08 16:44:12 -080096# Enable llvm's compiler-rt for these targets.
97TARGET_COMPILER_RT_ENABLED = (
98 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070099 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700100 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -0800101 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800102)
103CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
104
Manoj Gupta946abb42017-04-12 14:27:19 -0700105TARGET_LLVM_PKGS_ENABLED = (
106 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700107 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700108 'aarch64-cros-linux-gnu',
Manoj Gupta93713122020-10-29 17:52:16 -0700109 'i686-pc-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700110 'x86_64-cros-linux-gnu',
111)
112
113LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700114 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
115 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700116 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700117}
118
David James66a09c42012-11-05 13:31:38 -0800119class Crossdev(object):
120 """Class for interacting with crossdev and caching its output."""
121
122 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
123 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800124 # Packages that needs separate handling, in addition to what we have from
125 # crossdev.
126 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700127 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800128 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700129 'libcxxabi': 'sys-libs',
130 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700131 'elfutils': 'dev-libs',
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700132 'llvm-libunwind': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800133 }
David James66a09c42012-11-05 13:31:38 -0800134
135 @classmethod
136 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400137 """Load crossdev cache from disk.
138
139 We invalidate the cache when crossdev updates or this script changes.
140 """
David James90239b92012-11-05 15:31:34 -0800141 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400142 # If we run the compiled/cached .pyc file, we'll read/hash that when we
143 # really always want to track the source .py file.
144 script = os.path.abspath(__file__)
145 if script.endswith('.pyc'):
146 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500147 setup_toolchains_hash = hashlib.md5(
148 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400149
150 cls._CACHE = {
151 'crossdev_version': crossdev_version,
152 'setup_toolchains_hash': setup_toolchains_hash,
153 }
154
155 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
156 if reconfig:
157 logging.debug('cache: forcing regen due to reconfig')
158 return
159
160 try:
161 file_data = osutils.ReadFile(cls._CACHE_FILE)
162 except IOError as e:
163 if e.errno != errno.ENOENT:
164 logging.warning('cache: reading failed: %s', e)
165 osutils.SafeUnlink(cls._CACHE_FILE)
166 return
167
168 try:
169 data = json.loads(file_data)
170 except ValueError as e:
171 logging.warning('cache: ignoring invalid content: %s', e)
172 return
173
174 if crossdev_version != data.get('crossdev_version'):
175 logging.debug('cache: rebuilding after crossdev upgrade')
176 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
177 logging.debug('cache: rebuilding after cros_setup_toolchains change')
178 else:
179 logging.debug('cache: content is up-to-date!')
180 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800181
182 @classmethod
183 def Save(cls):
184 """Store crossdev cache on disk."""
185 # Save the cache from the successful run.
186 with open(cls._CACHE_FILE, 'w') as f:
187 json.dump(cls._CACHE, f)
188
189 @classmethod
190 def GetConfig(cls, target):
191 """Returns a map of crossdev provided variables about a tuple."""
192 CACHE_ATTR = '_target_tuple_map'
193
194 val = cls._CACHE.setdefault(CACHE_ATTR, {})
195 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400196 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400197 conf = {
198 'crosspkgs': [],
199 'target': toolchain.GetHostTuple(),
200 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400201 if target == 'host':
202 packages_list = HOST_PACKAGES
203 else:
204 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400205 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400206 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400207 else:
208 # Build the crossdev command.
209 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
210 if target in TARGET_COMPILER_RT_ENABLED:
211 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
212 if target in TARGET_GO_ENABLED:
213 cmd.extend(CROSSDEV_GO_ARGS)
214 if target in TARGET_LLVM_PKGS_ENABLED:
215 for pkg in LLVM_PKGS_TABLE:
216 cmd.extend(LLVM_PKGS_TABLE[pkg])
217 cmd.extend(['-t', target])
218 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400219 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500220 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500221 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400222 # List of tuples split at the first '=', converted into dict.
223 conf = dict((k, cros_build_lib.ShellUnquote(v))
224 for k, v in (x.split('=', 1) for x in out))
225 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800226
Mike Frysinger66bfde52017-09-12 16:42:57 -0400227 manual_pkgs = cls.MANUAL_PKGS
228
229 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400230 conf[pkg + '_pn'] = pkg
231 conf[pkg + '_category'] = cat
232 if pkg not in conf['crosspkgs']:
233 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800234
235 val[target] = conf
236
David James66a09c42012-11-05 13:31:38 -0800237 return val[target]
238
239 @classmethod
240 def UpdateTargets(cls, targets, usepkg, config_only=False):
241 """Calls crossdev to initialize a cross target.
242
243 Args:
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000244 targets: The list of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700245 usepkg: Copies the commandline opts.
246 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800247 """
248 configured_targets = cls._CACHE.setdefault('configured_targets', [])
249
250 cmdbase = ['crossdev', '--show-fail-log']
251 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
252 # Pick stable by default, and override as necessary.
253 cmdbase.extend(['-P', '--oneshot'])
254 if usepkg:
255 cmdbase.extend(['-P', '--getbinpkg',
256 '-P', '--usepkgonly',
257 '--without-headers'])
258
Christopher Wileyb22c0712015-06-02 10:37:03 -0700259 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800260 cmdbase.extend(['--overlays', overlays])
261 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
262
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000263 # Build target by the reversed alphabetical order to make sure
264 # armv7a-cros-linux-gnueabihf builds before armv7a-cros-linux-gnueabi
265 # because some dependency issue. This can be reverted once we
266 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
267 for target in sorted(targets, reverse=True):
268 if config_only and target in configured_targets:
269 continue
David James66a09c42012-11-05 13:31:38 -0800270
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000271 cmd = cmdbase + ['-t', target]
272
273 for pkg in GetTargetPackages(target):
274 if pkg == 'gdb':
275 # Gdb does not have selectable versions.
276 cmd.append('--ex-gdb')
277 elif pkg == 'ex_compiler-rt':
278 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
279 elif pkg == 'ex_go':
280 # Go does not have selectable versions.
281 cmd.extend(CROSSDEV_GO_ARGS)
282 elif pkg in LLVM_PKGS_TABLE:
283 cmd.extend(LLVM_PKGS_TABLE[pkg])
284 elif pkg in cls.MANUAL_PKGS:
285 pass
286 else:
287 # The first of the desired versions is the "primary" one.
288 version = GetDesiredPackageVersions(target, pkg)[0]
289 cmd.extend(['--%s' % pkg, version])
290
291 cmd.extend(targets[target]['crossdev'].split())
292 if config_only:
293 # In this case we want to just quietly reinit
294 cmd.append('--init-target')
Mike Frysinger0282d222019-12-17 17:15:48 -0500295 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
David James66a09c42012-11-05 13:31:38 -0800296 else:
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000297 cros_build_lib.run(cmd)
David James66a09c42012-11-05 13:31:38 -0800298
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000299 configured_targets.append(target)
David James66a09c42012-11-05 13:31:38 -0800300
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100301
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100302def GetTargetPackages(target):
303 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800304 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100305 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800306 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100307
308
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100309# Portage helper functions:
310def GetPortagePackage(target, package):
311 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800312 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100313 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400314 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100315 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100316 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100317 category = conf['category']
318 # Portage package:
319 pn = conf[package + '_pn']
320 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500321 assert category
322 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100323 return '%s/%s' % (category, pn)
324
325
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700326def PortageTrees(root):
327 """Return the portage trees for a given root."""
328 if root == '/':
329 return portage.db['/']
330 # The portage logic requires the path always end in a slash.
331 root = root.rstrip('/') + '/'
332 return portage.create_trees(target_root=root, config_root=root)[root]
333
334
335def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100336 """Extracts the list of current versions of a target, package pair.
337
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500338 Args:
339 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700340 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500342 Returns:
343 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100344 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100345 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700346 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347 version = portage.versions.cpv_getversion(pkg)
348 versions.append(version)
349 return versions
350
351
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700352def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100353 """Extracts the current stable version for a given package.
354
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500355 Args:
356 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
357 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700358 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100359
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500360 Returns:
361 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100362 """
David James90239b92012-11-05 15:31:34 -0800363 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700364 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800365 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100366
367
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700368def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369 """Resolves keywords in a given version list for a particular package.
370
371 Resolving means replacing PACKAGE_STABLE with the actual number.
372
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500373 Args:
374 target: The target to operate on (e.g. i686-pc-linux-gnu)
375 package: The target/package to operate on (e.g. gcc)
376 versions: List of versions to resolve
377 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700378 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100379
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500380 Returns:
381 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100382 """
383 resolved = []
David James90239b92012-11-05 15:31:34 -0800384 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700385 if not installed:
386 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100387 for version in versions:
388 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700389 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400390 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100391 resolved.append(version)
392 return resolved
393
394
395def GetDesiredPackageVersions(target, package):
396 """Produces the list of desired versions for each target, package pair.
397
398 The first version in the list is implicitly treated as primary, ie.
399 the version that will be initialized by crossdev and selected.
400
401 If the version is PACKAGE_STABLE, it really means the current version which
402 is emerged by using the package atom with no particular version key.
403 Since crossdev unmasks all packages by default, this will actually
404 mean 'unstable' in most cases.
405
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500406 Args:
407 target: The target to operate on (e.g. i686-pc-linux-gnu)
408 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100409
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500410 Returns:
411 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100412 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400413 if package in GetTargetPackages(target):
414 return [PACKAGE_STABLE]
415 else:
416 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417
418
419def TargetIsInitialized(target):
420 """Verifies if the given list of targets has been correctly initialized.
421
422 This determines whether we have to call crossdev while emerging
423 toolchain packages or can do it using emerge. Emerge is naturally
424 preferred, because all packages can be updated in a single pass.
425
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500426 Args:
427 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100428
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500429 Returns:
430 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100431 """
432 # Check if packages for the given target all have a proper version.
433 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100434 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800435 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100436 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400437 if not (GetStablePackageVersion(atom, True) and
438 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439 return False
440 return True
441 except cros_build_lib.RunCommandError:
442 # Fails - The target has likely never been initialized before.
443 return False
444
445
446def RemovePackageMask(target):
447 """Removes a package.mask file for the given platform.
448
449 The pre-existing package.mask files can mess with the keywords.
450
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500451 Args:
452 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100453 """
454 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700455 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100456
457
Zdenek Behan508dcce2011-12-05 15:39:32 +0100458# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700459def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500460 """Rebuild libtool as needed
461
462 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
463 gcc, libtool will break. We can't use binary packages either as those will
464 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700465
466 Args:
467 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500468 """
469 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700470 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500471 for line in f:
472 # Look for a line like:
473 # sys_lib_search_path_spec="..."
474 # It'll be a list of paths and gcc will be one of them.
475 if line.startswith('sys_lib_search_path_spec='):
476 line = line.rstrip()
477 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400478 root_path = os.path.join(root, path.lstrip(os.path.sep))
479 logging.debug('Libtool: checking %s', root_path)
480 if not os.path.exists(root_path):
481 logging.info('Rebuilding libtool after gcc upgrade')
482 logging.info(' %s', line)
483 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500484 needs_update = True
485 break
486
487 if needs_update:
488 break
489
490 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700491 cmd = [EMERGE_CMD, '--oneshot']
492 if root != '/':
493 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
494 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400495 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400496 else:
497 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500498
499
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700500def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100501 """Determines which packages need update/unmerge and defers to portage.
502
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500503 Args:
504 targets: The list of targets to update
505 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700506 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100507 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100508 # For each target, we do two things. Figure out the list of updates,
509 # and figure out the appropriate keywords/masks. Crossdev will initialize
510 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400511 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800512 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000513 # Used to keep track of post-cross packages. These are allowed to have
514 # implicit dependencies on toolchain packages, and therefore need to
515 # be built last.
516 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000518 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400519 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520 # Record the highest needed version for each target, for masking purposes.
521 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100522 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100523 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400524 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100525 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700526 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100527 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200528 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400529 if pkg in NEW_PACKAGES and usepkg:
530 # Skip this binary package (for now).
531 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100532 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400533 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000534 if is_post_cross_target:
535 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400537 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400539 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800540 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541
Mike Frysinger3bba5032016-09-20 14:15:04 -0400542 logging.info('Updating packages:')
543 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100544
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100545 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100546 if usepkg:
547 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700548 if root != '/':
549 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100550
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000551 if usepkg:
552 # Since we are not building from source, we can handle
553 # all packages in one go.
554 cmd.extend(packages)
555 cros_build_lib.run(cmd)
556 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700557 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
558 if pre_cross_items:
559 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000560 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700561 if post_cross_items:
562 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800563 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100564
565
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700566def CleanTargets(targets, root='/'):
567 """Unmerges old packages that are assumed unnecessary.
568
569 Args:
570 targets: The list of targets to clean up.
571 root: The install root in which we want packages cleaned up.
572 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100573 unmergemap = {}
574 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400575 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100576 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400577 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100578 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700579 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100580 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700581 # NOTE: This refers to installed packages (vartree) rather than the
582 # Portage version (porttree and/or bintree) when determining the current
583 # version. While this isn't the most accurate thing to do, it is probably
584 # a good simple compromise, which should have the desired result of
585 # uninstalling everything but the latest installed version. In
586 # particular, using the bintree (--usebinpkg) requires a non-trivial
587 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200588 desired_num = VersionListToNumeric(target, package, desired, True)
589 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400590 logging.warning('Error detecting stable version for %s, '
591 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200592 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593 unmergemap[pkg] = set(current).difference(desired_num)
594
595 # Cleaning doesn't care about consistency and rebuilding package.* files.
596 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400597 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100598 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
599
600 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400601 logging.info('Cleaning packages:')
602 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100603 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700604 if root != '/':
605 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100606 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400607 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100608 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400609 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100610
611
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200612def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100613 """Runs gcc-config and binutils-config to select the desired.
614
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500615 Args:
616 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700617 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100618 """
619 for package in ['gcc', 'binutils']:
620 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400621 # See if this package is part of this target.
622 if package not in GetTargetPackages(target):
623 logging.debug('%s: %s is not used', target, package)
624 continue
625
Zdenek Behan508dcce2011-12-05 15:39:32 +0100626 # Pick the first version in the numbered list as the selected one.
627 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700628 desired_num = VersionListToNumeric(target, package, desired, True,
629 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100630 desired = desired_num[0]
631 # *-config does not play revisions, strip them, keep just PV.
632 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
633
Mike Frysinger785b0c32017-09-13 01:35:59 -0400634 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100635 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800636 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100637
638 # And finally, attach target to it.
639 desired = '%s-%s' % (target, desired)
640
David James7ec5efc2012-11-06 09:39:49 -0800641 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700642 if root != '/':
643 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800644 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400645 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500646 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500647 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500648 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700649
650 # Do not reconfig when the current is live or nothing needs to be done.
651 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100652 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500653 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400654 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100655
656
Mike Frysinger35247af2012-11-16 18:58:06 -0500657def ExpandTargets(targets_wanted):
658 """Expand any possible toolchain aliases into full targets
659
660 This will expand 'all' and 'sdk' into the respective toolchain tuples.
661
662 Args:
663 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500664
Mike Frysinger35247af2012-11-16 18:58:06 -0500665 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300666 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500667 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500668 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700669 if targets_wanted == set(['boards']):
670 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300671 return {}
672
673 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500674 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300675 return all_targets
676 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500677 # Filter out all the non-sdk toolchains as we don't want to mess
678 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300679 return toolchain.FilterToolchains(all_targets, 'sdk', True)
680
681 # Verify user input.
682 nonexistent = targets_wanted.difference(all_targets)
683 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400684 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300685 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500686
687
David Jamesf8c672f2012-11-06 13:38:11 -0800688def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700689 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100690 """Performs all steps to create a synchronized toolchain enviroment.
691
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500692 Args:
693 usepkg: Use prebuilt packages
694 deleteold: Unmerge deprecated packages
695 hostonly: Only setup the host toolchain
696 reconfig: Reload crossdev config and reselect toolchains
697 targets_wanted: All the targets to update
698 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700699 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100700 """
David Jamesf8c672f2012-11-06 13:38:11 -0800701 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100702 if not hostonly:
703 # For hostonly, we can skip most of the below logic, much of which won't
704 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500705 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400706
Don Garrettc0c74002015-10-09 12:58:19 -0700707 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300708 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400709 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800710 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100711
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100712 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400713 for target in targets:
714 if TargetIsInitialized(target):
715 reconfig_targets[target] = targets[target]
716 else:
717 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100718 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400719 logging.info('The following targets need to be re-initialized:')
720 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800721 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200722 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800723 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100724
Mike Frysinger66814c32017-10-09 18:11:46 -0400725 # If we're building a subset of toolchains for a board, we might not have
726 # all the tuples that the packages expect. We don't define the "full" set
727 # of tuples currently other than "whatever the full sdk has normally".
728 if usepkg or set(('all', 'sdk')) & targets_wanted:
729 # Since we have cross-compilers now, we can update these packages.
730 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400731
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100732 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400733 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100734
735 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700736 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200737 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800738
739 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700740 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100741
Mike Frysingerc880a962013-11-08 13:59:06 -0500742 # Now that we've cleared out old versions, see if we need to rebuild
743 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700744 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500745
Zdenek Behan508dcce2011-12-05 15:39:32 +0100746
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700747def ShowConfig(name):
748 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500749
750 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700751 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500752 """
Don Garrettc0c74002015-10-09 12:58:19 -0700753 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500754 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400755 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400756 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400757 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
758 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500759
760
Mike Frysinger35247af2012-11-16 18:58:06 -0500761def GeneratePathWrapper(root, wrappath, path):
762 """Generate a shell script to execute another shell script
763
764 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
765 argv[0] won't be pointing to the correct path, generate a shell script that
766 just executes another program with its full path.
767
768 Args:
769 root: The root tree to generate scripts inside of
770 wrappath: The full path (inside |root|) to create the wrapper
771 path: The target program which this wrapper will execute
772 """
773 replacements = {
774 'path': path,
775 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
776 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900777
778 # Do not use exec here, because exec invokes script with absolute path in
779 # argv0. Keeping relativeness allows us to remove abs path from compile result
780 # and leads directory independent build cache sharing in some distributed
781 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500782 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900783basedir=$(dirname "$0")
784"${basedir}/%(relroot)s%(path)s" "$@"
785exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500786""" % replacements
787 root_wrapper = root + wrappath
788 if os.path.islink(root_wrapper):
789 os.unlink(root_wrapper)
790 else:
791 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
792 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400793 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500794
795
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700796def FixClangXXWrapper(root, path):
797 """Fix wrapper shell scripts and symlinks for invoking clang++
798
799 In a typical installation, clang++ symlinks to clang, which symlinks to the
800 elf executable. The executable distinguishes between clang and clang++ based
801 on argv[0].
802
803 When invoked through the LdsoWrapper, argv[0] always contains the path to the
804 executable elf file, making clang/clang++ invocations indistinguishable.
805
806 This function detects if the elf executable being wrapped is clang-X.Y, and
807 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
808
809 The calling sequence now becomes:
810 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
811 the Ldsowrapper).
812 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
813 to the original clang-3.9 elf.
814 -) The difference this time is that inside the elf file execution, $0 is
815 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
816
Manoj Guptaae268142018-04-27 23:28:36 -0700817 Update: Starting since clang 7, the clang and clang++ are symlinks to
818 clang-7 binary, not clang-7.0. The pattern match is extended to handle
819 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
820
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700821 Args:
822 root: The root tree to generate scripts / symlinks inside of
823 path: The target elf for which LdsoWrapper was created
824 """
Manoj Guptaae268142018-04-27 23:28:36 -0700825 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700826 logging.info('fixing clang++ invocation for %s', path)
827 clangdir = os.path.dirname(root + path)
828 clang = os.path.basename(path)
829 clangxx = clang.replace('clang', 'clang++')
830
831 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
832 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
833
834 # Create a hardlink clang++-X.Y pointing to clang-X.Y
835 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
836
837 # Adjust the clang++ symlink to point to clang++-X.Y
838 os.unlink(os.path.join(clangdir, 'clang++'))
839 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
840
841
Mike Frysinger35247af2012-11-16 18:58:06 -0500842def FileIsCrosSdkElf(elf):
843 """Determine if |elf| is an ELF that we execute in the cros_sdk
844
845 We don't need this to be perfect, just quick. It makes sure the ELF
846 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
847
848 Args:
849 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500850
Mike Frysinger35247af2012-11-16 18:58:06 -0500851 Returns:
852 True if we think |elf| is a native ELF
853 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500854 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500855 data = f.read(20)
856 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500857 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500858 data[4:5] == b'\x02' and
859 data[5:6] == b'\x01' and
860 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500861
862
863def IsPathPackagable(ptype, path):
864 """Should the specified file be included in a toolchain package?
865
866 We only need to handle files as we'll create dirs as we need them.
867
868 Further, trim files that won't be useful:
869 - non-english translations (.mo) since it'd require env vars
870 - debug files since these are for the host compiler itself
871 - info/man pages as they're big, and docs are online, and the
872 native docs should work fine for the most part (`man gcc`)
873
874 Args:
875 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
876 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500877
Mike Frysinger35247af2012-11-16 18:58:06 -0500878 Returns:
879 True if we want to include this path in the package
880 """
881 return not (ptype in ('dir',) or
882 path.startswith('/usr/lib/debug/') or
883 os.path.splitext(path)[1] == '.mo' or
884 ('/man/' in path or '/info/' in path))
885
886
887def ReadlinkRoot(path, root):
888 """Like os.readlink(), but relative to a |root|
889
890 Args:
891 path: The symlink to read
892 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500893
Mike Frysinger35247af2012-11-16 18:58:06 -0500894 Returns:
895 A fully resolved symlink path
896 """
897 while os.path.islink(root + path):
898 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
899 return path
900
901
902def _GetFilesForTarget(target, root='/'):
903 """Locate all the files to package for |target|
904
905 This does not cover ELF dependencies.
906
907 Args:
908 target: The toolchain target name
909 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500910
Mike Frysinger35247af2012-11-16 18:58:06 -0500911 Returns:
912 A tuple of a set of all packable paths, and a set of all paths which
913 are also native ELFs
914 """
915 paths = set()
916 elfs = set()
917
918 # Find all the files owned by the packages for this target.
919 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500920
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700921 # Skip Go compiler from redistributable packages.
922 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
923 # into it. Due to this, the toolchain cannot be unpacked anywhere
924 # else and be readily useful. To enable packaging Go, we need to:
925 # -) Tweak the wrappers/environment to override GOROOT
926 # automatically based on the unpack location.
927 # -) Make sure the ELF dependency checking and wrapping logic
928 # below skips the Go toolchain executables and libraries.
929 # -) Make sure the packaging process maintains the relative
930 # timestamps of precompiled standard library packages.
931 # (see dev-lang/go ebuild for details).
932 if pkg == 'ex_go':
933 continue
934
Yunlian Jiang36f35242018-04-27 10:18:40 -0700935 # Use armv7a-cros-linux-gnueabi/compiler-rt for
936 # armv7a-cros-linux-gnueabihf/compiler-rt.
937 # Currently the armv7a-cros-linux-gnueabi is actually
938 # the same as armv7a-cros-linux-gnueabihf with different names.
939 # Because of that, for compiler-rt, it generates the same binary in
940 # the same location. To avoid the installation conflict, we do not
941 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
942 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
943 # toolchain.
944 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
945 atom = GetPortagePackage(target, pkg)
946 cat, pn = atom.split('/')
947 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700948 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
949 settings=portage.settings)
950 contents = dblink.getcontents()
951 if not contents:
952 if 'hf' in target:
953 new_target = 'armv7a-cros-linux-gnueabi'
954 else:
955 new_target = 'armv7a-cros-linux-gnueabihf'
956 atom = GetPortagePackage(new_target, pkg)
957 else:
958 atom = GetPortagePackage(target, pkg)
959
Mike Frysinger35247af2012-11-16 18:58:06 -0500960 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700961 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700962 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500963
Mike Frysinger35247af2012-11-16 18:58:06 -0500964 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
965 settings=portage.settings)
966 contents = dblink.getcontents()
967 for obj in contents:
968 ptype = contents[obj][0]
969 if not IsPathPackagable(ptype, obj):
970 continue
971
972 if ptype == 'obj':
973 # For native ELFs, we need to pull in their dependencies too.
974 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500975 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500976 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500977 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500978 paths.add(obj)
979
980 return paths, elfs
981
982
983def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500984 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500985 """Link in all packable files and their runtime dependencies
986
987 This also wraps up executable ELFs with helper scripts.
988
989 Args:
990 output_dir: The output directory to store files
991 paths: All the files to include
992 elfs: All the files which are ELFs (a subset of |paths|)
993 ldpaths: A dict of static ldpath information
994 path_rewrite_func: User callback to rewrite paths in output_dir
995 root: The root path to pull all packages/files from
996 """
997 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400998 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500999 for path in paths:
1000 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001001 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001002 dst = output_dir + new_path
1003 osutils.SafeMakedirs(os.path.dirname(dst))
1004
1005 # Is this a symlink which we have to rewrite or wrap?
1006 # Delay wrap check until after we have created all paths.
1007 src = root + path
1008 if os.path.islink(src):
1009 tgt = os.readlink(src)
1010 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001011 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001012
1013 # Rewrite absolute links to relative and then generate the symlink
1014 # ourselves. All other symlinks can be hardlinked below.
1015 if tgt[0] == '/':
1016 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1017 os.symlink(tgt, dst)
1018 continue
1019
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001020 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001021 os.link(src, dst)
1022
Mike Frysinger35247af2012-11-16 18:58:06 -05001023 # Locate all the dependencies for all the ELFs. Stick them all in the
1024 # top level "lib" dir to make the wrapper simpler. This exact path does
1025 # not matter since we execute ldso directly, and we tell the ldso the
1026 # exact path to search for its libraries.
1027 libdir = os.path.join(output_dir, 'lib')
1028 osutils.SafeMakedirs(libdir)
1029 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001030 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001031 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001032 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001033 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001034 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001035 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001036
Mike Frysinger9fe02342019-12-12 17:52:53 -05001037 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1038 if os.path.basename(elf).startswith('libopcodes-'):
1039 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001040
Mike Frysinger00b129f2021-04-21 18:11:48 -04001041 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001042 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001043 src = path = lib_data['path']
1044 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001045 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001046 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001047
1048 # No need to try and copy the same source lib multiple times.
1049 if path in donelibs:
1050 continue
1051 donelibs.add(path)
1052
1053 # Die if we try to normalize different source libs with the same basename.
1054 if lib in basenamelibs:
1055 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1056 os.path.join('/lib', lib), path,
1057 ' '.join(x for x in donelibs
1058 if x != path and os.path.basename(x) == lib))
1059 # TODO(crbug.com/917193): Make this fatal.
1060 # cros_build_lib.Die('Unable to resolve lib conflicts')
1061 continue
1062 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001063
1064 # Needed libs are the SONAME, but that is usually a symlink, not a
1065 # real file. So link in the target rather than the symlink itself.
1066 # We have to walk all the possible symlinks (SONAME could point to a
1067 # symlink which points to a symlink), and we have to handle absolute
1068 # ourselves (since we have a "root" argument).
1069 dst = os.path.join(libdir, os.path.basename(path))
1070 src = ReadlinkRoot(src, root)
1071
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001072 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001073 os.link(root + src, dst)
1074
Mike Frysinger00b129f2021-04-21 18:11:48 -04001075 # Do not create wrapper for libc. crbug.com/766827
1076 if interp and not glibc_re.search(elf):
1077 # Generate a wrapper if it is executable.
1078 interp = os.path.join('/lib', os.path.basename(interp))
1079 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1080 libpaths=e['rpath'] + e['runpath'])
1081 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1082
1083 # Wrap any symlinks to the wrapper.
1084 if elf in sym_paths:
1085 link = sym_paths[elf]
1086 GeneratePathWrapper(output_dir, link, elf)
1087
Mike Frysinger35247af2012-11-16 18:58:06 -05001088
1089def _EnvdGetVar(envd, var):
1090 """Given a Gentoo env.d file, extract a var from it
1091
1092 Args:
1093 envd: The env.d file to load (may be a glob path)
1094 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001095
Mike Frysinger35247af2012-11-16 18:58:06 -05001096 Returns:
1097 The value of |var|
1098 """
1099 envds = glob.glob(envd)
1100 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1101 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001102 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001103
1104
1105def _ProcessBinutilsConfig(target, output_dir):
1106 """Do what binutils-config would have done"""
1107 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001108
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001109 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001110 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1111 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001112 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001113 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001114 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1115 % globpath)
1116 srcpath = srcpath[0]
1117 ld_path = os.path.join(srcpath, 'ld')
1118 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1119 ld_path = os.path.join(srcpath, 'ld.bfd')
1120 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001121
Mike Frysinger78b7a812014-11-26 19:45:23 -05001122 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001123 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1124 for prog in os.listdir(output_dir + srcpath):
1125 # Skip binaries already wrapped.
1126 if not prog.endswith('.real'):
1127 GeneratePathWrapper(output_dir, binpath + prog,
1128 os.path.join(srcpath, prog))
1129 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1130 os.path.join(srcpath, prog))
1131
David James27ac4ae2012-12-03 23:16:15 -08001132 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001133 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001134 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1135 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1136 output_dir + libpath)
1137
1138
1139def _ProcessGccConfig(target, output_dir):
1140 """Do what gcc-config would have done"""
1141 binpath = '/bin'
1142 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1143 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1144 for prog in os.listdir(output_dir + srcpath):
1145 # Skip binaries already wrapped.
1146 if (not prog.endswith('.real') and
1147 not prog.endswith('.elf') and
1148 prog.startswith(target)):
1149 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1150 os.path.join(srcpath, prog))
1151 return srcpath
1152
1153
Frank Henigman179ec7c2015-02-06 03:01:09 -05001154def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1155 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001156 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001157
Tobias Boschddd16492019-08-14 09:29:54 -07001158 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001159 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001160 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1161 # Can't update the wrapper in place to not affect the chroot,
1162 # but only the extracted toolchain.
1163 os.unlink(sysroot_wrapper)
1164 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001165 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001166
1167
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001168def _CreateMainLibDir(target, output_dir):
1169 """Create some lib dirs so that compiler can get the right Gcc paths"""
1170 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1171 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1172
1173
Mike Frysinger35247af2012-11-16 18:58:06 -05001174def _ProcessDistroCleanups(target, output_dir):
1175 """Clean up the tree and remove all distro-specific requirements
1176
1177 Args:
1178 target: The toolchain target name
1179 output_dir: The output directory to clean up
1180 """
1181 _ProcessBinutilsConfig(target, output_dir)
1182 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001183 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001184 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001185
1186 osutils.RmDir(os.path.join(output_dir, 'etc'))
1187
1188
1189def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1190 """Setup a tree from the packages for the specified target
1191
1192 This populates a path with all the files from toolchain packages so that
1193 a tarball can easily be generated from the result.
1194
1195 Args:
1196 target: The target to create a packagable root from
1197 output_dir: The output directory to place all the files
1198 ldpaths: A dict of static ldpath information
1199 root: The root path to pull all packages/files from
1200 """
1201 # Find all the files owned by the packages for this target.
1202 paths, elfs = _GetFilesForTarget(target, root=root)
1203
1204 # Link in all the package's files, any ELF dependencies, and wrap any
1205 # executable ELFs with helper scripts.
1206 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001207 """Move /usr/bin to /bin so people can just use that toplevel dir
1208
George Burgess IVca1d7612020-10-01 00:38:32 -07001209 Note we do not apply this to clang or rust - there is correlation between
1210 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001211 """
George Burgess IVca1d7612020-10-01 00:38:32 -07001212 if (path.startswith('/usr/bin/') and
1213 not any(x in path for x in ('clang', 'rust', 'cargo'))):
Han Shen699ea192016-03-02 10:42:47 -08001214 return path[4:]
1215 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001216 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1217 path_rewrite_func=MoveUsrBinToBin, root=root)
1218
1219 # The packages, when part of the normal distro, have helper scripts
1220 # that setup paths and such. Since we are making this standalone, we
1221 # need to preprocess all that ourselves.
1222 _ProcessDistroCleanups(target, output_dir)
1223
1224
1225def CreatePackages(targets_wanted, output_dir, root='/'):
1226 """Create redistributable cross-compiler packages for the specified targets
1227
1228 This creates toolchain packages that should be usable in conjunction with
1229 a downloaded sysroot (created elsewhere).
1230
1231 Tarballs (one per target) will be created in $PWD.
1232
1233 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001234 targets_wanted: The targets to package up.
1235 output_dir: The directory to put the packages in.
1236 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001237 """
Ralph Nathan03047282015-03-23 11:09:32 -07001238 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001239 osutils.SafeMakedirs(output_dir)
1240 ldpaths = lddtree.LoadLdpaths(root)
1241 targets = ExpandTargets(targets_wanted)
1242
Mike Frysinger221bd822017-09-29 02:51:47 -04001243 with osutils.TempDir(prefix='create-packages') as tempdir:
1244 logging.debug('Using tempdir: %s', tempdir)
1245
Mike Frysinger35247af2012-11-16 18:58:06 -05001246 # We have to split the root generation from the compression stages. This is
1247 # because we hardlink in all the files (to avoid overhead of reading/writing
1248 # the copies multiple times). But tar gets angry if a file's hardlink count
1249 # changes from when it starts reading a file to when it finishes.
1250 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1251 for target in targets:
1252 output_target_dir = os.path.join(tempdir, target)
1253 queue.put([target, output_target_dir, ldpaths, root])
1254
1255 # Build the tarball.
1256 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1257 for target in targets:
1258 tar_file = os.path.join(output_dir, target + '.tar.xz')
1259 queue.put([tar_file, os.path.join(tempdir, target)])
1260
1261
Mike Frysinger07534cf2017-09-12 17:40:21 -04001262def GetParser():
1263 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001264 parser = commandline.ArgumentParser(description=__doc__)
1265 parser.add_argument('-u', '--nousepkg',
1266 action='store_false', dest='usepkg', default=True,
1267 help='Use prebuilt packages if possible')
1268 parser.add_argument('-d', '--deleteold',
1269 action='store_true', dest='deleteold', default=False,
1270 help='Unmerge deprecated packages')
1271 parser.add_argument('-t', '--targets',
1272 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001273 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001274 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001275 "allowed. Defaults to 'sdk'.")
1276 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1277 help='Comma separated list of boards whose toolchains we '
1278 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001279 parser.add_argument('--hostonly',
1280 dest='hostonly', default=False, action='store_true',
1281 help='Only setup the host toolchain. '
1282 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001283 parser.add_argument('--show-board-cfg', '--show-cfg',
1284 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001285 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001286 parser.add_argument('--show-packages', default=None,
1287 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001288 parser.add_argument('--create-packages',
1289 action='store_true', default=False,
1290 help='Build redistributable packages')
1291 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1292 help='Output directory')
1293 parser.add_argument('--reconfig', default=False, action='store_true',
1294 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001295 parser.add_argument('--sysroot', type='path',
1296 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001297 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001298
Mike Frysinger07534cf2017-09-12 17:40:21 -04001299
1300def main(argv):
1301 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001302 options = parser.parse_args(argv)
1303 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001304
Mike Frysinger35247af2012-11-16 18:58:06 -05001305 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001306 conflicting_options = (
1307 options.cfg_name,
1308 options.show_packages,
1309 options.create_packages,
1310 )
1311 if sum(bool(x) for x in conflicting_options) > 1:
1312 parser.error('conflicting options: create-packages & show-packages & '
1313 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001314
Gilad Arnold8195b532015-04-07 10:56:30 +03001315 targets_wanted = set(options.targets.split(','))
1316 boards_wanted = (set(options.include_boards.split(','))
1317 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001318
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001319 if options.cfg_name:
1320 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001321 elif options.show_packages is not None:
1322 cros_build_lib.AssertInsideChroot()
1323 target = options.show_packages
1324 Crossdev.Load(False)
1325 for package in GetTargetPackages(target):
1326 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001327 elif options.create_packages:
1328 cros_build_lib.AssertInsideChroot()
1329 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001330 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001331 else:
1332 cros_build_lib.AssertInsideChroot()
1333 # This has to be always run as root.
1334 if os.geteuid() != 0:
1335 cros_build_lib.Die('this script must be run as root')
1336
1337 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001338 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001339 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001340 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001341 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001342 Crossdev.Save()
1343
1344 return 0