blob: e47e746ae13a19d59422ef3233a9d8f3b1f4f4ab [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',
George Burgess IV95882172022-06-01 11:10:11 -070056 'dev-lang/rust-bootstrap',
57 'dev-lang/rust-host',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080058 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040059 'sys-devel/binutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040060 'sys-devel/gcc',
61 'sys-devel/llvm',
62 'sys-kernel/linux-headers',
63 'sys-libs/glibc',
64 'sys-libs/libcxx',
65 'sys-libs/libcxxabi',
Manoj Guptade64cb22019-03-31 18:48:58 -070066 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040067)
68
Mike Frysinger785b0c32017-09-13 01:35:59 -040069# These packages are also installed into the host SDK. However, they require
70# the cross-compilers to be installed first (because they need them to actually
71# build), so we have to delay their installation.
72HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070073 'dev-lang/rust',
Mike Frysinger61a24392017-10-17 17:14:27 -040074 'virtual/target-sdk-post-cross',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010075 'dev-embedded/coreboot-sdk',
Dan Callaghan7cc58ff2022-02-16 08:38:13 +110076 'dev-embedded/hps-sdk',
George Burgess IV288ecc82021-04-21 07:52:45 -070077 'dev-embedded/ti50-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040078)
79
80# New packages that we're in the process of adding to the SDK. Since the SDK
81# bot hasn't had a chance to run yet, there are no binary packages available,
82# so we have to list them here and wait. Once it completes, entries here can
83# be removed so they'll end up on bots & dev's systems.
George Burgess IV66e199c2021-05-05 15:38:40 -070084NEW_PACKAGES = ()
Mike Frysinger785b0c32017-09-13 01:35:59 -040085
Rahul Chaudhry4b803052015-05-13 15:25:56 -070086# Enable the Go compiler for these targets.
87TARGET_GO_ENABLED = (
88 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070089 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070090 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070091 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070092)
93CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
94
Adrian Ratiubf0b9af2022-05-02 14:48:15 +030095CROSSDEV_LIBXCRYPT_ARGS = ['--ex-pkg', 'sys-libs/libxcrypt']
96
Manoj Gupta1b5642e2017-03-08 16:44:12 -080097# Enable llvm's compiler-rt for these targets.
98TARGET_COMPILER_RT_ENABLED = (
99 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -0700100 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700101 'aarch64-cros-linux-gnu',
Manoj Guptabf1b6422021-11-08 09:50:20 -0800102 'arm-none-eabi',
Manoj Gupta21f3a082018-03-06 21:25:39 -0800103 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800104)
105CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
106
Manoj Gupta946abb42017-04-12 14:27:19 -0700107TARGET_LLVM_PKGS_ENABLED = (
Manoj Gupta847b6092022-06-01 10:26:34 -0700108 'armv7m-cros-eabi',
Manoj Gupta946abb42017-04-12 14:27:19 -0700109 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700110 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700111 'aarch64-cros-linux-gnu',
Mike Frysingerd96d5442021-11-09 05:12:34 -0500112 'i686-cros-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700113 'x86_64-cros-linux-gnu',
114)
115
116LLVM_PKGS_TABLE = {
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700117 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700118 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
119 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700120}
121
David James66a09c42012-11-05 13:31:38 -0800122class Crossdev(object):
123 """Class for interacting with crossdev and caching its output."""
124
125 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
126 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800127 # Packages that needs separate handling, in addition to what we have from
128 # crossdev.
129 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700130 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800131 'llvm': 'sys-devel',
Manoj Gupta6502bcd2021-08-07 14:25:01 -0700132 'llvm-libunwind': 'sys-libs',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700133 'libcxxabi': 'sys-libs',
134 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700135 'elfutils': 'dev-libs',
Han Shene23782f2016-02-18 12:20:00 -0800136 }
David James66a09c42012-11-05 13:31:38 -0800137
138 @classmethod
139 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400140 """Load crossdev cache from disk.
141
142 We invalidate the cache when crossdev updates or this script changes.
143 """
David James90239b92012-11-05 15:31:34 -0800144 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400145 # If we run the compiled/cached .pyc file, we'll read/hash that when we
146 # really always want to track the source .py file.
147 script = os.path.abspath(__file__)
148 if script.endswith('.pyc'):
149 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500150 setup_toolchains_hash = hashlib.md5(
151 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400152
153 cls._CACHE = {
154 'crossdev_version': crossdev_version,
155 'setup_toolchains_hash': setup_toolchains_hash,
156 }
157
158 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
159 if reconfig:
160 logging.debug('cache: forcing regen due to reconfig')
161 return
162
163 try:
164 file_data = osutils.ReadFile(cls._CACHE_FILE)
165 except IOError as e:
166 if e.errno != errno.ENOENT:
167 logging.warning('cache: reading failed: %s', e)
168 osutils.SafeUnlink(cls._CACHE_FILE)
169 return
170
171 try:
172 data = json.loads(file_data)
173 except ValueError as e:
174 logging.warning('cache: ignoring invalid content: %s', e)
175 return
176
177 if crossdev_version != data.get('crossdev_version'):
178 logging.debug('cache: rebuilding after crossdev upgrade')
179 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
180 logging.debug('cache: rebuilding after cros_setup_toolchains change')
181 else:
182 logging.debug('cache: content is up-to-date!')
183 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800184
185 @classmethod
186 def Save(cls):
187 """Store crossdev cache on disk."""
188 # Save the cache from the successful run.
189 with open(cls._CACHE_FILE, 'w') as f:
190 json.dump(cls._CACHE, f)
191
192 @classmethod
193 def GetConfig(cls, target):
194 """Returns a map of crossdev provided variables about a tuple."""
195 CACHE_ATTR = '_target_tuple_map'
196
197 val = cls._CACHE.setdefault(CACHE_ATTR, {})
198 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400199 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400200 conf = {
201 'crosspkgs': [],
202 'target': toolchain.GetHostTuple(),
203 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400204 if target == 'host':
205 packages_list = HOST_PACKAGES
206 else:
207 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400208 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400209 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400210 else:
211 # Build the crossdev command.
212 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
Adrian Ratiubf0b9af2022-05-02 14:48:15 +0300213 # Enable libxcrypt for all linux-gnu targets.
214 if 'cros-linux-gnu' in target:
215 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400216 if target in TARGET_COMPILER_RT_ENABLED:
217 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400218 if target in TARGET_LLVM_PKGS_ENABLED:
219 for pkg in LLVM_PKGS_TABLE:
220 cmd.extend(LLVM_PKGS_TABLE[pkg])
Manoj Gupta78607282021-05-24 16:58:13 +0000221 if target in TARGET_GO_ENABLED:
222 cmd.extend(CROSSDEV_GO_ARGS)
Mike Frysinger66bfde52017-09-12 16:42:57 -0400223 cmd.extend(['-t', target])
224 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400225 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500226 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500227 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400228 # List of tuples split at the first '=', converted into dict.
229 conf = dict((k, cros_build_lib.ShellUnquote(v))
230 for k, v in (x.split('=', 1) for x in out))
231 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800232
Mike Frysinger66bfde52017-09-12 16:42:57 -0400233 manual_pkgs = cls.MANUAL_PKGS
234
235 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400236 conf[pkg + '_pn'] = pkg
237 conf[pkg + '_category'] = cat
238 if pkg not in conf['crosspkgs']:
239 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800240
241 val[target] = conf
242
David James66a09c42012-11-05 13:31:38 -0800243 return val[target]
244
245 @classmethod
246 def UpdateTargets(cls, targets, usepkg, config_only=False):
247 """Calls crossdev to initialize a cross target.
248
249 Args:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700250 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700251 usepkg: Copies the commandline opts.
252 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800253 """
254 configured_targets = cls._CACHE.setdefault('configured_targets', [])
Manoj Gupta4d016f62021-10-19 16:39:34 -0700255 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800256
Manoj Gupta4d016f62021-10-19 16:39:34 -0700257 # Schedule all of the targets in parallel, and let them run.
258 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
259 for target_name in targets:
260 # We already started this target in this loop.
261 if target_name in started_targets:
262 continue
263 # The target is already configured.
264 if config_only and target_name in configured_targets:
265 continue
266 queue.put([target_name, targets[target_name], usepkg, config_only])
267 started_targets.add(target_name)
268
269 @classmethod
270 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
271 """Calls crossdev to initialize a cross target.
272
273 Args:
274 target_name: The name of the target to initialize.
275 target: The target info for initializing.
276 usepkg: Copies the commandline opts.
277 config_only: Just update.
278 """
279 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800280 cmdbase = ['crossdev', '--show-fail-log']
281 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
282 # Pick stable by default, and override as necessary.
283 cmdbase.extend(['-P', '--oneshot'])
284 if usepkg:
285 cmdbase.extend(['-P', '--getbinpkg',
286 '-P', '--usepkgonly',
287 '--without-headers'])
288
Christopher Wileyb22c0712015-06-02 10:37:03 -0700289 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800290 cmdbase.extend(['--overlays', overlays])
291 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
292
Manoj Gupta4d016f62021-10-19 16:39:34 -0700293 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800294
Manoj Gupta4d016f62021-10-19 16:39:34 -0700295 for pkg in GetTargetPackages(target_name):
296 if pkg == 'gdb':
297 # Gdb does not have selectable versions.
298 cmd.append('--ex-gdb')
Adrian Ratiubf0b9af2022-05-02 14:48:15 +0300299 elif pkg == 'ex_libxcrypt':
300 cmd.extend(CROSSDEV_LIBXCRYPT_ARGS)
Manoj Gupta4d016f62021-10-19 16:39:34 -0700301 elif pkg == 'ex_compiler-rt':
302 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
303 elif pkg == 'ex_go':
304 # Go does not have selectable versions.
305 cmd.extend(CROSSDEV_GO_ARGS)
306 elif pkg in LLVM_PKGS_TABLE:
307 cmd.extend(LLVM_PKGS_TABLE[pkg])
308 elif pkg in cls.MANUAL_PKGS:
309 pass
David James66a09c42012-11-05 13:31:38 -0800310 else:
Manoj Gupta4d016f62021-10-19 16:39:34 -0700311 # The first of the desired versions is the "primary" one.
312 version = GetDesiredPackageVersions(target_name, pkg)[0]
313 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800314
Manoj Gupta4d016f62021-10-19 16:39:34 -0700315 cmd.extend(target['crossdev'].split())
316
317 if config_only:
318 # In this case we want to just quietly reinit
319 cmd.append('--init-target')
320 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
321 else:
322 cros_build_lib.run(cmd)
323
324 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800325
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100326
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100327def GetTargetPackages(target):
328 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800329 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100330 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800331 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100332
333
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100334# Portage helper functions:
335def GetPortagePackage(target, package):
336 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800337 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100338 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400339 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100340 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100342 category = conf['category']
343 # Portage package:
344 pn = conf[package + '_pn']
345 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500346 assert category
347 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100348 return '%s/%s' % (category, pn)
349
350
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700351def PortageTrees(root):
352 """Return the portage trees for a given root."""
353 if root == '/':
354 return portage.db['/']
355 # The portage logic requires the path always end in a slash.
356 root = root.rstrip('/') + '/'
357 return portage.create_trees(target_root=root, config_root=root)[root]
358
359
360def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100361 """Extracts the list of current versions of a target, package pair.
362
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500363 Args:
364 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700365 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100366
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500367 Returns:
368 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100370 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700371 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100372 version = portage.versions.cpv_getversion(pkg)
373 versions.append(version)
374 return versions
375
376
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700377def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378 """Extracts the current stable version for a given package.
379
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500380 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500381 atom: The target/package to operate on e.g. i686-cros-linux-gnu/gcc
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500382 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700383 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100384
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500385 Returns:
386 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100387 """
David James90239b92012-11-05 15:31:34 -0800388 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700389 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800390 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100391
392
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700393def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100394 """Resolves keywords in a given version list for a particular package.
395
396 Resolving means replacing PACKAGE_STABLE with the actual number.
397
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500398 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500399 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500400 package: The target/package to operate on (e.g. gcc)
401 versions: List of versions to resolve
402 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700403 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100404
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500405 Returns:
406 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100407 """
408 resolved = []
David James90239b92012-11-05 15:31:34 -0800409 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700410 if not installed:
411 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100412 for version in versions:
413 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700414 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400415 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100416 resolved.append(version)
417 return resolved
418
419
420def GetDesiredPackageVersions(target, package):
421 """Produces the list of desired versions for each target, package pair.
422
423 The first version in the list is implicitly treated as primary, ie.
424 the version that will be initialized by crossdev and selected.
425
426 If the version is PACKAGE_STABLE, it really means the current version which
427 is emerged by using the package atom with no particular version key.
428 Since crossdev unmasks all packages by default, this will actually
429 mean 'unstable' in most cases.
430
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500431 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500432 target: The target to operate on (e.g. i686-cros-linux-gnu)
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500433 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100434
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500435 Returns:
436 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400438 if package in GetTargetPackages(target):
439 return [PACKAGE_STABLE]
440 else:
441 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442
443
444def TargetIsInitialized(target):
445 """Verifies if the given list of targets has been correctly initialized.
446
447 This determines whether we have to call crossdev while emerging
448 toolchain packages or can do it using emerge. Emerge is naturally
449 preferred, because all packages can be updated in a single pass.
450
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500451 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500452 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100453
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500454 Returns:
455 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100456 """
457 # Check if packages for the given target all have a proper version.
458 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100459 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800460 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100461 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400462 if not (GetStablePackageVersion(atom, True) and
463 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100464 return False
465 return True
466 except cros_build_lib.RunCommandError:
467 # Fails - The target has likely never been initialized before.
468 return False
469
470
471def RemovePackageMask(target):
472 """Removes a package.mask file for the given platform.
473
474 The pre-existing package.mask files can mess with the keywords.
475
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500476 Args:
Mike Frysingerd96d5442021-11-09 05:12:34 -0500477 target: The target to operate on (e.g. i686-cros-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100478 """
479 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700480 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100481
482
Zdenek Behan508dcce2011-12-05 15:39:32 +0100483# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700484def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500485 """Rebuild libtool as needed
486
487 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
488 gcc, libtool will break. We can't use binary packages either as those will
489 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700490
491 Args:
492 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500493 """
494 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700495 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500496 for line in f:
497 # Look for a line like:
498 # sys_lib_search_path_spec="..."
499 # It'll be a list of paths and gcc will be one of them.
500 if line.startswith('sys_lib_search_path_spec='):
501 line = line.rstrip()
502 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400503 root_path = os.path.join(root, path.lstrip(os.path.sep))
504 logging.debug('Libtool: checking %s', root_path)
505 if not os.path.exists(root_path):
506 logging.info('Rebuilding libtool after gcc upgrade')
507 logging.info(' %s', line)
508 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500509 needs_update = True
510 break
511
512 if needs_update:
513 break
514
515 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700516 cmd = [EMERGE_CMD, '--oneshot']
517 if root != '/':
518 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
519 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400520 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400521 else:
522 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500523
524
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700525def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100526 """Determines which packages need update/unmerge and defers to portage.
527
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500528 Args:
529 targets: The list of targets to update
530 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700531 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100533 # For each target, we do two things. Figure out the list of updates,
534 # and figure out the appropriate keywords/masks. Crossdev will initialize
535 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400536 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800537 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000538 # Used to keep track of post-cross packages. These are allowed to have
539 # implicit dependencies on toolchain packages, and therefore need to
540 # be built last.
541 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100542 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000543 is_post_cross_target = target.endswith('-post-cross')
Mike Frysinger3bba5032016-09-20 14:15:04 -0400544 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100545 # Record the highest needed version for each target, for masking purposes.
546 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100547 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100548 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400549 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100550 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700551 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100552 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200553 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400554 if pkg in NEW_PACKAGES and usepkg:
555 # Skip this binary package (for now).
556 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100557 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400558 logging.debug(' %s -> %s', current, desired_num)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000559 if is_post_cross_target:
560 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400562 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100563 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400564 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800565 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100566
Mike Frysinger3bba5032016-09-20 14:15:04 -0400567 logging.info('Updating packages:')
568 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100569
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100570 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100571 if usepkg:
572 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700573 if root != '/':
574 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100575
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000576 if usepkg:
577 # Since we are not building from source, we can handle
578 # all packages in one go.
579 cmd.extend(packages)
580 cros_build_lib.run(cmd)
581 else:
George Burgess IVd7762ab2021-04-29 10:54:55 -0700582 pre_cross_items = [pkg for pkg in packages if pkg not in post_cross_pkgs]
583 if pre_cross_items:
584 cros_build_lib.run(cmd + pre_cross_items)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000585 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
George Burgess IVd7762ab2021-04-29 10:54:55 -0700586 if post_cross_items:
587 cros_build_lib.run(cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800588 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100589
590
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700591def CleanTargets(targets, root='/'):
592 """Unmerges old packages that are assumed unnecessary.
593
594 Args:
595 targets: The list of targets to clean up.
596 root: The install root in which we want packages cleaned up.
597 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100598 unmergemap = {}
599 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400600 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100601 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400602 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100603 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700604 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100605 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700606 # NOTE: This refers to installed packages (vartree) rather than the
607 # Portage version (porttree and/or bintree) when determining the current
608 # version. While this isn't the most accurate thing to do, it is probably
609 # a good simple compromise, which should have the desired result of
610 # uninstalling everything but the latest installed version. In
611 # particular, using the bintree (--usebinpkg) requires a non-trivial
612 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200613 desired_num = VersionListToNumeric(target, package, desired, True)
614 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400615 logging.warning('Error detecting stable version for %s, '
616 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200617 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100618 unmergemap[pkg] = set(current).difference(desired_num)
619
620 # Cleaning doesn't care about consistency and rebuilding package.* files.
621 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400622 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100623 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
624
625 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400626 logging.info('Cleaning packages:')
627 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100628 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700629 if root != '/':
630 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100631 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400632 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100633 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400634 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100635
636
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200637def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100638 """Runs gcc-config and binutils-config to select the desired.
639
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500640 Args:
641 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700642 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100643 """
644 for package in ['gcc', 'binutils']:
645 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400646 # See if this package is part of this target.
647 if package not in GetTargetPackages(target):
648 logging.debug('%s: %s is not used', target, package)
649 continue
650
Zdenek Behan508dcce2011-12-05 15:39:32 +0100651 # Pick the first version in the numbered list as the selected one.
652 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700653 desired_num = VersionListToNumeric(target, package, desired, True,
654 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100655 desired = desired_num[0]
656 # *-config does not play revisions, strip them, keep just PV.
657 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
658
Mike Frysinger785b0c32017-09-13 01:35:59 -0400659 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100660 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800661 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100662
663 # And finally, attach target to it.
664 desired = '%s-%s' % (target, desired)
665
David James7ec5efc2012-11-06 09:39:49 -0800666 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700667 if root != '/':
668 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800669 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400670 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500671 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500672 extra_env=extra_env)
Mike Frysinger876a8e52022-06-23 18:07:30 -0400673 current = result.stdout.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700674
675 # Do not reconfig when the current is live or nothing needs to be done.
676 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100677 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500678 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400679 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100680
681
Mike Frysinger35247af2012-11-16 18:58:06 -0500682def ExpandTargets(targets_wanted):
683 """Expand any possible toolchain aliases into full targets
684
685 This will expand 'all' and 'sdk' into the respective toolchain tuples.
686
687 Args:
688 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500689
Mike Frysinger35247af2012-11-16 18:58:06 -0500690 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300691 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500692 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500693 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700694 if targets_wanted == set(['boards']):
695 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300696 return {}
697
698 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500699 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300700 return all_targets
701 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500702 # Filter out all the non-sdk toolchains as we don't want to mess
703 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300704 return toolchain.FilterToolchains(all_targets, 'sdk', True)
705
706 # Verify user input.
707 nonexistent = targets_wanted.difference(all_targets)
708 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400709 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300710 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500711
712
David Jamesf8c672f2012-11-06 13:38:11 -0800713def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700714 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100715 """Performs all steps to create a synchronized toolchain enviroment.
716
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500717 Args:
718 usepkg: Use prebuilt packages
719 deleteold: Unmerge deprecated packages
720 hostonly: Only setup the host toolchain
721 reconfig: Reload crossdev config and reselect toolchains
722 targets_wanted: All the targets to update
723 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700724 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100725 """
David Jamesf8c672f2012-11-06 13:38:11 -0800726 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100727 if not hostonly:
728 # For hostonly, we can skip most of the below logic, much of which won't
729 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500730 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400731
Mike Frysingerd246fb92021-10-26 16:08:39 -0400732 # Filter out toolchains that don't (yet) have a binpkg available.
733 if usepkg:
734 for target in list(targets.keys()):
735 if not targets[target]['have-binpkg']:
736 del targets[target]
737
Don Garrettc0c74002015-10-09 12:58:19 -0700738 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300739 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400740 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800741 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100742
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100743 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400744 for target in targets:
745 if TargetIsInitialized(target):
746 reconfig_targets[target] = targets[target]
747 else:
748 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100749 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400750 logging.info('The following targets need to be re-initialized:')
751 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800752 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200753 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800754 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100755
Mike Frysinger66814c32017-10-09 18:11:46 -0400756 # If we're building a subset of toolchains for a board, we might not have
757 # all the tuples that the packages expect. We don't define the "full" set
758 # of tuples currently other than "whatever the full sdk has normally".
759 if usepkg or set(('all', 'sdk')) & targets_wanted:
760 # Since we have cross-compilers now, we can update these packages.
761 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400762
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100763 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400764 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100765
766 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700767 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200768 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800769
770 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700771 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100772
Mike Frysingerc880a962013-11-08 13:59:06 -0500773 # Now that we've cleared out old versions, see if we need to rebuild
774 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700775 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500776
Zdenek Behan508dcce2011-12-05 15:39:32 +0100777
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700778def ShowConfig(name):
779 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500780
781 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700782 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500783 """
Don Garrettc0c74002015-10-09 12:58:19 -0700784 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500785 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400786 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400787 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400788 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
789 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500790
791
Mike Frysinger35247af2012-11-16 18:58:06 -0500792def GeneratePathWrapper(root, wrappath, path):
793 """Generate a shell script to execute another shell script
794
795 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
796 argv[0] won't be pointing to the correct path, generate a shell script that
797 just executes another program with its full path.
798
799 Args:
800 root: The root tree to generate scripts inside of
801 wrappath: The full path (inside |root|) to create the wrapper
802 path: The target program which this wrapper will execute
803 """
804 replacements = {
805 'path': path,
806 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
807 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900808
809 # Do not use exec here, because exec invokes script with absolute path in
810 # argv0. Keeping relativeness allows us to remove abs path from compile result
811 # and leads directory independent build cache sharing in some distributed
812 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500813 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900814basedir=$(dirname "$0")
815"${basedir}/%(relroot)s%(path)s" "$@"
816exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500817""" % replacements
818 root_wrapper = root + wrappath
819 if os.path.islink(root_wrapper):
820 os.unlink(root_wrapper)
821 else:
822 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
823 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400824 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500825
826
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700827def FixClangXXWrapper(root, path):
828 """Fix wrapper shell scripts and symlinks for invoking clang++
829
830 In a typical installation, clang++ symlinks to clang, which symlinks to the
831 elf executable. The executable distinguishes between clang and clang++ based
832 on argv[0].
833
834 When invoked through the LdsoWrapper, argv[0] always contains the path to the
835 executable elf file, making clang/clang++ invocations indistinguishable.
836
837 This function detects if the elf executable being wrapped is clang-X.Y, and
838 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
839
840 The calling sequence now becomes:
841 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
842 the Ldsowrapper).
843 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
844 to the original clang-3.9 elf.
845 -) The difference this time is that inside the elf file execution, $0 is
846 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
847
Manoj Guptaae268142018-04-27 23:28:36 -0700848 Update: Starting since clang 7, the clang and clang++ are symlinks to
849 clang-7 binary, not clang-7.0. The pattern match is extended to handle
850 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
851
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700852 Args:
853 root: The root tree to generate scripts / symlinks inside of
854 path: The target elf for which LdsoWrapper was created
855 """
Manoj Guptaae268142018-04-27 23:28:36 -0700856 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700857 logging.info('fixing clang++ invocation for %s', path)
858 clangdir = os.path.dirname(root + path)
859 clang = os.path.basename(path)
860 clangxx = clang.replace('clang', 'clang++')
861
862 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
863 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
864
865 # Create a hardlink clang++-X.Y pointing to clang-X.Y
866 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
867
868 # Adjust the clang++ symlink to point to clang++-X.Y
869 os.unlink(os.path.join(clangdir, 'clang++'))
870 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
871
872
Mike Frysinger35247af2012-11-16 18:58:06 -0500873def FileIsCrosSdkElf(elf):
874 """Determine if |elf| is an ELF that we execute in the cros_sdk
875
876 We don't need this to be perfect, just quick. It makes sure the ELF
877 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
878
879 Args:
880 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500881
Mike Frysinger35247af2012-11-16 18:58:06 -0500882 Returns:
883 True if we think |elf| is a native ELF
884 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500885 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500886 data = f.read(20)
887 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500888 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500889 data[4:5] == b'\x02' and
890 data[5:6] == b'\x01' and
891 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500892
893
894def IsPathPackagable(ptype, path):
895 """Should the specified file be included in a toolchain package?
896
897 We only need to handle files as we'll create dirs as we need them.
898
899 Further, trim files that won't be useful:
900 - non-english translations (.mo) since it'd require env vars
901 - debug files since these are for the host compiler itself
902 - info/man pages as they're big, and docs are online, and the
903 native docs should work fine for the most part (`man gcc`)
904
905 Args:
906 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
907 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500908
Mike Frysinger35247af2012-11-16 18:58:06 -0500909 Returns:
910 True if we want to include this path in the package
911 """
912 return not (ptype in ('dir',) or
913 path.startswith('/usr/lib/debug/') or
914 os.path.splitext(path)[1] == '.mo' or
915 ('/man/' in path or '/info/' in path))
916
917
918def ReadlinkRoot(path, root):
919 """Like os.readlink(), but relative to a |root|
920
921 Args:
922 path: The symlink to read
923 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500924
Mike Frysinger35247af2012-11-16 18:58:06 -0500925 Returns:
926 A fully resolved symlink path
927 """
928 while os.path.islink(root + path):
929 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
930 return path
931
932
933def _GetFilesForTarget(target, root='/'):
934 """Locate all the files to package for |target|
935
936 This does not cover ELF dependencies.
937
938 Args:
939 target: The toolchain target name
940 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500941
Mike Frysinger35247af2012-11-16 18:58:06 -0500942 Returns:
943 A tuple of a set of all packable paths, and a set of all paths which
944 are also native ELFs
945 """
946 paths = set()
947 elfs = set()
948
949 # Find all the files owned by the packages for this target.
950 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500951
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700952 # Skip Go compiler from redistributable packages.
953 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
954 # into it. Due to this, the toolchain cannot be unpacked anywhere
955 # else and be readily useful. To enable packaging Go, we need to:
956 # -) Tweak the wrappers/environment to override GOROOT
957 # automatically based on the unpack location.
958 # -) Make sure the ELF dependency checking and wrapping logic
959 # below skips the Go toolchain executables and libraries.
960 # -) Make sure the packaging process maintains the relative
961 # timestamps of precompiled standard library packages.
962 # (see dev-lang/go ebuild for details).
963 if pkg == 'ex_go':
964 continue
965
Yunlian Jiang36f35242018-04-27 10:18:40 -0700966 # Use armv7a-cros-linux-gnueabi/compiler-rt for
967 # armv7a-cros-linux-gnueabihf/compiler-rt.
968 # Currently the armv7a-cros-linux-gnueabi is actually
969 # the same as armv7a-cros-linux-gnueabihf with different names.
970 # Because of that, for compiler-rt, it generates the same binary in
971 # the same location. To avoid the installation conflict, we do not
972 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
973 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
974 # toolchain.
975 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
976 atom = GetPortagePackage(target, pkg)
977 cat, pn = atom.split('/')
978 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700979 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
980 settings=portage.settings)
981 contents = dblink.getcontents()
982 if not contents:
983 if 'hf' in target:
984 new_target = 'armv7a-cros-linux-gnueabi'
985 else:
986 new_target = 'armv7a-cros-linux-gnueabihf'
987 atom = GetPortagePackage(new_target, pkg)
988 else:
989 atom = GetPortagePackage(target, pkg)
990
Mike Frysinger35247af2012-11-16 18:58:06 -0500991 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700992 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700993 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500994
Mike Frysinger35247af2012-11-16 18:58:06 -0500995 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
996 settings=portage.settings)
997 contents = dblink.getcontents()
998 for obj in contents:
999 ptype = contents[obj][0]
1000 if not IsPathPackagable(ptype, obj):
1001 continue
1002
1003 if ptype == 'obj':
1004 # For native ELFs, we need to pull in their dependencies too.
1005 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001006 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001007 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001008 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -05001009 paths.add(obj)
1010
1011 return paths, elfs
1012
1013
1014def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -05001015 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -05001016 """Link in all packable files and their runtime dependencies
1017
1018 This also wraps up executable ELFs with helper scripts.
1019
1020 Args:
1021 output_dir: The output directory to store files
1022 paths: All the files to include
1023 elfs: All the files which are ELFs (a subset of |paths|)
1024 ldpaths: A dict of static ldpath information
1025 path_rewrite_func: User callback to rewrite paths in output_dir
1026 root: The root path to pull all packages/files from
1027 """
1028 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001029 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001030 for path in paths:
1031 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001032 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001033 dst = output_dir + new_path
1034 osutils.SafeMakedirs(os.path.dirname(dst))
1035
1036 # Is this a symlink which we have to rewrite or wrap?
1037 # Delay wrap check until after we have created all paths.
1038 src = root + path
1039 if os.path.islink(src):
1040 tgt = os.readlink(src)
1041 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001042 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001043
1044 # Rewrite absolute links to relative and then generate the symlink
1045 # ourselves. All other symlinks can be hardlinked below.
1046 if tgt[0] == '/':
1047 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1048 os.symlink(tgt, dst)
1049 continue
1050
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001051 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001052 os.link(src, dst)
1053
Mike Frysinger35247af2012-11-16 18:58:06 -05001054 # Locate all the dependencies for all the ELFs. Stick them all in the
1055 # top level "lib" dir to make the wrapper simpler. This exact path does
1056 # not matter since we execute ldso directly, and we tell the ldso the
1057 # exact path to search for its libraries.
1058 libdir = os.path.join(output_dir, 'lib')
1059 osutils.SafeMakedirs(libdir)
1060 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001061 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001062 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001063 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001064 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001065 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001066 interp = e['interp']
Mike Frysinger221bd822017-09-29 02:51:47 -04001067
Mike Frysinger9fe02342019-12-12 17:52:53 -05001068 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1069 if os.path.basename(elf).startswith('libopcodes-'):
1070 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001071
Mike Frysinger00b129f2021-04-21 18:11:48 -04001072 # Copy all the dependencies before we copy the program & generate wrappers.
Mike Frysinger9fe02342019-12-12 17:52:53 -05001073 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001074 src = path = lib_data['path']
1075 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001076 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001077 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001078
1079 # No need to try and copy the same source lib multiple times.
1080 if path in donelibs:
1081 continue
1082 donelibs.add(path)
1083
1084 # Die if we try to normalize different source libs with the same basename.
1085 if lib in basenamelibs:
1086 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1087 os.path.join('/lib', lib), path,
1088 ' '.join(x for x in donelibs
1089 if x != path and os.path.basename(x) == lib))
1090 # TODO(crbug.com/917193): Make this fatal.
1091 # cros_build_lib.Die('Unable to resolve lib conflicts')
1092 continue
1093 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001094
1095 # Needed libs are the SONAME, but that is usually a symlink, not a
1096 # real file. So link in the target rather than the symlink itself.
1097 # We have to walk all the possible symlinks (SONAME could point to a
1098 # symlink which points to a symlink), and we have to handle absolute
1099 # ourselves (since we have a "root" argument).
1100 dst = os.path.join(libdir, os.path.basename(path))
1101 src = ReadlinkRoot(src, root)
1102
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001103 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001104 os.link(root + src, dst)
1105
Mike Frysinger00b129f2021-04-21 18:11:48 -04001106 # Do not create wrapper for libc. crbug.com/766827
1107 if interp and not glibc_re.search(elf):
1108 # Generate a wrapper if it is executable.
1109 interp = os.path.join('/lib', os.path.basename(interp))
1110 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1111 libpaths=e['rpath'] + e['runpath'])
1112 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
1113
1114 # Wrap any symlinks to the wrapper.
1115 if elf in sym_paths:
1116 link = sym_paths[elf]
1117 GeneratePathWrapper(output_dir, link, elf)
1118
Mike Frysinger35247af2012-11-16 18:58:06 -05001119
1120def _EnvdGetVar(envd, var):
1121 """Given a Gentoo env.d file, extract a var from it
1122
1123 Args:
1124 envd: The env.d file to load (may be a glob path)
1125 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001126
Mike Frysinger35247af2012-11-16 18:58:06 -05001127 Returns:
1128 The value of |var|
1129 """
1130 envds = glob.glob(envd)
1131 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1132 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001133 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001134
1135
1136def _ProcessBinutilsConfig(target, output_dir):
1137 """Do what binutils-config would have done"""
1138 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001139
Sloan Johnsonc59e9262022-06-10 20:51:53 +00001140 # Locate the bin dir holding the linker and perform some confidence checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001141 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1142 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001143 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001144 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001145 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1146 % globpath)
1147 srcpath = srcpath[0]
1148 ld_path = os.path.join(srcpath, 'ld')
1149 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1150 ld_path = os.path.join(srcpath, 'ld.bfd')
1151 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001152
Mike Frysinger78b7a812014-11-26 19:45:23 -05001153 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001154 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1155 for prog in os.listdir(output_dir + srcpath):
1156 # Skip binaries already wrapped.
1157 if not prog.endswith('.real'):
1158 GeneratePathWrapper(output_dir, binpath + prog,
1159 os.path.join(srcpath, prog))
1160 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1161 os.path.join(srcpath, prog))
1162
David James27ac4ae2012-12-03 23:16:15 -08001163 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001164 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001165 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1166 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1167 output_dir + libpath)
1168
1169
1170def _ProcessGccConfig(target, output_dir):
1171 """Do what gcc-config would have done"""
1172 binpath = '/bin'
1173 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1174 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1175 for prog in os.listdir(output_dir + srcpath):
1176 # Skip binaries already wrapped.
1177 if (not prog.endswith('.real') and
1178 not prog.endswith('.elf') and
1179 prog.startswith(target)):
1180 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1181 os.path.join(srcpath, prog))
1182 return srcpath
1183
1184
Frank Henigman179ec7c2015-02-06 03:01:09 -05001185def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1186 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001187 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001188
Tobias Boschddd16492019-08-14 09:29:54 -07001189 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001190 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001191 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1192 # Can't update the wrapper in place to not affect the chroot,
1193 # but only the extracted toolchain.
1194 os.unlink(sysroot_wrapper)
1195 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001196 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001197
1198
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001199def _ProcessClangWrappers(target, output_dir):
1200 """Remove chroot-specific things from our sysroot wrappers"""
1201 clang_bin_path = '/usr/bin'
1202 # Disable ccache from clang wrappers.
1203 _ProcessSysrootWrappers(target, output_dir, clang_bin_path)
1204 GeneratePathWrapper(output_dir, f'/bin/{target}-clang',
1205 f'/usr/bin/{target}-clang')
1206 GeneratePathWrapper(output_dir, f'/bin/{target}-clang++',
1207 f'/usr/bin/{target}-clang++')
1208
1209
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001210def _CreateMainLibDir(target, output_dir):
1211 """Create some lib dirs so that compiler can get the right Gcc paths"""
1212 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1213 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1214
1215
Manoj Guptadf8b3872022-01-13 11:57:36 -08001216def _CreateRemoteToolchainFile(output_dir):
1217 """Create a remote_toolchain_inputs file for reclient/RBE"""
1218 # The inputs file lists all files/shared libraries needed to run clang.
1219 # All inputs are relative to location of clang binary and one input
1220 # location per line of file e.g.
1221 # clang-13.elf
1222 # clang++-13.elf
1223 # relative/path/to/clang/resource/directory
1224
1225 clang_path = os.path.join(output_dir, 'usr/bin')
1226 # Add needed shared libraries and internal files e.g. allowlists.
1227 toolchain_inputs = ['../../lib']
1228 clang_shared_dirs = glob.glob(
1229 os.path.join(output_dir, 'usr/lib64/clang/*/share'))
1230 for clang_dir in clang_shared_dirs:
1231 toolchain_inputs.append(os.path.relpath(clang_dir, clang_path))
1232
1233 # Add actual clang binaries/wrappers.
1234 for clang_files in glob.glob(os.path.join(clang_path, 'clang*-[0-9]*')):
1235 toolchain_inputs.append(os.path.basename(clang_files))
1236
1237 with open(os.path.join(clang_path, 'remote_toolchain_inputs'), 'w') as f:
1238 f.writelines('%s\n' % line for line in toolchain_inputs)
1239
1240
Mike Frysinger35247af2012-11-16 18:58:06 -05001241def _ProcessDistroCleanups(target, output_dir):
1242 """Clean up the tree and remove all distro-specific requirements
1243
1244 Args:
1245 target: The toolchain target name
1246 output_dir: The output directory to clean up
1247 """
1248 _ProcessBinutilsConfig(target, output_dir)
1249 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001250 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001251 _ProcessClangWrappers(target, output_dir)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001252 _CreateMainLibDir(target, output_dir)
Manoj Guptadf8b3872022-01-13 11:57:36 -08001253 _CreateRemoteToolchainFile(output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001254
1255 osutils.RmDir(os.path.join(output_dir, 'etc'))
1256
1257
1258def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1259 """Setup a tree from the packages for the specified target
1260
1261 This populates a path with all the files from toolchain packages so that
1262 a tarball can easily be generated from the result.
1263
1264 Args:
1265 target: The target to create a packagable root from
1266 output_dir: The output directory to place all the files
1267 ldpaths: A dict of static ldpath information
1268 root: The root path to pull all packages/files from
1269 """
1270 # Find all the files owned by the packages for this target.
1271 paths, elfs = _GetFilesForTarget(target, root=root)
1272
1273 # Link in all the package's files, any ELF dependencies, and wrap any
1274 # executable ELFs with helper scripts.
1275 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001276 """Move /usr/bin to /bin so people can just use that toplevel dir
1277
George Burgess IVca1d7612020-10-01 00:38:32 -07001278 Note we do not apply this to clang or rust - there is correlation between
1279 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001280 """
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001281 NO_MOVE_PATTERNS = ('clang', 'rust', 'cargo', 'sysroot_wrapper')
George Burgess IVca1d7612020-10-01 00:38:32 -07001282 if (path.startswith('/usr/bin/') and
Manoj Gupta61bf9db2020-03-23 21:28:04 -07001283 not any(x in path for x in NO_MOVE_PATTERNS)):
Han Shen699ea192016-03-02 10:42:47 -08001284 return path[4:]
1285 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001286 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1287 path_rewrite_func=MoveUsrBinToBin, root=root)
1288
1289 # The packages, when part of the normal distro, have helper scripts
1290 # that setup paths and such. Since we are making this standalone, we
1291 # need to preprocess all that ourselves.
1292 _ProcessDistroCleanups(target, output_dir)
1293
1294
1295def CreatePackages(targets_wanted, output_dir, root='/'):
1296 """Create redistributable cross-compiler packages for the specified targets
1297
1298 This creates toolchain packages that should be usable in conjunction with
1299 a downloaded sysroot (created elsewhere).
1300
1301 Tarballs (one per target) will be created in $PWD.
1302
1303 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001304 targets_wanted: The targets to package up.
1305 output_dir: The directory to put the packages in.
1306 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001307 """
Ralph Nathan03047282015-03-23 11:09:32 -07001308 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001309 osutils.SafeMakedirs(output_dir)
1310 ldpaths = lddtree.LoadLdpaths(root)
1311 targets = ExpandTargets(targets_wanted)
1312
Mike Frysinger221bd822017-09-29 02:51:47 -04001313 with osutils.TempDir(prefix='create-packages') as tempdir:
1314 logging.debug('Using tempdir: %s', tempdir)
1315
Mike Frysinger35247af2012-11-16 18:58:06 -05001316 # We have to split the root generation from the compression stages. This is
1317 # because we hardlink in all the files (to avoid overhead of reading/writing
1318 # the copies multiple times). But tar gets angry if a file's hardlink count
1319 # changes from when it starts reading a file to when it finishes.
1320 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1321 for target in targets:
1322 output_target_dir = os.path.join(tempdir, target)
1323 queue.put([target, output_target_dir, ldpaths, root])
1324
1325 # Build the tarball.
1326 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1327 for target in targets:
1328 tar_file = os.path.join(output_dir, target + '.tar.xz')
1329 queue.put([tar_file, os.path.join(tempdir, target)])
1330
1331
Mike Frysinger07534cf2017-09-12 17:40:21 -04001332def GetParser():
1333 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001334 parser = commandline.ArgumentParser(description=__doc__)
1335 parser.add_argument('-u', '--nousepkg',
1336 action='store_false', dest='usepkg', default=True,
Tom Hughesaa41d192022-06-07 16:44:11 -07001337 help='Do not use prebuilt packages')
Mike Frysinger0c808452014-11-06 17:30:23 -05001338 parser.add_argument('-d', '--deleteold',
1339 action='store_true', dest='deleteold', default=False,
1340 help='Unmerge deprecated packages')
1341 parser.add_argument('-t', '--targets',
1342 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001343 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001344 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001345 "allowed. Defaults to 'sdk'.")
1346 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1347 help='Comma separated list of boards whose toolchains we '
1348 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001349 parser.add_argument('--hostonly',
1350 dest='hostonly', default=False, action='store_true',
1351 help='Only setup the host toolchain. '
1352 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001353 parser.add_argument('--show-board-cfg', '--show-cfg',
1354 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001355 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001356 parser.add_argument('--show-packages', default=None,
1357 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001358 parser.add_argument('--create-packages',
1359 action='store_true', default=False,
1360 help='Build redistributable packages')
1361 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1362 help='Output directory')
1363 parser.add_argument('--reconfig', default=False, action='store_true',
1364 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001365 parser.add_argument('--sysroot', type='path',
1366 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001367 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001368
Mike Frysinger07534cf2017-09-12 17:40:21 -04001369
1370def main(argv):
1371 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001372 options = parser.parse_args(argv)
1373 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001374
Mike Frysinger35247af2012-11-16 18:58:06 -05001375 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001376 conflicting_options = (
1377 options.cfg_name,
1378 options.show_packages,
1379 options.create_packages,
1380 )
1381 if sum(bool(x) for x in conflicting_options) > 1:
1382 parser.error('conflicting options: create-packages & show-packages & '
1383 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001384
Gilad Arnold8195b532015-04-07 10:56:30 +03001385 targets_wanted = set(options.targets.split(','))
1386 boards_wanted = (set(options.include_boards.split(','))
1387 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001388
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001389 if options.cfg_name:
1390 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001391 elif options.show_packages is not None:
1392 cros_build_lib.AssertInsideChroot()
1393 target = options.show_packages
1394 Crossdev.Load(False)
1395 for package in GetTargetPackages(target):
1396 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001397 elif options.create_packages:
1398 cros_build_lib.AssertInsideChroot()
1399 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001400 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001401 else:
1402 cros_build_lib.AssertInsideChroot()
1403 # This has to be always run as root.
Ram Chandrasekar69751282022-02-25 21:07:36 +00001404 if osutils.IsNonRootUser():
Mike Frysinger35247af2012-11-16 18:58:06 -05001405 cros_build_lib.Die('this script must be run as root')
1406
1407 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001408 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001409 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001410 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001411 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001412 Crossdev.Save()
1413
1414 return 0