blob: da8dab4325c7071db440ae12ebc9f7ddb3f90420 [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
Adrian Ratiubf0b9af2022-05-02 14:48:15 +030094CROSSDEV_LIBXCRYPT_ARGS = ['--ex-pkg', 'sys-libs/libxcrypt']
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 Guptabf1b6422021-11-08 09:50:20 -0800101 'arm-none-eabi',
Manoj Gupta21f3a082018-03-06 21:25:39 -0800102 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800103)
104CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
105
Manoj Gupta946abb42017-04-12 14:27:19 -0700106TARGET_LLVM_PKGS_ENABLED = (
Manoj Gupta847b6092022-06-01 10:26:34 -0700107 'armv7m-cros-eabi',
Manoj Gupta946abb42017-04-12 14:27:19 -0700108 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700109 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700110 'aarch64-cros-linux-gnu',
Mike Frysingerd96d5442021-11-09 05:12:34 -0500111 'i686-cros-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700112 'x86_64-cros-linux-gnu',
113)
114
115LLVM_PKGS_TABLE = {
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700116 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700117 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
118 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700119}
120
David James66a09c42012-11-05 13:31:38 -0800121class Crossdev(object):
122 """Class for interacting with crossdev and caching its output."""
123
124 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
125 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800126 # Packages that needs separate handling, in addition to what we have from
127 # crossdev.
128 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700129 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800130 'llvm': 'sys-devel',
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700131 'llvm-libunwind': 'sys-libs',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700132 'libcxxabi': 'sys-libs',
133 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700134 'elfutils': 'dev-libs',
Han Shene23782f2016-02-18 12:20:00 -0800135 }
David James66a09c42012-11-05 13:31:38 -0800136
137 @classmethod
138 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400139 """Load crossdev cache from disk.
140
141 We invalidate the cache when crossdev updates or this script changes.
142 """
David James90239b92012-11-05 15:31:34 -0800143 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400144 # If we run the compiled/cached .pyc file, we'll read/hash that when we
145 # really always want to track the source .py file.
146 script = os.path.abspath(__file__)
147 if script.endswith('.pyc'):
148 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500149 setup_toolchains_hash = hashlib.md5(
150 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400151
152 cls._CACHE = {
153 'crossdev_version': crossdev_version,
154 'setup_toolchains_hash': setup_toolchains_hash,
155 }
156
157 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
158 if reconfig:
159 logging.debug('cache: forcing regen due to reconfig')
160 return
161
162 try:
163 file_data = osutils.ReadFile(cls._CACHE_FILE)
164 except IOError as e:
165 if e.errno != errno.ENOENT:
166 logging.warning('cache: reading failed: %s', e)
167 osutils.SafeUnlink(cls._CACHE_FILE)
168 return
169
170 try:
171 data = json.loads(file_data)
172 except ValueError as e:
173 logging.warning('cache: ignoring invalid content: %s', e)
174 return
175
176 if crossdev_version != data.get('crossdev_version'):
177 logging.debug('cache: rebuilding after crossdev upgrade')
178 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
179 logging.debug('cache: rebuilding after cros_setup_toolchains change')
180 else:
181 logging.debug('cache: content is up-to-date!')
182 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800183
184 @classmethod
185 def Save(cls):
186 """Store crossdev cache on disk."""
187 # Save the cache from the successful run.
188 with open(cls._CACHE_FILE, 'w') as f:
189 json.dump(cls._CACHE, f)
190
191 @classmethod
192 def GetConfig(cls, target):
193 """Returns a map of crossdev provided variables about a tuple."""
194 CACHE_ATTR = '_target_tuple_map'
195
196 val = cls._CACHE.setdefault(CACHE_ATTR, {})
197 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400198 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400199 conf = {
200 'crosspkgs': [],
201 'target': toolchain.GetHostTuple(),
202 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400203 if target == 'host':
204 packages_list = HOST_PACKAGES
205 else:
206 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400207 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400208 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400209 else:
210 # Build the crossdev command.
211 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
Adrian Ratiubf0b9af2022-05-02 14:48:15 +0300212 # Enable libxcrypt for all linux-gnu targets.
213 if 'cros-linux-gnu' in target:
214 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400215 if target in TARGET_COMPILER_RT_ENABLED:
216 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400217 if target in TARGET_LLVM_PKGS_ENABLED:
218 for pkg in LLVM_PKGS_TABLE:
219 cmd.extend(LLVM_PKGS_TABLE[pkg])
Manoj Gupta78607282021-05-24 16:58:13 +0000220 if target in TARGET_GO_ENABLED:
221 cmd.extend(CROSSDEV_GO_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400222 cmd.extend(['-t', target])
223 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400224 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500225 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500226 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400227 # List of tuples split at the first '=', converted into dict.
228 conf = dict((k, cros_build_lib.ShellUnquote(v))
229 for k, v in (x.split('=', 1) for x in out))
230 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800231
Mike Frysinger66bfde52017-09-12 16:42:57 -0400232 manual_pkgs = cls.MANUAL_PKGS
233
234 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400235 conf[pkg + '_pn'] = pkg
236 conf[pkg + '_category'] = cat
237 if pkg not in conf['crosspkgs']:
238 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800239
240 val[target] = conf
241
David James66a09c42012-11-05 13:31:38 -0800242 return val[target]
243
244 @classmethod
245 def UpdateTargets(cls, targets, usepkg, config_only=False):
246 """Calls crossdev to initialize a cross target.
247
248 Args:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700249 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700250 usepkg: Copies the commandline opts.
251 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800252 """
253 configured_targets = cls._CACHE.setdefault('configured_targets', [])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700254 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800255
Manoj Gupta4d016f62021-10-19 16:39:34 -0700256 # Schedule all of the targets in parallel, and let them run.
257 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
258 for target_name in targets:
259 # We already started this target in this loop.
260 if target_name in started_targets:
261 continue
262 # The target is already configured.
263 if config_only and target_name in configured_targets:
264 continue
265 queue.put([target_name, targets[target_name], usepkg, config_only])
266 started_targets.add(target_name)
267
268 @classmethod
269 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
270 """Calls crossdev to initialize a cross target.
271
272 Args:
273 target_name: The name of the target to initialize.
274 target: The target info for initializing.
275 usepkg: Copies the commandline opts.
276 config_only: Just update.
277 """
278 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800279 cmdbase = ['crossdev', '--show-fail-log']
280 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
281 # Pick stable by default, and override as necessary.
282 cmdbase.extend(['-P', '--oneshot'])
283 if usepkg:
284 cmdbase.extend(['-P', '--getbinpkg',
285 '-P', '--usepkgonly',
286 '--without-headers'])
287
Christopher Wileyb22c0712015-06-02 10:37:03 -0700288 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800289 cmdbase.extend(['--overlays', overlays])
290 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
291
Manoj Gupta4d016f62021-10-19 16:39:34 -0700292 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800293
Manoj Gupta4d016f62021-10-19 16:39:34 -0700294 for pkg in GetTargetPackages(target_name):
295 if pkg == 'gdb':
296 # Gdb does not have selectable versions.
297 cmd.append('--ex-gdb')
Adrian Ratiubf0b9af2022-05-02 14:48:15 +0300298 elif pkg == 'ex_libxcrypt':
299 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
Manoj Gupta4d016f62021-10-19 16:39:34 -0700300 elif pkg == 'ex_compiler-rt':
301 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
302 elif pkg == 'ex_go':
303 # Go does not have selectable versions.
304 cmd.extend(CROSSDEV_GO_ARGS)
305 elif pkg in LLVM_PKGS_TABLE:
306 cmd.extend(LLVM_PKGS_TABLE[pkg])
307 elif pkg in cls.MANUAL_PKGS:
308 pass
David James66a09c42012-11-05 13:31:38 -0800309 else:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700310 # The first of the desired versions is the "primary" one.
311 version = GetDesiredPackageVersions(target_name, pkg)[0]
312 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800313
Manoj Gupta4d016f62021-10-19 16:39:34 -0700314 cmd.extend(target['crossdev'].split())
315
316 if config_only:
317 # In this case we want to just quietly reinit
318 cmd.append('--init-target')
319 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
320 else:
321 cros_build_lib.run(cmd)
322
323 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800324
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100325
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100326def GetTargetPackages(target):
327 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800328 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100329 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800330 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100331
332
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100333# Portage helper functions:
334def GetPortagePackage(target, package):
335 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800336 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100337 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400338 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100339 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100340 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100341 category = conf['category']
342 # Portage package:
343 pn = conf[package + '_pn']
344 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500345 assert category
346 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100347 return '%s/%s' % (category, pn)
348
349
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700350def PortageTrees(root):
351 """Return the portage trees for a given root."""
352 if root == '/':
353 return portage.db['/']
354 # The portage logic requires the path always end in a slash.
355 root = root.rstrip('/') + '/'
356 return portage.create_trees(target_root=root, config_root=root)[root]
357
358
359def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360 """Extracts the list of current versions of a target, package pair.
361
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500362 Args:
363 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700364 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100365
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500366 Returns:
367 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100368 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700370 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100371 version = portage.versions.cpv_getversion(pkg)
372 versions.append(version)
373 return versions
374
375
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700376def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100377 """Extracts the current stable version for a given package.
378
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500379 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500380 atom: The target/package to operate on e.g. i686-cros-linux-gnu/gcc
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500381 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700382 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100383
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500384 Returns:
385 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100386 """
David James90239b92012-11-05 15:31:34 -0800387 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700388 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800389 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100390
391
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700392def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100393 """Resolves keywords in a given version list for a particular package.
394
395 Resolving means replacing PACKAGE_STABLE with the actual number.
396
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500397 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500398 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500399 package: The target/package to operate on (e.g. gcc)
400 versions: List of versions to resolve
401 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700402 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100403
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500404 Returns:
405 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100406 """
407 resolved = []
David James90239b92012-11-05 15:31:34 -0800408 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700409 if not installed:
410 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100411 for version in versions:
412 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700413 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400414 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415 resolved.append(version)
416 return resolved
417
418
419def GetDesiredPackageVersions(target, package):
420 """Produces the list of desired versions for each target, package pair.
421
422 The first version in the list is implicitly treated as primary, ie.
423 the version that will be initialized by crossdev and selected.
424
425 If the version is PACKAGE_STABLE, it really means the current version which
426 is emerged by using the package atom with no particular version key.
427 Since crossdev unmasks all packages by default, this will actually
428 mean 'unstable' in most cases.
429
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500430 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500431 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500432 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500434 Returns:
435 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100436 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400437 if package in GetTargetPackages(target):
438 return [PACKAGE_STABLE]
439 else:
440 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100441
442
443def TargetIsInitialized(target):
444 """Verifies if the given list of targets has been correctly initialized.
445
446 This determines whether we have to call crossdev while emerging
447 toolchain packages or can do it using emerge. Emerge is naturally
448 preferred, because all packages can be updated in a single pass.
449
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500450 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500451 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100452
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500453 Returns:
454 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455 """
456 # Check if packages for the given target all have a proper version.
457 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100458 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800459 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100460 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400461 if not (GetStablePackageVersion(atom, True) and
462 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100463 return False
464 return True
465 except cros_build_lib.RunCommandError:
466 # Fails - The target has likely never been initialized before.
467 return False
468
469
470def RemovePackageMask(target):
471 """Removes a package.mask file for the given platform.
472
473 The pre-existing package.mask files can mess with the keywords.
474
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500475 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500476 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100477 """
478 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700479 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100480
481
Zdenek Behan508dcce2011-12-05 15:39:32 +0100482# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700483def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500484 """Rebuild libtool as needed
485
486 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
487 gcc, libtool will break. We can't use binary packages either as those will
488 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700489
490 Args:
491 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500492 """
493 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700494 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500495 for line in f:
496 # Look for a line like:
497 # sys_lib_search_path_spec="..."
498 # It'll be a list of paths and gcc will be one of them.
499 if line.startswith('sys_lib_search_path_spec='):
500 line = line.rstrip()
501 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400502 root_path = os.path.join(root, path.lstrip(os.path.sep))
503 logging.debug('Libtool: checking %s', root_path)
504 if not os.path.exists(root_path):
505 logging.info('Rebuilding libtool after gcc upgrade')
506 logging.info(' %s', line)
507 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500508 needs_update = True
509 break
510
511 if needs_update:
512 break
513
514 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700515 cmd = [EMERGE_CMD, '--oneshot']
516 if root != '/':
517 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
518 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400519 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400520 else:
521 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500522
523
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700524def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100525 """Determines which packages need update/unmerge and defers to portage.
526
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500527 Args:
528 targets: The list of targets to update
529 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700530 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100531 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532 # For each target, we do two things. Figure out the list of updates,
533 # and figure out the appropriate keywords/masks. Crossdev will initialize
534 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400535 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800536 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000537 # Used to keep track of post-cross packages. These are allowed to have
538 # implicit dependencies on toolchain packages, and therefore need to
539 # be built last.
540 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000542 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400543 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100544 # Record the highest needed version for each target, for masking purposes.
545 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100546 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100547 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400548 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100549 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700550 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100551 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200552 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400553 if pkg in NEW_PACKAGES and usepkg:
554 # Skip this binary package (for now).
555 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100556 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400557 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000558 if is_post_cross_target:
559 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100560
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400561 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100562 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400563 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800564 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100565
Mike Frysinger3bba5032016-09-20 14:15:04 -0400566 logging.info('Updating packages:')
567 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100568
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100569 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100570 if usepkg:
571 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700572 if root != '/':
573 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100574
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000575 if usepkg:
576 # Since we are not building from source, we can handle
577 # all packages in one go.
578 cmd.extend(packages)
579 cros_build_lib.run(cmd)
580 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700581 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
582 if pre_cross_items:
583 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000584 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700585 if post_cross_items:
586 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800587 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100588
589
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700590def CleanTargets(targets, root='/'):
591 """Unmerges old packages that are assumed unnecessary.
592
593 Args:
594 targets: The list of targets to clean up.
595 root: The install root in which we want packages cleaned up.
596 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100597 unmergemap = {}
598 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400599 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100600 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400601 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100602 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700603 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100604 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700605 # NOTE: This refers to installed packages (vartree) rather than the
606 # Portage version (porttree and/or bintree) when determining the current
607 # version. While this isn't the most accurate thing to do, it is probably
608 # a good simple compromise, which should have the desired result of
609 # uninstalling everything but the latest installed version. In
610 # particular, using the bintree (--usebinpkg) requires a non-trivial
611 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200612 desired_num = VersionListToNumeric(target, package, desired, True)
613 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400614 logging.warning('Error detecting stable version for %s, '
615 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200616 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100617 unmergemap[pkg] = set(current).difference(desired_num)
618
619 # Cleaning doesn't care about consistency and rebuilding package.* files.
620 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400621 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100622 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
623
624 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400625 logging.info('Cleaning packages:')
626 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100627 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700628 if root != '/':
629 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100630 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400631 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100632 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400633 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100634
635
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200636def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100637 """Runs gcc-config and binutils-config to select the desired.
638
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500639 Args:
640 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700641 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100642 """
643 for package in ['gcc', 'binutils']:
644 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400645 # See if this package is part of this target.
646 if package not in GetTargetPackages(target):
647 logging.debug('%s: %s is not used', target, package)
648 continue
649
Zdenek Behan508dcce2011-12-05 15:39:32 +0100650 # Pick the first version in the numbered list as the selected one.
651 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700652 desired_num = VersionListToNumeric(target, package, desired, True,
653 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100654 desired = desired_num[0]
655 # *-config does not play revisions, strip them, keep just PV.
656 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
657
Mike Frysinger785b0c32017-09-13 01:35:59 -0400658 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100659 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800660 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100661
662 # And finally, attach target to it.
663 desired = '%s-%s' % (target, desired)
664
David James7ec5efc2012-11-06 09:39:49 -0800665 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700666 if root != '/':
667 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800668 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400669 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500670 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500671 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500672 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700673
674 # Do not reconfig when the current is live or nothing needs to be done.
675 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100676 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500677 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400678 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100679
680
Mike Frysinger35247af2012-11-16 18:58:06 -0500681def ExpandTargets(targets_wanted):
682 """Expand any possible toolchain aliases into full targets
683
684 This will expand 'all' and 'sdk' into the respective toolchain tuples.
685
686 Args:
687 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500688
Mike Frysinger35247af2012-11-16 18:58:06 -0500689 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300690 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500691 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500692 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700693 if targets_wanted == set(['boards']):
694 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300695 return {}
696
697 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500698 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300699 return all_targets
700 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500701 # Filter out all the non-sdk toolchains as we don't want to mess
702 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300703 return toolchain.FilterToolchains(all_targets, 'sdk', True)
704
705 # Verify user input.
706 nonexistent = targets_wanted.difference(all_targets)
707 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400708 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300709 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500710
711
David Jamesf8c672f2012-11-06 13:38:11 -0800712def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700713 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100714 """Performs all steps to create a synchronized toolchain enviroment.
715
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500716 Args:
717 usepkg: Use prebuilt packages
718 deleteold: Unmerge deprecated packages
719 hostonly: Only setup the host toolchain
720 reconfig: Reload crossdev config and reselect toolchains
721 targets_wanted: All the targets to update
722 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700723 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100724 """
David Jamesf8c672f2012-11-06 13:38:11 -0800725 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100726 if not hostonly:
727 # For hostonly, we can skip most of the below logic, much of which won't
728 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500729 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400730
Mike Frysingerd246fb92021-10-26 16:08:39 -0400731 # Filter out toolchains that don't (yet) have a binpkg available.
732 if usepkg:
733 for target in list(targets.keys()):
734 if not targets[target]['have-binpkg']:
735 del targets[target]
736
Don Garrettc0c74002015-10-09 12:58:19 -0700737 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300738 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400739 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800740 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100741
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100742 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400743 for target in targets:
744 if TargetIsInitialized(target):
745 reconfig_targets[target] = targets[target]
746 else:
747 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100748 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400749 logging.info('The following targets need to be re-initialized:')
750 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800751 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200752 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800753 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100754
Mike Frysinger66814c32017-10-09 18:11:46 -0400755 # If we're building a subset of toolchains for a board, we might not have
756 # all the tuples that the packages expect. We don't define the "full" set
757 # of tuples currently other than "whatever the full sdk has normally".
758 if usepkg or set(('all', 'sdk')) & targets_wanted:
759 # Since we have cross-compilers now, we can update these packages.
760 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400761
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100762 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400763 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100764
765 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700766 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200767 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800768
769 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700770 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100771
Mike Frysingerc880a962013-11-08 13:59:06 -0500772 # Now that we've cleared out old versions, see if we need to rebuild
773 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700774 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500775
Zdenek Behan508dcce2011-12-05 15:39:32 +0100776
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700777def ShowConfig(name):
778 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500779
780 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700781 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500782 """
Don Garrettc0c74002015-10-09 12:58:19 -0700783 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500784 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400785 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400786 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400787 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
788 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500789
790
Mike Frysinger35247af2012-11-16 18:58:06 -0500791def GeneratePathWrapper(root, wrappath, path):
792 """Generate a shell script to execute another shell script
793
794 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
795 argv[0] won't be pointing to the correct path, generate a shell script that
796 just executes another program with its full path.
797
798 Args:
799 root: The root tree to generate scripts inside of
800 wrappath: The full path (inside |root|) to create the wrapper
801 path: The target program which this wrapper will execute
802 """
803 replacements = {
804 'path': path,
805 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
806 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900807
808 # Do not use exec here, because exec invokes script with absolute path in
809 # argv0. Keeping relativeness allows us to remove abs path from compile result
810 # and leads directory independent build cache sharing in some distributed
811 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500812 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900813basedir=$(dirname "$0")
814"${basedir}/%(relroot)s%(path)s" "$@"
815exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500816""" % replacements
817 root_wrapper = root + wrappath
818 if os.path.islink(root_wrapper):
819 os.unlink(root_wrapper)
820 else:
821 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
822 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400823 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500824
825
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700826def FixClangXXWrapper(root, path):
827 """Fix wrapper shell scripts and symlinks for invoking clang++
828
829 In a typical installation, clang++ symlinks to clang, which symlinks to the
830 elf executable. The executable distinguishes between clang and clang++ based
831 on argv[0].
832
833 When invoked through the LdsoWrapper, argv[0] always contains the path to the
834 executable elf file, making clang/clang++ invocations indistinguishable.
835
836 This function detects if the elf executable being wrapped is clang-X.Y, and
837 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
838
839 The calling sequence now becomes:
840 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
841 the Ldsowrapper).
842 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
843 to the original clang-3.9 elf.
844 -) The difference this time is that inside the elf file execution, $0 is
845 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
846
Manoj Guptaae268142018-04-27 23:28:36 -0700847 Update: Starting since clang 7, the clang and clang++ are symlinks to
848 clang-7 binary, not clang-7.0. The pattern match is extended to handle
849 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
850
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700851 Args:
852 root: The root tree to generate scripts / symlinks inside of
853 path: The target elf for which LdsoWrapper was created
854 """
Manoj Guptaae268142018-04-27 23:28:36 -0700855 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700856 logging.info('fixing clang++ invocation for %s', path)
857 clangdir = os.path.dirname(root + path)
858 clang = os.path.basename(path)
859 clangxx = clang.replace('clang', 'clang++')
860
861 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
862 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
863
864 # Create a hardlink clang++-X.Y pointing to clang-X.Y
865 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
866
867 # Adjust the clang++ symlink to point to clang++-X.Y
868 os.unlink(os.path.join(clangdir, 'clang++'))
869 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
870
871
Mike Frysinger35247af2012-11-16 18:58:06 -0500872def FileIsCrosSdkElf(elf):
873 """Determine if |elf| is an ELF that we execute in the cros_sdk
874
875 We don't need this to be perfect, just quick. It makes sure the ELF
876 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
877
878 Args:
879 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500880
Mike Frysinger35247af2012-11-16 18:58:06 -0500881 Returns:
882 True if we think |elf| is a native ELF
883 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500884 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500885 data = f.read(20)
886 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500887 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500888 data[4:5] == b'\x02' and
889 data[5:6] == b'\x01' and
890 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500891
892
893def IsPathPackagable(ptype, path):
894 """Should the specified file be included in a toolchain package?
895
896 We only need to handle files as we'll create dirs as we need them.
897
898 Further, trim files that won't be useful:
899 - non-english translations (.mo) since it'd require env vars
900 - debug files since these are for the host compiler itself
901 - info/man pages as they're big, and docs are online, and the
902 native docs should work fine for the most part (`man gcc`)
903
904 Args:
905 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
906 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500907
Mike Frysinger35247af2012-11-16 18:58:06 -0500908 Returns:
909 True if we want to include this path in the package
910 """
911 return not (ptype in ('dir',) or
912 path.startswith('/usr/lib/debug/') or
913 os.path.splitext(path)[1] == '.mo' or
914 ('/man/' in path or '/info/' in path))
915
916
917def ReadlinkRoot(path, root):
918 """Like os.readlink(), but relative to a |root|
919
920 Args:
921 path: The symlink to read
922 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500923
Mike Frysinger35247af2012-11-16 18:58:06 -0500924 Returns:
925 A fully resolved symlink path
926 """
927 while os.path.islink(root + path):
928 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
929 return path
930
931
932def _GetFilesForTarget(target, root='/'):
933 """Locate all the files to package for |target|
934
935 This does not cover ELF dependencies.
936
937 Args:
938 target: The toolchain target name
939 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500940
Mike Frysinger35247af2012-11-16 18:58:06 -0500941 Returns:
942 A tuple of a set of all packable paths, and a set of all paths which
943 are also native ELFs
944 """
945 paths = set()
946 elfs = set()
947
948 # Find all the files owned by the packages for this target.
949 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500950
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700951 # Skip Go compiler from redistributable packages.
952 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
953 # into it. Due to this, the toolchain cannot be unpacked anywhere
954 # else and be readily useful. To enable packaging Go, we need to:
955 # -) Tweak the wrappers/environment to override GOROOT
956 # automatically based on the unpack location.
957 # -) Make sure the ELF dependency checking and wrapping logic
958 # below skips the Go toolchain executables and libraries.
959 # -) Make sure the packaging process maintains the relative
960 # timestamps of precompiled standard library packages.
961 # (see dev-lang/go ebuild for details).
962 if pkg == 'ex_go':
963 continue
964
Yunlian Jiang36f35242018-04-27 10:18:40 -0700965 # Use armv7a-cros-linux-gnueabi/compiler-rt for
966 # armv7a-cros-linux-gnueabihf/compiler-rt.
967 # Currently the armv7a-cros-linux-gnueabi is actually
968 # the same as armv7a-cros-linux-gnueabihf with different names.
969 # Because of that, for compiler-rt, it generates the same binary in
970 # the same location. To avoid the installation conflict, we do not
971 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
972 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
973 # toolchain.
974 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
975 atom = GetPortagePackage(target, pkg)
976 cat, pn = atom.split('/')
977 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700978 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
979 settings=portage.settings)
980 contents = dblink.getcontents()
981 if not contents:
982 if 'hf' in target:
983 new_target = 'armv7a-cros-linux-gnueabi'
984 else:
985 new_target = 'armv7a-cros-linux-gnueabihf'
986 atom = GetPortagePackage(new_target, pkg)
987 else:
988 atom = GetPortagePackage(target, pkg)
989
Mike Frysinger35247af2012-11-16 18:58:06 -0500990 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700991 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700992 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500993
Mike Frysinger35247af2012-11-16 18:58:06 -0500994 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
995 settings=portage.settings)
996 contents = dblink.getcontents()
997 for obj in contents:
998 ptype = contents[obj][0]
999 if not IsPathPackagable(ptype, obj):
1000 continue
1001
1002 if ptype == 'obj':
1003 # For native ELFs, we need to pull in their dependencies too.
1004 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001005 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001006 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001007 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001008 paths.add(obj)
1009
1010 return paths, elfs
1011
1012
1013def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -05001014 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -05001015 """Link in all packable files and their runtime dependencies
1016
1017 This also wraps up executable ELFs with helper scripts.
1018
1019 Args:
1020 output_dir: The output directory to store files
1021 paths: All the files to include
1022 elfs: All the files which are ELFs (a subset of |paths|)
1023 ldpaths: A dict of static ldpath information
1024 path_rewrite_func: User callback to rewrite paths in output_dir
1025 root: The root path to pull all packages/files from
1026 """
1027 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001028 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001029 for path in paths:
1030 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001031 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001032 dst = output_dir + new_path
1033 osutils.SafeMakedirs(os.path.dirname(dst))
1034
1035 # Is this a symlink which we have to rewrite or wrap?
1036 # Delay wrap check until after we have created all paths.
1037 src = root + path
1038 if os.path.islink(src):
1039 tgt = os.readlink(src)
1040 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001041 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001042
1043 # Rewrite absolute links to relative and then generate the symlink
1044 # ourselves. All other symlinks can be hardlinked below.
1045 if tgt[0] == '/':
1046 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1047 os.symlink(tgt, dst)
1048 continue
1049
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001050 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001051 os.link(src, dst)
1052
Mike Frysinger35247af2012-11-16 18:58:06 -05001053 # Locate all the dependencies for all the ELFs. Stick them all in the
1054 # top level "lib" dir to make the wrapper simpler. This exact path does
1055 # not matter since we execute ldso directly, and we tell the ldso the
1056 # exact path to search for its libraries.
1057 libdir = os.path.join(output_dir, 'lib')
1058 osutils.SafeMakedirs(libdir)
1059 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001060 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001061 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001062 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001063 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001064 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001065 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001066
Mike Frysinger9fe02342019-12-12 17:52:53 -05001067 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1068 if os.path.basename(elf).startswith('libopcodes-'):
1069 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001070
Mike Frysinger00b129f2021-04-21 18:11:48 -04001071 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001072 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001073 src = path = lib_data['path']
1074 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001075 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001076 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001077
1078 # No need to try and copy the same source lib multiple times.
1079 if path in donelibs:
1080 continue
1081 donelibs.add(path)
1082
1083 # Die if we try to normalize different source libs with the same basename.
1084 if lib in basenamelibs:
1085 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1086 os.path.join('/lib', lib), path,
1087 ' '.join(x for x in donelibs
1088 if x != path and os.path.basename(x) == lib))
1089 # TODO(crbug.com/917193): Make this fatal.
1090 # cros_build_lib.Die('Unable to resolve lib conflicts')
1091 continue
1092 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001093
1094 # Needed libs are the SONAME, but that is usually a symlink, not a
1095 # real file. So link in the target rather than the symlink itself.
1096 # We have to walk all the possible symlinks (SONAME could point to a
1097 # symlink which points to a symlink), and we have to handle absolute
1098 # ourselves (since we have a "root" argument).
1099 dst = os.path.join(libdir, os.path.basename(path))
1100 src = ReadlinkRoot(src, root)
1101
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001102 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001103 os.link(root + src, dst)
1104
Mike Frysinger00b129f2021-04-21 18:11:48 -04001105 # Do not create wrapper for libc. crbug.com/766827
1106 if interp and not glibc_re.search(elf):
1107 # Generate a wrapper if it is executable.
1108 interp = os.path.join('/lib', os.path.basename(interp))
1109 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1110 libpaths=e['rpath'] + e['runpath'])
1111 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1112
1113 # Wrap any symlinks to the wrapper.
1114 if elf in sym_paths:
1115 link = sym_paths[elf]
1116 GeneratePathWrapper(output_dir, link, elf)
1117
Mike Frysinger35247af2012-11-16 18:58:06 -05001118
1119def _EnvdGetVar(envd, var):
1120 """Given a Gentoo env.d file, extract a var from it
1121
1122 Args:
1123 envd: The env.d file to load (may be a glob path)
1124 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001125
Mike Frysinger35247af2012-11-16 18:58:06 -05001126 Returns:
1127 The value of |var|
1128 """
1129 envds = glob.glob(envd)
1130 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1131 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001132 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001133
1134
1135def _ProcessBinutilsConfig(target, output_dir):
1136 """Do what binutils-config would have done"""
1137 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001138
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001139 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001140 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1141 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001142 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001143 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001144 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1145 % globpath)
1146 srcpath = srcpath[0]
1147 ld_path = os.path.join(srcpath, 'ld')
1148 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1149 ld_path = os.path.join(srcpath, 'ld.bfd')
1150 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001151
Mike Frysinger78b7a812014-11-26 19:45:23 -05001152 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001153 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1154 for prog in os.listdir(output_dir + srcpath):
1155 # Skip binaries already wrapped.
1156 if not prog.endswith('.real'):
1157 GeneratePathWrapper(output_dir, binpath + prog,
1158 os.path.join(srcpath, prog))
1159 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1160 os.path.join(srcpath, prog))
1161
David James27ac4ae2012-12-03 23:16:15 -08001162 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001163 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001164 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1165 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1166 output_dir + libpath)
1167
1168
1169def _ProcessGccConfig(target, output_dir):
1170 """Do what gcc-config would have done"""
1171 binpath = '/bin'
1172 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1173 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1174 for prog in os.listdir(output_dir + srcpath):
1175 # Skip binaries already wrapped.
1176 if (not prog.endswith('.real') and
1177 not prog.endswith('.elf') and
1178 prog.startswith(target)):
1179 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1180 os.path.join(srcpath, prog))
1181 return srcpath
1182
1183
Frank Henigman179ec7c2015-02-06 03:01:09 -05001184def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1185 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001186 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001187
Tobias Boschddd16492019-08-14 09:29:54 -07001188 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001189 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001190 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1191 # Can't update the wrapper in place to not affect the chroot,
1192 # but only the extracted toolchain.
1193 os.unlink(sysroot_wrapper)
1194 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001195 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001196
1197
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001198def _ProcessClangWrappers(target, output_dir):
1199 """Remove chroot-specific things from our sysroot wrappers"""
1200 clang_bin_path = '/usr/bin'
1201 # Disable ccache from clang wrappers.
1202 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1203 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1204 f'/usr/bin/{target}-clang')
1205 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1206 f'/usr/bin/{target}-clang++')
1207
1208
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001209def _CreateMainLibDir(target, output_dir):
1210 """Create some lib dirs so that compiler can get the right Gcc paths"""
1211 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1212 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1213
1214
Manoj Guptadf8b3872022-01-13 11:57:36 -08001215def _CreateRemoteToolchainFile(output_dir):
1216 """Create a remote_toolchain_inputs file for reclient/RBE"""
1217 # The inputs file lists all files/shared libraries needed to run clang.
1218 # All inputs are relative to location of clang binary and one input
1219 # location per line of file e.g.
1220 # clang-13.elf
1221 # clang++-13.elf
1222 # relative/path/to/clang/resource/directory
1223
1224 clang_path = os.path.join(output_dir, 'usr/bin')
1225 # Add needed shared libraries and internal files e.g. allowlists.
1226 toolchain_inputs = ['../../lib']
1227 clang_shared_dirs = glob.glob(
1228 os.path.join(output_dir, 'usr/lib64/clang/*/share'))
1229 for clang_dir in clang_shared_dirs:
1230 toolchain_inputs.append(os.path.relpath(clang_dir, clang_path))
1231
1232 # Add actual clang binaries/wrappers.
1233 for clang_files in glob.glob(os.path.join(clang_path, 'clang*-[0-9]*')):
1234 toolchain_inputs.append(os.path.basename(clang_files))
1235
1236 with open(os.path.join(clang_path, 'remote_toolchain_inputs'), 'w') as f:
1237 f.writelines('%s\n' % line for line in toolchain_inputs)
1238
1239
Mike Frysinger35247af2012-11-16 18:58:06 -05001240def _ProcessDistroCleanups(target, output_dir):
1241 """Clean up the tree and remove all distro-specific requirements
1242
1243 Args:
1244 target: The toolchain target name
1245 output_dir: The output directory to clean up
1246 """
1247 _ProcessBinutilsConfig(target, output_dir)
1248 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001249 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001250 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001251 _CreateMainLibDir(target, output_dir)
Manoj Guptadf8b3872022-01-13 11:57:36 -08001252 _CreateRemoteToolchainFile(output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001253
1254 osutils.RmDir(os.path.join(output_dir, 'etc'))
1255
1256
1257def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1258 """Setup a tree from the packages for the specified target
1259
1260 This populates a path with all the files from toolchain packages so that
1261 a tarball can easily be generated from the result.
1262
1263 Args:
1264 target: The target to create a packagable root from
1265 output_dir: The output directory to place all the files
1266 ldpaths: A dict of static ldpath information
1267 root: The root path to pull all packages/files from
1268 """
1269 # Find all the files owned by the packages for this target.
1270 paths, elfs = _GetFilesForTarget(target, root=root)
1271
1272 # Link in all the package's files, any ELF dependencies, and wrap any
1273 # executable ELFs with helper scripts.
1274 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001275 """Move /usr/bin to /bin so people can just use that toplevel dir
1276
George Burgess IVca1d7612020-10-01 00:38:32 -07001277 Note we do not apply this to clang or rust - there is correlation between
1278 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001279 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001280 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001281 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001282 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001283 return path[4:]
1284 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001285 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1286 path_rewrite_func=MoveUsrBinToBin, root=root)
1287
1288 # The packages, when part of the normal distro, have helper scripts
1289 # that setup paths and such. Since we are making this standalone, we
1290 # need to preprocess all that ourselves.
1291 _ProcessDistroCleanups(target, output_dir)
1292
1293
1294def CreatePackages(targets_wanted, output_dir, root='/'):
1295 """Create redistributable cross-compiler packages for the specified targets
1296
1297 This creates toolchain packages that should be usable in conjunction with
1298 a downloaded sysroot (created elsewhere).
1299
1300 Tarballs (one per target) will be created in $PWD.
1301
1302 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001303 targets_wanted: The targets to package up.
1304 output_dir: The directory to put the packages in.
1305 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001306 """
Ralph Nathan03047282015-03-23 11:09:32 -07001307 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001308 osutils.SafeMakedirs(output_dir)
1309 ldpaths = lddtree.LoadLdpaths(root)
1310 targets = ExpandTargets(targets_wanted)
1311
Mike Frysinger221bd822017-09-29 02:51:47 -04001312 with osutils.TempDir(prefix='create-packages') as tempdir:
1313 logging.debug('Using tempdir: %s', tempdir)
1314
Mike Frysinger35247af2012-11-16 18:58:06 -05001315 # We have to split the root generation from the compression stages. This is
1316 # because we hardlink in all the files (to avoid overhead of reading/writing
1317 # the copies multiple times). But tar gets angry if a file's hardlink count
1318 # changes from when it starts reading a file to when it finishes.
1319 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1320 for target in targets:
1321 output_target_dir = os.path.join(tempdir, target)
1322 queue.put([target, output_target_dir, ldpaths, root])
1323
1324 # Build the tarball.
1325 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1326 for target in targets:
1327 tar_file = os.path.join(output_dir, target + '.tar.xz')
1328 queue.put([tar_file, os.path.join(tempdir, target)])
1329
1330
Mike Frysinger07534cf2017-09-12 17:40:21 -04001331def GetParser():
1332 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001333 parser = commandline.ArgumentParser(description=__doc__)
1334 parser.add_argument('-u', '--nousepkg',
1335 action='store_false', dest='usepkg', default=True,
1336 help='Use prebuilt packages if possible')
1337 parser.add_argument('-d', '--deleteold',
1338 action='store_true', dest='deleteold', default=False,
1339 help='Unmerge deprecated packages')
1340 parser.add_argument('-t', '--targets',
1341 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001342 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001343 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001344 "allowed. Defaults to 'sdk'.")
1345 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1346 help='Comma separated list of boards whose toolchains we '
1347 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001348 parser.add_argument('--hostonly',
1349 dest='hostonly', default=False, action='store_true',
1350 help='Only setup the host toolchain. '
1351 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001352 parser.add_argument('--show-board-cfg', '--show-cfg',
1353 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001354 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001355 parser.add_argument('--show-packages', default=None,
1356 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001357 parser.add_argument('--create-packages',
1358 action='store_true', default=False,
1359 help='Build redistributable packages')
1360 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1361 help='Output directory')
1362 parser.add_argument('--reconfig', default=False, action='store_true',
1363 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001364 parser.add_argument('--sysroot', type='path',
1365 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001366 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001367
Mike Frysinger07534cf2017-09-12 17:40:21 -04001368
1369def main(argv):
1370 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001371 options = parser.parse_args(argv)
1372 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001373
Mike Frysinger35247af2012-11-16 18:58:06 -05001374 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001375 conflicting_options = (
1376 options.cfg_name,
1377 options.show_packages,
1378 options.create_packages,
1379 )
1380 if sum(bool(x) for x in conflicting_options) > 1:
1381 parser.error('conflicting options: create-packages & show-packages & '
1382 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001383
Gilad Arnold8195b532015-04-07 10:56:30 +03001384 targets_wanted = set(options.targets.split(','))
1385 boards_wanted = (set(options.include_boards.split(','))
1386 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001387
Manoj Gupta847b6092022-06-01 10:26:34 -07001388 # pylint: disable=global-statement
1389 # Disable installing libc++ for armv7m-cros-eabi till new binary prebuilt
1390 # package is available. TODO(b/234507656): Remove once done.
1391 global TARGET_LLVM_PKGS_ENABLED
1392 if options.usepkg:
1393 TARGET_LLVM_PKGS_ENABLED = (
1394 'armv7a-cros-linux-gnueabi',
1395 'armv7a-cros-linux-gnueabihf',
1396 'aarch64-cros-linux-gnu',
1397 'i686-cros-linux-gnu',
1398 'x86_64-cros-linux-gnu',
1399 )
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001400 if options.cfg_name:
1401 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001402 elif options.show_packages is not None:
1403 cros_build_lib.AssertInsideChroot()
1404 target = options.show_packages
1405 Crossdev.Load(False)
1406 for package in GetTargetPackages(target):
1407 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001408 elif options.create_packages:
1409 cros_build_lib.AssertInsideChroot()
1410 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001411 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001412 else:
1413 cros_build_lib.AssertInsideChroot()
1414 # This has to be always run as root.
Ram Chandrasekar69751282022-02-25 21:07:36 +00001415 if osutils.IsNonRootUser():
Mike Frysinger35247af2012-11-16 18:58:06 -05001416 cros_build_lib.Die('this script must be run as root')
1417
1418 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001419 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001420 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001421 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001422 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001423 Crossdev.Save()
1424
1425 return 0