blob: c7a2ff9482599e5f6ebe7aa63beba5fec8de5013 [file] [log] [blame]
Zdenek Behan508dcce2011-12-05 15:39:32 +01001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Mike Frysinger750c5f52014-09-16 16:16:57 -04005"""This script manages the installed toolchains in the chroot."""
Zdenek Behan508dcce2011-12-05 15:39:32 +01006
Mike Frysinger3ed47722017-08-08 14:59:08 -04007import errno
Mike Frysinger35247af2012-11-16 18:58:06 -05008import glob
Mike Frysinger3ed47722017-08-08 14:59:08 -04009import hashlib
Mike Frysinger7ccee992012-06-01 21:27:59 -040010import json
Chris McDonald59650c32021-07-20 15:29:28 -060011import logging
Zdenek Behan508dcce2011-12-05 15:39:32 +010012import os
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -070013import re
Tobias Boschddd16492019-08-14 09:29:54 -070014import shutil
Zdenek Behan508dcce2011-12-05 15:39:32 +010015
Chris McDonald59650c32021-07-20 15:29:28 -060016from chromite.third_party import lddtree
17
Mike Frysinger506e75f2012-12-17 14:21:13 -050018from chromite.lib import commandline
Mike Frysinger95452702021-01-23 00:07:22 -050019from chromite.lib import constants
Brian Harring503f3ab2012-03-09 21:39:41 -080020from chromite.lib import cros_build_lib
Brian Harringaf019fb2012-05-10 15:06:13 -070021from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050022from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080023from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040024from chromite.utils import key_value_store
Mike Frysinger35247af2012-11-16 18:58:06 -050025
Zdenek Behan508dcce2011-12-05 15:39:32 +010026
Mike Frysinger31596002012-12-03 23:54:24 -050027if cros_build_lib.IsInsideChroot():
28 # Only import portage after we've checked that we're inside the chroot.
29 # Outside may not have portage, in which case the above may not happen.
30 # We'll check in main() if the operation needs portage.
Mike Frysinger27e21b72018-07-12 14:20:21 -040031 # pylint: disable=import-error
Mike Frysinger31596002012-12-03 23:54:24 -050032 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010033
34
Matt Tennantf1e30972012-03-02 16:30:07 -080035EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010036PACKAGE_STABLE = '[stable]'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010037
Mike Frysinger4cd80b62022-05-04 20:39:01 -040038CHROMIUMOS_OVERLAY = os.path.join(
39 constants.CHROOT_SOURCE_ROOT, constants.CHROMIUMOS_OVERLAY_DIR)
40ECLASS_OVERLAY = os.path.join(
41 constants.CHROOT_SOURCE_ROOT, constants.ECLASS_OVERLAY_DIR)
42STABLE_OVERLAY = os.path.join(
43 constants.CHROOT_SOURCE_ROOT, constants.PORTAGE_STABLE_OVERLAY_DIR)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010044CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010045
46
Mike Frysinger66bfde52017-09-12 16:42:57 -040047# The exact list of host toolchain packages we care about. These are the
48# packages that bots/devs install only from binpkgs and rely on the SDK bot
49# (chromiumos-sdk) to validate+uprev.
50#
Mike Frysinger66bfde52017-09-12 16:42:57 -040051# We don't use crossdev to manage the host toolchain for us, especially since
52# we diverge significantly now (with llvm/clang/etc...), and we don't need or
53# want crossdev managing /etc/portage config files for the sdk
54HOST_PACKAGES = (
55 'dev-lang/go',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080056 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040057 'sys-devel/binutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040058 'sys-devel/gcc',
59 'sys-devel/llvm',
60 'sys-kernel/linux-headers',
61 'sys-libs/glibc',
62 'sys-libs/libcxx',
63 'sys-libs/libcxxabi',
Manoj Guptade64cb22019-03-31 18:48:58 -070064 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040065)
66
Mike Frysinger785b0c32017-09-13 01:35:59 -040067# These packages are also installed into the host SDK. However, they require
68# the cross-compilers to be installed first (because they need them to actually
69# build), so we have to delay their installation.
70HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070071 'dev-lang/rust',
George Burgess IV9d1e2322021-04-30 09:54:45 -070072 'dev-lang/rust-bootstrap',
Mike Frysinger61a24392017-10-17 17:14:27 -040073 'virtual/target-sdk-post-cross',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010074 'dev-embedded/coreboot-sdk',
Dan Callaghan7cc58ff2022-02-16 08:38:13 +110075 'dev-embedded/hps-sdk',
George Burgess IV288ecc82021-04-21 07:52:45 -070076 'dev-embedded/ti50-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040077)
78
79# New packages that we're in the process of adding to the SDK. Since the SDK
80# bot hasn't had a chance to run yet, there are no binary packages available,
81# so we have to list them here and wait. Once it completes, entries here can
82# be removed so they'll end up on bots & dev's systems.
George Burgess IV66e199c2021-05-05 15:38:40 -070083NEW_PACKAGES = ()
Mike Frysinger785b0c32017-09-13 01:35:59 -040084
Rahul Chaudhry4b803052015-05-13 15:25:56 -070085# Enable the Go compiler for these targets.
86TARGET_GO_ENABLED = (
87 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070088 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070089 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070090 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070091)
92CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
93
Manoj Gupta1b5642e2017-03-08 16:44:12 -080094# Enable llvm's compiler-rt for these targets.
95TARGET_COMPILER_RT_ENABLED = (
96 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070097 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070098 'aarch64-cros-linux-gnu',
Manoj Guptabf1b6422021-11-08 09:50:20 -080099 'arm-none-eabi',
Manoj Gupta21f3a082018-03-06 21:25:39 -0800100 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800101)
102CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
103
Manoj Gupta946abb42017-04-12 14:27:19 -0700104TARGET_LLVM_PKGS_ENABLED = (
105 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700106 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700107 'aarch64-cros-linux-gnu',
Mike Frysingerd96d5442021-11-09 05:12:34 -0500108 'i686-cros-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700109 'x86_64-cros-linux-gnu',
110)
111
112LLVM_PKGS_TABLE = {
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700113 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700114 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
115 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700116}
117
David James66a09c42012-11-05 13:31:38 -0800118class Crossdev(object):
119 """Class for interacting with crossdev and caching its output."""
120
121 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
122 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800123 # Packages that needs separate handling, in addition to what we have from
124 # crossdev.
125 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700126 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800127 'llvm': 'sys-devel',
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700128 'llvm-libunwind': 'sys-libs',
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',
Han Shene23782f2016-02-18 12:20:00 -0800132 }
David James66a09c42012-11-05 13:31:38 -0800133
134 @classmethod
135 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400136 """Load crossdev cache from disk.
137
138 We invalidate the cache when crossdev updates or this script changes.
139 """
David James90239b92012-11-05 15:31:34 -0800140 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400141 # If we run the compiled/cached .pyc file, we'll read/hash that when we
142 # really always want to track the source .py file.
143 script = os.path.abspath(__file__)
144 if script.endswith('.pyc'):
145 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500146 setup_toolchains_hash = hashlib.md5(
147 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400148
149 cls._CACHE = {
150 'crossdev_version': crossdev_version,
151 'setup_toolchains_hash': setup_toolchains_hash,
152 }
153
154 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
155 if reconfig:
156 logging.debug('cache: forcing regen due to reconfig')
157 return
158
159 try:
160 file_data = osutils.ReadFile(cls._CACHE_FILE)
161 except IOError as e:
162 if e.errno != errno.ENOENT:
163 logging.warning('cache: reading failed: %s', e)
164 osutils.SafeUnlink(cls._CACHE_FILE)
165 return
166
167 try:
168 data = json.loads(file_data)
169 except ValueError as e:
170 logging.warning('cache: ignoring invalid content: %s', e)
171 return
172
173 if crossdev_version != data.get('crossdev_version'):
174 logging.debug('cache: rebuilding after crossdev upgrade')
175 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
176 logging.debug('cache: rebuilding after cros_setup_toolchains change')
177 else:
178 logging.debug('cache: content is up-to-date!')
179 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800180
181 @classmethod
182 def Save(cls):
183 """Store crossdev cache on disk."""
184 # Save the cache from the successful run.
185 with open(cls._CACHE_FILE, 'w') as f:
186 json.dump(cls._CACHE, f)
187
188 @classmethod
189 def GetConfig(cls, target):
190 """Returns a map of crossdev provided variables about a tuple."""
191 CACHE_ATTR = '_target_tuple_map'
192
193 val = cls._CACHE.setdefault(CACHE_ATTR, {})
194 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400195 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400196 conf = {
197 'crosspkgs': [],
198 'target': toolchain.GetHostTuple(),
199 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400200 if target == 'host':
201 packages_list = HOST_PACKAGES
202 else:
203 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400204 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400205 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400206 else:
207 # Build the crossdev command.
208 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
209 if target in TARGET_COMPILER_RT_ENABLED:
210 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400211 if target in TARGET_LLVM_PKGS_ENABLED:
212 for pkg in LLVM_PKGS_TABLE:
213 cmd.extend(LLVM_PKGS_TABLE[pkg])
Manoj Gupta78607282021-05-24 16:58:13 +0000214 if target in TARGET_GO_ENABLED:
215 cmd.extend(CROSSDEV_GO_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400216 cmd.extend(['-t', target])
217 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400218 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500219 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500220 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400221 # List of tuples split at the first '=', converted into dict.
222 conf = dict((k, cros_build_lib.ShellUnquote(v))
223 for k, v in (x.split('=', 1) for x in out))
224 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800225
Mike Frysinger66bfde52017-09-12 16:42:57 -0400226 manual_pkgs = cls.MANUAL_PKGS
227
228 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400229 conf[pkg + '_pn'] = pkg
230 conf[pkg + '_category'] = cat
231 if pkg not in conf['crosspkgs']:
232 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800233
234 val[target] = conf
235
David James66a09c42012-11-05 13:31:38 -0800236 return val[target]
237
238 @classmethod
239 def UpdateTargets(cls, targets, usepkg, config_only=False):
240 """Calls crossdev to initialize a cross target.
241
242 Args:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700243 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700244 usepkg: Copies the commandline opts.
245 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800246 """
247 configured_targets = cls._CACHE.setdefault('configured_targets', [])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700248 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800249
Manoj Gupta4d016f62021-10-19 16:39:34 -0700250 # Schedule all of the targets in parallel, and let them run.
251 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
252 for target_name in targets:
253 # We already started this target in this loop.
254 if target_name in started_targets:
255 continue
256 # The target is already configured.
257 if config_only and target_name in configured_targets:
258 continue
259 queue.put([target_name, targets[target_name], usepkg, config_only])
260 started_targets.add(target_name)
261
262 @classmethod
263 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
264 """Calls crossdev to initialize a cross target.
265
266 Args:
267 target_name: The name of the target to initialize.
268 target: The target info for initializing.
269 usepkg: Copies the commandline opts.
270 config_only: Just update.
271 """
272 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800273 cmdbase = ['crossdev', '--show-fail-log']
274 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
275 # Pick stable by default, and override as necessary.
276 cmdbase.extend(['-P', '--oneshot'])
277 if usepkg:
278 cmdbase.extend(['-P', '--getbinpkg',
279 '-P', '--usepkgonly',
280 '--without-headers'])
281
Christopher Wileyb22c0712015-06-02 10:37:03 -0700282 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800283 cmdbase.extend(['--overlays', overlays])
284 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
285
Manoj Gupta4d016f62021-10-19 16:39:34 -0700286 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800287
Manoj Gupta4d016f62021-10-19 16:39:34 -0700288 for pkg in GetTargetPackages(target_name):
289 if pkg == 'gdb':
290 # Gdb does not have selectable versions.
291 cmd.append('--ex-gdb')
292 elif pkg == 'ex_compiler-rt':
293 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
294 elif pkg == 'ex_go':
295 # Go does not have selectable versions.
296 cmd.extend(CROSSDEV_GO_ARGS)
297 elif pkg in LLVM_PKGS_TABLE:
298 cmd.extend(LLVM_PKGS_TABLE[pkg])
299 elif pkg in cls.MANUAL_PKGS:
300 pass
David James66a09c42012-11-05 13:31:38 -0800301 else:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700302 # The first of the desired versions is the "primary" one.
303 version = GetDesiredPackageVersions(target_name, pkg)[0]
304 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800305
Manoj Gupta4d016f62021-10-19 16:39:34 -0700306 cmd.extend(target['crossdev'].split())
307
308 if config_only:
309 # In this case we want to just quietly reinit
310 cmd.append('--init-target')
311 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
312 else:
313 cros_build_lib.run(cmd)
314
315 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800316
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100317
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100318def GetTargetPackages(target):
319 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800320 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100321 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800322 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100323
324
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100325# Portage helper functions:
326def GetPortagePackage(target, package):
327 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800328 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100329 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400330 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100331 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100332 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100333 category = conf['category']
334 # Portage package:
335 pn = conf[package + '_pn']
336 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500337 assert category
338 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100339 return '%s/%s' % (category, pn)
340
341
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700342def PortageTrees(root):
343 """Return the portage trees for a given root."""
344 if root == '/':
345 return portage.db['/']
346 # The portage logic requires the path always end in a slash.
347 root = root.rstrip('/') + '/'
348 return portage.create_trees(target_root=root, config_root=root)[root]
349
350
351def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100352 """Extracts the list of current versions of a target, package pair.
353
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500354 Args:
355 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700356 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100357
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500358 Returns:
359 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100361 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700362 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100363 version = portage.versions.cpv_getversion(pkg)
364 versions.append(version)
365 return versions
366
367
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700368def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369 """Extracts the current stable version for a given package.
370
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500371 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500372 atom: The target/package to operate on e.g. i686-cros-linux-gnu/gcc
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500373 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700374 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100375
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500376 Returns:
377 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378 """
David James90239b92012-11-05 15:31:34 -0800379 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700380 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800381 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100382
383
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700384def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100385 """Resolves keywords in a given version list for a particular package.
386
387 Resolving means replacing PACKAGE_STABLE with the actual number.
388
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500389 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500390 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500391 package: The target/package to operate on (e.g. gcc)
392 versions: List of versions to resolve
393 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700394 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100395
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500396 Returns:
397 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100398 """
399 resolved = []
David James90239b92012-11-05 15:31:34 -0800400 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700401 if not installed:
402 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100403 for version in versions:
404 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700405 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400406 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100407 resolved.append(version)
408 return resolved
409
410
411def GetDesiredPackageVersions(target, package):
412 """Produces the list of desired versions for each target, package pair.
413
414 The first version in the list is implicitly treated as primary, ie.
415 the version that will be initialized by crossdev and selected.
416
417 If the version is PACKAGE_STABLE, it really means the current version which
418 is emerged by using the package atom with no particular version key.
419 Since crossdev unmasks all packages by default, this will actually
420 mean 'unstable' in most cases.
421
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500422 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500423 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500424 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100425
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500426 Returns:
427 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100428 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400429 if package in GetTargetPackages(target):
430 return [PACKAGE_STABLE]
431 else:
432 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433
434
435def TargetIsInitialized(target):
436 """Verifies if the given list of targets has been correctly initialized.
437
438 This determines whether we have to call crossdev while emerging
439 toolchain packages or can do it using emerge. Emerge is naturally
440 preferred, because all packages can be updated in a single pass.
441
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500442 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500443 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100444
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500445 Returns:
446 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100447 """
448 # Check if packages for the given target all have a proper version.
449 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100450 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800451 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100452 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400453 if not (GetStablePackageVersion(atom, True) and
454 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455 return False
456 return True
457 except cros_build_lib.RunCommandError:
458 # Fails - The target has likely never been initialized before.
459 return False
460
461
462def RemovePackageMask(target):
463 """Removes a package.mask file for the given platform.
464
465 The pre-existing package.mask files can mess with the keywords.
466
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500467 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500468 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100469 """
470 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700471 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100472
473
Zdenek Behan508dcce2011-12-05 15:39:32 +0100474# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700475def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500476 """Rebuild libtool as needed
477
478 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
479 gcc, libtool will break. We can't use binary packages either as those will
480 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700481
482 Args:
483 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500484 """
485 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700486 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500487 for line in f:
488 # Look for a line like:
489 # sys_lib_search_path_spec="..."
490 # It'll be a list of paths and gcc will be one of them.
491 if line.startswith('sys_lib_search_path_spec='):
492 line = line.rstrip()
493 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400494 root_path = os.path.join(root, path.lstrip(os.path.sep))
495 logging.debug('Libtool: checking %s', root_path)
496 if not os.path.exists(root_path):
497 logging.info('Rebuilding libtool after gcc upgrade')
498 logging.info(' %s', line)
499 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500500 needs_update = True
501 break
502
503 if needs_update:
504 break
505
506 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700507 cmd = [EMERGE_CMD, '--oneshot']
508 if root != '/':
509 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
510 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400511 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400512 else:
513 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500514
515
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700516def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517 """Determines which packages need update/unmerge and defers to portage.
518
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500519 Args:
520 targets: The list of targets to update
521 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700522 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100523 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100524 # For each target, we do two things. Figure out the list of updates,
525 # and figure out the appropriate keywords/masks. Crossdev will initialize
526 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400527 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800528 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000529 # Used to keep track of post-cross packages. These are allowed to have
530 # implicit dependencies on toolchain packages, and therefore need to
531 # be built last.
532 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100533 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000534 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400535 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536 # Record the highest needed version for each target, for masking purposes.
537 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100538 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400540 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100541 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700542 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200544 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400545 if pkg in NEW_PACKAGES and usepkg:
546 # Skip this binary package (for now).
547 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100548 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400549 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000550 if is_post_cross_target:
551 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100552
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400553 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100554 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400555 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800556 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100557
Mike Frysinger3bba5032016-09-20 14:15:04 -0400558 logging.info('Updating packages:')
559 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100560
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100561 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100562 if usepkg:
563 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700564 if root != '/':
565 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100566
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000567 if usepkg:
568 # Since we are not building from source, we can handle
569 # all packages in one go.
570 cmd.extend(packages)
571 cros_build_lib.run(cmd)
572 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700573 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
574 if pre_cross_items:
575 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000576 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700577 if post_cross_items:
578 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800579 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100580
581
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700582def CleanTargets(targets, root='/'):
583 """Unmerges old packages that are assumed unnecessary.
584
585 Args:
586 targets: The list of targets to clean up.
587 root: The install root in which we want packages cleaned up.
588 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100589 unmergemap = {}
590 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400591 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100592 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400593 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100594 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700595 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100596 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700597 # NOTE: This refers to installed packages (vartree) rather than the
598 # Portage version (porttree and/or bintree) when determining the current
599 # version. While this isn't the most accurate thing to do, it is probably
600 # a good simple compromise, which should have the desired result of
601 # uninstalling everything but the latest installed version. In
602 # particular, using the bintree (--usebinpkg) requires a non-trivial
603 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200604 desired_num = VersionListToNumeric(target, package, desired, True)
605 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400606 logging.warning('Error detecting stable version for %s, '
607 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200608 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100609 unmergemap[pkg] = set(current).difference(desired_num)
610
611 # Cleaning doesn't care about consistency and rebuilding package.* files.
612 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400613 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100614 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
615
616 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400617 logging.info('Cleaning packages:')
618 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100619 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700620 if root != '/':
621 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100622 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400623 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100624 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400625 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100626
627
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200628def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100629 """Runs gcc-config and binutils-config to select the desired.
630
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500631 Args:
632 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700633 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100634 """
635 for package in ['gcc', 'binutils']:
636 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400637 # See if this package is part of this target.
638 if package not in GetTargetPackages(target):
639 logging.debug('%s: %s is not used', target, package)
640 continue
641
Zdenek Behan508dcce2011-12-05 15:39:32 +0100642 # Pick the first version in the numbered list as the selected one.
643 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700644 desired_num = VersionListToNumeric(target, package, desired, True,
645 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100646 desired = desired_num[0]
647 # *-config does not play revisions, strip them, keep just PV.
648 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
649
Mike Frysinger785b0c32017-09-13 01:35:59 -0400650 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100651 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800652 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100653
654 # And finally, attach target to it.
655 desired = '%s-%s' % (target, desired)
656
David James7ec5efc2012-11-06 09:39:49 -0800657 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700658 if root != '/':
659 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800660 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400661 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500662 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500663 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500664 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700665
666 # Do not reconfig when the current is live or nothing needs to be done.
667 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100668 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500669 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400670 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100671
672
Mike Frysinger35247af2012-11-16 18:58:06 -0500673def ExpandTargets(targets_wanted):
674 """Expand any possible toolchain aliases into full targets
675
676 This will expand 'all' and 'sdk' into the respective toolchain tuples.
677
678 Args:
679 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500680
Mike Frysinger35247af2012-11-16 18:58:06 -0500681 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300682 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500683 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500684 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700685 if targets_wanted == set(['boards']):
686 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300687 return {}
688
689 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500690 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300691 return all_targets
692 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500693 # Filter out all the non-sdk toolchains as we don't want to mess
694 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300695 return toolchain.FilterToolchains(all_targets, 'sdk', True)
696
697 # Verify user input.
698 nonexistent = targets_wanted.difference(all_targets)
699 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400700 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300701 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500702
703
David Jamesf8c672f2012-11-06 13:38:11 -0800704def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700705 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100706 """Performs all steps to create a synchronized toolchain enviroment.
707
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500708 Args:
709 usepkg: Use prebuilt packages
710 deleteold: Unmerge deprecated packages
711 hostonly: Only setup the host toolchain
712 reconfig: Reload crossdev config and reselect toolchains
713 targets_wanted: All the targets to update
714 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700715 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100716 """
David Jamesf8c672f2012-11-06 13:38:11 -0800717 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100718 if not hostonly:
719 # For hostonly, we can skip most of the below logic, much of which won't
720 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500721 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400722
Mike Frysingerd246fb92021-10-26 16:08:39 -0400723 # Filter out toolchains that don't (yet) have a binpkg available.
724 if usepkg:
725 for target in list(targets.keys()):
726 if not targets[target]['have-binpkg']:
727 del targets[target]
728
Don Garrettc0c74002015-10-09 12:58:19 -0700729 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300730 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400731 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800732 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100733
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100734 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400735 for target in targets:
736 if TargetIsInitialized(target):
737 reconfig_targets[target] = targets[target]
738 else:
739 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100740 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400741 logging.info('The following targets need to be re-initialized:')
742 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800743 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200744 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800745 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100746
Mike Frysinger66814c32017-10-09 18:11:46 -0400747 # If we're building a subset of toolchains for a board, we might not have
748 # all the tuples that the packages expect. We don't define the "full" set
749 # of tuples currently other than "whatever the full sdk has normally".
750 if usepkg or set(('all', 'sdk')) & targets_wanted:
751 # Since we have cross-compilers now, we can update these packages.
752 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400753
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100754 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400755 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100756
757 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700758 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200759 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800760
761 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700762 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100763
Mike Frysingerc880a962013-11-08 13:59:06 -0500764 # Now that we've cleared out old versions, see if we need to rebuild
765 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700766 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500767
Zdenek Behan508dcce2011-12-05 15:39:32 +0100768
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700769def ShowConfig(name):
770 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500771
772 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700773 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500774 """
Don Garrettc0c74002015-10-09 12:58:19 -0700775 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500776 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400777 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400778 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400779 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
780 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500781
782
Mike Frysinger35247af2012-11-16 18:58:06 -0500783def GeneratePathWrapper(root, wrappath, path):
784 """Generate a shell script to execute another shell script
785
786 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
787 argv[0] won't be pointing to the correct path, generate a shell script that
788 just executes another program with its full path.
789
790 Args:
791 root: The root tree to generate scripts inside of
792 wrappath: The full path (inside |root|) to create the wrapper
793 path: The target program which this wrapper will execute
794 """
795 replacements = {
796 'path': path,
797 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
798 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900799
800 # Do not use exec here, because exec invokes script with absolute path in
801 # argv0. Keeping relativeness allows us to remove abs path from compile result
802 # and leads directory independent build cache sharing in some distributed
803 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500804 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900805basedir=$(dirname "$0")
806"${basedir}/%(relroot)s%(path)s" "$@"
807exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500808""" % replacements
809 root_wrapper = root + wrappath
810 if os.path.islink(root_wrapper):
811 os.unlink(root_wrapper)
812 else:
813 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
814 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400815 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500816
817
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700818def FixClangXXWrapper(root, path):
819 """Fix wrapper shell scripts and symlinks for invoking clang++
820
821 In a typical installation, clang++ symlinks to clang, which symlinks to the
822 elf executable. The executable distinguishes between clang and clang++ based
823 on argv[0].
824
825 When invoked through the LdsoWrapper, argv[0] always contains the path to the
826 executable elf file, making clang/clang++ invocations indistinguishable.
827
828 This function detects if the elf executable being wrapped is clang-X.Y, and
829 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
830
831 The calling sequence now becomes:
832 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
833 the Ldsowrapper).
834 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
835 to the original clang-3.9 elf.
836 -) The difference this time is that inside the elf file execution, $0 is
837 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
838
Manoj Guptaae268142018-04-27 23:28:36 -0700839 Update: Starting since clang 7, the clang and clang++ are symlinks to
840 clang-7 binary, not clang-7.0. The pattern match is extended to handle
841 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
842
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700843 Args:
844 root: The root tree to generate scripts / symlinks inside of
845 path: The target elf for which LdsoWrapper was created
846 """
Manoj Guptaae268142018-04-27 23:28:36 -0700847 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700848 logging.info('fixing clang++ invocation for %s', path)
849 clangdir = os.path.dirname(root + path)
850 clang = os.path.basename(path)
851 clangxx = clang.replace('clang', 'clang++')
852
853 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
854 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
855
856 # Create a hardlink clang++-X.Y pointing to clang-X.Y
857 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
858
859 # Adjust the clang++ symlink to point to clang++-X.Y
860 os.unlink(os.path.join(clangdir, 'clang++'))
861 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
862
863
Mike Frysinger35247af2012-11-16 18:58:06 -0500864def FileIsCrosSdkElf(elf):
865 """Determine if |elf| is an ELF that we execute in the cros_sdk
866
867 We don't need this to be perfect, just quick. It makes sure the ELF
868 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
869
870 Args:
871 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500872
Mike Frysinger35247af2012-11-16 18:58:06 -0500873 Returns:
874 True if we think |elf| is a native ELF
875 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500876 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500877 data = f.read(20)
878 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500879 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500880 data[4:5] == b'\x02' and
881 data[5:6] == b'\x01' and
882 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500883
884
885def IsPathPackagable(ptype, path):
886 """Should the specified file be included in a toolchain package?
887
888 We only need to handle files as we'll create dirs as we need them.
889
890 Further, trim files that won't be useful:
891 - non-english translations (.mo) since it'd require env vars
892 - debug files since these are for the host compiler itself
893 - info/man pages as they're big, and docs are online, and the
894 native docs should work fine for the most part (`man gcc`)
895
896 Args:
897 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
898 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500899
Mike Frysinger35247af2012-11-16 18:58:06 -0500900 Returns:
901 True if we want to include this path in the package
902 """
903 return not (ptype in ('dir',) or
904 path.startswith('/usr/lib/debug/') or
905 os.path.splitext(path)[1] == '.mo' or
906 ('/man/' in path or '/info/' in path))
907
908
909def ReadlinkRoot(path, root):
910 """Like os.readlink(), but relative to a |root|
911
912 Args:
913 path: The symlink to read
914 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500915
Mike Frysinger35247af2012-11-16 18:58:06 -0500916 Returns:
917 A fully resolved symlink path
918 """
919 while os.path.islink(root + path):
920 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
921 return path
922
923
924def _GetFilesForTarget(target, root='/'):
925 """Locate all the files to package for |target|
926
927 This does not cover ELF dependencies.
928
929 Args:
930 target: The toolchain target name
931 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500932
Mike Frysinger35247af2012-11-16 18:58:06 -0500933 Returns:
934 A tuple of a set of all packable paths, and a set of all paths which
935 are also native ELFs
936 """
937 paths = set()
938 elfs = set()
939
940 # Find all the files owned by the packages for this target.
941 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500942
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700943 # Skip Go compiler from redistributable packages.
944 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
945 # into it. Due to this, the toolchain cannot be unpacked anywhere
946 # else and be readily useful. To enable packaging Go, we need to:
947 # -) Tweak the wrappers/environment to override GOROOT
948 # automatically based on the unpack location.
949 # -) Make sure the ELF dependency checking and wrapping logic
950 # below skips the Go toolchain executables and libraries.
951 # -) Make sure the packaging process maintains the relative
952 # timestamps of precompiled standard library packages.
953 # (see dev-lang/go ebuild for details).
954 if pkg == 'ex_go':
955 continue
956
Yunlian Jiang36f35242018-04-27 10:18:40 -0700957 # Use armv7a-cros-linux-gnueabi/compiler-rt for
958 # armv7a-cros-linux-gnueabihf/compiler-rt.
959 # Currently the armv7a-cros-linux-gnueabi is actually
960 # the same as armv7a-cros-linux-gnueabihf with different names.
961 # Because of that, for compiler-rt, it generates the same binary in
962 # the same location. To avoid the installation conflict, we do not
963 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
964 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
965 # toolchain.
966 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
967 atom = GetPortagePackage(target, pkg)
968 cat, pn = atom.split('/')
969 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700970 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
971 settings=portage.settings)
972 contents = dblink.getcontents()
973 if not contents:
974 if 'hf' in target:
975 new_target = 'armv7a-cros-linux-gnueabi'
976 else:
977 new_target = 'armv7a-cros-linux-gnueabihf'
978 atom = GetPortagePackage(new_target, pkg)
979 else:
980 atom = GetPortagePackage(target, pkg)
981
Mike Frysinger35247af2012-11-16 18:58:06 -0500982 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700983 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700984 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500985
Mike Frysinger35247af2012-11-16 18:58:06 -0500986 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
987 settings=portage.settings)
988 contents = dblink.getcontents()
989 for obj in contents:
990 ptype = contents[obj][0]
991 if not IsPathPackagable(ptype, obj):
992 continue
993
994 if ptype == 'obj':
995 # For native ELFs, we need to pull in their dependencies too.
996 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500997 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500998 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500999 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001000 paths.add(obj)
1001
1002 return paths, elfs
1003
1004
1005def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -05001006 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -05001007 """Link in all packable files and their runtime dependencies
1008
1009 This also wraps up executable ELFs with helper scripts.
1010
1011 Args:
1012 output_dir: The output directory to store files
1013 paths: All the files to include
1014 elfs: All the files which are ELFs (a subset of |paths|)
1015 ldpaths: A dict of static ldpath information
1016 path_rewrite_func: User callback to rewrite paths in output_dir
1017 root: The root path to pull all packages/files from
1018 """
1019 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001020 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001021 for path in paths:
1022 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001023 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001024 dst = output_dir + new_path
1025 osutils.SafeMakedirs(os.path.dirname(dst))
1026
1027 # Is this a symlink which we have to rewrite or wrap?
1028 # Delay wrap check until after we have created all paths.
1029 src = root + path
1030 if os.path.islink(src):
1031 tgt = os.readlink(src)
1032 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001033 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001034
1035 # Rewrite absolute links to relative and then generate the symlink
1036 # ourselves. All other symlinks can be hardlinked below.
1037 if tgt[0] == '/':
1038 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1039 os.symlink(tgt, dst)
1040 continue
1041
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001042 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001043 os.link(src, dst)
1044
Mike Frysinger35247af2012-11-16 18:58:06 -05001045 # Locate all the dependencies for all the ELFs. Stick them all in the
1046 # top level "lib" dir to make the wrapper simpler. This exact path does
1047 # not matter since we execute ldso directly, and we tell the ldso the
1048 # exact path to search for its libraries.
1049 libdir = os.path.join(output_dir, 'lib')
1050 osutils.SafeMakedirs(libdir)
1051 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001052 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001053 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001054 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001055 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001056 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001057 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001058
Mike Frysinger9fe02342019-12-12 17:52:53 -05001059 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1060 if os.path.basename(elf).startswith('libopcodes-'):
1061 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001062
Mike Frysinger00b129f2021-04-21 18:11:48 -04001063 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001064 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001065 src = path = lib_data['path']
1066 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001067 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001068 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001069
1070 # No need to try and copy the same source lib multiple times.
1071 if path in donelibs:
1072 continue
1073 donelibs.add(path)
1074
1075 # Die if we try to normalize different source libs with the same basename.
1076 if lib in basenamelibs:
1077 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1078 os.path.join('/lib', lib), path,
1079 ' '.join(x for x in donelibs
1080 if x != path and os.path.basename(x) == lib))
1081 # TODO(crbug.com/917193): Make this fatal.
1082 # cros_build_lib.Die('Unable to resolve lib conflicts')
1083 continue
1084 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001085
1086 # Needed libs are the SONAME, but that is usually a symlink, not a
1087 # real file. So link in the target rather than the symlink itself.
1088 # We have to walk all the possible symlinks (SONAME could point to a
1089 # symlink which points to a symlink), and we have to handle absolute
1090 # ourselves (since we have a "root" argument).
1091 dst = os.path.join(libdir, os.path.basename(path))
1092 src = ReadlinkRoot(src, root)
1093
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001094 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001095 os.link(root + src, dst)
1096
Mike Frysinger00b129f2021-04-21 18:11:48 -04001097 # Do not create wrapper for libc. crbug.com/766827
1098 if interp and not glibc_re.search(elf):
1099 # Generate a wrapper if it is executable.
1100 interp = os.path.join('/lib', os.path.basename(interp))
1101 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1102 libpaths=e['rpath'] + e['runpath'])
1103 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1104
1105 # Wrap any symlinks to the wrapper.
1106 if elf in sym_paths:
1107 link = sym_paths[elf]
1108 GeneratePathWrapper(output_dir, link, elf)
1109
Mike Frysinger35247af2012-11-16 18:58:06 -05001110
1111def _EnvdGetVar(envd, var):
1112 """Given a Gentoo env.d file, extract a var from it
1113
1114 Args:
1115 envd: The env.d file to load (may be a glob path)
1116 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001117
Mike Frysinger35247af2012-11-16 18:58:06 -05001118 Returns:
1119 The value of |var|
1120 """
1121 envds = glob.glob(envd)
1122 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1123 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001124 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001125
1126
1127def _ProcessBinutilsConfig(target, output_dir):
1128 """Do what binutils-config would have done"""
1129 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001130
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001131 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001132 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1133 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001134 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001135 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001136 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1137 % globpath)
1138 srcpath = srcpath[0]
1139 ld_path = os.path.join(srcpath, 'ld')
1140 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1141 ld_path = os.path.join(srcpath, 'ld.bfd')
1142 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001143
Mike Frysinger78b7a812014-11-26 19:45:23 -05001144 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001145 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1146 for prog in os.listdir(output_dir + srcpath):
1147 # Skip binaries already wrapped.
1148 if not prog.endswith('.real'):
1149 GeneratePathWrapper(output_dir, binpath + prog,
1150 os.path.join(srcpath, prog))
1151 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1152 os.path.join(srcpath, prog))
1153
David James27ac4ae2012-12-03 23:16:15 -08001154 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001155 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001156 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1157 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1158 output_dir + libpath)
1159
1160
1161def _ProcessGccConfig(target, output_dir):
1162 """Do what gcc-config would have done"""
1163 binpath = '/bin'
1164 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1165 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1166 for prog in os.listdir(output_dir + srcpath):
1167 # Skip binaries already wrapped.
1168 if (not prog.endswith('.real') and
1169 not prog.endswith('.elf') and
1170 prog.startswith(target)):
1171 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1172 os.path.join(srcpath, prog))
1173 return srcpath
1174
1175
Frank Henigman179ec7c2015-02-06 03:01:09 -05001176def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1177 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001178 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001179
Tobias Boschddd16492019-08-14 09:29:54 -07001180 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001181 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001182 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1183 # Can't update the wrapper in place to not affect the chroot,
1184 # but only the extracted toolchain.
1185 os.unlink(sysroot_wrapper)
1186 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001187 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001188
1189
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001190def _ProcessClangWrappers(target, output_dir):
1191 """Remove chroot-specific things from our sysroot wrappers"""
1192 clang_bin_path = '/usr/bin'
1193 # Disable ccache from clang wrappers.
1194 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1195 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1196 f'/usr/bin/{target}-clang')
1197 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1198 f'/usr/bin/{target}-clang++')
1199
1200
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001201def _CreateMainLibDir(target, output_dir):
1202 """Create some lib dirs so that compiler can get the right Gcc paths"""
1203 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1204 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1205
1206
Manoj Guptadf8b3872022-01-13 11:57:36 -08001207def _CreateRemoteToolchainFile(output_dir):
1208 """Create a remote_toolchain_inputs file for reclient/RBE"""
1209 # The inputs file lists all files/shared libraries needed to run clang.
1210 # All inputs are relative to location of clang binary and one input
1211 # location per line of file e.g.
1212 # clang-13.elf
1213 # clang++-13.elf
1214 # relative/path/to/clang/resource/directory
1215
1216 clang_path = os.path.join(output_dir, 'usr/bin')
1217 # Add needed shared libraries and internal files e.g. allowlists.
1218 toolchain_inputs = ['../../lib']
1219 clang_shared_dirs = glob.glob(
1220 os.path.join(output_dir, 'usr/lib64/clang/*/share'))
1221 for clang_dir in clang_shared_dirs:
1222 toolchain_inputs.append(os.path.relpath(clang_dir, clang_path))
1223
1224 # Add actual clang binaries/wrappers.
1225 for clang_files in glob.glob(os.path.join(clang_path, 'clang*-[0-9]*')):
1226 toolchain_inputs.append(os.path.basename(clang_files))
1227
1228 with open(os.path.join(clang_path, 'remote_toolchain_inputs'), 'w') as f:
1229 f.writelines('%s\n' % line for line in toolchain_inputs)
1230
1231
Mike Frysinger35247af2012-11-16 18:58:06 -05001232def _ProcessDistroCleanups(target, output_dir):
1233 """Clean up the tree and remove all distro-specific requirements
1234
1235 Args:
1236 target: The toolchain target name
1237 output_dir: The output directory to clean up
1238 """
1239 _ProcessBinutilsConfig(target, output_dir)
1240 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001241 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001242 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001243 _CreateMainLibDir(target, output_dir)
Manoj Guptadf8b3872022-01-13 11:57:36 -08001244 _CreateRemoteToolchainFile(output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001245
1246 osutils.RmDir(os.path.join(output_dir, 'etc'))
1247
1248
1249def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1250 """Setup a tree from the packages for the specified target
1251
1252 This populates a path with all the files from toolchain packages so that
1253 a tarball can easily be generated from the result.
1254
1255 Args:
1256 target: The target to create a packagable root from
1257 output_dir: The output directory to place all the files
1258 ldpaths: A dict of static ldpath information
1259 root: The root path to pull all packages/files from
1260 """
1261 # Find all the files owned by the packages for this target.
1262 paths, elfs = _GetFilesForTarget(target, root=root)
1263
1264 # Link in all the package's files, any ELF dependencies, and wrap any
1265 # executable ELFs with helper scripts.
1266 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001267 """Move /usr/bin to /bin so people can just use that toplevel dir
1268
George Burgess IVca1d7612020-10-01 00:38:32 -07001269 Note we do not apply this to clang or rust - there is correlation between
1270 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001271 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001272 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001273 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001274 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001275 return path[4:]
1276 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001277 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1278 path_rewrite_func=MoveUsrBinToBin, root=root)
1279
1280 # The packages, when part of the normal distro, have helper scripts
1281 # that setup paths and such. Since we are making this standalone, we
1282 # need to preprocess all that ourselves.
1283 _ProcessDistroCleanups(target, output_dir)
1284
1285
1286def CreatePackages(targets_wanted, output_dir, root='/'):
1287 """Create redistributable cross-compiler packages for the specified targets
1288
1289 This creates toolchain packages that should be usable in conjunction with
1290 a downloaded sysroot (created elsewhere).
1291
1292 Tarballs (one per target) will be created in $PWD.
1293
1294 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001295 targets_wanted: The targets to package up.
1296 output_dir: The directory to put the packages in.
1297 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001298 """
Ralph Nathan03047282015-03-23 11:09:32 -07001299 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001300 osutils.SafeMakedirs(output_dir)
1301 ldpaths = lddtree.LoadLdpaths(root)
1302 targets = ExpandTargets(targets_wanted)
1303
Mike Frysinger221bd822017-09-29 02:51:47 -04001304 with osutils.TempDir(prefix='create-packages') as tempdir:
1305 logging.debug('Using tempdir: %s', tempdir)
1306
Mike Frysinger35247af2012-11-16 18:58:06 -05001307 # We have to split the root generation from the compression stages. This is
1308 # because we hardlink in all the files (to avoid overhead of reading/writing
1309 # the copies multiple times). But tar gets angry if a file's hardlink count
1310 # changes from when it starts reading a file to when it finishes.
1311 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1312 for target in targets:
1313 output_target_dir = os.path.join(tempdir, target)
1314 queue.put([target, output_target_dir, ldpaths, root])
1315
1316 # Build the tarball.
1317 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1318 for target in targets:
1319 tar_file = os.path.join(output_dir, target + '.tar.xz')
1320 queue.put([tar_file, os.path.join(tempdir, target)])
1321
1322
Mike Frysinger07534cf2017-09-12 17:40:21 -04001323def GetParser():
1324 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001325 parser = commandline.ArgumentParser(description=__doc__)
1326 parser.add_argument('-u', '--nousepkg',
1327 action='store_false', dest='usepkg', default=True,
1328 help='Use prebuilt packages if possible')
1329 parser.add_argument('-d', '--deleteold',
1330 action='store_true', dest='deleteold', default=False,
1331 help='Unmerge deprecated packages')
1332 parser.add_argument('-t', '--targets',
1333 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001334 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001335 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001336 "allowed. Defaults to 'sdk'.")
1337 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1338 help='Comma separated list of boards whose toolchains we '
1339 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001340 parser.add_argument('--hostonly',
1341 dest='hostonly', default=False, action='store_true',
1342 help='Only setup the host toolchain. '
1343 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001344 parser.add_argument('--show-board-cfg', '--show-cfg',
1345 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001346 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001347 parser.add_argument('--show-packages', default=None,
1348 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001349 parser.add_argument('--create-packages',
1350 action='store_true', default=False,
1351 help='Build redistributable packages')
1352 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1353 help='Output directory')
1354 parser.add_argument('--reconfig', default=False, action='store_true',
1355 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001356 parser.add_argument('--sysroot', type='path',
1357 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001358 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001359
Mike Frysinger07534cf2017-09-12 17:40:21 -04001360
1361def main(argv):
1362 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001363 options = parser.parse_args(argv)
1364 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001365
Mike Frysinger35247af2012-11-16 18:58:06 -05001366 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001367 conflicting_options = (
1368 options.cfg_name,
1369 options.show_packages,
1370 options.create_packages,
1371 )
1372 if sum(bool(x) for x in conflicting_options) > 1:
1373 parser.error('conflicting options: create-packages & show-packages & '
1374 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001375
Gilad Arnold8195b532015-04-07 10:56:30 +03001376 targets_wanted = set(options.targets.split(','))
1377 boards_wanted = (set(options.include_boards.split(','))
1378 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001379
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001380 if options.cfg_name:
1381 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001382 elif options.show_packages is not None:
1383 cros_build_lib.AssertInsideChroot()
1384 target = options.show_packages
1385 Crossdev.Load(False)
1386 for package in GetTargetPackages(target):
1387 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001388 elif options.create_packages:
1389 cros_build_lib.AssertInsideChroot()
1390 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001391 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001392 else:
1393 cros_build_lib.AssertInsideChroot()
1394 # This has to be always run as root.
Ram Chandrasekar69751282022-02-25 21:07:36 +00001395 if osutils.IsNonRootUser():
Mike Frysinger35247af2012-11-16 18:58:06 -05001396 cros_build_lib.Die('this script must be run as root')
1397
1398 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001399 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001400 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001401 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001402 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001403 Crossdev.Save()
1404
1405 return 0