blob: dad8f5f015203e90259976497b2693f293dd4119 [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 Frysinger383367e2014-09-16 15:06:17 -04007from __future__ import print_function
8
Mike Frysinger3ed47722017-08-08 14:59:08 -04009import errno
Mike Frysinger35247af2012-11-16 18:58:06 -050010import glob
Mike Frysinger3ed47722017-08-08 14:59:08 -040011import hashlib
Mike Frysinger7ccee992012-06-01 21:27:59 -040012import json
Zdenek Behan508dcce2011-12-05 15:39:32 +010013import os
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -070014import re
Zdenek Behan508dcce2011-12-05 15:39:32 +010015
Aviv Keshetb7519e12016-10-04 00:50:00 -070016from chromite.lib import constants
Mike Frysinger506e75f2012-12-17 14:21:13 -050017from chromite.lib import commandline
Brian Harring503f3ab2012-03-09 21:39:41 -080018from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070019from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070020from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050021from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080022from chromite.lib import toolchain
Mike Frysinger35247af2012-11-16 18:58:06 -050023
24# Needs to be after chromite imports.
25import lddtree
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.
Don Garrett25f309a2014-03-19 14:02:12 -070031 # pylint: disable=F0401
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
38CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070039ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010040STABLE_OVERLAY = '/usr/local/portage/stable'
41CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010042
43
Mike Frysinger66bfde52017-09-12 16:42:57 -040044# The exact list of host toolchain packages we care about. These are the
45# packages that bots/devs install only from binpkgs and rely on the SDK bot
46# (chromiumos-sdk) to validate+uprev.
47#
Mike Frysinger66bfde52017-09-12 16:42:57 -040048# We don't use crossdev to manage the host toolchain for us, especially since
49# we diverge significantly now (with llvm/clang/etc...), and we don't need or
50# want crossdev managing /etc/portage config files for the sdk
51HOST_PACKAGES = (
52 'dev-lang/go',
53 'sys-devel/binutils',
54 'sys-devel/clang',
55 'sys-devel/gcc',
56 'sys-devel/llvm',
57 'sys-kernel/linux-headers',
58 'sys-libs/glibc',
59 'sys-libs/libcxx',
60 'sys-libs/libcxxabi',
61)
62
Rahul Chaudhry4b803052015-05-13 15:25:56 -070063# Enable the Go compiler for these targets.
64TARGET_GO_ENABLED = (
65 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070066 'armv7a-cros-linux-gnueabi',
67)
68CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
69
Manoj Gupta1b5642e2017-03-08 16:44:12 -080070# Enable llvm's compiler-rt for these targets.
71TARGET_COMPILER_RT_ENABLED = (
72 'armv7a-cros-linux-gnueabi',
Manoj Gupta946abb42017-04-12 14:27:19 -070073 'aarch64-cros-linux-gnu',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080074)
75CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
76
Manoj Gupta946abb42017-04-12 14:27:19 -070077TARGET_LLVM_PKGS_ENABLED = (
78 'armv7a-cros-linux-gnueabi',
79 'aarch64-cros-linux-gnu',
80 'x86_64-cros-linux-gnu',
81)
82
83LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -070084 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
85 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -070086}
87
Zdenek Behan508dcce2011-12-05 15:39:32 +010088# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
89CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050090 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -040091 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -080092 'armv7a-cros-linux-gnueabi': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -050093 'i686-pc-linux-gnu' : '-gold',
94 'x86_64-cros-linux-gnu' : '-gold',
95 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010096}
Zdenek Behan508dcce2011-12-05 15:39:32 +010097
98
David James66a09c42012-11-05 13:31:38 -080099class Crossdev(object):
100 """Class for interacting with crossdev and caching its output."""
101
102 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
103 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800104 # Packages that needs separate handling, in addition to what we have from
105 # crossdev.
106 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700107 'clang': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800108 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700109 'libcxxabi': 'sys-libs',
110 'libcxx': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800111 }
David James66a09c42012-11-05 13:31:38 -0800112
113 @classmethod
114 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400115 """Load crossdev cache from disk.
116
117 We invalidate the cache when crossdev updates or this script changes.
118 """
David James90239b92012-11-05 15:31:34 -0800119 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400120 # If we run the compiled/cached .pyc file, we'll read/hash that when we
121 # really always want to track the source .py file.
122 script = os.path.abspath(__file__)
123 if script.endswith('.pyc'):
124 script = script[:-1]
125 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
126
127 cls._CACHE = {
128 'crossdev_version': crossdev_version,
129 'setup_toolchains_hash': setup_toolchains_hash,
130 }
131
132 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
133 if reconfig:
134 logging.debug('cache: forcing regen due to reconfig')
135 return
136
137 try:
138 file_data = osutils.ReadFile(cls._CACHE_FILE)
139 except IOError as e:
140 if e.errno != errno.ENOENT:
141 logging.warning('cache: reading failed: %s', e)
142 osutils.SafeUnlink(cls._CACHE_FILE)
143 return
144
145 try:
146 data = json.loads(file_data)
147 except ValueError as e:
148 logging.warning('cache: ignoring invalid content: %s', e)
149 return
150
151 if crossdev_version != data.get('crossdev_version'):
152 logging.debug('cache: rebuilding after crossdev upgrade')
153 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
154 logging.debug('cache: rebuilding after cros_setup_toolchains change')
155 else:
156 logging.debug('cache: content is up-to-date!')
157 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800158
159 @classmethod
160 def Save(cls):
161 """Store crossdev cache on disk."""
162 # Save the cache from the successful run.
163 with open(cls._CACHE_FILE, 'w') as f:
164 json.dump(cls._CACHE, f)
165
166 @classmethod
167 def GetConfig(cls, target):
168 """Returns a map of crossdev provided variables about a tuple."""
169 CACHE_ATTR = '_target_tuple_map'
170
171 val = cls._CACHE.setdefault(CACHE_ATTR, {})
172 if not target in val:
David James66a09c42012-11-05 13:31:38 -0800173 if target == 'host':
Mike Frysinger66bfde52017-09-12 16:42:57 -0400174 conf = {
175 'crosspkgs': [],
176 'target': toolchain.GetHostTuple(),
177 }
178 manual_pkgs = dict((pkg, cat) for cat, pkg in
179 [x.split('/') for x in HOST_PACKAGES])
180 else:
181 # Build the crossdev command.
182 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
183 if target in TARGET_COMPILER_RT_ENABLED:
184 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
185 if target in TARGET_GO_ENABLED:
186 cmd.extend(CROSSDEV_GO_ARGS)
187 if target in TARGET_LLVM_PKGS_ENABLED:
188 for pkg in LLVM_PKGS_TABLE:
189 cmd.extend(LLVM_PKGS_TABLE[pkg])
190 cmd.extend(['-t', target])
191 # Catch output of crossdev.
192 out = cros_build_lib.RunCommand(
193 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
194 # List of tuples split at the first '=', converted into dict.
195 conf = dict((k, cros_build_lib.ShellUnquote(v))
196 for k, v in (x.split('=', 1) for x in out))
197 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800198
Mike Frysinger66bfde52017-09-12 16:42:57 -0400199 manual_pkgs = cls.MANUAL_PKGS
200
201 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400202 conf[pkg + '_pn'] = pkg
203 conf[pkg + '_category'] = cat
204 if pkg not in conf['crosspkgs']:
205 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800206
207 val[target] = conf
208
David James66a09c42012-11-05 13:31:38 -0800209 return val[target]
210
211 @classmethod
212 def UpdateTargets(cls, targets, usepkg, config_only=False):
213 """Calls crossdev to initialize a cross target.
214
215 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700216 targets: The list of targets to initialize using crossdev.
217 usepkg: Copies the commandline opts.
218 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800219 """
220 configured_targets = cls._CACHE.setdefault('configured_targets', [])
221
222 cmdbase = ['crossdev', '--show-fail-log']
223 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
224 # Pick stable by default, and override as necessary.
225 cmdbase.extend(['-P', '--oneshot'])
226 if usepkg:
227 cmdbase.extend(['-P', '--getbinpkg',
228 '-P', '--usepkgonly',
229 '--without-headers'])
230
Christopher Wileyb22c0712015-06-02 10:37:03 -0700231 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800232 cmdbase.extend(['--overlays', overlays])
233 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
234
235 for target in targets:
236 if config_only and target in configured_targets:
237 continue
238
239 cmd = cmdbase + ['-t', target]
240
241 for pkg in GetTargetPackages(target):
242 if pkg == 'gdb':
243 # Gdb does not have selectable versions.
244 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700245 elif pkg == 'ex_compiler-rt':
246 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700247 elif pkg == 'ex_go':
248 # Go does not have selectable versions.
249 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700250 elif pkg in LLVM_PKGS_TABLE:
251 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800252 elif pkg in cls.MANUAL_PKGS:
253 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700254 else:
255 # The first of the desired versions is the "primary" one.
256 version = GetDesiredPackageVersions(target, pkg)[0]
257 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800258
259 cmd.extend(targets[target]['crossdev'].split())
260 if config_only:
261 # In this case we want to just quietly reinit
262 cmd.append('--init-target')
263 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
264 else:
265 cros_build_lib.RunCommand(cmd)
266
267 configured_targets.append(target)
268
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100269
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100270def GetTargetPackages(target):
271 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800272 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100273 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800274 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100275
276
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100277# Portage helper functions:
278def GetPortagePackage(target, package):
279 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800280 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100281 # Portage category:
Han Shene23782f2016-02-18 12:20:00 -0800282 if target == 'host' or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100283 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100284 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100285 category = conf['category']
286 # Portage package:
287 pn = conf[package + '_pn']
288 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500289 assert category
290 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100291 return '%s/%s' % (category, pn)
292
293
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700294def PortageTrees(root):
295 """Return the portage trees for a given root."""
296 if root == '/':
297 return portage.db['/']
298 # The portage logic requires the path always end in a slash.
299 root = root.rstrip('/') + '/'
300 return portage.create_trees(target_root=root, config_root=root)[root]
301
302
303def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100304 """Extracts the list of current versions of a target, package pair.
305
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500306 Args:
307 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700308 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100309
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500310 Returns:
311 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100312 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100313 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500314 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700315 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100316 version = portage.versions.cpv_getversion(pkg)
317 versions.append(version)
318 return versions
319
320
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700321def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100322 """Extracts the current stable version for a given package.
323
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500324 Args:
325 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
326 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700327 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100328
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500329 Returns:
330 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100331 """
David James90239b92012-11-05 15:31:34 -0800332 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500333 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700334 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800335 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100336
337
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700338def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100339 """Resolves keywords in a given version list for a particular package.
340
341 Resolving means replacing PACKAGE_STABLE with the actual number.
342
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500343 Args:
344 target: The target to operate on (e.g. i686-pc-linux-gnu)
345 package: The target/package to operate on (e.g. gcc)
346 versions: List of versions to resolve
347 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700348 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100349
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500350 Returns:
351 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100352 """
353 resolved = []
David James90239b92012-11-05 15:31:34 -0800354 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700355 if not installed:
356 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100357 for version in versions:
358 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700359 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400360 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100361 resolved.append(version)
362 return resolved
363
364
365def GetDesiredPackageVersions(target, package):
366 """Produces the list of desired versions for each target, package pair.
367
368 The first version in the list is implicitly treated as primary, ie.
369 the version that will be initialized by crossdev and selected.
370
371 If the version is PACKAGE_STABLE, it really means the current version which
372 is emerged by using the package atom with no particular version key.
373 Since crossdev unmasks all packages by default, this will actually
374 mean 'unstable' in most cases.
375
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500376 Args:
377 target: The target to operate on (e.g. i686-pc-linux-gnu)
378 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100379
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500380 Returns:
381 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100382 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400383 if package in GetTargetPackages(target):
384 return [PACKAGE_STABLE]
385 else:
386 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100387
388
389def TargetIsInitialized(target):
390 """Verifies if the given list of targets has been correctly initialized.
391
392 This determines whether we have to call crossdev while emerging
393 toolchain packages or can do it using emerge. Emerge is naturally
394 preferred, because all packages can be updated in a single pass.
395
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500396 Args:
397 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100398
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500399 Returns:
400 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100401 """
402 # Check if packages for the given target all have a proper version.
403 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100404 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800405 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100406 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400407 if not (GetStablePackageVersion(atom, True) and
408 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100409 return False
410 return True
411 except cros_build_lib.RunCommandError:
412 # Fails - The target has likely never been initialized before.
413 return False
414
415
416def RemovePackageMask(target):
417 """Removes a package.mask file for the given platform.
418
419 The pre-existing package.mask files can mess with the keywords.
420
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500421 Args:
422 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100423 """
424 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700425 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100426
427
Zdenek Behan508dcce2011-12-05 15:39:32 +0100428# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700429def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500430 """Rebuild libtool as needed
431
432 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
433 gcc, libtool will break. We can't use binary packages either as those will
434 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700435
436 Args:
437 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500438 """
439 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700440 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500441 for line in f:
442 # Look for a line like:
443 # sys_lib_search_path_spec="..."
444 # It'll be a list of paths and gcc will be one of them.
445 if line.startswith('sys_lib_search_path_spec='):
446 line = line.rstrip()
447 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400448 root_path = os.path.join(root, path.lstrip(os.path.sep))
449 logging.debug('Libtool: checking %s', root_path)
450 if not os.path.exists(root_path):
451 logging.info('Rebuilding libtool after gcc upgrade')
452 logging.info(' %s', line)
453 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500454 needs_update = True
455 break
456
457 if needs_update:
458 break
459
460 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700461 cmd = [EMERGE_CMD, '--oneshot']
462 if root != '/':
463 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
464 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500465 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400466 else:
467 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500468
469
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700470def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100471 """Determines which packages need update/unmerge and defers to portage.
472
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500473 Args:
474 targets: The list of targets to update
475 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700476 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100477 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100478 # For each target, we do two things. Figure out the list of updates,
479 # and figure out the appropriate keywords/masks. Crossdev will initialize
480 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400481 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800482 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100483 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400484 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100485 # Record the highest needed version for each target, for masking purposes.
486 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100487 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100488 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400489 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100490 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700491 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100492 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200493 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100494 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400495 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100496
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400497 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100498 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400499 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800500 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100501
Mike Frysinger3bba5032016-09-20 14:15:04 -0400502 logging.info('Updating packages:')
503 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100504
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100505 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100506 if usepkg:
507 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700508 if root != '/':
509 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510
511 cmd.extend(packages)
512 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800513 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100514
515
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700516def CleanTargets(targets, root='/'):
517 """Unmerges old packages that are assumed unnecessary.
518
519 Args:
520 targets: The list of targets to clean up.
521 root: The install root in which we want packages cleaned up.
522 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100523 unmergemap = {}
524 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400525 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100526 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400527 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100528 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700529 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100530 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700531 # NOTE: This refers to installed packages (vartree) rather than the
532 # Portage version (porttree and/or bintree) when determining the current
533 # version. While this isn't the most accurate thing to do, it is probably
534 # a good simple compromise, which should have the desired result of
535 # uninstalling everything but the latest installed version. In
536 # particular, using the bintree (--usebinpkg) requires a non-trivial
537 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200538 desired_num = VersionListToNumeric(target, package, desired, True)
539 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400540 logging.warning('Error detecting stable version for %s, '
541 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200542 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543 unmergemap[pkg] = set(current).difference(desired_num)
544
545 # Cleaning doesn't care about consistency and rebuilding package.* files.
546 packages = []
547 for pkg, vers in unmergemap.iteritems():
548 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
549
550 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400551 logging.info('Cleaning packages:')
552 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100553 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700554 if root != '/':
555 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100556 cmd.extend(packages)
557 cros_build_lib.RunCommand(cmd)
558 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400559 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100560
561
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700562def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100563 """Runs gcc-config and binutils-config to select the desired.
564
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500565 Args:
566 targets: The targets to select
567 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700568 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100569 """
570 for package in ['gcc', 'binutils']:
571 for target in targets:
572 # Pick the first version in the numbered list as the selected one.
573 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700574 desired_num = VersionListToNumeric(target, package, desired, True,
575 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100576 desired = desired_num[0]
577 # *-config does not play revisions, strip them, keep just PV.
578 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
579
580 if target == 'host':
581 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800582 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100583
584 # And finally, attach target to it.
585 desired = '%s-%s' % (target, desired)
586
587 # Target specific hacks
588 if package in suffixes:
589 if target in suffixes[package]:
590 desired += suffixes[package][target]
591
David James7ec5efc2012-11-06 09:39:49 -0800592 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700593 if root != '/':
594 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800595 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500596 result = cros_build_lib.RunCommand(
597 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
598 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700599
600 # Do not reconfig when the current is live or nothing needs to be done.
601 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100602 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500603 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700604 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100605
606
Mike Frysinger35247af2012-11-16 18:58:06 -0500607def ExpandTargets(targets_wanted):
608 """Expand any possible toolchain aliases into full targets
609
610 This will expand 'all' and 'sdk' into the respective toolchain tuples.
611
612 Args:
613 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500614
Mike Frysinger35247af2012-11-16 18:58:06 -0500615 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300616 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500617 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500618 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700619 if targets_wanted == set(['boards']):
620 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300621 return {}
622
623 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500624 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300625 return all_targets
626 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500627 # Filter out all the non-sdk toolchains as we don't want to mess
628 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300629 return toolchain.FilterToolchains(all_targets, 'sdk', True)
630
631 # Verify user input.
632 nonexistent = targets_wanted.difference(all_targets)
633 if nonexistent:
634 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
635 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500636
637
David Jamesf8c672f2012-11-06 13:38:11 -0800638def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700639 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100640 """Performs all steps to create a synchronized toolchain enviroment.
641
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500642 Args:
643 usepkg: Use prebuilt packages
644 deleteold: Unmerge deprecated packages
645 hostonly: Only setup the host toolchain
646 reconfig: Reload crossdev config and reselect toolchains
647 targets_wanted: All the targets to update
648 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700649 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100650 """
David Jamesf8c672f2012-11-06 13:38:11 -0800651 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100652 if not hostonly:
653 # For hostonly, we can skip most of the below logic, much of which won't
654 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500655 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400656
Don Garrettc0c74002015-10-09 12:58:19 -0700657 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300658 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400659 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800660 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100661
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100662 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400663 for target in targets:
664 if TargetIsInitialized(target):
665 reconfig_targets[target] = targets[target]
666 else:
667 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100668 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400669 logging.info('The following targets need to be re-initialized:')
670 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800671 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200672 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800673 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100674
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100675 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400676 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100677
678 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700679 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
680 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800681
682 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700683 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100684
Mike Frysingerc880a962013-11-08 13:59:06 -0500685 # Now that we've cleared out old versions, see if we need to rebuild
686 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700687 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500688
Zdenek Behan508dcce2011-12-05 15:39:32 +0100689
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700690def ShowConfig(name):
691 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500692
693 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700694 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500695 """
Don Garrettc0c74002015-10-09 12:58:19 -0700696 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500697 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400698 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400699 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800700 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400701 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500702
703
Mike Frysinger35247af2012-11-16 18:58:06 -0500704def GeneratePathWrapper(root, wrappath, path):
705 """Generate a shell script to execute another shell script
706
707 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
708 argv[0] won't be pointing to the correct path, generate a shell script that
709 just executes another program with its full path.
710
711 Args:
712 root: The root tree to generate scripts inside of
713 wrappath: The full path (inside |root|) to create the wrapper
714 path: The target program which this wrapper will execute
715 """
716 replacements = {
717 'path': path,
718 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
719 }
720 wrapper = """#!/bin/sh
721base=$(realpath "$0")
722basedir=${base%%/*}
723exec "${basedir}/%(relroot)s%(path)s" "$@"
724""" % replacements
725 root_wrapper = root + wrappath
726 if os.path.islink(root_wrapper):
727 os.unlink(root_wrapper)
728 else:
729 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
730 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400731 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500732
733
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700734def FixClangXXWrapper(root, path):
735 """Fix wrapper shell scripts and symlinks for invoking clang++
736
737 In a typical installation, clang++ symlinks to clang, which symlinks to the
738 elf executable. The executable distinguishes between clang and clang++ based
739 on argv[0].
740
741 When invoked through the LdsoWrapper, argv[0] always contains the path to the
742 executable elf file, making clang/clang++ invocations indistinguishable.
743
744 This function detects if the elf executable being wrapped is clang-X.Y, and
745 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
746
747 The calling sequence now becomes:
748 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
749 the Ldsowrapper).
750 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
751 to the original clang-3.9 elf.
752 -) The difference this time is that inside the elf file execution, $0 is
753 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
754
755 Args:
756 root: The root tree to generate scripts / symlinks inside of
757 path: The target elf for which LdsoWrapper was created
758 """
759 if re.match(r'/usr/bin/clang-\d+\.\d+$', path):
760 logging.info('fixing clang++ invocation for %s', path)
761 clangdir = os.path.dirname(root + path)
762 clang = os.path.basename(path)
763 clangxx = clang.replace('clang', 'clang++')
764
765 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
766 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
767
768 # Create a hardlink clang++-X.Y pointing to clang-X.Y
769 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
770
771 # Adjust the clang++ symlink to point to clang++-X.Y
772 os.unlink(os.path.join(clangdir, 'clang++'))
773 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
774
775
Mike Frysinger35247af2012-11-16 18:58:06 -0500776def FileIsCrosSdkElf(elf):
777 """Determine if |elf| is an ELF that we execute in the cros_sdk
778
779 We don't need this to be perfect, just quick. It makes sure the ELF
780 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
781
782 Args:
783 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500784
Mike Frysinger35247af2012-11-16 18:58:06 -0500785 Returns:
786 True if we think |elf| is a native ELF
787 """
788 with open(elf) as f:
789 data = f.read(20)
790 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
791 return (data[0:4] == '\x7fELF' and
792 data[4] == '\x02' and
793 data[5] == '\x01' and
794 data[18] == '\x3e')
795
796
797def IsPathPackagable(ptype, path):
798 """Should the specified file be included in a toolchain package?
799
800 We only need to handle files as we'll create dirs as we need them.
801
802 Further, trim files that won't be useful:
803 - non-english translations (.mo) since it'd require env vars
804 - debug files since these are for the host compiler itself
805 - info/man pages as they're big, and docs are online, and the
806 native docs should work fine for the most part (`man gcc`)
807
808 Args:
809 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
810 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500811
Mike Frysinger35247af2012-11-16 18:58:06 -0500812 Returns:
813 True if we want to include this path in the package
814 """
815 return not (ptype in ('dir',) or
816 path.startswith('/usr/lib/debug/') or
817 os.path.splitext(path)[1] == '.mo' or
818 ('/man/' in path or '/info/' in path))
819
820
821def ReadlinkRoot(path, root):
822 """Like os.readlink(), but relative to a |root|
823
824 Args:
825 path: The symlink to read
826 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500827
Mike Frysinger35247af2012-11-16 18:58:06 -0500828 Returns:
829 A fully resolved symlink path
830 """
831 while os.path.islink(root + path):
832 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
833 return path
834
835
836def _GetFilesForTarget(target, root='/'):
837 """Locate all the files to package for |target|
838
839 This does not cover ELF dependencies.
840
841 Args:
842 target: The toolchain target name
843 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500844
Mike Frysinger35247af2012-11-16 18:58:06 -0500845 Returns:
846 A tuple of a set of all packable paths, and a set of all paths which
847 are also native ELFs
848 """
849 paths = set()
850 elfs = set()
851
852 # Find all the files owned by the packages for this target.
853 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500854
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700855 # Skip Go compiler from redistributable packages.
856 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
857 # into it. Due to this, the toolchain cannot be unpacked anywhere
858 # else and be readily useful. To enable packaging Go, we need to:
859 # -) Tweak the wrappers/environment to override GOROOT
860 # automatically based on the unpack location.
861 # -) Make sure the ELF dependency checking and wrapping logic
862 # below skips the Go toolchain executables and libraries.
863 # -) Make sure the packaging process maintains the relative
864 # timestamps of precompiled standard library packages.
865 # (see dev-lang/go ebuild for details).
866 if pkg == 'ex_go':
867 continue
868
Mike Frysinger35247af2012-11-16 18:58:06 -0500869 atom = GetPortagePackage(target, pkg)
870 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700871 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700872 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500873
874 # pylint: disable=E1101
875 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
876 settings=portage.settings)
877 contents = dblink.getcontents()
878 for obj in contents:
879 ptype = contents[obj][0]
880 if not IsPathPackagable(ptype, obj):
881 continue
882
883 if ptype == 'obj':
884 # For native ELFs, we need to pull in their dependencies too.
885 if FileIsCrosSdkElf(obj):
886 elfs.add(obj)
887 paths.add(obj)
888
889 return paths, elfs
890
891
892def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500893 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500894 """Link in all packable files and their runtime dependencies
895
896 This also wraps up executable ELFs with helper scripts.
897
898 Args:
899 output_dir: The output directory to store files
900 paths: All the files to include
901 elfs: All the files which are ELFs (a subset of |paths|)
902 ldpaths: A dict of static ldpath information
903 path_rewrite_func: User callback to rewrite paths in output_dir
904 root: The root path to pull all packages/files from
905 """
906 # Link in all the files.
907 sym_paths = []
908 for path in paths:
909 new_path = path_rewrite_func(path)
910 dst = output_dir + new_path
911 osutils.SafeMakedirs(os.path.dirname(dst))
912
913 # Is this a symlink which we have to rewrite or wrap?
914 # Delay wrap check until after we have created all paths.
915 src = root + path
916 if os.path.islink(src):
917 tgt = os.readlink(src)
918 if os.path.sep in tgt:
919 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
920
921 # Rewrite absolute links to relative and then generate the symlink
922 # ourselves. All other symlinks can be hardlinked below.
923 if tgt[0] == '/':
924 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
925 os.symlink(tgt, dst)
926 continue
927
928 os.link(src, dst)
929
930 # Now see if any of the symlinks need to be wrapped.
931 for sym, tgt in sym_paths:
932 if tgt in elfs:
933 GeneratePathWrapper(output_dir, sym, tgt)
934
935 # Locate all the dependencies for all the ELFs. Stick them all in the
936 # top level "lib" dir to make the wrapper simpler. This exact path does
937 # not matter since we execute ldso directly, and we tell the ldso the
938 # exact path to search for its libraries.
939 libdir = os.path.join(output_dir, 'lib')
940 osutils.SafeMakedirs(libdir)
941 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -0400942 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -0500943 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400944 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500945 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -0700946 # Do not create wrapper for libc. crbug.com/766827
947 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -0500948 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400949 interp = os.path.join('/lib', os.path.basename(interp))
950 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
951 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700952 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -0500953
954 for lib, lib_data in e['libs'].iteritems():
955 if lib in donelibs:
956 continue
957
958 src = path = lib_data['path']
959 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -0700960 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -0500961 continue
962 donelibs.add(lib)
963
964 # Needed libs are the SONAME, but that is usually a symlink, not a
965 # real file. So link in the target rather than the symlink itself.
966 # We have to walk all the possible symlinks (SONAME could point to a
967 # symlink which points to a symlink), and we have to handle absolute
968 # ourselves (since we have a "root" argument).
969 dst = os.path.join(libdir, os.path.basename(path))
970 src = ReadlinkRoot(src, root)
971
972 os.link(root + src, dst)
973
974
975def _EnvdGetVar(envd, var):
976 """Given a Gentoo env.d file, extract a var from it
977
978 Args:
979 envd: The env.d file to load (may be a glob path)
980 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -0500981
Mike Frysinger35247af2012-11-16 18:58:06 -0500982 Returns:
983 The value of |var|
984 """
985 envds = glob.glob(envd)
986 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
987 envd = envds[0]
988 return cros_build_lib.LoadKeyValueFile(envd)[var]
989
990
991def _ProcessBinutilsConfig(target, output_dir):
992 """Do what binutils-config would have done"""
993 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500994
995 # Locate the bin dir holding the gold linker.
996 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
997 target, 'binutils-bin')
998 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -0500999 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001000 if not srcpath:
1001 # Maybe this target doesn't support gold.
1002 globpath = os.path.join(binutils_bin_path, '*')
1003 srcpath = glob.glob(globpath)
1004 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1005 % globpath)
1006 srcpath = srcpath[0]
1007 ld_path = os.path.join(srcpath, 'ld')
1008 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1009 ld_path = os.path.join(srcpath, 'ld.bfd')
1010 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1011 ld_path = os.path.join(srcpath, 'ld.gold')
1012 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1013 % ld_path)
1014
1015 # Nope, no gold support to be found.
1016 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001017 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001018 else:
1019 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001020 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001021
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001022 # Package the binutils-bin directory without the '-gold' suffix
1023 # if gold is not enabled as the default linker for this target.
1024 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1025 if not gold_supported:
1026 srcpath = srcpath[:-len('-gold')]
1027 ld_path = os.path.join(srcpath, 'ld')
1028 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1029
Mike Frysinger78b7a812014-11-26 19:45:23 -05001030 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001031 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1032 for prog in os.listdir(output_dir + srcpath):
1033 # Skip binaries already wrapped.
1034 if not prog.endswith('.real'):
1035 GeneratePathWrapper(output_dir, binpath + prog,
1036 os.path.join(srcpath, prog))
1037 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1038 os.path.join(srcpath, prog))
1039
David James27ac4ae2012-12-03 23:16:15 -08001040 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001041 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1042 if gold_supported:
1043 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001044 else:
1045 # If gold is not enabled as the default linker and 2 env.d
1046 # files exist, pick the one without the '-gold' suffix.
1047 envds = sorted(glob.glob(envd))
1048 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1049 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001050 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1051 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1052 output_dir + libpath)
1053
1054
1055def _ProcessGccConfig(target, output_dir):
1056 """Do what gcc-config would have done"""
1057 binpath = '/bin'
1058 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1059 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1060 for prog in os.listdir(output_dir + srcpath):
1061 # Skip binaries already wrapped.
1062 if (not prog.endswith('.real') and
1063 not prog.endswith('.elf') and
1064 prog.startswith(target)):
1065 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1066 os.path.join(srcpath, prog))
1067 return srcpath
1068
1069
Frank Henigman179ec7c2015-02-06 03:01:09 -05001070def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1071 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001072 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001073 for sysroot_wrapper in glob.glob(os.path.join(
1074 output_dir + srcpath, 'sysroot_wrapper*')):
1075 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
1076 for num in xrange(len(contents)):
1077 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001078 assert 'True' in contents[num]
1079 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001080 break
1081 # Can't update the wrapper in place since it's a hardlink to a file in /.
1082 os.unlink(sysroot_wrapper)
1083 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1084 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001085
1086
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001087def _CreateMainLibDir(target, output_dir):
1088 """Create some lib dirs so that compiler can get the right Gcc paths"""
1089 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1090 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1091
1092
Mike Frysinger35247af2012-11-16 18:58:06 -05001093def _ProcessDistroCleanups(target, output_dir):
1094 """Clean up the tree and remove all distro-specific requirements
1095
1096 Args:
1097 target: The toolchain target name
1098 output_dir: The output directory to clean up
1099 """
1100 _ProcessBinutilsConfig(target, output_dir)
1101 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001102 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001103 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001104
1105 osutils.RmDir(os.path.join(output_dir, 'etc'))
1106
1107
1108def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1109 """Setup a tree from the packages for the specified target
1110
1111 This populates a path with all the files from toolchain packages so that
1112 a tarball can easily be generated from the result.
1113
1114 Args:
1115 target: The target to create a packagable root from
1116 output_dir: The output directory to place all the files
1117 ldpaths: A dict of static ldpath information
1118 root: The root path to pull all packages/files from
1119 """
1120 # Find all the files owned by the packages for this target.
1121 paths, elfs = _GetFilesForTarget(target, root=root)
1122
1123 # Link in all the package's files, any ELF dependencies, and wrap any
1124 # executable ELFs with helper scripts.
1125 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001126 """Move /usr/bin to /bin so people can just use that toplevel dir
1127
1128 Note we do not apply this to clang - there is correlation between clang's
1129 search path for libraries / inclusion and its installation path.
1130 """
1131 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1132 return path[4:]
1133 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001134 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1135 path_rewrite_func=MoveUsrBinToBin, root=root)
1136
1137 # The packages, when part of the normal distro, have helper scripts
1138 # that setup paths and such. Since we are making this standalone, we
1139 # need to preprocess all that ourselves.
1140 _ProcessDistroCleanups(target, output_dir)
1141
1142
1143def CreatePackages(targets_wanted, output_dir, root='/'):
1144 """Create redistributable cross-compiler packages for the specified targets
1145
1146 This creates toolchain packages that should be usable in conjunction with
1147 a downloaded sysroot (created elsewhere).
1148
1149 Tarballs (one per target) will be created in $PWD.
1150
1151 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001152 targets_wanted: The targets to package up.
1153 output_dir: The directory to put the packages in.
1154 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001155 """
Ralph Nathan03047282015-03-23 11:09:32 -07001156 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001157 osutils.SafeMakedirs(output_dir)
1158 ldpaths = lddtree.LoadLdpaths(root)
1159 targets = ExpandTargets(targets_wanted)
1160
David James4bc13702013-03-26 08:08:04 -07001161 with osutils.TempDir() as tempdir:
Mike Frysinger35247af2012-11-16 18:58:06 -05001162 # We have to split the root generation from the compression stages. This is
1163 # because we hardlink in all the files (to avoid overhead of reading/writing
1164 # the copies multiple times). But tar gets angry if a file's hardlink count
1165 # changes from when it starts reading a file to when it finishes.
1166 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1167 for target in targets:
1168 output_target_dir = os.path.join(tempdir, target)
1169 queue.put([target, output_target_dir, ldpaths, root])
1170
1171 # Build the tarball.
1172 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1173 for target in targets:
1174 tar_file = os.path.join(output_dir, target + '.tar.xz')
1175 queue.put([tar_file, os.path.join(tempdir, target)])
1176
1177
Mike Frysinger07534cf2017-09-12 17:40:21 -04001178def GetParser():
1179 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001180 parser = commandline.ArgumentParser(description=__doc__)
1181 parser.add_argument('-u', '--nousepkg',
1182 action='store_false', dest='usepkg', default=True,
1183 help='Use prebuilt packages if possible')
1184 parser.add_argument('-d', '--deleteold',
1185 action='store_true', dest='deleteold', default=False,
1186 help='Unmerge deprecated packages')
1187 parser.add_argument('-t', '--targets',
1188 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001189 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001190 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001191 "allowed. Defaults to 'sdk'.")
1192 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1193 help='Comma separated list of boards whose toolchains we '
1194 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001195 parser.add_argument('--hostonly',
1196 dest='hostonly', default=False, action='store_true',
1197 help='Only setup the host toolchain. '
1198 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001199 parser.add_argument('--show-board-cfg', '--show-cfg',
1200 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001201 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001202 parser.add_argument('--show-packages', default=None,
1203 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001204 parser.add_argument('--create-packages',
1205 action='store_true', default=False,
1206 help='Build redistributable packages')
1207 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1208 help='Output directory')
1209 parser.add_argument('--reconfig', default=False, action='store_true',
1210 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001211 parser.add_argument('--sysroot', type='path',
1212 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001213 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001214
Mike Frysinger07534cf2017-09-12 17:40:21 -04001215
1216def main(argv):
1217 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001218 options = parser.parse_args(argv)
1219 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001220
Mike Frysinger35247af2012-11-16 18:58:06 -05001221 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001222 conflicting_options = (
1223 options.cfg_name,
1224 options.show_packages,
1225 options.create_packages,
1226 )
1227 if sum(bool(x) for x in conflicting_options) > 1:
1228 parser.error('conflicting options: create-packages & show-packages & '
1229 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001230
Gilad Arnold8195b532015-04-07 10:56:30 +03001231 targets_wanted = set(options.targets.split(','))
1232 boards_wanted = (set(options.include_boards.split(','))
1233 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001234
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001235 if options.cfg_name:
1236 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001237 elif options.show_packages is not None:
1238 cros_build_lib.AssertInsideChroot()
1239 target = options.show_packages
1240 Crossdev.Load(False)
1241 for package in GetTargetPackages(target):
1242 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001243 elif options.create_packages:
1244 cros_build_lib.AssertInsideChroot()
1245 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001246 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001247 else:
1248 cros_build_lib.AssertInsideChroot()
1249 # This has to be always run as root.
1250 if os.geteuid() != 0:
1251 cros_build_lib.Die('this script must be run as root')
1252
1253 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001254 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001255 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001256 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001257 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001258 Crossdev.Save()
1259
1260 return 0