blob: 64b8825c411b68a967d78870b121f4e8fc93e554 [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
Zdenek Behan508dcce2011-12-05 15:39:32 +010016
Aviv Keshetb7519e12016-10-04 00:50:00 -070017from chromite.lib import constants
Mike Frysinger506e75f2012-12-17 14:21:13 -050018from chromite.lib import commandline
Brian Harring503f3ab2012-03-09 21:39:41 -080019from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070020from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070021from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050022from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080023from chromite.lib import toolchain
Mike Frysinger35247af2012-11-16 18:58:06 -050024
25# Needs to be after chromite imports.
26import lddtree
Zdenek Behan508dcce2011-12-05 15:39:32 +010027
Mike Frysinger31596002012-12-03 23:54:24 -050028if cros_build_lib.IsInsideChroot():
29 # Only import portage after we've checked that we're inside the chroot.
30 # Outside may not have portage, in which case the above may not happen.
31 # We'll check in main() if the operation needs portage.
Don Garrett25f309a2014-03-19 14:02:12 -070032 # pylint: disable=F0401
Mike Frysinger31596002012-12-03 23:54:24 -050033 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010034
35
Matt Tennantf1e30972012-03-02 16:30:07 -080036EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010037PACKAGE_STABLE = '[stable]'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010038
39CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070040ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010041STABLE_OVERLAY = '/usr/local/portage/stable'
42CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010043
44
Mike Frysinger66bfde52017-09-12 16:42:57 -040045# The exact list of host toolchain packages we care about. These are the
46# packages that bots/devs install only from binpkgs and rely on the SDK bot
47# (chromiumos-sdk) to validate+uprev.
48#
Mike Frysinger66bfde52017-09-12 16:42:57 -040049# We don't use crossdev to manage the host toolchain for us, especially since
50# we diverge significantly now (with llvm/clang/etc...), and we don't need or
51# want crossdev managing /etc/portage config files for the sdk
52HOST_PACKAGES = (
53 'dev-lang/go',
54 'sys-devel/binutils',
55 'sys-devel/clang',
56 'sys-devel/gcc',
Yunlian Jiangf5721f32017-10-31 11:43:11 -070057 'sys-devel/lld',
Mike Frysinger66bfde52017-09-12 16:42:57 -040058 'sys-devel/llvm',
59 'sys-kernel/linux-headers',
60 'sys-libs/glibc',
61 'sys-libs/libcxx',
62 'sys-libs/libcxxabi',
63)
64
Mike Frysinger785b0c32017-09-13 01:35:59 -040065# These packages are also installed into the host SDK. However, they require
66# the cross-compilers to be installed first (because they need them to actually
67# build), so we have to delay their installation.
68HOST_POST_CROSS_PACKAGES = (
Mike Frysinger61a24392017-10-17 17:14:27 -040069 'virtual/target-sdk-post-cross',
Mike Frysinger785b0c32017-09-13 01:35:59 -040070)
71
72# New packages that we're in the process of adding to the SDK. Since the SDK
73# bot hasn't had a chance to run yet, there are no binary packages available,
74# so we have to list them here and wait. Once it completes, entries here can
75# be removed so they'll end up on bots & dev's systems.
76NEW_PACKAGES = (
Mike Frysinger785b0c32017-09-13 01:35:59 -040077)
78
Rahul Chaudhry4b803052015-05-13 15:25:56 -070079# Enable the Go compiler for these targets.
80TARGET_GO_ENABLED = (
81 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070082 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070083 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070084 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070085)
86CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
87
Manoj Gupta1b5642e2017-03-08 16:44:12 -080088# Enable llvm's compiler-rt for these targets.
89TARGET_COMPILER_RT_ENABLED = (
90 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070091 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070092 'aarch64-cros-linux-gnu',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080093)
94CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
95
Manoj Gupta946abb42017-04-12 14:27:19 -070096TARGET_LLVM_PKGS_ENABLED = (
97 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070098 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070099 'aarch64-cros-linux-gnu',
100 'x86_64-cros-linux-gnu',
101)
102
103LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700104 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
105 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700106}
107
Zdenek Behan508dcce2011-12-05 15:39:32 +0100108# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
109CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500110 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -0400111 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -0800112 'armv7a-cros-linux-gnueabi': '-gold',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700113 'armv7a-cros-linux-gnueabihf': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500114 'i686-pc-linux-gnu' : '-gold',
115 'x86_64-cros-linux-gnu' : '-gold',
116 },
Zdenek Behan508dcce2011-12-05 15:39:32 +0100117}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100118
119
David James66a09c42012-11-05 13:31:38 -0800120class Crossdev(object):
121 """Class for interacting with crossdev and caching its output."""
122
123 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
124 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800125 # Packages that needs separate handling, in addition to what we have from
126 # crossdev.
127 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700128 'clang': 'sys-devel',
Yunlian Jiang18ae9982017-11-03 09:15:31 -0700129 'lld': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800130 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700131 'libcxxabi': 'sys-libs',
132 'libcxx': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800133 }
David James66a09c42012-11-05 13:31:38 -0800134
135 @classmethod
136 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400137 """Load crossdev cache from disk.
138
139 We invalidate the cache when crossdev updates or this script changes.
140 """
David James90239b92012-11-05 15:31:34 -0800141 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400142 # If we run the compiled/cached .pyc file, we'll read/hash that when we
143 # really always want to track the source .py file.
144 script = os.path.abspath(__file__)
145 if script.endswith('.pyc'):
146 script = script[:-1]
147 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
148
149 cls._CACHE = {
150 'crossdev_version': crossdev_version,
151 'setup_toolchains_hash': setup_toolchains_hash,
152 }
153
154 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
155 if reconfig:
156 logging.debug('cache: forcing regen due to reconfig')
157 return
158
159 try:
160 file_data = osutils.ReadFile(cls._CACHE_FILE)
161 except IOError as e:
162 if e.errno != errno.ENOENT:
163 logging.warning('cache: reading failed: %s', e)
164 osutils.SafeUnlink(cls._CACHE_FILE)
165 return
166
167 try:
168 data = json.loads(file_data)
169 except ValueError as e:
170 logging.warning('cache: ignoring invalid content: %s', e)
171 return
172
173 if crossdev_version != data.get('crossdev_version'):
174 logging.debug('cache: rebuilding after crossdev upgrade')
175 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
176 logging.debug('cache: rebuilding after cros_setup_toolchains change')
177 else:
178 logging.debug('cache: content is up-to-date!')
179 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800180
181 @classmethod
182 def Save(cls):
183 """Store crossdev cache on disk."""
184 # Save the cache from the successful run.
185 with open(cls._CACHE_FILE, 'w') as f:
186 json.dump(cls._CACHE, f)
187
188 @classmethod
189 def GetConfig(cls, target):
190 """Returns a map of crossdev provided variables about a tuple."""
191 CACHE_ATTR = '_target_tuple_map'
192
193 val = cls._CACHE.setdefault(CACHE_ATTR, {})
194 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400195 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400196 conf = {
197 'crosspkgs': [],
198 'target': toolchain.GetHostTuple(),
199 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400200 if target == 'host':
201 packages_list = HOST_PACKAGES
202 else:
203 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400204 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400205 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400206 else:
207 # Build the crossdev command.
208 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
209 if target in TARGET_COMPILER_RT_ENABLED:
210 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
211 if target in TARGET_GO_ENABLED:
212 cmd.extend(CROSSDEV_GO_ARGS)
213 if target in TARGET_LLVM_PKGS_ENABLED:
214 for pkg in LLVM_PKGS_TABLE:
215 cmd.extend(LLVM_PKGS_TABLE[pkg])
216 cmd.extend(['-t', target])
217 # Catch output of crossdev.
218 out = cros_build_lib.RunCommand(
219 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
220 # List of tuples split at the first '=', converted into dict.
221 conf = dict((k, cros_build_lib.ShellUnquote(v))
222 for k, v in (x.split('=', 1) for x in out))
223 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800224
Mike Frysinger66bfde52017-09-12 16:42:57 -0400225 manual_pkgs = cls.MANUAL_PKGS
226
227 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400228 conf[pkg + '_pn'] = pkg
229 conf[pkg + '_category'] = cat
230 if pkg not in conf['crosspkgs']:
231 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800232
233 val[target] = conf
234
David James66a09c42012-11-05 13:31:38 -0800235 return val[target]
236
237 @classmethod
238 def UpdateTargets(cls, targets, usepkg, config_only=False):
239 """Calls crossdev to initialize a cross target.
240
241 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700242 targets: The list of targets to initialize using crossdev.
243 usepkg: Copies the commandline opts.
244 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800245 """
246 configured_targets = cls._CACHE.setdefault('configured_targets', [])
247
248 cmdbase = ['crossdev', '--show-fail-log']
249 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
250 # Pick stable by default, and override as necessary.
251 cmdbase.extend(['-P', '--oneshot'])
252 if usepkg:
253 cmdbase.extend(['-P', '--getbinpkg',
254 '-P', '--usepkgonly',
255 '--without-headers'])
256
Christopher Wileyb22c0712015-06-02 10:37:03 -0700257 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800258 cmdbase.extend(['--overlays', overlays])
259 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
260
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700261 # Build target by the alphabetical order to make sure
262 # armv7a-cros-linux-gnueabihf builds after armv7a-cros-linux-gnueabi
263 # because some dependency issue. This can be reverted once we
264 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
265 for target in sorted(targets):
David James66a09c42012-11-05 13:31:38 -0800266 if config_only and target in configured_targets:
267 continue
268
269 cmd = cmdbase + ['-t', target]
270
271 for pkg in GetTargetPackages(target):
272 if pkg == 'gdb':
273 # Gdb does not have selectable versions.
274 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700275 elif pkg == 'ex_compiler-rt':
276 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700277 elif pkg == 'ex_go':
278 # Go does not have selectable versions.
279 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700280 elif pkg in LLVM_PKGS_TABLE:
281 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800282 elif pkg in cls.MANUAL_PKGS:
283 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700284 else:
285 # The first of the desired versions is the "primary" one.
286 version = GetDesiredPackageVersions(target, pkg)[0]
287 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800288
289 cmd.extend(targets[target]['crossdev'].split())
290 if config_only:
291 # In this case we want to just quietly reinit
292 cmd.append('--init-target')
293 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
294 else:
295 cros_build_lib.RunCommand(cmd)
296
297 configured_targets.append(target)
298
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100299
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100300def GetTargetPackages(target):
301 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800302 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100303 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800304 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100305
306
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100307# Portage helper functions:
308def GetPortagePackage(target, package):
309 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800310 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100311 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400312 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100313 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100314 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100315 category = conf['category']
316 # Portage package:
317 pn = conf[package + '_pn']
318 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500319 assert category
320 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100321 return '%s/%s' % (category, pn)
322
323
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700324def PortageTrees(root):
325 """Return the portage trees for a given root."""
326 if root == '/':
327 return portage.db['/']
328 # The portage logic requires the path always end in a slash.
329 root = root.rstrip('/') + '/'
330 return portage.create_trees(target_root=root, config_root=root)[root]
331
332
333def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100334 """Extracts the list of current versions of a target, package pair.
335
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500336 Args:
337 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700338 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100339
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500340 Returns:
341 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100342 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100343 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500344 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700345 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100346 version = portage.versions.cpv_getversion(pkg)
347 versions.append(version)
348 return versions
349
350
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700351def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100352 """Extracts the current stable version for a given package.
353
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500354 Args:
355 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
356 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700357 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100358
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500359 Returns:
360 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100361 """
David James90239b92012-11-05 15:31:34 -0800362 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500363 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700364 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800365 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100366
367
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700368def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369 """Resolves keywords in a given version list for a particular package.
370
371 Resolving means replacing PACKAGE_STABLE with the actual number.
372
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500373 Args:
374 target: The target to operate on (e.g. i686-pc-linux-gnu)
375 package: The target/package to operate on (e.g. gcc)
376 versions: List of versions to resolve
377 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700378 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100379
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500380 Returns:
381 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100382 """
383 resolved = []
David James90239b92012-11-05 15:31:34 -0800384 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700385 if not installed:
386 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100387 for version in versions:
388 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700389 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400390 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100391 resolved.append(version)
392 return resolved
393
394
395def GetDesiredPackageVersions(target, package):
396 """Produces the list of desired versions for each target, package pair.
397
398 The first version in the list is implicitly treated as primary, ie.
399 the version that will be initialized by crossdev and selected.
400
401 If the version is PACKAGE_STABLE, it really means the current version which
402 is emerged by using the package atom with no particular version key.
403 Since crossdev unmasks all packages by default, this will actually
404 mean 'unstable' in most cases.
405
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500406 Args:
407 target: The target to operate on (e.g. i686-pc-linux-gnu)
408 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100409
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500410 Returns:
411 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100412 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400413 if package in GetTargetPackages(target):
414 return [PACKAGE_STABLE]
415 else:
416 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417
418
419def TargetIsInitialized(target):
420 """Verifies if the given list of targets has been correctly initialized.
421
422 This determines whether we have to call crossdev while emerging
423 toolchain packages or can do it using emerge. Emerge is naturally
424 preferred, because all packages can be updated in a single pass.
425
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500426 Args:
427 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100428
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500429 Returns:
430 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100431 """
432 # Check if packages for the given target all have a proper version.
433 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100434 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800435 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100436 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400437 if not (GetStablePackageVersion(atom, True) and
438 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439 return False
440 return True
441 except cros_build_lib.RunCommandError:
442 # Fails - The target has likely never been initialized before.
443 return False
444
445
446def RemovePackageMask(target):
447 """Removes a package.mask file for the given platform.
448
449 The pre-existing package.mask files can mess with the keywords.
450
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500451 Args:
452 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100453 """
454 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700455 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100456
457
Zdenek Behan508dcce2011-12-05 15:39:32 +0100458# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700459def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500460 """Rebuild libtool as needed
461
462 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
463 gcc, libtool will break. We can't use binary packages either as those will
464 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700465
466 Args:
467 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500468 """
469 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700470 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500471 for line in f:
472 # Look for a line like:
473 # sys_lib_search_path_spec="..."
474 # It'll be a list of paths and gcc will be one of them.
475 if line.startswith('sys_lib_search_path_spec='):
476 line = line.rstrip()
477 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400478 root_path = os.path.join(root, path.lstrip(os.path.sep))
479 logging.debug('Libtool: checking %s', root_path)
480 if not os.path.exists(root_path):
481 logging.info('Rebuilding libtool after gcc upgrade')
482 logging.info(' %s', line)
483 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500484 needs_update = True
485 break
486
487 if needs_update:
488 break
489
490 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700491 cmd = [EMERGE_CMD, '--oneshot']
492 if root != '/':
493 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
494 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500495 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400496 else:
497 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500498
499
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700500def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100501 """Determines which packages need update/unmerge and defers to portage.
502
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500503 Args:
504 targets: The list of targets to update
505 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700506 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100507 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100508 # For each target, we do two things. Figure out the list of updates,
509 # and figure out the appropriate keywords/masks. Crossdev will initialize
510 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400511 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800512 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100513 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400514 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100515 # Record the highest needed version for each target, for masking purposes.
516 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100517 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100518 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400519 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100520 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700521 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100522 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200523 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400524 if pkg in NEW_PACKAGES and usepkg:
525 # Skip this binary package (for now).
526 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100527 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400528 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400530 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100531 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400532 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800533 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534
Mike Frysinger3bba5032016-09-20 14:15:04 -0400535 logging.info('Updating packages:')
536 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100537
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100538 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539 if usepkg:
540 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700541 if root != '/':
542 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543
544 cmd.extend(packages)
545 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800546 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100547
548
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700549def CleanTargets(targets, root='/'):
550 """Unmerges old packages that are assumed unnecessary.
551
552 Args:
553 targets: The list of targets to clean up.
554 root: The install root in which we want packages cleaned up.
555 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100556 unmergemap = {}
557 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400558 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100559 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400560 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100561 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700562 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100563 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700564 # NOTE: This refers to installed packages (vartree) rather than the
565 # Portage version (porttree and/or bintree) when determining the current
566 # version. While this isn't the most accurate thing to do, it is probably
567 # a good simple compromise, which should have the desired result of
568 # uninstalling everything but the latest installed version. In
569 # particular, using the bintree (--usebinpkg) requires a non-trivial
570 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200571 desired_num = VersionListToNumeric(target, package, desired, True)
572 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400573 logging.warning('Error detecting stable version for %s, '
574 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200575 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100576 unmergemap[pkg] = set(current).difference(desired_num)
577
578 # Cleaning doesn't care about consistency and rebuilding package.* files.
579 packages = []
580 for pkg, vers in unmergemap.iteritems():
581 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
582
583 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400584 logging.info('Cleaning packages:')
585 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100586 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700587 if root != '/':
588 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100589 cmd.extend(packages)
590 cros_build_lib.RunCommand(cmd)
591 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400592 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593
594
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700595def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100596 """Runs gcc-config and binutils-config to select the desired.
597
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500598 Args:
599 targets: The targets to select
600 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700601 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100602 """
603 for package in ['gcc', 'binutils']:
604 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400605 # See if this package is part of this target.
606 if package not in GetTargetPackages(target):
607 logging.debug('%s: %s is not used', target, package)
608 continue
609
Zdenek Behan508dcce2011-12-05 15:39:32 +0100610 # Pick the first version in the numbered list as the selected one.
611 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700612 desired_num = VersionListToNumeric(target, package, desired, True,
613 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100614 desired = desired_num[0]
615 # *-config does not play revisions, strip them, keep just PV.
616 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
617
Mike Frysinger785b0c32017-09-13 01:35:59 -0400618 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100619 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800620 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100621
622 # And finally, attach target to it.
623 desired = '%s-%s' % (target, desired)
624
625 # Target specific hacks
626 if package in suffixes:
627 if target in suffixes[package]:
628 desired += suffixes[package][target]
629
David James7ec5efc2012-11-06 09:39:49 -0800630 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700631 if root != '/':
632 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800633 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500634 result = cros_build_lib.RunCommand(
635 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
636 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700637
638 # Do not reconfig when the current is live or nothing needs to be done.
639 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100640 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500641 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700642 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100643
644
Mike Frysinger35247af2012-11-16 18:58:06 -0500645def ExpandTargets(targets_wanted):
646 """Expand any possible toolchain aliases into full targets
647
648 This will expand 'all' and 'sdk' into the respective toolchain tuples.
649
650 Args:
651 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500652
Mike Frysinger35247af2012-11-16 18:58:06 -0500653 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300654 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500655 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500656 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700657 if targets_wanted == set(['boards']):
658 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300659 return {}
660
661 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500662 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300663 return all_targets
664 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500665 # Filter out all the non-sdk toolchains as we don't want to mess
666 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300667 return toolchain.FilterToolchains(all_targets, 'sdk', True)
668
669 # Verify user input.
670 nonexistent = targets_wanted.difference(all_targets)
671 if nonexistent:
672 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
673 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500674
675
David Jamesf8c672f2012-11-06 13:38:11 -0800676def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700677 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100678 """Performs all steps to create a synchronized toolchain enviroment.
679
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500680 Args:
681 usepkg: Use prebuilt packages
682 deleteold: Unmerge deprecated packages
683 hostonly: Only setup the host toolchain
684 reconfig: Reload crossdev config and reselect toolchains
685 targets_wanted: All the targets to update
686 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700687 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100688 """
David Jamesf8c672f2012-11-06 13:38:11 -0800689 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100690 if not hostonly:
691 # For hostonly, we can skip most of the below logic, much of which won't
692 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500693 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400694
Don Garrettc0c74002015-10-09 12:58:19 -0700695 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300696 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400697 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800698 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100699
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100700 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400701 for target in targets:
702 if TargetIsInitialized(target):
703 reconfig_targets[target] = targets[target]
704 else:
705 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100706 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400707 logging.info('The following targets need to be re-initialized:')
708 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800709 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200710 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800711 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100712
Mike Frysinger66814c32017-10-09 18:11:46 -0400713 # If we're building a subset of toolchains for a board, we might not have
714 # all the tuples that the packages expect. We don't define the "full" set
715 # of tuples currently other than "whatever the full sdk has normally".
716 if usepkg or set(('all', 'sdk')) & targets_wanted:
717 # Since we have cross-compilers now, we can update these packages.
718 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400719
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100720 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400721 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100722
723 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700724 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
725 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800726
727 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700728 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100729
Mike Frysingerc880a962013-11-08 13:59:06 -0500730 # Now that we've cleared out old versions, see if we need to rebuild
731 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700732 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500733
Zdenek Behan508dcce2011-12-05 15:39:32 +0100734
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700735def ShowConfig(name):
736 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500737
738 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700739 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500740 """
Don Garrettc0c74002015-10-09 12:58:19 -0700741 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500742 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400743 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400744 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800745 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400746 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500747
748
Mike Frysinger35247af2012-11-16 18:58:06 -0500749def GeneratePathWrapper(root, wrappath, path):
750 """Generate a shell script to execute another shell script
751
752 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
753 argv[0] won't be pointing to the correct path, generate a shell script that
754 just executes another program with its full path.
755
756 Args:
757 root: The root tree to generate scripts inside of
758 wrappath: The full path (inside |root|) to create the wrapper
759 path: The target program which this wrapper will execute
760 """
761 replacements = {
762 'path': path,
763 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
764 }
765 wrapper = """#!/bin/sh
766base=$(realpath "$0")
767basedir=${base%%/*}
768exec "${basedir}/%(relroot)s%(path)s" "$@"
769""" % replacements
770 root_wrapper = root + wrappath
771 if os.path.islink(root_wrapper):
772 os.unlink(root_wrapper)
773 else:
774 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
775 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400776 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500777
778
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700779def FixClangXXWrapper(root, path):
780 """Fix wrapper shell scripts and symlinks for invoking clang++
781
782 In a typical installation, clang++ symlinks to clang, which symlinks to the
783 elf executable. The executable distinguishes between clang and clang++ based
784 on argv[0].
785
786 When invoked through the LdsoWrapper, argv[0] always contains the path to the
787 executable elf file, making clang/clang++ invocations indistinguishable.
788
789 This function detects if the elf executable being wrapped is clang-X.Y, and
790 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
791
792 The calling sequence now becomes:
793 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
794 the Ldsowrapper).
795 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
796 to the original clang-3.9 elf.
797 -) The difference this time is that inside the elf file execution, $0 is
798 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
799
800 Args:
801 root: The root tree to generate scripts / symlinks inside of
802 path: The target elf for which LdsoWrapper was created
803 """
804 if re.match(r'/usr/bin/clang-\d+\.\d+$', path):
805 logging.info('fixing clang++ invocation for %s', path)
806 clangdir = os.path.dirname(root + path)
807 clang = os.path.basename(path)
808 clangxx = clang.replace('clang', 'clang++')
809
810 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
811 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
812
813 # Create a hardlink clang++-X.Y pointing to clang-X.Y
814 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
815
816 # Adjust the clang++ symlink to point to clang++-X.Y
817 os.unlink(os.path.join(clangdir, 'clang++'))
818 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
819
820
Mike Frysinger35247af2012-11-16 18:58:06 -0500821def FileIsCrosSdkElf(elf):
822 """Determine if |elf| is an ELF that we execute in the cros_sdk
823
824 We don't need this to be perfect, just quick. It makes sure the ELF
825 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
826
827 Args:
828 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500829
Mike Frysinger35247af2012-11-16 18:58:06 -0500830 Returns:
831 True if we think |elf| is a native ELF
832 """
833 with open(elf) as f:
834 data = f.read(20)
835 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
836 return (data[0:4] == '\x7fELF' and
837 data[4] == '\x02' and
838 data[5] == '\x01' and
839 data[18] == '\x3e')
840
841
842def IsPathPackagable(ptype, path):
843 """Should the specified file be included in a toolchain package?
844
845 We only need to handle files as we'll create dirs as we need them.
846
847 Further, trim files that won't be useful:
848 - non-english translations (.mo) since it'd require env vars
849 - debug files since these are for the host compiler itself
850 - info/man pages as they're big, and docs are online, and the
851 native docs should work fine for the most part (`man gcc`)
852
853 Args:
854 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
855 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500856
Mike Frysinger35247af2012-11-16 18:58:06 -0500857 Returns:
858 True if we want to include this path in the package
859 """
860 return not (ptype in ('dir',) or
861 path.startswith('/usr/lib/debug/') or
862 os.path.splitext(path)[1] == '.mo' or
863 ('/man/' in path or '/info/' in path))
864
865
866def ReadlinkRoot(path, root):
867 """Like os.readlink(), but relative to a |root|
868
869 Args:
870 path: The symlink to read
871 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500872
Mike Frysinger35247af2012-11-16 18:58:06 -0500873 Returns:
874 A fully resolved symlink path
875 """
876 while os.path.islink(root + path):
877 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
878 return path
879
880
881def _GetFilesForTarget(target, root='/'):
882 """Locate all the files to package for |target|
883
884 This does not cover ELF dependencies.
885
886 Args:
887 target: The toolchain target name
888 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500889
Mike Frysinger35247af2012-11-16 18:58:06 -0500890 Returns:
891 A tuple of a set of all packable paths, and a set of all paths which
892 are also native ELFs
893 """
894 paths = set()
895 elfs = set()
896
897 # Find all the files owned by the packages for this target.
898 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500899
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700900 # Skip Go compiler from redistributable packages.
901 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
902 # into it. Due to this, the toolchain cannot be unpacked anywhere
903 # else and be readily useful. To enable packaging Go, we need to:
904 # -) Tweak the wrappers/environment to override GOROOT
905 # automatically based on the unpack location.
906 # -) Make sure the ELF dependency checking and wrapping logic
907 # below skips the Go toolchain executables and libraries.
908 # -) Make sure the packaging process maintains the relative
909 # timestamps of precompiled standard library packages.
910 # (see dev-lang/go ebuild for details).
911 if pkg == 'ex_go':
912 continue
913
Mike Frysinger35247af2012-11-16 18:58:06 -0500914 atom = GetPortagePackage(target, pkg)
915 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700916 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700917 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500918
919 # pylint: disable=E1101
920 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
921 settings=portage.settings)
922 contents = dblink.getcontents()
923 for obj in contents:
924 ptype = contents[obj][0]
925 if not IsPathPackagable(ptype, obj):
926 continue
927
928 if ptype == 'obj':
929 # For native ELFs, we need to pull in their dependencies too.
930 if FileIsCrosSdkElf(obj):
931 elfs.add(obj)
932 paths.add(obj)
933
934 return paths, elfs
935
936
937def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500938 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500939 """Link in all packable files and their runtime dependencies
940
941 This also wraps up executable ELFs with helper scripts.
942
943 Args:
944 output_dir: The output directory to store files
945 paths: All the files to include
946 elfs: All the files which are ELFs (a subset of |paths|)
947 ldpaths: A dict of static ldpath information
948 path_rewrite_func: User callback to rewrite paths in output_dir
949 root: The root path to pull all packages/files from
950 """
951 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400952 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500953 for path in paths:
954 new_path = path_rewrite_func(path)
955 dst = output_dir + new_path
956 osutils.SafeMakedirs(os.path.dirname(dst))
957
958 # Is this a symlink which we have to rewrite or wrap?
959 # Delay wrap check until after we have created all paths.
960 src = root + path
961 if os.path.islink(src):
962 tgt = os.readlink(src)
963 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -0400964 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -0500965
966 # Rewrite absolute links to relative and then generate the symlink
967 # ourselves. All other symlinks can be hardlinked below.
968 if tgt[0] == '/':
969 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
970 os.symlink(tgt, dst)
971 continue
972
973 os.link(src, dst)
974
Mike Frysinger35247af2012-11-16 18:58:06 -0500975 # Locate all the dependencies for all the ELFs. Stick them all in the
976 # top level "lib" dir to make the wrapper simpler. This exact path does
977 # not matter since we execute ldso directly, and we tell the ldso the
978 # exact path to search for its libraries.
979 libdir = os.path.join(output_dir, 'lib')
980 osutils.SafeMakedirs(libdir)
981 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -0400982 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -0500983 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400984 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500985 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -0700986 # Do not create wrapper for libc. crbug.com/766827
987 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -0500988 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400989 interp = os.path.join('/lib', os.path.basename(interp))
990 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
991 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700992 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -0500993
Mike Frysinger221bd822017-09-29 02:51:47 -0400994 # Wrap any symlinks to the wrapper.
995 if elf in sym_paths:
996 link = sym_paths[elf]
997 GeneratePathWrapper(output_dir, link, elf)
998
Mike Frysinger35247af2012-11-16 18:58:06 -0500999 for lib, lib_data in e['libs'].iteritems():
1000 if lib in donelibs:
1001 continue
1002
1003 src = path = lib_data['path']
1004 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001005 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001006 continue
1007 donelibs.add(lib)
1008
1009 # Needed libs are the SONAME, but that is usually a symlink, not a
1010 # real file. So link in the target rather than the symlink itself.
1011 # We have to walk all the possible symlinks (SONAME could point to a
1012 # symlink which points to a symlink), and we have to handle absolute
1013 # ourselves (since we have a "root" argument).
1014 dst = os.path.join(libdir, os.path.basename(path))
1015 src = ReadlinkRoot(src, root)
1016
1017 os.link(root + src, dst)
1018
1019
1020def _EnvdGetVar(envd, var):
1021 """Given a Gentoo env.d file, extract a var from it
1022
1023 Args:
1024 envd: The env.d file to load (may be a glob path)
1025 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001026
Mike Frysinger35247af2012-11-16 18:58:06 -05001027 Returns:
1028 The value of |var|
1029 """
1030 envds = glob.glob(envd)
1031 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1032 envd = envds[0]
1033 return cros_build_lib.LoadKeyValueFile(envd)[var]
1034
1035
1036def _ProcessBinutilsConfig(target, output_dir):
1037 """Do what binutils-config would have done"""
1038 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001039
1040 # Locate the bin dir holding the gold linker.
1041 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1042 target, 'binutils-bin')
1043 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001044 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001045 if not srcpath:
1046 # Maybe this target doesn't support gold.
1047 globpath = os.path.join(binutils_bin_path, '*')
1048 srcpath = glob.glob(globpath)
1049 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1050 % globpath)
1051 srcpath = srcpath[0]
1052 ld_path = os.path.join(srcpath, 'ld')
1053 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1054 ld_path = os.path.join(srcpath, 'ld.bfd')
1055 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1056 ld_path = os.path.join(srcpath, 'ld.gold')
1057 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1058 % ld_path)
1059
1060 # Nope, no gold support to be found.
1061 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001062 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001063 else:
1064 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001065 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001066
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001067 # Package the binutils-bin directory without the '-gold' suffix
1068 # if gold is not enabled as the default linker for this target.
1069 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1070 if not gold_supported:
1071 srcpath = srcpath[:-len('-gold')]
1072 ld_path = os.path.join(srcpath, 'ld')
1073 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1074
Mike Frysinger78b7a812014-11-26 19:45:23 -05001075 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001076 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1077 for prog in os.listdir(output_dir + srcpath):
1078 # Skip binaries already wrapped.
1079 if not prog.endswith('.real'):
1080 GeneratePathWrapper(output_dir, binpath + prog,
1081 os.path.join(srcpath, prog))
1082 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1083 os.path.join(srcpath, prog))
1084
David James27ac4ae2012-12-03 23:16:15 -08001085 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001086 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1087 if gold_supported:
1088 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001089 else:
1090 # If gold is not enabled as the default linker and 2 env.d
1091 # files exist, pick the one without the '-gold' suffix.
1092 envds = sorted(glob.glob(envd))
1093 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1094 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001095 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1096 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1097 output_dir + libpath)
1098
1099
1100def _ProcessGccConfig(target, output_dir):
1101 """Do what gcc-config would have done"""
1102 binpath = '/bin'
1103 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1104 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1105 for prog in os.listdir(output_dir + srcpath):
1106 # Skip binaries already wrapped.
1107 if (not prog.endswith('.real') and
1108 not prog.endswith('.elf') and
1109 prog.startswith(target)):
1110 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1111 os.path.join(srcpath, prog))
1112 return srcpath
1113
1114
Frank Henigman179ec7c2015-02-06 03:01:09 -05001115def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1116 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001117 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001118 for sysroot_wrapper in glob.glob(os.path.join(
1119 output_dir + srcpath, 'sysroot_wrapper*')):
1120 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
Douglas Andersoncc828a52017-10-13 13:07:25 -07001121
1122 # In order to optimize startup time in the chroot we run python a little
1123 # differently there. Put it back to the more portable way here.
1124 # See http://crbug.com/773138 for some details.
1125 if contents[0] == '#!/usr/bin/python2 -S':
1126 contents[0] = '#!/usr/bin/env python2'
1127
Frank Henigman179ec7c2015-02-06 03:01:09 -05001128 for num in xrange(len(contents)):
1129 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001130 assert 'True' in contents[num]
1131 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001132 break
1133 # Can't update the wrapper in place since it's a hardlink to a file in /.
1134 os.unlink(sysroot_wrapper)
1135 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1136 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001137
1138
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001139def _CreateMainLibDir(target, output_dir):
1140 """Create some lib dirs so that compiler can get the right Gcc paths"""
1141 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1142 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1143
1144
Mike Frysinger35247af2012-11-16 18:58:06 -05001145def _ProcessDistroCleanups(target, output_dir):
1146 """Clean up the tree and remove all distro-specific requirements
1147
1148 Args:
1149 target: The toolchain target name
1150 output_dir: The output directory to clean up
1151 """
1152 _ProcessBinutilsConfig(target, output_dir)
1153 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001154 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001155 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001156
1157 osutils.RmDir(os.path.join(output_dir, 'etc'))
1158
1159
1160def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1161 """Setup a tree from the packages for the specified target
1162
1163 This populates a path with all the files from toolchain packages so that
1164 a tarball can easily be generated from the result.
1165
1166 Args:
1167 target: The target to create a packagable root from
1168 output_dir: The output directory to place all the files
1169 ldpaths: A dict of static ldpath information
1170 root: The root path to pull all packages/files from
1171 """
1172 # Find all the files owned by the packages for this target.
1173 paths, elfs = _GetFilesForTarget(target, root=root)
1174
1175 # Link in all the package's files, any ELF dependencies, and wrap any
1176 # executable ELFs with helper scripts.
1177 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001178 """Move /usr/bin to /bin so people can just use that toplevel dir
1179
1180 Note we do not apply this to clang - there is correlation between clang's
1181 search path for libraries / inclusion and its installation path.
1182 """
1183 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1184 return path[4:]
1185 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001186 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1187 path_rewrite_func=MoveUsrBinToBin, root=root)
1188
1189 # The packages, when part of the normal distro, have helper scripts
1190 # that setup paths and such. Since we are making this standalone, we
1191 # need to preprocess all that ourselves.
1192 _ProcessDistroCleanups(target, output_dir)
1193
1194
1195def CreatePackages(targets_wanted, output_dir, root='/'):
1196 """Create redistributable cross-compiler packages for the specified targets
1197
1198 This creates toolchain packages that should be usable in conjunction with
1199 a downloaded sysroot (created elsewhere).
1200
1201 Tarballs (one per target) will be created in $PWD.
1202
1203 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001204 targets_wanted: The targets to package up.
1205 output_dir: The directory to put the packages in.
1206 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001207 """
Ralph Nathan03047282015-03-23 11:09:32 -07001208 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001209 osutils.SafeMakedirs(output_dir)
1210 ldpaths = lddtree.LoadLdpaths(root)
1211 targets = ExpandTargets(targets_wanted)
1212
Mike Frysinger221bd822017-09-29 02:51:47 -04001213 with osutils.TempDir(prefix='create-packages') as tempdir:
1214 logging.debug('Using tempdir: %s', tempdir)
1215
Mike Frysinger35247af2012-11-16 18:58:06 -05001216 # We have to split the root generation from the compression stages. This is
1217 # because we hardlink in all the files (to avoid overhead of reading/writing
1218 # the copies multiple times). But tar gets angry if a file's hardlink count
1219 # changes from when it starts reading a file to when it finishes.
1220 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1221 for target in targets:
1222 output_target_dir = os.path.join(tempdir, target)
1223 queue.put([target, output_target_dir, ldpaths, root])
1224
1225 # Build the tarball.
1226 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1227 for target in targets:
1228 tar_file = os.path.join(output_dir, target + '.tar.xz')
1229 queue.put([tar_file, os.path.join(tempdir, target)])
1230
1231
Mike Frysinger07534cf2017-09-12 17:40:21 -04001232def GetParser():
1233 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001234 parser = commandline.ArgumentParser(description=__doc__)
1235 parser.add_argument('-u', '--nousepkg',
1236 action='store_false', dest='usepkg', default=True,
1237 help='Use prebuilt packages if possible')
1238 parser.add_argument('-d', '--deleteold',
1239 action='store_true', dest='deleteold', default=False,
1240 help='Unmerge deprecated packages')
1241 parser.add_argument('-t', '--targets',
1242 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001243 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001244 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001245 "allowed. Defaults to 'sdk'.")
1246 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1247 help='Comma separated list of boards whose toolchains we '
1248 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001249 parser.add_argument('--hostonly',
1250 dest='hostonly', default=False, action='store_true',
1251 help='Only setup the host toolchain. '
1252 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001253 parser.add_argument('--show-board-cfg', '--show-cfg',
1254 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001255 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001256 parser.add_argument('--show-packages', default=None,
1257 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001258 parser.add_argument('--create-packages',
1259 action='store_true', default=False,
1260 help='Build redistributable packages')
1261 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1262 help='Output directory')
1263 parser.add_argument('--reconfig', default=False, action='store_true',
1264 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001265 parser.add_argument('--sysroot', type='path',
1266 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001267 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001268
Mike Frysinger07534cf2017-09-12 17:40:21 -04001269
1270def main(argv):
1271 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001272 options = parser.parse_args(argv)
1273 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001274
Mike Frysinger35247af2012-11-16 18:58:06 -05001275 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001276 conflicting_options = (
1277 options.cfg_name,
1278 options.show_packages,
1279 options.create_packages,
1280 )
1281 if sum(bool(x) for x in conflicting_options) > 1:
1282 parser.error('conflicting options: create-packages & show-packages & '
1283 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001284
Gilad Arnold8195b532015-04-07 10:56:30 +03001285 targets_wanted = set(options.targets.split(','))
1286 boards_wanted = (set(options.include_boards.split(','))
1287 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001288
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001289 if options.cfg_name:
1290 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001291 elif options.show_packages is not None:
1292 cros_build_lib.AssertInsideChroot()
1293 target = options.show_packages
1294 Crossdev.Load(False)
1295 for package in GetTargetPackages(target):
1296 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001297 elif options.create_packages:
1298 cros_build_lib.AssertInsideChroot()
1299 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001300 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001301 else:
1302 cros_build_lib.AssertInsideChroot()
1303 # This has to be always run as root.
1304 if os.geteuid() != 0:
1305 cros_build_lib.Die('this script must be run as root')
1306
1307 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001308 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001309 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001310 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001311 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001312 Crossdev.Save()
1313
1314 return 0