blob: 4c063af326f2fabbeed573cec65467b0a1a8358a [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Zdenek Behan508dcce2011-12-05 15:39:32 +01002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Mike Frysinger750c5f52014-09-16 16:16:57 -04006"""This script manages the installed toolchains in the chroot."""
Zdenek Behan508dcce2011-12-05 15:39:32 +01007
Mike Frysinger383367e2014-09-16 15:06:17 -04008from __future__ import print_function
9
Mike Frysinger3ed47722017-08-08 14:59:08 -040010import errno
Mike Frysinger35247af2012-11-16 18:58:06 -050011import glob
Mike Frysinger3ed47722017-08-08 14:59:08 -040012import hashlib
Mike Frysinger7ccee992012-06-01 21:27:59 -040013import json
Zdenek Behan508dcce2011-12-05 15:39:32 +010014import os
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -070015import re
Tobias Boschddd16492019-08-14 09:29:54 -070016import shutil
Zdenek Behan508dcce2011-12-05 15:39:32 +010017
Aviv Keshetb7519e12016-10-04 00:50:00 -070018from chromite.lib import constants
Mike Frysinger506e75f2012-12-17 14:21:13 -050019from chromite.lib import commandline
Brian Harring503f3ab2012-03-09 21:39:41 -080020from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070021from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070022from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050023from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080024from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040025from chromite.utils import key_value_store
Mike Frysinger35247af2012-11-16 18:58:06 -050026
27# Needs to be after chromite imports.
28import lddtree
Zdenek Behan508dcce2011-12-05 15:39:32 +010029
Mike Frysinger31596002012-12-03 23:54:24 -050030if cros_build_lib.IsInsideChroot():
31 # Only import portage after we've checked that we're inside the chroot.
32 # Outside may not have portage, in which case the above may not happen.
33 # We'll check in main() if the operation needs portage.
Mike Frysinger27e21b72018-07-12 14:20:21 -040034 # pylint: disable=import-error
Mike Frysinger31596002012-12-03 23:54:24 -050035 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010036
37
Matt Tennantf1e30972012-03-02 16:30:07 -080038EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010039PACKAGE_STABLE = '[stable]'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010040
41CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070042ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010043STABLE_OVERLAY = '/usr/local/portage/stable'
44CROSSDEV_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',
58 'sys-devel/clang',
59 'sys-devel/gcc',
Yunlian Jiangf5721f32017-10-31 11:43:11 -070060 'sys-devel/lld',
Mike Frysinger66bfde52017-09-12 16:42:57 -040061 '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',
Mike Frysinger785b0c32017-09-13 01:35:59 -040076)
77
78# New packages that we're in the process of adding to the SDK. Since the SDK
79# bot hasn't had a chance to run yet, there are no binary packages available,
80# so we have to list them here and wait. Once it completes, entries here can
81# be removed so they'll end up on bots & dev's systems.
82NEW_PACKAGES = (
Mike Frysinger785b0c32017-09-13 01:35:59 -040083)
84
Rahul Chaudhry4b803052015-05-13 15:25:56 -070085# Enable the Go compiler for these targets.
86TARGET_GO_ENABLED = (
87 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070088 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070089 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070090 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070091)
92CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
93
Manoj Gupta1b5642e2017-03-08 16:44:12 -080094# Enable llvm's compiler-rt for these targets.
95TARGET_COMPILER_RT_ENABLED = (
96 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070097 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070098 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -080099 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800100)
101CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
102
Manoj Gupta946abb42017-04-12 14:27:19 -0700103TARGET_LLVM_PKGS_ENABLED = (
104 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700105 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700106 'aarch64-cros-linux-gnu',
107 'x86_64-cros-linux-gnu',
108)
109
110LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700111 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
112 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700113 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700114}
115
Zdenek Behan508dcce2011-12-05 15:39:32 +0100116# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
117CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500118 'binutils' : {
Manoj Guptaa91e38e2018-11-15 11:07:48 -0800119 'aarch64-cros-linux-gnu' : '-gold',
Mike Frysinger8a83c622015-05-28 00:35:05 -0400120 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -0800121 'armv7a-cros-linux-gnueabi': '-gold',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700122 'armv7a-cros-linux-gnueabihf': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500123 'i686-pc-linux-gnu' : '-gold',
124 'x86_64-cros-linux-gnu' : '-gold',
125 },
Zdenek Behan508dcce2011-12-05 15:39:32 +0100126}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100127
128
David James66a09c42012-11-05 13:31:38 -0800129class Crossdev(object):
130 """Class for interacting with crossdev and caching its output."""
131
132 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
133 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800134 # Packages that needs separate handling, in addition to what we have from
135 # crossdev.
136 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700137 'clang': 'sys-devel',
Yunlian Jiang18ae9982017-11-03 09:15:31 -0700138 'lld': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800139 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700140 'libcxxabi': 'sys-libs',
141 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700142 'elfutils': 'dev-libs',
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700143 'llvm-libunwind': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800144 }
David James66a09c42012-11-05 13:31:38 -0800145
146 @classmethod
147 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400148 """Load crossdev cache from disk.
149
150 We invalidate the cache when crossdev updates or this script changes.
151 """
David James90239b92012-11-05 15:31:34 -0800152 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400153 # If we run the compiled/cached .pyc file, we'll read/hash that when we
154 # really always want to track the source .py file.
155 script = os.path.abspath(__file__)
156 if script.endswith('.pyc'):
157 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500158 setup_toolchains_hash = hashlib.md5(
159 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400160
161 cls._CACHE = {
162 'crossdev_version': crossdev_version,
163 'setup_toolchains_hash': setup_toolchains_hash,
164 }
165
166 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
167 if reconfig:
168 logging.debug('cache: forcing regen due to reconfig')
169 return
170
171 try:
172 file_data = osutils.ReadFile(cls._CACHE_FILE)
173 except IOError as e:
174 if e.errno != errno.ENOENT:
175 logging.warning('cache: reading failed: %s', e)
176 osutils.SafeUnlink(cls._CACHE_FILE)
177 return
178
179 try:
180 data = json.loads(file_data)
181 except ValueError as e:
182 logging.warning('cache: ignoring invalid content: %s', e)
183 return
184
185 if crossdev_version != data.get('crossdev_version'):
186 logging.debug('cache: rebuilding after crossdev upgrade')
187 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
188 logging.debug('cache: rebuilding after cros_setup_toolchains change')
189 else:
190 logging.debug('cache: content is up-to-date!')
191 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800192
193 @classmethod
194 def Save(cls):
195 """Store crossdev cache on disk."""
196 # Save the cache from the successful run.
197 with open(cls._CACHE_FILE, 'w') as f:
198 json.dump(cls._CACHE, f)
199
200 @classmethod
201 def GetConfig(cls, target):
202 """Returns a map of crossdev provided variables about a tuple."""
203 CACHE_ATTR = '_target_tuple_map'
204
205 val = cls._CACHE.setdefault(CACHE_ATTR, {})
206 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400207 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400208 conf = {
209 'crosspkgs': [],
210 'target': toolchain.GetHostTuple(),
211 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400212 if target == 'host':
213 packages_list = HOST_PACKAGES
214 else:
215 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400216 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400217 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400218 else:
219 # Build the crossdev command.
220 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
221 if target in TARGET_COMPILER_RT_ENABLED:
222 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
223 if target in TARGET_GO_ENABLED:
224 cmd.extend(CROSSDEV_GO_ARGS)
225 if target in TARGET_LLVM_PKGS_ENABLED:
226 for pkg in LLVM_PKGS_TABLE:
227 cmd.extend(LLVM_PKGS_TABLE[pkg])
228 cmd.extend(['-t', target])
229 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400230 out = cros_build_lib.run(
Mike Frysingerb3202be2019-11-15 22:25:59 -0500231 cmd, print_cmd=False, redirect_stdout=True,
232 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400233 # List of tuples split at the first '=', converted into dict.
234 conf = dict((k, cros_build_lib.ShellUnquote(v))
235 for k, v in (x.split('=', 1) for x in out))
236 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800237
Mike Frysinger66bfde52017-09-12 16:42:57 -0400238 manual_pkgs = cls.MANUAL_PKGS
239
240 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400241 conf[pkg + '_pn'] = pkg
242 conf[pkg + '_category'] = cat
243 if pkg not in conf['crosspkgs']:
244 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800245
246 val[target] = conf
247
David James66a09c42012-11-05 13:31:38 -0800248 return val[target]
249
250 @classmethod
251 def UpdateTargets(cls, targets, usepkg, config_only=False):
252 """Calls crossdev to initialize a cross target.
253
254 Args:
LaMont Jones4bfedef2019-11-07 15:39:43 -0700255 targets: The dict of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700256 usepkg: Copies the commandline opts.
257 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800258 """
259 configured_targets = cls._CACHE.setdefault('configured_targets', [])
LaMont Jones4bfedef2019-11-07 15:39:43 -0700260 started_targets = set()
David James66a09c42012-11-05 13:31:38 -0800261
LaMont Jones4bfedef2019-11-07 15:39:43 -0700262 # Schedule all of the targets in parallel, and let them run.
263 with parallel.BackgroundTaskRunner(cls._UpdateTarget) as queue:
264 for target_name in targets:
265 # We already started this target in this loop.
266 if target_name in started_targets:
267 continue
268 # The target is already configured.
269 if config_only and target_name in configured_targets:
270 continue
271 queue.put([target_name, targets[target_name], usepkg, config_only])
272 started_targets.add(target_name)
273
274 @classmethod
275 def _UpdateTarget(cls, target_name, target, usepkg, config_only):
276 """Calls crossdev to initialize a cross target.
277
278 Args:
279 target_name: The name of the target to initialize.
280 target: The target info for initializing.
281 usepkg: Copies the commandline opts.
282 config_only: Just update.
283 """
284 configured_targets = cls._CACHE.setdefault('configured_targets', [])
David James66a09c42012-11-05 13:31:38 -0800285 cmdbase = ['crossdev', '--show-fail-log']
286 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
287 # Pick stable by default, and override as necessary.
288 cmdbase.extend(['-P', '--oneshot'])
289 if usepkg:
290 cmdbase.extend(['-P', '--getbinpkg',
291 '-P', '--usepkgonly',
292 '--without-headers'])
293
Christopher Wileyb22c0712015-06-02 10:37:03 -0700294 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800295 cmdbase.extend(['--overlays', overlays])
296 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
297
LaMont Jones4bfedef2019-11-07 15:39:43 -0700298 cmd = cmdbase + ['-t', target_name]
David James66a09c42012-11-05 13:31:38 -0800299
LaMont Jones4bfedef2019-11-07 15:39:43 -0700300 for pkg in GetTargetPackages(target_name):
301 if pkg == 'gdb':
302 # Gdb does not have selectable versions.
303 cmd.append('--ex-gdb')
304 elif pkg == 'ex_compiler-rt':
305 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
306 elif pkg == 'ex_go':
307 # Go does not have selectable versions.
308 cmd.extend(CROSSDEV_GO_ARGS)
309 elif pkg in LLVM_PKGS_TABLE:
310 cmd.extend(LLVM_PKGS_TABLE[pkg])
311 elif pkg in cls.MANUAL_PKGS:
312 pass
David James66a09c42012-11-05 13:31:38 -0800313 else:
LaMont Jones4bfedef2019-11-07 15:39:43 -0700314 # The first of the desired versions is the "primary" one.
315 version = GetDesiredPackageVersions(target_name, pkg)[0]
316 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800317
LaMont Jones4bfedef2019-11-07 15:39:43 -0700318 cmd.extend(target['crossdev'].split())
319 if config_only:
320 # In this case we want to just quietly reinit
321 cmd.append('--init-target')
322 cros_build_lib.run(cmd, print_cmd=False, redirect_stdout=True)
323 else:
324 cros_build_lib.run(cmd)
325
326 configured_targets.append(target_name)
David James66a09c42012-11-05 13:31:38 -0800327
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100328
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100329def GetTargetPackages(target):
330 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800331 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100332 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800333 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100334
335
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100336# Portage helper functions:
337def GetPortagePackage(target, package):
338 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800339 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100340 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400341 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100342 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100343 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100344 category = conf['category']
345 # Portage package:
346 pn = conf[package + '_pn']
347 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500348 assert category
349 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100350 return '%s/%s' % (category, pn)
351
352
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700353def PortageTrees(root):
354 """Return the portage trees for a given root."""
355 if root == '/':
356 return portage.db['/']
357 # The portage logic requires the path always end in a slash.
358 root = root.rstrip('/') + '/'
359 return portage.create_trees(target_root=root, config_root=root)[root]
360
361
362def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100363 """Extracts the list of current versions of a target, package pair.
364
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500365 Args:
366 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700367 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100368
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500369 Returns:
370 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100371 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100372 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700373 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100374 version = portage.versions.cpv_getversion(pkg)
375 versions.append(version)
376 return versions
377
378
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700379def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100380 """Extracts the current stable version for a given package.
381
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500382 Args:
383 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
384 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700385 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100386
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500387 Returns:
388 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100389 """
David James90239b92012-11-05 15:31:34 -0800390 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700391 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800392 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100393
394
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700395def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100396 """Resolves keywords in a given version list for a particular package.
397
398 Resolving means replacing PACKAGE_STABLE with the actual number.
399
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500400 Args:
401 target: The target to operate on (e.g. i686-pc-linux-gnu)
402 package: The target/package to operate on (e.g. gcc)
403 versions: List of versions to resolve
404 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700405 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100406
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500407 Returns:
408 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100409 """
410 resolved = []
David James90239b92012-11-05 15:31:34 -0800411 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700412 if not installed:
413 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100414 for version in versions:
415 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700416 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400417 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100418 resolved.append(version)
419 return resolved
420
421
422def GetDesiredPackageVersions(target, package):
423 """Produces the list of desired versions for each target, package pair.
424
425 The first version in the list is implicitly treated as primary, ie.
426 the version that will be initialized by crossdev and selected.
427
428 If the version is PACKAGE_STABLE, it really means the current version which
429 is emerged by using the package atom with no particular version key.
430 Since crossdev unmasks all packages by default, this will actually
431 mean 'unstable' in most cases.
432
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500433 Args:
434 target: The target to operate on (e.g. i686-pc-linux-gnu)
435 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100436
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500437 Returns:
438 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400440 if package in GetTargetPackages(target):
441 return [PACKAGE_STABLE]
442 else:
443 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100444
445
446def TargetIsInitialized(target):
447 """Verifies if the given list of targets has been correctly initialized.
448
449 This determines whether we have to call crossdev while emerging
450 toolchain packages or can do it using emerge. Emerge is naturally
451 preferred, because all packages can be updated in a single pass.
452
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500453 Args:
454 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500456 Returns:
457 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100458 """
459 # Check if packages for the given target all have a proper version.
460 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100461 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800462 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100463 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400464 if not (GetStablePackageVersion(atom, True) and
465 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100466 return False
467 return True
468 except cros_build_lib.RunCommandError:
469 # Fails - The target has likely never been initialized before.
470 return False
471
472
473def RemovePackageMask(target):
474 """Removes a package.mask file for the given platform.
475
476 The pre-existing package.mask files can mess with the keywords.
477
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500478 Args:
479 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100480 """
481 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700482 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100483
484
Zdenek Behan508dcce2011-12-05 15:39:32 +0100485# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700486def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500487 """Rebuild libtool as needed
488
489 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
490 gcc, libtool will break. We can't use binary packages either as those will
491 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700492
493 Args:
494 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500495 """
496 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700497 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500498 for line in f:
499 # Look for a line like:
500 # sys_lib_search_path_spec="..."
501 # It'll be a list of paths and gcc will be one of them.
502 if line.startswith('sys_lib_search_path_spec='):
503 line = line.rstrip()
504 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400505 root_path = os.path.join(root, path.lstrip(os.path.sep))
506 logging.debug('Libtool: checking %s', root_path)
507 if not os.path.exists(root_path):
508 logging.info('Rebuilding libtool after gcc upgrade')
509 logging.info(' %s', line)
510 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500511 needs_update = True
512 break
513
514 if needs_update:
515 break
516
517 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700518 cmd = [EMERGE_CMD, '--oneshot']
519 if root != '/':
520 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
521 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400522 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400523 else:
524 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500525
526
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700527def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528 """Determines which packages need update/unmerge and defers to portage.
529
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500530 Args:
531 targets: The list of targets to update
532 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700533 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535 # For each target, we do two things. Figure out the list of updates,
536 # and figure out the appropriate keywords/masks. Crossdev will initialize
537 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400538 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800539 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400541 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100542 # Record the highest needed version for each target, for masking purposes.
543 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100544 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100545 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400546 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100547 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700548 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100549 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200550 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400551 if pkg in NEW_PACKAGES and usepkg:
552 # Skip this binary package (for now).
553 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100554 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400555 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100556
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400557 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400559 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800560 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561
Mike Frysinger3bba5032016-09-20 14:15:04 -0400562 logging.info('Updating packages:')
563 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100564
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100565 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100566 if usepkg:
567 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700568 if root != '/':
569 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100570
571 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400572 cros_build_lib.run(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800573 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100574
575
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700576def CleanTargets(targets, root='/'):
577 """Unmerges old packages that are assumed unnecessary.
578
579 Args:
580 targets: The list of targets to clean up.
581 root: The install root in which we want packages cleaned up.
582 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100583 unmergemap = {}
584 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400585 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100586 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400587 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100588 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700589 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100590 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700591 # NOTE: This refers to installed packages (vartree) rather than the
592 # Portage version (porttree and/or bintree) when determining the current
593 # version. While this isn't the most accurate thing to do, it is probably
594 # a good simple compromise, which should have the desired result of
595 # uninstalling everything but the latest installed version. In
596 # particular, using the bintree (--usebinpkg) requires a non-trivial
597 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200598 desired_num = VersionListToNumeric(target, package, desired, True)
599 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400600 logging.warning('Error detecting stable version for %s, '
601 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200602 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603 unmergemap[pkg] = set(current).difference(desired_num)
604
605 # Cleaning doesn't care about consistency and rebuilding package.* files.
606 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400607 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100608 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
609
610 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400611 logging.info('Cleaning packages:')
612 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100613 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700614 if root != '/':
615 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100616 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400617 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100618 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400619 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100620
621
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700622def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100623 """Runs gcc-config and binutils-config to select the desired.
624
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500625 Args:
626 targets: The targets to select
627 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700628 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100629 """
630 for package in ['gcc', 'binutils']:
631 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400632 # See if this package is part of this target.
633 if package not in GetTargetPackages(target):
634 logging.debug('%s: %s is not used', target, package)
635 continue
636
Zdenek Behan508dcce2011-12-05 15:39:32 +0100637 # Pick the first version in the numbered list as the selected one.
638 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700639 desired_num = VersionListToNumeric(target, package, desired, True,
640 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100641 desired = desired_num[0]
642 # *-config does not play revisions, strip them, keep just PV.
643 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
644
Mike Frysinger785b0c32017-09-13 01:35:59 -0400645 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100646 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800647 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100648
649 # And finally, attach target to it.
650 desired = '%s-%s' % (target, desired)
651
652 # Target specific hacks
653 if package in suffixes:
654 if target in suffixes[package]:
655 desired += suffixes[package][target]
656
David James7ec5efc2012-11-06 09:39:49 -0800657 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700658 if root != '/':
659 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800660 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400661 result = cros_build_lib.run(
Mike Frysingerb3202be2019-11-15 22:25:59 -0500662 cmd, print_cmd=False, redirect_stdout=True, encoding='utf-8',
663 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500664 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700665
666 # Do not reconfig when the current is live or nothing needs to be done.
667 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100668 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500669 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400670 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100671
672
Mike Frysinger35247af2012-11-16 18:58:06 -0500673def ExpandTargets(targets_wanted):
674 """Expand any possible toolchain aliases into full targets
675
676 This will expand 'all' and 'sdk' into the respective toolchain tuples.
677
678 Args:
679 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500680
Mike Frysinger35247af2012-11-16 18:58:06 -0500681 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300682 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500683 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500684 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700685 if targets_wanted == set(['boards']):
686 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300687 return {}
688
689 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500690 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300691 return all_targets
692 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500693 # Filter out all the non-sdk toolchains as we don't want to mess
694 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300695 return toolchain.FilterToolchains(all_targets, 'sdk', True)
696
697 # Verify user input.
698 nonexistent = targets_wanted.difference(all_targets)
699 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400700 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300701 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500702
703
David Jamesf8c672f2012-11-06 13:38:11 -0800704def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700705 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100706 """Performs all steps to create a synchronized toolchain enviroment.
707
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500708 Args:
709 usepkg: Use prebuilt packages
710 deleteold: Unmerge deprecated packages
711 hostonly: Only setup the host toolchain
712 reconfig: Reload crossdev config and reselect toolchains
713 targets_wanted: All the targets to update
714 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700715 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100716 """
David Jamesf8c672f2012-11-06 13:38:11 -0800717 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100718 if not hostonly:
719 # For hostonly, we can skip most of the below logic, much of which won't
720 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500721 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400722
Don Garrettc0c74002015-10-09 12:58:19 -0700723 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300724 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400725 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800726 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100727
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100728 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400729 for target in targets:
730 if TargetIsInitialized(target):
731 reconfig_targets[target] = targets[target]
732 else:
733 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100734 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400735 logging.info('The following targets need to be re-initialized:')
736 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800737 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200738 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800739 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100740
Mike Frysinger66814c32017-10-09 18:11:46 -0400741 # If we're building a subset of toolchains for a board, we might not have
742 # all the tuples that the packages expect. We don't define the "full" set
743 # of tuples currently other than "whatever the full sdk has normally".
744 if usepkg or set(('all', 'sdk')) & targets_wanted:
745 # Since we have cross-compilers now, we can update these packages.
746 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400747
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100748 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400749 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100750
751 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700752 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
753 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800754
755 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700756 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100757
Mike Frysingerc880a962013-11-08 13:59:06 -0500758 # Now that we've cleared out old versions, see if we need to rebuild
759 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700760 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500761
Zdenek Behan508dcce2011-12-05 15:39:32 +0100762
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700763def ShowConfig(name):
764 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500765
766 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700767 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500768 """
Don Garrettc0c74002015-10-09 12:58:19 -0700769 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500770 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400771 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400772 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400773 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
774 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500775
776
Mike Frysinger35247af2012-11-16 18:58:06 -0500777def GeneratePathWrapper(root, wrappath, path):
778 """Generate a shell script to execute another shell script
779
780 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
781 argv[0] won't be pointing to the correct path, generate a shell script that
782 just executes another program with its full path.
783
784 Args:
785 root: The root tree to generate scripts inside of
786 wrappath: The full path (inside |root|) to create the wrapper
787 path: The target program which this wrapper will execute
788 """
789 replacements = {
790 'path': path,
791 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
792 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900793
794 # Do not use exec here, because exec invokes script with absolute path in
795 # argv0. Keeping relativeness allows us to remove abs path from compile result
796 # and leads directory independent build cache sharing in some distributed
797 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500798 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900799basedir=$(dirname "$0")
800"${basedir}/%(relroot)s%(path)s" "$@"
801exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500802""" % replacements
803 root_wrapper = root + wrappath
804 if os.path.islink(root_wrapper):
805 os.unlink(root_wrapper)
806 else:
807 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
808 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400809 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500810
811
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700812def FixClangXXWrapper(root, path):
813 """Fix wrapper shell scripts and symlinks for invoking clang++
814
815 In a typical installation, clang++ symlinks to clang, which symlinks to the
816 elf executable. The executable distinguishes between clang and clang++ based
817 on argv[0].
818
819 When invoked through the LdsoWrapper, argv[0] always contains the path to the
820 executable elf file, making clang/clang++ invocations indistinguishable.
821
822 This function detects if the elf executable being wrapped is clang-X.Y, and
823 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
824
825 The calling sequence now becomes:
826 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
827 the Ldsowrapper).
828 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
829 to the original clang-3.9 elf.
830 -) The difference this time is that inside the elf file execution, $0 is
831 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
832
Manoj Guptaae268142018-04-27 23:28:36 -0700833 Update: Starting since clang 7, the clang and clang++ are symlinks to
834 clang-7 binary, not clang-7.0. The pattern match is extended to handle
835 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
836
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700837 Args:
838 root: The root tree to generate scripts / symlinks inside of
839 path: The target elf for which LdsoWrapper was created
840 """
Manoj Guptaae268142018-04-27 23:28:36 -0700841 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700842 logging.info('fixing clang++ invocation for %s', path)
843 clangdir = os.path.dirname(root + path)
844 clang = os.path.basename(path)
845 clangxx = clang.replace('clang', 'clang++')
846
847 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
848 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
849
850 # Create a hardlink clang++-X.Y pointing to clang-X.Y
851 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
852
853 # Adjust the clang++ symlink to point to clang++-X.Y
854 os.unlink(os.path.join(clangdir, 'clang++'))
855 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
856
857
Mike Frysinger35247af2012-11-16 18:58:06 -0500858def FileIsCrosSdkElf(elf):
859 """Determine if |elf| is an ELF that we execute in the cros_sdk
860
861 We don't need this to be perfect, just quick. It makes sure the ELF
862 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
863
864 Args:
865 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500866
Mike Frysinger35247af2012-11-16 18:58:06 -0500867 Returns:
868 True if we think |elf| is a native ELF
869 """
870 with open(elf) as f:
871 data = f.read(20)
872 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
873 return (data[0:4] == '\x7fELF' and
874 data[4] == '\x02' and
875 data[5] == '\x01' and
876 data[18] == '\x3e')
877
878
879def IsPathPackagable(ptype, path):
880 """Should the specified file be included in a toolchain package?
881
882 We only need to handle files as we'll create dirs as we need them.
883
884 Further, trim files that won't be useful:
885 - non-english translations (.mo) since it'd require env vars
886 - debug files since these are for the host compiler itself
887 - info/man pages as they're big, and docs are online, and the
888 native docs should work fine for the most part (`man gcc`)
889
890 Args:
891 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
892 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500893
Mike Frysinger35247af2012-11-16 18:58:06 -0500894 Returns:
895 True if we want to include this path in the package
896 """
897 return not (ptype in ('dir',) or
898 path.startswith('/usr/lib/debug/') or
899 os.path.splitext(path)[1] == '.mo' or
900 ('/man/' in path or '/info/' in path))
901
902
903def ReadlinkRoot(path, root):
904 """Like os.readlink(), but relative to a |root|
905
906 Args:
907 path: The symlink to read
908 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500909
Mike Frysinger35247af2012-11-16 18:58:06 -0500910 Returns:
911 A fully resolved symlink path
912 """
913 while os.path.islink(root + path):
914 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
915 return path
916
917
918def _GetFilesForTarget(target, root='/'):
919 """Locate all the files to package for |target|
920
921 This does not cover ELF dependencies.
922
923 Args:
924 target: The toolchain target name
925 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500926
Mike Frysinger35247af2012-11-16 18:58:06 -0500927 Returns:
928 A tuple of a set of all packable paths, and a set of all paths which
929 are also native ELFs
930 """
931 paths = set()
932 elfs = set()
933
934 # Find all the files owned by the packages for this target.
935 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500936
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700937 # Skip Go compiler from redistributable packages.
938 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
939 # into it. Due to this, the toolchain cannot be unpacked anywhere
940 # else and be readily useful. To enable packaging Go, we need to:
941 # -) Tweak the wrappers/environment to override GOROOT
942 # automatically based on the unpack location.
943 # -) Make sure the ELF dependency checking and wrapping logic
944 # below skips the Go toolchain executables and libraries.
945 # -) Make sure the packaging process maintains the relative
946 # timestamps of precompiled standard library packages.
947 # (see dev-lang/go ebuild for details).
948 if pkg == 'ex_go':
949 continue
950
Yunlian Jiang36f35242018-04-27 10:18:40 -0700951 # Use armv7a-cros-linux-gnueabi/compiler-rt for
952 # armv7a-cros-linux-gnueabihf/compiler-rt.
953 # Currently the armv7a-cros-linux-gnueabi is actually
954 # the same as armv7a-cros-linux-gnueabihf with different names.
955 # Because of that, for compiler-rt, it generates the same binary in
956 # the same location. To avoid the installation conflict, we do not
957 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
958 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
959 # toolchain.
960 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
961 atom = GetPortagePackage(target, pkg)
962 cat, pn = atom.split('/')
963 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700964 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
965 settings=portage.settings)
966 contents = dblink.getcontents()
967 if not contents:
968 if 'hf' in target:
969 new_target = 'armv7a-cros-linux-gnueabi'
970 else:
971 new_target = 'armv7a-cros-linux-gnueabihf'
972 atom = GetPortagePackage(new_target, pkg)
973 else:
974 atom = GetPortagePackage(target, pkg)
975
Mike Frysinger35247af2012-11-16 18:58:06 -0500976 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700977 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700978 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500979
Mike Frysinger35247af2012-11-16 18:58:06 -0500980 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
981 settings=portage.settings)
982 contents = dblink.getcontents()
983 for obj in contents:
984 ptype = contents[obj][0]
985 if not IsPathPackagable(ptype, obj):
986 continue
987
988 if ptype == 'obj':
989 # For native ELFs, we need to pull in their dependencies too.
990 if FileIsCrosSdkElf(obj):
991 elfs.add(obj)
992 paths.add(obj)
993
994 return paths, elfs
995
996
997def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500998 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500999 """Link in all packable files and their runtime dependencies
1000
1001 This also wraps up executable ELFs with helper scripts.
1002
1003 Args:
1004 output_dir: The output directory to store files
1005 paths: All the files to include
1006 elfs: All the files which are ELFs (a subset of |paths|)
1007 ldpaths: A dict of static ldpath information
1008 path_rewrite_func: User callback to rewrite paths in output_dir
1009 root: The root path to pull all packages/files from
1010 """
1011 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001012 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001013 for path in paths:
1014 new_path = path_rewrite_func(path)
1015 dst = output_dir + new_path
1016 osutils.SafeMakedirs(os.path.dirname(dst))
1017
1018 # Is this a symlink which we have to rewrite or wrap?
1019 # Delay wrap check until after we have created all paths.
1020 src = root + path
1021 if os.path.islink(src):
1022 tgt = os.readlink(src)
1023 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001024 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001025
1026 # Rewrite absolute links to relative and then generate the symlink
1027 # ourselves. All other symlinks can be hardlinked below.
1028 if tgt[0] == '/':
1029 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1030 os.symlink(tgt, dst)
1031 continue
1032
1033 os.link(src, dst)
1034
Mike Frysinger35247af2012-11-16 18:58:06 -05001035 # Locate all the dependencies for all the ELFs. Stick them all in the
1036 # top level "lib" dir to make the wrapper simpler. This exact path does
1037 # not matter since we execute ldso directly, and we tell the ldso the
1038 # exact path to search for its libraries.
1039 libdir = os.path.join(output_dir, 'lib')
1040 osutils.SafeMakedirs(libdir)
1041 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001042 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001043 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001044 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -05001045 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -07001046 # Do not create wrapper for libc. crbug.com/766827
1047 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -05001048 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -04001049 interp = os.path.join('/lib', os.path.basename(interp))
1050 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1051 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -07001052 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -05001053
Mike Frysinger221bd822017-09-29 02:51:47 -04001054 # Wrap any symlinks to the wrapper.
1055 if elf in sym_paths:
1056 link = sym_paths[elf]
1057 GeneratePathWrapper(output_dir, link, elf)
1058
Mike Frysinger0bdbc102019-06-13 15:27:29 -04001059 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001060 if lib in donelibs:
1061 continue
1062
1063 src = path = lib_data['path']
1064 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001065 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001066 continue
1067 donelibs.add(lib)
1068
1069 # Needed libs are the SONAME, but that is usually a symlink, not a
1070 # real file. So link in the target rather than the symlink itself.
1071 # We have to walk all the possible symlinks (SONAME could point to a
1072 # symlink which points to a symlink), and we have to handle absolute
1073 # ourselves (since we have a "root" argument).
1074 dst = os.path.join(libdir, os.path.basename(path))
1075 src = ReadlinkRoot(src, root)
1076
1077 os.link(root + src, dst)
1078
1079
1080def _EnvdGetVar(envd, var):
1081 """Given a Gentoo env.d file, extract a var from it
1082
1083 Args:
1084 envd: The env.d file to load (may be a glob path)
1085 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001086
Mike Frysinger35247af2012-11-16 18:58:06 -05001087 Returns:
1088 The value of |var|
1089 """
1090 envds = glob.glob(envd)
1091 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1092 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001093 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001094
1095
1096def _ProcessBinutilsConfig(target, output_dir):
1097 """Do what binutils-config would have done"""
1098 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001099
1100 # Locate the bin dir holding the gold linker.
1101 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1102 target, 'binutils-bin')
1103 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001104 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001105 if not srcpath:
1106 # Maybe this target doesn't support gold.
1107 globpath = os.path.join(binutils_bin_path, '*')
1108 srcpath = glob.glob(globpath)
1109 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1110 % globpath)
1111 srcpath = srcpath[0]
1112 ld_path = os.path.join(srcpath, 'ld')
1113 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1114 ld_path = os.path.join(srcpath, 'ld.bfd')
1115 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1116 ld_path = os.path.join(srcpath, 'ld.gold')
1117 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1118 % ld_path)
1119
1120 # Nope, no gold support to be found.
1121 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001122 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001123 else:
1124 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001125 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001126
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001127 # Package the binutils-bin directory without the '-gold' suffix
1128 # if gold is not enabled as the default linker for this target.
1129 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1130 if not gold_supported:
1131 srcpath = srcpath[:-len('-gold')]
1132 ld_path = os.path.join(srcpath, 'ld')
1133 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1134
Mike Frysinger78b7a812014-11-26 19:45:23 -05001135 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001136 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1137 for prog in os.listdir(output_dir + srcpath):
1138 # Skip binaries already wrapped.
1139 if not prog.endswith('.real'):
1140 GeneratePathWrapper(output_dir, binpath + prog,
1141 os.path.join(srcpath, prog))
1142 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1143 os.path.join(srcpath, prog))
1144
David James27ac4ae2012-12-03 23:16:15 -08001145 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001146 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1147 if gold_supported:
1148 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001149 else:
1150 # If gold is not enabled as the default linker and 2 env.d
1151 # files exist, pick the one without the '-gold' suffix.
1152 envds = sorted(glob.glob(envd))
1153 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1154 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001155 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1156 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1157 output_dir + libpath)
1158
1159
1160def _ProcessGccConfig(target, output_dir):
1161 """Do what gcc-config would have done"""
1162 binpath = '/bin'
1163 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1164 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1165 for prog in os.listdir(output_dir + srcpath):
1166 # Skip binaries already wrapped.
1167 if (not prog.endswith('.real') and
1168 not prog.endswith('.elf') and
1169 prog.startswith(target)):
1170 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1171 os.path.join(srcpath, prog))
1172 return srcpath
1173
1174
Frank Henigman179ec7c2015-02-06 03:01:09 -05001175def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1176 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001177 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001178
Tobias Boschddd16492019-08-14 09:29:54 -07001179 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001180 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001181 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1182 # Can't update the wrapper in place to not affect the chroot,
1183 # but only the extracted toolchain.
1184 os.unlink(sysroot_wrapper)
1185 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001186 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001187
1188
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001189def _CreateMainLibDir(target, output_dir):
1190 """Create some lib dirs so that compiler can get the right Gcc paths"""
1191 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1192 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1193
1194
Mike Frysinger35247af2012-11-16 18:58:06 -05001195def _ProcessDistroCleanups(target, output_dir):
1196 """Clean up the tree and remove all distro-specific requirements
1197
1198 Args:
1199 target: The toolchain target name
1200 output_dir: The output directory to clean up
1201 """
1202 _ProcessBinutilsConfig(target, output_dir)
1203 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001204 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001205 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001206
1207 osutils.RmDir(os.path.join(output_dir, 'etc'))
1208
1209
1210def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1211 """Setup a tree from the packages for the specified target
1212
1213 This populates a path with all the files from toolchain packages so that
1214 a tarball can easily be generated from the result.
1215
1216 Args:
1217 target: The target to create a packagable root from
1218 output_dir: The output directory to place all the files
1219 ldpaths: A dict of static ldpath information
1220 root: The root path to pull all packages/files from
1221 """
1222 # Find all the files owned by the packages for this target.
1223 paths, elfs = _GetFilesForTarget(target, root=root)
1224
1225 # Link in all the package's files, any ELF dependencies, and wrap any
1226 # executable ELFs with helper scripts.
1227 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001228 """Move /usr/bin to /bin so people can just use that toplevel dir
1229
1230 Note we do not apply this to clang - there is correlation between clang's
1231 search path for libraries / inclusion and its installation path.
1232 """
1233 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1234 return path[4:]
1235 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001236 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1237 path_rewrite_func=MoveUsrBinToBin, root=root)
1238
1239 # The packages, when part of the normal distro, have helper scripts
1240 # that setup paths and such. Since we are making this standalone, we
1241 # need to preprocess all that ourselves.
1242 _ProcessDistroCleanups(target, output_dir)
1243
1244
1245def CreatePackages(targets_wanted, output_dir, root='/'):
1246 """Create redistributable cross-compiler packages for the specified targets
1247
1248 This creates toolchain packages that should be usable in conjunction with
1249 a downloaded sysroot (created elsewhere).
1250
1251 Tarballs (one per target) will be created in $PWD.
1252
1253 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001254 targets_wanted: The targets to package up.
1255 output_dir: The directory to put the packages in.
1256 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001257 """
Ralph Nathan03047282015-03-23 11:09:32 -07001258 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001259 osutils.SafeMakedirs(output_dir)
1260 ldpaths = lddtree.LoadLdpaths(root)
1261 targets = ExpandTargets(targets_wanted)
1262
Mike Frysinger221bd822017-09-29 02:51:47 -04001263 with osutils.TempDir(prefix='create-packages') as tempdir:
1264 logging.debug('Using tempdir: %s', tempdir)
1265
Mike Frysinger35247af2012-11-16 18:58:06 -05001266 # We have to split the root generation from the compression stages. This is
1267 # because we hardlink in all the files (to avoid overhead of reading/writing
1268 # the copies multiple times). But tar gets angry if a file's hardlink count
1269 # changes from when it starts reading a file to when it finishes.
1270 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1271 for target in targets:
1272 output_target_dir = os.path.join(tempdir, target)
1273 queue.put([target, output_target_dir, ldpaths, root])
1274
1275 # Build the tarball.
1276 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1277 for target in targets:
1278 tar_file = os.path.join(output_dir, target + '.tar.xz')
1279 queue.put([tar_file, os.path.join(tempdir, target)])
1280
1281
Mike Frysinger07534cf2017-09-12 17:40:21 -04001282def GetParser():
1283 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001284 parser = commandline.ArgumentParser(description=__doc__)
1285 parser.add_argument('-u', '--nousepkg',
1286 action='store_false', dest='usepkg', default=True,
1287 help='Use prebuilt packages if possible')
1288 parser.add_argument('-d', '--deleteold',
1289 action='store_true', dest='deleteold', default=False,
1290 help='Unmerge deprecated packages')
1291 parser.add_argument('-t', '--targets',
1292 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001293 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001294 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001295 "allowed. Defaults to 'sdk'.")
1296 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1297 help='Comma separated list of boards whose toolchains we '
1298 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001299 parser.add_argument('--hostonly',
1300 dest='hostonly', default=False, action='store_true',
1301 help='Only setup the host toolchain. '
1302 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001303 parser.add_argument('--show-board-cfg', '--show-cfg',
1304 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001305 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001306 parser.add_argument('--show-packages', default=None,
1307 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001308 parser.add_argument('--create-packages',
1309 action='store_true', default=False,
1310 help='Build redistributable packages')
1311 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1312 help='Output directory')
1313 parser.add_argument('--reconfig', default=False, action='store_true',
1314 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001315 parser.add_argument('--sysroot', type='path',
1316 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001317 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001318
Mike Frysinger07534cf2017-09-12 17:40:21 -04001319
1320def main(argv):
1321 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001322 options = parser.parse_args(argv)
1323 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001324
Mike Frysinger35247af2012-11-16 18:58:06 -05001325 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001326 conflicting_options = (
1327 options.cfg_name,
1328 options.show_packages,
1329 options.create_packages,
1330 )
1331 if sum(bool(x) for x in conflicting_options) > 1:
1332 parser.error('conflicting options: create-packages & show-packages & '
1333 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001334
Gilad Arnold8195b532015-04-07 10:56:30 +03001335 targets_wanted = set(options.targets.split(','))
1336 boards_wanted = (set(options.include_boards.split(','))
1337 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001338
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001339 if options.cfg_name:
1340 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001341 elif options.show_packages is not None:
1342 cros_build_lib.AssertInsideChroot()
1343 target = options.show_packages
1344 Crossdev.Load(False)
1345 for package in GetTargetPackages(target):
1346 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001347 elif options.create_packages:
1348 cros_build_lib.AssertInsideChroot()
1349 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001350 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001351 else:
1352 cros_build_lib.AssertInsideChroot()
1353 # This has to be always run as root.
1354 if os.geteuid() != 0:
1355 cros_build_lib.Die('this script must be run as root')
1356
1357 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001358 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001359 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001360 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001361 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001362 Crossdev.Save()
1363
1364 return 0