blob: f74a3a3d1379009856075bb77d16c6b078b85c54 [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',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080054 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040055 'sys-devel/binutils',
56 'sys-devel/clang',
57 'sys-devel/gcc',
Yunlian Jiangf5721f32017-10-31 11:43:11 -070058 'sys-devel/lld',
Mike Frysinger66bfde52017-09-12 16:42:57 -040059 'sys-devel/llvm',
60 'sys-kernel/linux-headers',
61 'sys-libs/glibc',
62 'sys-libs/libcxx',
63 'sys-libs/libcxxabi',
64)
65
Mike Frysinger785b0c32017-09-13 01:35:59 -040066# These packages are also installed into the host SDK. However, they require
67# the cross-compilers to be installed first (because they need them to actually
68# build), so we have to delay their installation.
69HOST_POST_CROSS_PACKAGES = (
Mike Frysinger61a24392017-10-17 17:14:27 -040070 'virtual/target-sdk-post-cross',
Mike Frysinger785b0c32017-09-13 01:35:59 -040071)
72
73# New packages that we're in the process of adding to the SDK. Since the SDK
74# bot hasn't had a chance to run yet, there are no binary packages available,
75# so we have to list them here and wait. Once it completes, entries here can
76# be removed so they'll end up on bots & dev's systems.
77NEW_PACKAGES = (
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 Chaudhry4d416582017-10-25 12:31:58 -070085 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070086)
87CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
88
Manoj Gupta1b5642e2017-03-08 16:44:12 -080089# Enable llvm's compiler-rt for these targets.
90TARGET_COMPILER_RT_ENABLED = (
91 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070092 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070093 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -080094 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080095)
96CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
97
Manoj Gupta946abb42017-04-12 14:27:19 -070098TARGET_LLVM_PKGS_ENABLED = (
99 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700100 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700101 'aarch64-cros-linux-gnu',
102 'x86_64-cros-linux-gnu',
103)
104
105LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700106 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
107 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700108}
109
Zdenek Behan508dcce2011-12-05 15:39:32 +0100110# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
111CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500112 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -0400113 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -0800114 'armv7a-cros-linux-gnueabi': '-gold',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700115 'armv7a-cros-linux-gnueabihf': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500116 'i686-pc-linux-gnu' : '-gold',
117 'x86_64-cros-linux-gnu' : '-gold',
118 },
Zdenek Behan508dcce2011-12-05 15:39:32 +0100119}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100120
121
David James66a09c42012-11-05 13:31:38 -0800122class Crossdev(object):
123 """Class for interacting with crossdev and caching its output."""
124
125 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
126 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800127 # Packages that needs separate handling, in addition to what we have from
128 # crossdev.
129 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700130 'clang': 'sys-devel',
Yunlian Jiang18ae9982017-11-03 09:15:31 -0700131 'lld': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800132 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700133 'libcxxabi': 'sys-libs',
134 'libcxx': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800135 }
David James66a09c42012-11-05 13:31:38 -0800136
137 @classmethod
138 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400139 """Load crossdev cache from disk.
140
141 We invalidate the cache when crossdev updates or this script changes.
142 """
David James90239b92012-11-05 15:31:34 -0800143 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400144 # If we run the compiled/cached .pyc file, we'll read/hash that when we
145 # really always want to track the source .py file.
146 script = os.path.abspath(__file__)
147 if script.endswith('.pyc'):
148 script = script[:-1]
149 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
150
151 cls._CACHE = {
152 'crossdev_version': crossdev_version,
153 'setup_toolchains_hash': setup_toolchains_hash,
154 }
155
156 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
157 if reconfig:
158 logging.debug('cache: forcing regen due to reconfig')
159 return
160
161 try:
162 file_data = osutils.ReadFile(cls._CACHE_FILE)
163 except IOError as e:
164 if e.errno != errno.ENOENT:
165 logging.warning('cache: reading failed: %s', e)
166 osutils.SafeUnlink(cls._CACHE_FILE)
167 return
168
169 try:
170 data = json.loads(file_data)
171 except ValueError as e:
172 logging.warning('cache: ignoring invalid content: %s', e)
173 return
174
175 if crossdev_version != data.get('crossdev_version'):
176 logging.debug('cache: rebuilding after crossdev upgrade')
177 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
178 logging.debug('cache: rebuilding after cros_setup_toolchains change')
179 else:
180 logging.debug('cache: content is up-to-date!')
181 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800182
183 @classmethod
184 def Save(cls):
185 """Store crossdev cache on disk."""
186 # Save the cache from the successful run.
187 with open(cls._CACHE_FILE, 'w') as f:
188 json.dump(cls._CACHE, f)
189
190 @classmethod
191 def GetConfig(cls, target):
192 """Returns a map of crossdev provided variables about a tuple."""
193 CACHE_ATTR = '_target_tuple_map'
194
195 val = cls._CACHE.setdefault(CACHE_ATTR, {})
196 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400197 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400198 conf = {
199 'crosspkgs': [],
200 'target': toolchain.GetHostTuple(),
201 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400202 if target == 'host':
203 packages_list = HOST_PACKAGES
204 else:
205 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400206 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400207 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400208 else:
209 # Build the crossdev command.
210 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
211 if target in TARGET_COMPILER_RT_ENABLED:
212 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
213 if target in TARGET_GO_ENABLED:
214 cmd.extend(CROSSDEV_GO_ARGS)
215 if target in TARGET_LLVM_PKGS_ENABLED:
216 for pkg in LLVM_PKGS_TABLE:
217 cmd.extend(LLVM_PKGS_TABLE[pkg])
218 cmd.extend(['-t', target])
219 # Catch output of crossdev.
220 out = cros_build_lib.RunCommand(
221 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
222 # List of tuples split at the first '=', converted into dict.
223 conf = dict((k, cros_build_lib.ShellUnquote(v))
224 for k, v in (x.split('=', 1) for x in out))
225 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800226
Mike Frysinger66bfde52017-09-12 16:42:57 -0400227 manual_pkgs = cls.MANUAL_PKGS
228
229 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400230 conf[pkg + '_pn'] = pkg
231 conf[pkg + '_category'] = cat
232 if pkg not in conf['crosspkgs']:
233 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800234
235 val[target] = conf
236
David James66a09c42012-11-05 13:31:38 -0800237 return val[target]
238
239 @classmethod
240 def UpdateTargets(cls, targets, usepkg, config_only=False):
241 """Calls crossdev to initialize a cross target.
242
243 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700244 targets: The list of targets to initialize using crossdev.
245 usepkg: Copies the commandline opts.
246 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800247 """
248 configured_targets = cls._CACHE.setdefault('configured_targets', [])
249
250 cmdbase = ['crossdev', '--show-fail-log']
251 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
252 # Pick stable by default, and override as necessary.
253 cmdbase.extend(['-P', '--oneshot'])
254 if usepkg:
255 cmdbase.extend(['-P', '--getbinpkg',
256 '-P', '--usepkgonly',
257 '--without-headers'])
258
Christopher Wileyb22c0712015-06-02 10:37:03 -0700259 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800260 cmdbase.extend(['--overlays', overlays])
261 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
262
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700263 # Build target by the alphabetical order to make sure
264 # armv7a-cros-linux-gnueabihf builds after armv7a-cros-linux-gnueabi
265 # because some dependency issue. This can be reverted once we
266 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
267 for target in sorted(targets):
David James66a09c42012-11-05 13:31:38 -0800268 if config_only and target in configured_targets:
269 continue
270
271 cmd = cmdbase + ['-t', target]
272
273 for pkg in GetTargetPackages(target):
274 if pkg == 'gdb':
275 # Gdb does not have selectable versions.
276 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700277 elif pkg == 'ex_compiler-rt':
278 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700279 elif pkg == 'ex_go':
280 # Go does not have selectable versions.
281 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700282 elif pkg in LLVM_PKGS_TABLE:
283 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800284 elif pkg in cls.MANUAL_PKGS:
285 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700286 else:
287 # The first of the desired versions is the "primary" one.
288 version = GetDesiredPackageVersions(target, pkg)[0]
289 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800290
291 cmd.extend(targets[target]['crossdev'].split())
292 if config_only:
293 # In this case we want to just quietly reinit
294 cmd.append('--init-target')
295 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
296 else:
297 cros_build_lib.RunCommand(cmd)
298
299 configured_targets.append(target)
300
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100301
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100302def GetTargetPackages(target):
303 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800304 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100305 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800306 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100307
308
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100309# Portage helper functions:
310def GetPortagePackage(target, package):
311 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800312 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100313 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400314 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100315 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100316 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100317 category = conf['category']
318 # Portage package:
319 pn = conf[package + '_pn']
320 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500321 assert category
322 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100323 return '%s/%s' % (category, pn)
324
325
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700326def PortageTrees(root):
327 """Return the portage trees for a given root."""
328 if root == '/':
329 return portage.db['/']
330 # The portage logic requires the path always end in a slash.
331 root = root.rstrip('/') + '/'
332 return portage.create_trees(target_root=root, config_root=root)[root]
333
334
335def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100336 """Extracts the list of current versions of a target, package pair.
337
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500338 Args:
339 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700340 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500342 Returns:
343 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100344 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100345 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500346 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700347 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100348 version = portage.versions.cpv_getversion(pkg)
349 versions.append(version)
350 return versions
351
352
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700353def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100354 """Extracts the current stable version for a given package.
355
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500356 Args:
357 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
358 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700359 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500361 Returns:
362 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100363 """
David James90239b92012-11-05 15:31:34 -0800364 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500365 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700366 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800367 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100368
369
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700370def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100371 """Resolves keywords in a given version list for a particular package.
372
373 Resolving means replacing PACKAGE_STABLE with the actual number.
374
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500375 Args:
376 target: The target to operate on (e.g. i686-pc-linux-gnu)
377 package: The target/package to operate on (e.g. gcc)
378 versions: List of versions to resolve
379 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700380 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100381
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500382 Returns:
383 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100384 """
385 resolved = []
David James90239b92012-11-05 15:31:34 -0800386 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700387 if not installed:
388 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100389 for version in versions:
390 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700391 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400392 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100393 resolved.append(version)
394 return resolved
395
396
397def GetDesiredPackageVersions(target, package):
398 """Produces the list of desired versions for each target, package pair.
399
400 The first version in the list is implicitly treated as primary, ie.
401 the version that will be initialized by crossdev and selected.
402
403 If the version is PACKAGE_STABLE, it really means the current version which
404 is emerged by using the package atom with no particular version key.
405 Since crossdev unmasks all packages by default, this will actually
406 mean 'unstable' in most cases.
407
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500408 Args:
409 target: The target to operate on (e.g. i686-pc-linux-gnu)
410 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100411
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500412 Returns:
413 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100414 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400415 if package in GetTargetPackages(target):
416 return [PACKAGE_STABLE]
417 else:
418 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419
420
421def TargetIsInitialized(target):
422 """Verifies if the given list of targets has been correctly initialized.
423
424 This determines whether we have to call crossdev while emerging
425 toolchain packages or can do it using emerge. Emerge is naturally
426 preferred, because all packages can be updated in a single pass.
427
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500428 Args:
429 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100430
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500431 Returns:
432 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433 """
434 # Check if packages for the given target all have a proper version.
435 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100436 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800437 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100438 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400439 if not (GetStablePackageVersion(atom, True) and
440 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100441 return False
442 return True
443 except cros_build_lib.RunCommandError:
444 # Fails - The target has likely never been initialized before.
445 return False
446
447
448def RemovePackageMask(target):
449 """Removes a package.mask file for the given platform.
450
451 The pre-existing package.mask files can mess with the keywords.
452
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500453 Args:
454 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455 """
456 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700457 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100458
459
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700461def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500462 """Rebuild libtool as needed
463
464 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
465 gcc, libtool will break. We can't use binary packages either as those will
466 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700467
468 Args:
469 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500470 """
471 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700472 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500473 for line in f:
474 # Look for a line like:
475 # sys_lib_search_path_spec="..."
476 # It'll be a list of paths and gcc will be one of them.
477 if line.startswith('sys_lib_search_path_spec='):
478 line = line.rstrip()
479 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400480 root_path = os.path.join(root, path.lstrip(os.path.sep))
481 logging.debug('Libtool: checking %s', root_path)
482 if not os.path.exists(root_path):
483 logging.info('Rebuilding libtool after gcc upgrade')
484 logging.info(' %s', line)
485 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500486 needs_update = True
487 break
488
489 if needs_update:
490 break
491
492 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700493 cmd = [EMERGE_CMD, '--oneshot']
494 if root != '/':
495 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
496 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500497 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400498 else:
499 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500500
501
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700502def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100503 """Determines which packages need update/unmerge and defers to portage.
504
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500505 Args:
506 targets: The list of targets to update
507 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700508 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100509 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510 # For each target, we do two things. Figure out the list of updates,
511 # and figure out the appropriate keywords/masks. Crossdev will initialize
512 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400513 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800514 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100515 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400516 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517 # Record the highest needed version for each target, for masking purposes.
518 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100519 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400521 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100522 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700523 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100524 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200525 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400526 if pkg in NEW_PACKAGES and usepkg:
527 # Skip this binary package (for now).
528 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100529 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400530 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100531
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400532 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100533 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400534 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800535 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536
Mike Frysinger3bba5032016-09-20 14:15:04 -0400537 logging.info('Updating packages:')
538 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100540 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541 if usepkg:
542 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700543 if root != '/':
544 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100545
546 cmd.extend(packages)
547 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800548 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100549
550
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700551def CleanTargets(targets, root='/'):
552 """Unmerges old packages that are assumed unnecessary.
553
554 Args:
555 targets: The list of targets to clean up.
556 root: The install root in which we want packages cleaned up.
557 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558 unmergemap = {}
559 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400560 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100561 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400562 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100563 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700564 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100565 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700566 # NOTE: This refers to installed packages (vartree) rather than the
567 # Portage version (porttree and/or bintree) when determining the current
568 # version. While this isn't the most accurate thing to do, it is probably
569 # a good simple compromise, which should have the desired result of
570 # uninstalling everything but the latest installed version. In
571 # particular, using the bintree (--usebinpkg) requires a non-trivial
572 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200573 desired_num = VersionListToNumeric(target, package, desired, True)
574 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400575 logging.warning('Error detecting stable version for %s, '
576 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200577 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100578 unmergemap[pkg] = set(current).difference(desired_num)
579
580 # Cleaning doesn't care about consistency and rebuilding package.* files.
581 packages = []
582 for pkg, vers in unmergemap.iteritems():
583 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
584
585 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400586 logging.info('Cleaning packages:')
587 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100588 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700589 if root != '/':
590 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100591 cmd.extend(packages)
592 cros_build_lib.RunCommand(cmd)
593 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400594 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595
596
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700597def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100598 """Runs gcc-config and binutils-config to select the desired.
599
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500600 Args:
601 targets: The targets to select
602 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700603 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100604 """
605 for package in ['gcc', 'binutils']:
606 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400607 # See if this package is part of this target.
608 if package not in GetTargetPackages(target):
609 logging.debug('%s: %s is not used', target, package)
610 continue
611
Zdenek Behan508dcce2011-12-05 15:39:32 +0100612 # Pick the first version in the numbered list as the selected one.
613 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700614 desired_num = VersionListToNumeric(target, package, desired, True,
615 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100616 desired = desired_num[0]
617 # *-config does not play revisions, strip them, keep just PV.
618 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
619
Mike Frysinger785b0c32017-09-13 01:35:59 -0400620 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100621 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800622 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100623
624 # And finally, attach target to it.
625 desired = '%s-%s' % (target, desired)
626
627 # Target specific hacks
628 if package in suffixes:
629 if target in suffixes[package]:
630 desired += suffixes[package][target]
631
David James7ec5efc2012-11-06 09:39:49 -0800632 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700633 if root != '/':
634 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800635 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500636 result = cros_build_lib.RunCommand(
637 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
638 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700639
640 # Do not reconfig when the current is live or nothing needs to be done.
641 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100642 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500643 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700644 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100645
646
Mike Frysinger35247af2012-11-16 18:58:06 -0500647def ExpandTargets(targets_wanted):
648 """Expand any possible toolchain aliases into full targets
649
650 This will expand 'all' and 'sdk' into the respective toolchain tuples.
651
652 Args:
653 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500654
Mike Frysinger35247af2012-11-16 18:58:06 -0500655 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300656 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500657 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500658 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700659 if targets_wanted == set(['boards']):
660 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300661 return {}
662
663 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500664 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300665 return all_targets
666 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500667 # Filter out all the non-sdk toolchains as we don't want to mess
668 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300669 return toolchain.FilterToolchains(all_targets, 'sdk', True)
670
671 # Verify user input.
672 nonexistent = targets_wanted.difference(all_targets)
673 if nonexistent:
674 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
675 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500676
677
David Jamesf8c672f2012-11-06 13:38:11 -0800678def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700679 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100680 """Performs all steps to create a synchronized toolchain enviroment.
681
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500682 Args:
683 usepkg: Use prebuilt packages
684 deleteold: Unmerge deprecated packages
685 hostonly: Only setup the host toolchain
686 reconfig: Reload crossdev config and reselect toolchains
687 targets_wanted: All the targets to update
688 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700689 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100690 """
David Jamesf8c672f2012-11-06 13:38:11 -0800691 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100692 if not hostonly:
693 # For hostonly, we can skip most of the below logic, much of which won't
694 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500695 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400696
Don Garrettc0c74002015-10-09 12:58:19 -0700697 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300698 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400699 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800700 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100701
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100702 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400703 for target in targets:
704 if TargetIsInitialized(target):
705 reconfig_targets[target] = targets[target]
706 else:
707 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100708 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400709 logging.info('The following targets need to be re-initialized:')
710 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800711 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200712 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800713 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100714
Mike Frysinger66814c32017-10-09 18:11:46 -0400715 # If we're building a subset of toolchains for a board, we might not have
716 # all the tuples that the packages expect. We don't define the "full" set
717 # of tuples currently other than "whatever the full sdk has normally".
718 if usepkg or set(('all', 'sdk')) & targets_wanted:
719 # Since we have cross-compilers now, we can update these packages.
720 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400721
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100722 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400723 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100724
725 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700726 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
727 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800728
729 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700730 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100731
Mike Frysingerc880a962013-11-08 13:59:06 -0500732 # Now that we've cleared out old versions, see if we need to rebuild
733 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700734 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500735
Zdenek Behan508dcce2011-12-05 15:39:32 +0100736
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700737def ShowConfig(name):
738 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500739
740 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700741 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500742 """
Don Garrettc0c74002015-10-09 12:58:19 -0700743 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500744 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400745 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400746 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800747 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400748 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500749
750
Mike Frysinger35247af2012-11-16 18:58:06 -0500751def GeneratePathWrapper(root, wrappath, path):
752 """Generate a shell script to execute another shell script
753
754 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
755 argv[0] won't be pointing to the correct path, generate a shell script that
756 just executes another program with its full path.
757
758 Args:
759 root: The root tree to generate scripts inside of
760 wrappath: The full path (inside |root|) to create the wrapper
761 path: The target program which this wrapper will execute
762 """
763 replacements = {
764 'path': path,
765 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
766 }
767 wrapper = """#!/bin/sh
768base=$(realpath "$0")
769basedir=${base%%/*}
770exec "${basedir}/%(relroot)s%(path)s" "$@"
771""" % replacements
772 root_wrapper = root + wrappath
773 if os.path.islink(root_wrapper):
774 os.unlink(root_wrapper)
775 else:
776 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
777 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400778 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500779
780
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700781def FixClangXXWrapper(root, path):
782 """Fix wrapper shell scripts and symlinks for invoking clang++
783
784 In a typical installation, clang++ symlinks to clang, which symlinks to the
785 elf executable. The executable distinguishes between clang and clang++ based
786 on argv[0].
787
788 When invoked through the LdsoWrapper, argv[0] always contains the path to the
789 executable elf file, making clang/clang++ invocations indistinguishable.
790
791 This function detects if the elf executable being wrapped is clang-X.Y, and
792 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
793
794 The calling sequence now becomes:
795 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
796 the Ldsowrapper).
797 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
798 to the original clang-3.9 elf.
799 -) The difference this time is that inside the elf file execution, $0 is
800 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
801
802 Args:
803 root: The root tree to generate scripts / symlinks inside of
804 path: The target elf for which LdsoWrapper was created
805 """
806 if re.match(r'/usr/bin/clang-\d+\.\d+$', path):
807 logging.info('fixing clang++ invocation for %s', path)
808 clangdir = os.path.dirname(root + path)
809 clang = os.path.basename(path)
810 clangxx = clang.replace('clang', 'clang++')
811
812 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
813 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
814
815 # Create a hardlink clang++-X.Y pointing to clang-X.Y
816 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
817
818 # Adjust the clang++ symlink to point to clang++-X.Y
819 os.unlink(os.path.join(clangdir, 'clang++'))
820 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
821
822
Mike Frysinger35247af2012-11-16 18:58:06 -0500823def FileIsCrosSdkElf(elf):
824 """Determine if |elf| is an ELF that we execute in the cros_sdk
825
826 We don't need this to be perfect, just quick. It makes sure the ELF
827 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
828
829 Args:
830 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500831
Mike Frysinger35247af2012-11-16 18:58:06 -0500832 Returns:
833 True if we think |elf| is a native ELF
834 """
835 with open(elf) as f:
836 data = f.read(20)
837 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
838 return (data[0:4] == '\x7fELF' and
839 data[4] == '\x02' and
840 data[5] == '\x01' and
841 data[18] == '\x3e')
842
843
844def IsPathPackagable(ptype, path):
845 """Should the specified file be included in a toolchain package?
846
847 We only need to handle files as we'll create dirs as we need them.
848
849 Further, trim files that won't be useful:
850 - non-english translations (.mo) since it'd require env vars
851 - debug files since these are for the host compiler itself
852 - info/man pages as they're big, and docs are online, and the
853 native docs should work fine for the most part (`man gcc`)
854
855 Args:
856 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
857 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500858
Mike Frysinger35247af2012-11-16 18:58:06 -0500859 Returns:
860 True if we want to include this path in the package
861 """
862 return not (ptype in ('dir',) or
863 path.startswith('/usr/lib/debug/') or
864 os.path.splitext(path)[1] == '.mo' or
865 ('/man/' in path or '/info/' in path))
866
867
868def ReadlinkRoot(path, root):
869 """Like os.readlink(), but relative to a |root|
870
871 Args:
872 path: The symlink to read
873 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500874
Mike Frysinger35247af2012-11-16 18:58:06 -0500875 Returns:
876 A fully resolved symlink path
877 """
878 while os.path.islink(root + path):
879 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
880 return path
881
882
883def _GetFilesForTarget(target, root='/'):
884 """Locate all the files to package for |target|
885
886 This does not cover ELF dependencies.
887
888 Args:
889 target: The toolchain target name
890 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500891
Mike Frysinger35247af2012-11-16 18:58:06 -0500892 Returns:
893 A tuple of a set of all packable paths, and a set of all paths which
894 are also native ELFs
895 """
896 paths = set()
897 elfs = set()
898
899 # Find all the files owned by the packages for this target.
900 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500901
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700902 # Skip Go compiler from redistributable packages.
903 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
904 # into it. Due to this, the toolchain cannot be unpacked anywhere
905 # else and be readily useful. To enable packaging Go, we need to:
906 # -) Tweak the wrappers/environment to override GOROOT
907 # automatically based on the unpack location.
908 # -) Make sure the ELF dependency checking and wrapping logic
909 # below skips the Go toolchain executables and libraries.
910 # -) Make sure the packaging process maintains the relative
911 # timestamps of precompiled standard library packages.
912 # (see dev-lang/go ebuild for details).
913 if pkg == 'ex_go':
914 continue
915
Mike Frysinger35247af2012-11-16 18:58:06 -0500916 atom = GetPortagePackage(target, pkg)
917 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700918 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700919 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500920
921 # pylint: disable=E1101
922 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
923 settings=portage.settings)
924 contents = dblink.getcontents()
925 for obj in contents:
926 ptype = contents[obj][0]
927 if not IsPathPackagable(ptype, obj):
928 continue
929
930 if ptype == 'obj':
931 # For native ELFs, we need to pull in their dependencies too.
932 if FileIsCrosSdkElf(obj):
933 elfs.add(obj)
934 paths.add(obj)
935
936 return paths, elfs
937
938
939def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500940 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500941 """Link in all packable files and their runtime dependencies
942
943 This also wraps up executable ELFs with helper scripts.
944
945 Args:
946 output_dir: The output directory to store files
947 paths: All the files to include
948 elfs: All the files which are ELFs (a subset of |paths|)
949 ldpaths: A dict of static ldpath information
950 path_rewrite_func: User callback to rewrite paths in output_dir
951 root: The root path to pull all packages/files from
952 """
953 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400954 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500955 for path in paths:
956 new_path = path_rewrite_func(path)
957 dst = output_dir + new_path
958 osutils.SafeMakedirs(os.path.dirname(dst))
959
960 # Is this a symlink which we have to rewrite or wrap?
961 # Delay wrap check until after we have created all paths.
962 src = root + path
963 if os.path.islink(src):
964 tgt = os.readlink(src)
965 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -0400966 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -0500967
968 # Rewrite absolute links to relative and then generate the symlink
969 # ourselves. All other symlinks can be hardlinked below.
970 if tgt[0] == '/':
971 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
972 os.symlink(tgt, dst)
973 continue
974
975 os.link(src, dst)
976
Mike Frysinger35247af2012-11-16 18:58:06 -0500977 # Locate all the dependencies for all the ELFs. Stick them all in the
978 # top level "lib" dir to make the wrapper simpler. This exact path does
979 # not matter since we execute ldso directly, and we tell the ldso the
980 # exact path to search for its libraries.
981 libdir = os.path.join(output_dir, 'lib')
982 osutils.SafeMakedirs(libdir)
983 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -0400984 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -0500985 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400986 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500987 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -0700988 # Do not create wrapper for libc. crbug.com/766827
989 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -0500990 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400991 interp = os.path.join('/lib', os.path.basename(interp))
992 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
993 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700994 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -0500995
Mike Frysinger221bd822017-09-29 02:51:47 -0400996 # Wrap any symlinks to the wrapper.
997 if elf in sym_paths:
998 link = sym_paths[elf]
999 GeneratePathWrapper(output_dir, link, elf)
1000
Mike Frysinger35247af2012-11-16 18:58:06 -05001001 for lib, lib_data in e['libs'].iteritems():
1002 if lib in donelibs:
1003 continue
1004
1005 src = path = lib_data['path']
1006 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001007 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001008 continue
1009 donelibs.add(lib)
1010
1011 # Needed libs are the SONAME, but that is usually a symlink, not a
1012 # real file. So link in the target rather than the symlink itself.
1013 # We have to walk all the possible symlinks (SONAME could point to a
1014 # symlink which points to a symlink), and we have to handle absolute
1015 # ourselves (since we have a "root" argument).
1016 dst = os.path.join(libdir, os.path.basename(path))
1017 src = ReadlinkRoot(src, root)
1018
1019 os.link(root + src, dst)
1020
1021
1022def _EnvdGetVar(envd, var):
1023 """Given a Gentoo env.d file, extract a var from it
1024
1025 Args:
1026 envd: The env.d file to load (may be a glob path)
1027 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001028
Mike Frysinger35247af2012-11-16 18:58:06 -05001029 Returns:
1030 The value of |var|
1031 """
1032 envds = glob.glob(envd)
1033 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1034 envd = envds[0]
1035 return cros_build_lib.LoadKeyValueFile(envd)[var]
1036
1037
1038def _ProcessBinutilsConfig(target, output_dir):
1039 """Do what binutils-config would have done"""
1040 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001041
1042 # Locate the bin dir holding the gold linker.
1043 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1044 target, 'binutils-bin')
1045 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001046 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001047 if not srcpath:
1048 # Maybe this target doesn't support gold.
1049 globpath = os.path.join(binutils_bin_path, '*')
1050 srcpath = glob.glob(globpath)
1051 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1052 % globpath)
1053 srcpath = srcpath[0]
1054 ld_path = os.path.join(srcpath, 'ld')
1055 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1056 ld_path = os.path.join(srcpath, 'ld.bfd')
1057 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1058 ld_path = os.path.join(srcpath, 'ld.gold')
1059 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1060 % ld_path)
1061
1062 # Nope, no gold support to be found.
1063 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001064 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001065 else:
1066 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001067 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001068
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001069 # Package the binutils-bin directory without the '-gold' suffix
1070 # if gold is not enabled as the default linker for this target.
1071 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1072 if not gold_supported:
1073 srcpath = srcpath[:-len('-gold')]
1074 ld_path = os.path.join(srcpath, 'ld')
1075 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1076
Mike Frysinger78b7a812014-11-26 19:45:23 -05001077 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001078 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1079 for prog in os.listdir(output_dir + srcpath):
1080 # Skip binaries already wrapped.
1081 if not prog.endswith('.real'):
1082 GeneratePathWrapper(output_dir, binpath + prog,
1083 os.path.join(srcpath, prog))
1084 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1085 os.path.join(srcpath, prog))
1086
David James27ac4ae2012-12-03 23:16:15 -08001087 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001088 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1089 if gold_supported:
1090 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001091 else:
1092 # If gold is not enabled as the default linker and 2 env.d
1093 # files exist, pick the one without the '-gold' suffix.
1094 envds = sorted(glob.glob(envd))
1095 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1096 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001097 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1098 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1099 output_dir + libpath)
1100
1101
1102def _ProcessGccConfig(target, output_dir):
1103 """Do what gcc-config would have done"""
1104 binpath = '/bin'
1105 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1106 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1107 for prog in os.listdir(output_dir + srcpath):
1108 # Skip binaries already wrapped.
1109 if (not prog.endswith('.real') and
1110 not prog.endswith('.elf') and
1111 prog.startswith(target)):
1112 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1113 os.path.join(srcpath, prog))
1114 return srcpath
1115
1116
Frank Henigman179ec7c2015-02-06 03:01:09 -05001117def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1118 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001119 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001120 for sysroot_wrapper in glob.glob(os.path.join(
1121 output_dir + srcpath, 'sysroot_wrapper*')):
1122 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
Douglas Andersoncc828a52017-10-13 13:07:25 -07001123
1124 # In order to optimize startup time in the chroot we run python a little
1125 # differently there. Put it back to the more portable way here.
1126 # See http://crbug.com/773138 for some details.
1127 if contents[0] == '#!/usr/bin/python2 -S':
1128 contents[0] = '#!/usr/bin/env python2'
1129
Frank Henigman179ec7c2015-02-06 03:01:09 -05001130 for num in xrange(len(contents)):
1131 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001132 assert 'True' in contents[num]
1133 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001134 break
1135 # Can't update the wrapper in place since it's a hardlink to a file in /.
1136 os.unlink(sysroot_wrapper)
1137 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1138 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001139
1140
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001141def _CreateMainLibDir(target, output_dir):
1142 """Create some lib dirs so that compiler can get the right Gcc paths"""
1143 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1144 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1145
1146
Mike Frysinger35247af2012-11-16 18:58:06 -05001147def _ProcessDistroCleanups(target, output_dir):
1148 """Clean up the tree and remove all distro-specific requirements
1149
1150 Args:
1151 target: The toolchain target name
1152 output_dir: The output directory to clean up
1153 """
1154 _ProcessBinutilsConfig(target, output_dir)
1155 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001156 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001157 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001158
1159 osutils.RmDir(os.path.join(output_dir, 'etc'))
1160
1161
1162def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1163 """Setup a tree from the packages for the specified target
1164
1165 This populates a path with all the files from toolchain packages so that
1166 a tarball can easily be generated from the result.
1167
1168 Args:
1169 target: The target to create a packagable root from
1170 output_dir: The output directory to place all the files
1171 ldpaths: A dict of static ldpath information
1172 root: The root path to pull all packages/files from
1173 """
1174 # Find all the files owned by the packages for this target.
1175 paths, elfs = _GetFilesForTarget(target, root=root)
1176
1177 # Link in all the package's files, any ELF dependencies, and wrap any
1178 # executable ELFs with helper scripts.
1179 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001180 """Move /usr/bin to /bin so people can just use that toplevel dir
1181
1182 Note we do not apply this to clang - there is correlation between clang's
1183 search path for libraries / inclusion and its installation path.
1184 """
1185 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1186 return path[4:]
1187 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001188 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1189 path_rewrite_func=MoveUsrBinToBin, root=root)
1190
1191 # The packages, when part of the normal distro, have helper scripts
1192 # that setup paths and such. Since we are making this standalone, we
1193 # need to preprocess all that ourselves.
1194 _ProcessDistroCleanups(target, output_dir)
1195
1196
1197def CreatePackages(targets_wanted, output_dir, root='/'):
1198 """Create redistributable cross-compiler packages for the specified targets
1199
1200 This creates toolchain packages that should be usable in conjunction with
1201 a downloaded sysroot (created elsewhere).
1202
1203 Tarballs (one per target) will be created in $PWD.
1204
1205 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001206 targets_wanted: The targets to package up.
1207 output_dir: The directory to put the packages in.
1208 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001209 """
Ralph Nathan03047282015-03-23 11:09:32 -07001210 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001211 osutils.SafeMakedirs(output_dir)
1212 ldpaths = lddtree.LoadLdpaths(root)
1213 targets = ExpandTargets(targets_wanted)
1214
Mike Frysinger221bd822017-09-29 02:51:47 -04001215 with osutils.TempDir(prefix='create-packages') as tempdir:
1216 logging.debug('Using tempdir: %s', tempdir)
1217
Mike Frysinger35247af2012-11-16 18:58:06 -05001218 # We have to split the root generation from the compression stages. This is
1219 # because we hardlink in all the files (to avoid overhead of reading/writing
1220 # the copies multiple times). But tar gets angry if a file's hardlink count
1221 # changes from when it starts reading a file to when it finishes.
1222 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1223 for target in targets:
1224 output_target_dir = os.path.join(tempdir, target)
1225 queue.put([target, output_target_dir, ldpaths, root])
1226
1227 # Build the tarball.
1228 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1229 for target in targets:
1230 tar_file = os.path.join(output_dir, target + '.tar.xz')
1231 queue.put([tar_file, os.path.join(tempdir, target)])
1232
1233
Mike Frysinger07534cf2017-09-12 17:40:21 -04001234def GetParser():
1235 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001236 parser = commandline.ArgumentParser(description=__doc__)
1237 parser.add_argument('-u', '--nousepkg',
1238 action='store_false', dest='usepkg', default=True,
1239 help='Use prebuilt packages if possible')
1240 parser.add_argument('-d', '--deleteold',
1241 action='store_true', dest='deleteold', default=False,
1242 help='Unmerge deprecated packages')
1243 parser.add_argument('-t', '--targets',
1244 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001245 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001246 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001247 "allowed. Defaults to 'sdk'.")
1248 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1249 help='Comma separated list of boards whose toolchains we '
1250 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001251 parser.add_argument('--hostonly',
1252 dest='hostonly', default=False, action='store_true',
1253 help='Only setup the host toolchain. '
1254 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001255 parser.add_argument('--show-board-cfg', '--show-cfg',
1256 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001257 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001258 parser.add_argument('--show-packages', default=None,
1259 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001260 parser.add_argument('--create-packages',
1261 action='store_true', default=False,
1262 help='Build redistributable packages')
1263 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1264 help='Output directory')
1265 parser.add_argument('--reconfig', default=False, action='store_true',
1266 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001267 parser.add_argument('--sysroot', type='path',
1268 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001269 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001270
Mike Frysinger07534cf2017-09-12 17:40:21 -04001271
1272def main(argv):
1273 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001274 options = parser.parse_args(argv)
1275 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001276
Mike Frysinger35247af2012-11-16 18:58:06 -05001277 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001278 conflicting_options = (
1279 options.cfg_name,
1280 options.show_packages,
1281 options.create_packages,
1282 )
1283 if sum(bool(x) for x in conflicting_options) > 1:
1284 parser.error('conflicting options: create-packages & show-packages & '
1285 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001286
Gilad Arnold8195b532015-04-07 10:56:30 +03001287 targets_wanted = set(options.targets.split(','))
1288 boards_wanted = (set(options.include_boards.split(','))
1289 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001290
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001291 if options.cfg_name:
1292 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001293 elif options.show_packages is not None:
1294 cros_build_lib.AssertInsideChroot()
1295 target = options.show_packages
1296 Crossdev.Load(False)
1297 for package in GetTargetPackages(target):
1298 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001299 elif options.create_packages:
1300 cros_build_lib.AssertInsideChroot()
1301 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001302 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001303 else:
1304 cros_build_lib.AssertInsideChroot()
1305 # This has to be always run as root.
1306 if os.geteuid() != 0:
1307 cros_build_lib.Die('this script must be run as root')
1308
1309 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001310 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001311 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001312 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001313 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001314 Crossdev.Save()
1315
1316 return 0