blob: 1ca445335feeb1160ecdc3d68c9aab595d600291 [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',
57 'sys-devel/llvm',
58 'sys-kernel/linux-headers',
59 'sys-libs/glibc',
60 'sys-libs/libcxx',
61 'sys-libs/libcxxabi',
62)
63
Mike Frysinger785b0c32017-09-13 01:35:59 -040064# These packages are also installed into the host SDK. However, they require
65# the cross-compilers to be installed first (because they need them to actually
66# build), so we have to delay their installation.
67HOST_POST_CROSS_PACKAGES = (
68 'dev-lang/rust',
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 Frysinger61a24392017-10-17 17:14:27 -040077 'virtual/target-sdk-post-cross',
Mike Frysinger785b0c32017-09-13 01:35:59 -040078)
79
Rahul Chaudhry4b803052015-05-13 15:25:56 -070080# Enable the Go compiler for these targets.
81TARGET_GO_ENABLED = (
82 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070083 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070084 'armv7a-cros-linux-gnueabihf',
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',
Han Shene23782f2016-02-18 12:20:00 -0800129 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700130 'libcxxabi': 'sys-libs',
131 'libcxx': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800132 }
David James66a09c42012-11-05 13:31:38 -0800133
134 @classmethod
135 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400136 """Load crossdev cache from disk.
137
138 We invalidate the cache when crossdev updates or this script changes.
139 """
David James90239b92012-11-05 15:31:34 -0800140 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400141 # If we run the compiled/cached .pyc file, we'll read/hash that when we
142 # really always want to track the source .py file.
143 script = os.path.abspath(__file__)
144 if script.endswith('.pyc'):
145 script = script[:-1]
146 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
147
148 cls._CACHE = {
149 'crossdev_version': crossdev_version,
150 'setup_toolchains_hash': setup_toolchains_hash,
151 }
152
153 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
154 if reconfig:
155 logging.debug('cache: forcing regen due to reconfig')
156 return
157
158 try:
159 file_data = osutils.ReadFile(cls._CACHE_FILE)
160 except IOError as e:
161 if e.errno != errno.ENOENT:
162 logging.warning('cache: reading failed: %s', e)
163 osutils.SafeUnlink(cls._CACHE_FILE)
164 return
165
166 try:
167 data = json.loads(file_data)
168 except ValueError as e:
169 logging.warning('cache: ignoring invalid content: %s', e)
170 return
171
172 if crossdev_version != data.get('crossdev_version'):
173 logging.debug('cache: rebuilding after crossdev upgrade')
174 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
175 logging.debug('cache: rebuilding after cros_setup_toolchains change')
176 else:
177 logging.debug('cache: content is up-to-date!')
178 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800179
180 @classmethod
181 def Save(cls):
182 """Store crossdev cache on disk."""
183 # Save the cache from the successful run.
184 with open(cls._CACHE_FILE, 'w') as f:
185 json.dump(cls._CACHE, f)
186
187 @classmethod
188 def GetConfig(cls, target):
189 """Returns a map of crossdev provided variables about a tuple."""
190 CACHE_ATTR = '_target_tuple_map'
191
192 val = cls._CACHE.setdefault(CACHE_ATTR, {})
193 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400194 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400195 conf = {
196 'crosspkgs': [],
197 'target': toolchain.GetHostTuple(),
198 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400199 if target == 'host':
200 packages_list = HOST_PACKAGES
201 else:
202 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400203 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400204 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400205 else:
206 # Build the crossdev command.
207 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
208 if target in TARGET_COMPILER_RT_ENABLED:
209 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
210 if target in TARGET_GO_ENABLED:
211 cmd.extend(CROSSDEV_GO_ARGS)
212 if target in TARGET_LLVM_PKGS_ENABLED:
213 for pkg in LLVM_PKGS_TABLE:
214 cmd.extend(LLVM_PKGS_TABLE[pkg])
215 cmd.extend(['-t', target])
216 # Catch output of crossdev.
217 out = cros_build_lib.RunCommand(
218 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
219 # List of tuples split at the first '=', converted into dict.
220 conf = dict((k, cros_build_lib.ShellUnquote(v))
221 for k, v in (x.split('=', 1) for x in out))
222 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800223
Mike Frysinger66bfde52017-09-12 16:42:57 -0400224 manual_pkgs = cls.MANUAL_PKGS
225
226 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400227 conf[pkg + '_pn'] = pkg
228 conf[pkg + '_category'] = cat
229 if pkg not in conf['crosspkgs']:
230 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800231
232 val[target] = conf
233
David James66a09c42012-11-05 13:31:38 -0800234 return val[target]
235
236 @classmethod
237 def UpdateTargets(cls, targets, usepkg, config_only=False):
238 """Calls crossdev to initialize a cross target.
239
240 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700241 targets: The list of targets to initialize using crossdev.
242 usepkg: Copies the commandline opts.
243 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800244 """
245 configured_targets = cls._CACHE.setdefault('configured_targets', [])
246
247 cmdbase = ['crossdev', '--show-fail-log']
248 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
249 # Pick stable by default, and override as necessary.
250 cmdbase.extend(['-P', '--oneshot'])
251 if usepkg:
252 cmdbase.extend(['-P', '--getbinpkg',
253 '-P', '--usepkgonly',
254 '--without-headers'])
255
Christopher Wileyb22c0712015-06-02 10:37:03 -0700256 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800257 cmdbase.extend(['--overlays', overlays])
258 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
259
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700260 # Build target by the alphabetical order to make sure
261 # armv7a-cros-linux-gnueabihf builds after armv7a-cros-linux-gnueabi
262 # because some dependency issue. This can be reverted once we
263 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
264 for target in sorted(targets):
David James66a09c42012-11-05 13:31:38 -0800265 if config_only and target in configured_targets:
266 continue
267
268 cmd = cmdbase + ['-t', target]
269
270 for pkg in GetTargetPackages(target):
271 if pkg == 'gdb':
272 # Gdb does not have selectable versions.
273 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700274 elif pkg == 'ex_compiler-rt':
275 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700276 elif pkg == 'ex_go':
277 # Go does not have selectable versions.
278 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700279 elif pkg in LLVM_PKGS_TABLE:
280 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800281 elif pkg in cls.MANUAL_PKGS:
282 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700283 else:
284 # The first of the desired versions is the "primary" one.
285 version = GetDesiredPackageVersions(target, pkg)[0]
286 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800287
288 cmd.extend(targets[target]['crossdev'].split())
289 if config_only:
290 # In this case we want to just quietly reinit
291 cmd.append('--init-target')
292 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
293 else:
294 cros_build_lib.RunCommand(cmd)
295
296 configured_targets.append(target)
297
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100298
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100299def GetTargetPackages(target):
300 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800301 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100302 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800303 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100304
305
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100306# Portage helper functions:
307def GetPortagePackage(target, package):
308 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800309 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100310 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400311 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100312 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100313 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100314 category = conf['category']
315 # Portage package:
316 pn = conf[package + '_pn']
317 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500318 assert category
319 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100320 return '%s/%s' % (category, pn)
321
322
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700323def PortageTrees(root):
324 """Return the portage trees for a given root."""
325 if root == '/':
326 return portage.db['/']
327 # The portage logic requires the path always end in a slash.
328 root = root.rstrip('/') + '/'
329 return portage.create_trees(target_root=root, config_root=root)[root]
330
331
332def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100333 """Extracts the list of current versions of a target, package pair.
334
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500335 Args:
336 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700337 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100338
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500339 Returns:
340 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100342 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500343 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700344 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100345 version = portage.versions.cpv_getversion(pkg)
346 versions.append(version)
347 return versions
348
349
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700350def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100351 """Extracts the current stable version for a given package.
352
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500353 Args:
354 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
355 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700356 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100357
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500358 Returns:
359 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360 """
David James90239b92012-11-05 15:31:34 -0800361 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500362 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700363 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800364 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100365
366
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700367def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100368 """Resolves keywords in a given version list for a particular package.
369
370 Resolving means replacing PACKAGE_STABLE with the actual number.
371
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500372 Args:
373 target: The target to operate on (e.g. i686-pc-linux-gnu)
374 package: The target/package to operate on (e.g. gcc)
375 versions: List of versions to resolve
376 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700377 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500379 Returns:
380 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100381 """
382 resolved = []
David James90239b92012-11-05 15:31:34 -0800383 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700384 if not installed:
385 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100386 for version in versions:
387 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700388 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400389 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100390 resolved.append(version)
391 return resolved
392
393
394def GetDesiredPackageVersions(target, package):
395 """Produces the list of desired versions for each target, package pair.
396
397 The first version in the list is implicitly treated as primary, ie.
398 the version that will be initialized by crossdev and selected.
399
400 If the version is PACKAGE_STABLE, it really means the current version which
401 is emerged by using the package atom with no particular version key.
402 Since crossdev unmasks all packages by default, this will actually
403 mean 'unstable' in most cases.
404
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500405 Args:
406 target: The target to operate on (e.g. i686-pc-linux-gnu)
407 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100408
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500409 Returns:
410 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100411 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400412 if package in GetTargetPackages(target):
413 return [PACKAGE_STABLE]
414 else:
415 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100416
417
418def TargetIsInitialized(target):
419 """Verifies if the given list of targets has been correctly initialized.
420
421 This determines whether we have to call crossdev while emerging
422 toolchain packages or can do it using emerge. Emerge is naturally
423 preferred, because all packages can be updated in a single pass.
424
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500425 Args:
426 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100427
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500428 Returns:
429 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100430 """
431 # Check if packages for the given target all have a proper version.
432 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100433 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800434 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100435 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400436 if not (GetStablePackageVersion(atom, True) and
437 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100438 return False
439 return True
440 except cros_build_lib.RunCommandError:
441 # Fails - The target has likely never been initialized before.
442 return False
443
444
445def RemovePackageMask(target):
446 """Removes a package.mask file for the given platform.
447
448 The pre-existing package.mask files can mess with the keywords.
449
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500450 Args:
451 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100452 """
453 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700454 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455
456
Zdenek Behan508dcce2011-12-05 15:39:32 +0100457# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700458def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500459 """Rebuild libtool as needed
460
461 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
462 gcc, libtool will break. We can't use binary packages either as those will
463 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700464
465 Args:
466 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500467 """
468 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700469 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500470 for line in f:
471 # Look for a line like:
472 # sys_lib_search_path_spec="..."
473 # It'll be a list of paths and gcc will be one of them.
474 if line.startswith('sys_lib_search_path_spec='):
475 line = line.rstrip()
476 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400477 root_path = os.path.join(root, path.lstrip(os.path.sep))
478 logging.debug('Libtool: checking %s', root_path)
479 if not os.path.exists(root_path):
480 logging.info('Rebuilding libtool after gcc upgrade')
481 logging.info(' %s', line)
482 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500483 needs_update = True
484 break
485
486 if needs_update:
487 break
488
489 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700490 cmd = [EMERGE_CMD, '--oneshot']
491 if root != '/':
492 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
493 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500494 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400495 else:
496 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500497
498
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700499def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100500 """Determines which packages need update/unmerge and defers to portage.
501
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500502 Args:
503 targets: The list of targets to update
504 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700505 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100506 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100507 # For each target, we do two things. Figure out the list of updates,
508 # and figure out the appropriate keywords/masks. Crossdev will initialize
509 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400510 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800511 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100512 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400513 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100514 # Record the highest needed version for each target, for masking purposes.
515 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100516 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400518 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100519 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700520 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100521 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200522 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400523 if pkg in NEW_PACKAGES and usepkg:
524 # Skip this binary package (for now).
525 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100526 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400527 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400529 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100530 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400531 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800532 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100533
Mike Frysinger3bba5032016-09-20 14:15:04 -0400534 logging.info('Updating packages:')
535 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100537 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538 if usepkg:
539 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700540 if root != '/':
541 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100542
543 cmd.extend(packages)
544 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800545 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100546
547
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700548def CleanTargets(targets, root='/'):
549 """Unmerges old packages that are assumed unnecessary.
550
551 Args:
552 targets: The list of targets to clean up.
553 root: The install root in which we want packages cleaned up.
554 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100555 unmergemap = {}
556 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400557 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100558 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400559 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100560 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700561 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100562 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700563 # NOTE: This refers to installed packages (vartree) rather than the
564 # Portage version (porttree and/or bintree) when determining the current
565 # version. While this isn't the most accurate thing to do, it is probably
566 # a good simple compromise, which should have the desired result of
567 # uninstalling everything but the latest installed version. In
568 # particular, using the bintree (--usebinpkg) requires a non-trivial
569 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200570 desired_num = VersionListToNumeric(target, package, desired, True)
571 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400572 logging.warning('Error detecting stable version for %s, '
573 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200574 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100575 unmergemap[pkg] = set(current).difference(desired_num)
576
577 # Cleaning doesn't care about consistency and rebuilding package.* files.
578 packages = []
579 for pkg, vers in unmergemap.iteritems():
580 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
581
582 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400583 logging.info('Cleaning packages:')
584 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100585 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700586 if root != '/':
587 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100588 cmd.extend(packages)
589 cros_build_lib.RunCommand(cmd)
590 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400591 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100592
593
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700594def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595 """Runs gcc-config and binutils-config to select the desired.
596
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500597 Args:
598 targets: The targets to select
599 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700600 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100601 """
602 for package in ['gcc', 'binutils']:
603 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400604 # See if this package is part of this target.
605 if package not in GetTargetPackages(target):
606 logging.debug('%s: %s is not used', target, package)
607 continue
608
Zdenek Behan508dcce2011-12-05 15:39:32 +0100609 # Pick the first version in the numbered list as the selected one.
610 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700611 desired_num = VersionListToNumeric(target, package, desired, True,
612 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100613 desired = desired_num[0]
614 # *-config does not play revisions, strip them, keep just PV.
615 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
616
Mike Frysinger785b0c32017-09-13 01:35:59 -0400617 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100618 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800619 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100620
621 # And finally, attach target to it.
622 desired = '%s-%s' % (target, desired)
623
624 # Target specific hacks
625 if package in suffixes:
626 if target in suffixes[package]:
627 desired += suffixes[package][target]
628
David James7ec5efc2012-11-06 09:39:49 -0800629 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700630 if root != '/':
631 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800632 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500633 result = cros_build_lib.RunCommand(
634 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
635 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700636
637 # Do not reconfig when the current is live or nothing needs to be done.
638 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100639 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500640 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700641 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100642
643
Mike Frysinger35247af2012-11-16 18:58:06 -0500644def ExpandTargets(targets_wanted):
645 """Expand any possible toolchain aliases into full targets
646
647 This will expand 'all' and 'sdk' into the respective toolchain tuples.
648
649 Args:
650 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500651
Mike Frysinger35247af2012-11-16 18:58:06 -0500652 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300653 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500654 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500655 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700656 if targets_wanted == set(['boards']):
657 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300658 return {}
659
660 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500661 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300662 return all_targets
663 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500664 # Filter out all the non-sdk toolchains as we don't want to mess
665 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300666 return toolchain.FilterToolchains(all_targets, 'sdk', True)
667
668 # Verify user input.
669 nonexistent = targets_wanted.difference(all_targets)
670 if nonexistent:
671 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
672 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500673
674
David Jamesf8c672f2012-11-06 13:38:11 -0800675def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700676 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100677 """Performs all steps to create a synchronized toolchain enviroment.
678
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500679 Args:
680 usepkg: Use prebuilt packages
681 deleteold: Unmerge deprecated packages
682 hostonly: Only setup the host toolchain
683 reconfig: Reload crossdev config and reselect toolchains
684 targets_wanted: All the targets to update
685 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700686 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100687 """
David Jamesf8c672f2012-11-06 13:38:11 -0800688 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100689 if not hostonly:
690 # For hostonly, we can skip most of the below logic, much of which won't
691 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500692 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400693
Don Garrettc0c74002015-10-09 12:58:19 -0700694 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300695 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400696 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800697 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100698
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100699 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400700 for target in targets:
701 if TargetIsInitialized(target):
702 reconfig_targets[target] = targets[target]
703 else:
704 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100705 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400706 logging.info('The following targets need to be re-initialized:')
707 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800708 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200709 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800710 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100711
Mike Frysinger66814c32017-10-09 18:11:46 -0400712 # If we're building a subset of toolchains for a board, we might not have
713 # all the tuples that the packages expect. We don't define the "full" set
714 # of tuples currently other than "whatever the full sdk has normally".
715 if usepkg or set(('all', 'sdk')) & targets_wanted:
716 # Since we have cross-compilers now, we can update these packages.
717 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400718
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100719 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400720 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100721
722 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700723 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
724 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800725
726 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700727 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100728
Mike Frysingerc880a962013-11-08 13:59:06 -0500729 # Now that we've cleared out old versions, see if we need to rebuild
730 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700731 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500732
Zdenek Behan508dcce2011-12-05 15:39:32 +0100733
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700734def ShowConfig(name):
735 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500736
737 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700738 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500739 """
Don Garrettc0c74002015-10-09 12:58:19 -0700740 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500741 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400742 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400743 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800744 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400745 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500746
747
Mike Frysinger35247af2012-11-16 18:58:06 -0500748def GeneratePathWrapper(root, wrappath, path):
749 """Generate a shell script to execute another shell script
750
751 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
752 argv[0] won't be pointing to the correct path, generate a shell script that
753 just executes another program with its full path.
754
755 Args:
756 root: The root tree to generate scripts inside of
757 wrappath: The full path (inside |root|) to create the wrapper
758 path: The target program which this wrapper will execute
759 """
760 replacements = {
761 'path': path,
762 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
763 }
764 wrapper = """#!/bin/sh
765base=$(realpath "$0")
766basedir=${base%%/*}
767exec "${basedir}/%(relroot)s%(path)s" "$@"
768""" % replacements
769 root_wrapper = root + wrappath
770 if os.path.islink(root_wrapper):
771 os.unlink(root_wrapper)
772 else:
773 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
774 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400775 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500776
777
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700778def FixClangXXWrapper(root, path):
779 """Fix wrapper shell scripts and symlinks for invoking clang++
780
781 In a typical installation, clang++ symlinks to clang, which symlinks to the
782 elf executable. The executable distinguishes between clang and clang++ based
783 on argv[0].
784
785 When invoked through the LdsoWrapper, argv[0] always contains the path to the
786 executable elf file, making clang/clang++ invocations indistinguishable.
787
788 This function detects if the elf executable being wrapped is clang-X.Y, and
789 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
790
791 The calling sequence now becomes:
792 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
793 the Ldsowrapper).
794 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
795 to the original clang-3.9 elf.
796 -) The difference this time is that inside the elf file execution, $0 is
797 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
798
799 Args:
800 root: The root tree to generate scripts / symlinks inside of
801 path: The target elf for which LdsoWrapper was created
802 """
803 if re.match(r'/usr/bin/clang-\d+\.\d+$', path):
804 logging.info('fixing clang++ invocation for %s', path)
805 clangdir = os.path.dirname(root + path)
806 clang = os.path.basename(path)
807 clangxx = clang.replace('clang', 'clang++')
808
809 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
810 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
811
812 # Create a hardlink clang++-X.Y pointing to clang-X.Y
813 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
814
815 # Adjust the clang++ symlink to point to clang++-X.Y
816 os.unlink(os.path.join(clangdir, 'clang++'))
817 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
818
819
Mike Frysinger35247af2012-11-16 18:58:06 -0500820def FileIsCrosSdkElf(elf):
821 """Determine if |elf| is an ELF that we execute in the cros_sdk
822
823 We don't need this to be perfect, just quick. It makes sure the ELF
824 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
825
826 Args:
827 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500828
Mike Frysinger35247af2012-11-16 18:58:06 -0500829 Returns:
830 True if we think |elf| is a native ELF
831 """
832 with open(elf) as f:
833 data = f.read(20)
834 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
835 return (data[0:4] == '\x7fELF' and
836 data[4] == '\x02' and
837 data[5] == '\x01' and
838 data[18] == '\x3e')
839
840
841def IsPathPackagable(ptype, path):
842 """Should the specified file be included in a toolchain package?
843
844 We only need to handle files as we'll create dirs as we need them.
845
846 Further, trim files that won't be useful:
847 - non-english translations (.mo) since it'd require env vars
848 - debug files since these are for the host compiler itself
849 - info/man pages as they're big, and docs are online, and the
850 native docs should work fine for the most part (`man gcc`)
851
852 Args:
853 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
854 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500855
Mike Frysinger35247af2012-11-16 18:58:06 -0500856 Returns:
857 True if we want to include this path in the package
858 """
859 return not (ptype in ('dir',) or
860 path.startswith('/usr/lib/debug/') or
861 os.path.splitext(path)[1] == '.mo' or
862 ('/man/' in path or '/info/' in path))
863
864
865def ReadlinkRoot(path, root):
866 """Like os.readlink(), but relative to a |root|
867
868 Args:
869 path: The symlink to read
870 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500871
Mike Frysinger35247af2012-11-16 18:58:06 -0500872 Returns:
873 A fully resolved symlink path
874 """
875 while os.path.islink(root + path):
876 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
877 return path
878
879
880def _GetFilesForTarget(target, root='/'):
881 """Locate all the files to package for |target|
882
883 This does not cover ELF dependencies.
884
885 Args:
886 target: The toolchain target name
887 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500888
Mike Frysinger35247af2012-11-16 18:58:06 -0500889 Returns:
890 A tuple of a set of all packable paths, and a set of all paths which
891 are also native ELFs
892 """
893 paths = set()
894 elfs = set()
895
896 # Find all the files owned by the packages for this target.
897 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500898
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700899 # Skip Go compiler from redistributable packages.
900 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
901 # into it. Due to this, the toolchain cannot be unpacked anywhere
902 # else and be readily useful. To enable packaging Go, we need to:
903 # -) Tweak the wrappers/environment to override GOROOT
904 # automatically based on the unpack location.
905 # -) Make sure the ELF dependency checking and wrapping logic
906 # below skips the Go toolchain executables and libraries.
907 # -) Make sure the packaging process maintains the relative
908 # timestamps of precompiled standard library packages.
909 # (see dev-lang/go ebuild for details).
910 if pkg == 'ex_go':
911 continue
912
Mike Frysinger35247af2012-11-16 18:58:06 -0500913 atom = GetPortagePackage(target, pkg)
914 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700915 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700916 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500917
918 # pylint: disable=E1101
919 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
920 settings=portage.settings)
921 contents = dblink.getcontents()
922 for obj in contents:
923 ptype = contents[obj][0]
924 if not IsPathPackagable(ptype, obj):
925 continue
926
927 if ptype == 'obj':
928 # For native ELFs, we need to pull in their dependencies too.
929 if FileIsCrosSdkElf(obj):
930 elfs.add(obj)
931 paths.add(obj)
932
933 return paths, elfs
934
935
936def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500937 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500938 """Link in all packable files and their runtime dependencies
939
940 This also wraps up executable ELFs with helper scripts.
941
942 Args:
943 output_dir: The output directory to store files
944 paths: All the files to include
945 elfs: All the files which are ELFs (a subset of |paths|)
946 ldpaths: A dict of static ldpath information
947 path_rewrite_func: User callback to rewrite paths in output_dir
948 root: The root path to pull all packages/files from
949 """
950 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400951 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500952 for path in paths:
953 new_path = path_rewrite_func(path)
954 dst = output_dir + new_path
955 osutils.SafeMakedirs(os.path.dirname(dst))
956
957 # Is this a symlink which we have to rewrite or wrap?
958 # Delay wrap check until after we have created all paths.
959 src = root + path
960 if os.path.islink(src):
961 tgt = os.readlink(src)
962 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -0400963 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -0500964
965 # Rewrite absolute links to relative and then generate the symlink
966 # ourselves. All other symlinks can be hardlinked below.
967 if tgt[0] == '/':
968 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
969 os.symlink(tgt, dst)
970 continue
971
972 os.link(src, dst)
973
Mike Frysinger35247af2012-11-16 18:58:06 -0500974 # Locate all the dependencies for all the ELFs. Stick them all in the
975 # top level "lib" dir to make the wrapper simpler. This exact path does
976 # not matter since we execute ldso directly, and we tell the ldso the
977 # exact path to search for its libraries.
978 libdir = os.path.join(output_dir, 'lib')
979 osutils.SafeMakedirs(libdir)
980 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -0400981 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -0500982 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400983 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500984 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -0700985 # Do not create wrapper for libc. crbug.com/766827
986 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -0500987 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400988 interp = os.path.join('/lib', os.path.basename(interp))
989 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
990 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700991 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -0500992
Mike Frysinger221bd822017-09-29 02:51:47 -0400993 # Wrap any symlinks to the wrapper.
994 if elf in sym_paths:
995 link = sym_paths[elf]
996 GeneratePathWrapper(output_dir, link, elf)
997
Mike Frysinger35247af2012-11-16 18:58:06 -0500998 for lib, lib_data in e['libs'].iteritems():
999 if lib in donelibs:
1000 continue
1001
1002 src = path = lib_data['path']
1003 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001004 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001005 continue
1006 donelibs.add(lib)
1007
1008 # Needed libs are the SONAME, but that is usually a symlink, not a
1009 # real file. So link in the target rather than the symlink itself.
1010 # We have to walk all the possible symlinks (SONAME could point to a
1011 # symlink which points to a symlink), and we have to handle absolute
1012 # ourselves (since we have a "root" argument).
1013 dst = os.path.join(libdir, os.path.basename(path))
1014 src = ReadlinkRoot(src, root)
1015
1016 os.link(root + src, dst)
1017
1018
1019def _EnvdGetVar(envd, var):
1020 """Given a Gentoo env.d file, extract a var from it
1021
1022 Args:
1023 envd: The env.d file to load (may be a glob path)
1024 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001025
Mike Frysinger35247af2012-11-16 18:58:06 -05001026 Returns:
1027 The value of |var|
1028 """
1029 envds = glob.glob(envd)
1030 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1031 envd = envds[0]
1032 return cros_build_lib.LoadKeyValueFile(envd)[var]
1033
1034
1035def _ProcessBinutilsConfig(target, output_dir):
1036 """Do what binutils-config would have done"""
1037 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001038
1039 # Locate the bin dir holding the gold linker.
1040 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1041 target, 'binutils-bin')
1042 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001043 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001044 if not srcpath:
1045 # Maybe this target doesn't support gold.
1046 globpath = os.path.join(binutils_bin_path, '*')
1047 srcpath = glob.glob(globpath)
1048 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1049 % globpath)
1050 srcpath = srcpath[0]
1051 ld_path = os.path.join(srcpath, 'ld')
1052 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1053 ld_path = os.path.join(srcpath, 'ld.bfd')
1054 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1055 ld_path = os.path.join(srcpath, 'ld.gold')
1056 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1057 % ld_path)
1058
1059 # Nope, no gold support to be found.
1060 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001061 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001062 else:
1063 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001064 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001065
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001066 # Package the binutils-bin directory without the '-gold' suffix
1067 # if gold is not enabled as the default linker for this target.
1068 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1069 if not gold_supported:
1070 srcpath = srcpath[:-len('-gold')]
1071 ld_path = os.path.join(srcpath, 'ld')
1072 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1073
Mike Frysinger78b7a812014-11-26 19:45:23 -05001074 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001075 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1076 for prog in os.listdir(output_dir + srcpath):
1077 # Skip binaries already wrapped.
1078 if not prog.endswith('.real'):
1079 GeneratePathWrapper(output_dir, binpath + prog,
1080 os.path.join(srcpath, prog))
1081 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1082 os.path.join(srcpath, prog))
1083
David James27ac4ae2012-12-03 23:16:15 -08001084 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001085 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1086 if gold_supported:
1087 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001088 else:
1089 # If gold is not enabled as the default linker and 2 env.d
1090 # files exist, pick the one without the '-gold' suffix.
1091 envds = sorted(glob.glob(envd))
1092 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1093 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001094 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1095 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1096 output_dir + libpath)
1097
1098
1099def _ProcessGccConfig(target, output_dir):
1100 """Do what gcc-config would have done"""
1101 binpath = '/bin'
1102 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1103 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1104 for prog in os.listdir(output_dir + srcpath):
1105 # Skip binaries already wrapped.
1106 if (not prog.endswith('.real') and
1107 not prog.endswith('.elf') and
1108 prog.startswith(target)):
1109 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1110 os.path.join(srcpath, prog))
1111 return srcpath
1112
1113
Frank Henigman179ec7c2015-02-06 03:01:09 -05001114def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1115 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001116 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001117 for sysroot_wrapper in glob.glob(os.path.join(
1118 output_dir + srcpath, 'sysroot_wrapper*')):
1119 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
Douglas Andersoncc828a52017-10-13 13:07:25 -07001120
1121 # In order to optimize startup time in the chroot we run python a little
1122 # differently there. Put it back to the more portable way here.
1123 # See http://crbug.com/773138 for some details.
1124 if contents[0] == '#!/usr/bin/python2 -S':
1125 contents[0] = '#!/usr/bin/env python2'
1126
Frank Henigman179ec7c2015-02-06 03:01:09 -05001127 for num in xrange(len(contents)):
1128 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001129 assert 'True' in contents[num]
1130 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001131 break
1132 # Can't update the wrapper in place since it's a hardlink to a file in /.
1133 os.unlink(sysroot_wrapper)
1134 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1135 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001136
1137
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001138def _CreateMainLibDir(target, output_dir):
1139 """Create some lib dirs so that compiler can get the right Gcc paths"""
1140 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1141 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1142
1143
Mike Frysinger35247af2012-11-16 18:58:06 -05001144def _ProcessDistroCleanups(target, output_dir):
1145 """Clean up the tree and remove all distro-specific requirements
1146
1147 Args:
1148 target: The toolchain target name
1149 output_dir: The output directory to clean up
1150 """
1151 _ProcessBinutilsConfig(target, output_dir)
1152 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001153 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001154 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001155
1156 osutils.RmDir(os.path.join(output_dir, 'etc'))
1157
1158
1159def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1160 """Setup a tree from the packages for the specified target
1161
1162 This populates a path with all the files from toolchain packages so that
1163 a tarball can easily be generated from the result.
1164
1165 Args:
1166 target: The target to create a packagable root from
1167 output_dir: The output directory to place all the files
1168 ldpaths: A dict of static ldpath information
1169 root: The root path to pull all packages/files from
1170 """
1171 # Find all the files owned by the packages for this target.
1172 paths, elfs = _GetFilesForTarget(target, root=root)
1173
1174 # Link in all the package's files, any ELF dependencies, and wrap any
1175 # executable ELFs with helper scripts.
1176 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001177 """Move /usr/bin to /bin so people can just use that toplevel dir
1178
1179 Note we do not apply this to clang - there is correlation between clang's
1180 search path for libraries / inclusion and its installation path.
1181 """
1182 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1183 return path[4:]
1184 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001185 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1186 path_rewrite_func=MoveUsrBinToBin, root=root)
1187
1188 # The packages, when part of the normal distro, have helper scripts
1189 # that setup paths and such. Since we are making this standalone, we
1190 # need to preprocess all that ourselves.
1191 _ProcessDistroCleanups(target, output_dir)
1192
1193
1194def CreatePackages(targets_wanted, output_dir, root='/'):
1195 """Create redistributable cross-compiler packages for the specified targets
1196
1197 This creates toolchain packages that should be usable in conjunction with
1198 a downloaded sysroot (created elsewhere).
1199
1200 Tarballs (one per target) will be created in $PWD.
1201
1202 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001203 targets_wanted: The targets to package up.
1204 output_dir: The directory to put the packages in.
1205 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001206 """
Ralph Nathan03047282015-03-23 11:09:32 -07001207 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001208 osutils.SafeMakedirs(output_dir)
1209 ldpaths = lddtree.LoadLdpaths(root)
1210 targets = ExpandTargets(targets_wanted)
1211
Mike Frysinger221bd822017-09-29 02:51:47 -04001212 with osutils.TempDir(prefix='create-packages') as tempdir:
1213 logging.debug('Using tempdir: %s', tempdir)
1214
Mike Frysinger35247af2012-11-16 18:58:06 -05001215 # We have to split the root generation from the compression stages. This is
1216 # because we hardlink in all the files (to avoid overhead of reading/writing
1217 # the copies multiple times). But tar gets angry if a file's hardlink count
1218 # changes from when it starts reading a file to when it finishes.
1219 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1220 for target in targets:
1221 output_target_dir = os.path.join(tempdir, target)
1222 queue.put([target, output_target_dir, ldpaths, root])
1223
1224 # Build the tarball.
1225 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1226 for target in targets:
1227 tar_file = os.path.join(output_dir, target + '.tar.xz')
1228 queue.put([tar_file, os.path.join(tempdir, target)])
1229
1230
Mike Frysinger07534cf2017-09-12 17:40:21 -04001231def GetParser():
1232 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001233 parser = commandline.ArgumentParser(description=__doc__)
1234 parser.add_argument('-u', '--nousepkg',
1235 action='store_false', dest='usepkg', default=True,
1236 help='Use prebuilt packages if possible')
1237 parser.add_argument('-d', '--deleteold',
1238 action='store_true', dest='deleteold', default=False,
1239 help='Unmerge deprecated packages')
1240 parser.add_argument('-t', '--targets',
1241 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001242 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001243 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001244 "allowed. Defaults to 'sdk'.")
1245 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1246 help='Comma separated list of boards whose toolchains we '
1247 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001248 parser.add_argument('--hostonly',
1249 dest='hostonly', default=False, action='store_true',
1250 help='Only setup the host toolchain. '
1251 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001252 parser.add_argument('--show-board-cfg', '--show-cfg',
1253 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001254 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001255 parser.add_argument('--show-packages', default=None,
1256 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001257 parser.add_argument('--create-packages',
1258 action='store_true', default=False,
1259 help='Build redistributable packages')
1260 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1261 help='Output directory')
1262 parser.add_argument('--reconfig', default=False, action='store_true',
1263 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001264 parser.add_argument('--sysroot', type='path',
1265 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001266 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001267
Mike Frysinger07534cf2017-09-12 17:40:21 -04001268
1269def main(argv):
1270 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001271 options = parser.parse_args(argv)
1272 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001273
Mike Frysinger35247af2012-11-16 18:58:06 -05001274 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001275 conflicting_options = (
1276 options.cfg_name,
1277 options.show_packages,
1278 options.create_packages,
1279 )
1280 if sum(bool(x) for x in conflicting_options) > 1:
1281 parser.error('conflicting options: create-packages & show-packages & '
1282 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001283
Gilad Arnold8195b532015-04-07 10:56:30 +03001284 targets_wanted = set(options.targets.split(','))
1285 boards_wanted = (set(options.include_boards.split(','))
1286 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001287
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001288 if options.cfg_name:
1289 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001290 elif options.show_packages is not None:
1291 cros_build_lib.AssertInsideChroot()
1292 target = options.show_packages
1293 Crossdev.Load(False)
1294 for package in GetTargetPackages(target):
1295 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001296 elif options.create_packages:
1297 cros_build_lib.AssertInsideChroot()
1298 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001299 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001300 else:
1301 cros_build_lib.AssertInsideChroot()
1302 # This has to be always run as root.
1303 if os.geteuid() != 0:
1304 cros_build_lib.Die('this script must be run as root')
1305
1306 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001307 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001308 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001309 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001310 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001311 Crossdev.Save()
1312
1313 return 0