blob: 1b4c1a97328b8fa66546c1b8fb87e641c2632d98 [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 = (
Mike Frysinger61a24392017-10-17 17:14:27 -040068 'virtual/target-sdk-post-cross',
Mike Frysinger785b0c32017-09-13 01:35:59 -040069)
70
71# New packages that we're in the process of adding to the SDK. Since the SDK
72# bot hasn't had a chance to run yet, there are no binary packages available,
73# so we have to list them here and wait. Once it completes, entries here can
74# be removed so they'll end up on bots & dev's systems.
75NEW_PACKAGES = (
Mike Frysinger785b0c32017-09-13 01:35:59 -040076)
77
Rahul Chaudhry4b803052015-05-13 15:25:56 -070078# Enable the Go compiler for these targets.
79TARGET_GO_ENABLED = (
80 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070081 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070082 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070083 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070084)
85CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
86
Manoj Gupta1b5642e2017-03-08 16:44:12 -080087# Enable llvm's compiler-rt for these targets.
88TARGET_COMPILER_RT_ENABLED = (
89 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070090 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070091 'aarch64-cros-linux-gnu',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080092)
93CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
94
Manoj Gupta946abb42017-04-12 14:27:19 -070095TARGET_LLVM_PKGS_ENABLED = (
96 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070097 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070098 'aarch64-cros-linux-gnu',
99 'x86_64-cros-linux-gnu',
100)
101
102LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700103 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
104 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700105}
106
Zdenek Behan508dcce2011-12-05 15:39:32 +0100107# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
108CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500109 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -0400110 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -0800111 'armv7a-cros-linux-gnueabi': '-gold',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700112 'armv7a-cros-linux-gnueabihf': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500113 'i686-pc-linux-gnu' : '-gold',
114 'x86_64-cros-linux-gnu' : '-gold',
115 },
Zdenek Behan508dcce2011-12-05 15:39:32 +0100116}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100117
118
David James66a09c42012-11-05 13:31:38 -0800119class Crossdev(object):
120 """Class for interacting with crossdev and caching its output."""
121
122 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
123 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800124 # Packages that needs separate handling, in addition to what we have from
125 # crossdev.
126 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700127 'clang': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800128 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700129 'libcxxabi': 'sys-libs',
130 'libcxx': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800131 }
David James66a09c42012-11-05 13:31:38 -0800132
133 @classmethod
134 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400135 """Load crossdev cache from disk.
136
137 We invalidate the cache when crossdev updates or this script changes.
138 """
David James90239b92012-11-05 15:31:34 -0800139 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400140 # If we run the compiled/cached .pyc file, we'll read/hash that when we
141 # really always want to track the source .py file.
142 script = os.path.abspath(__file__)
143 if script.endswith('.pyc'):
144 script = script[:-1]
145 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
146
147 cls._CACHE = {
148 'crossdev_version': crossdev_version,
149 'setup_toolchains_hash': setup_toolchains_hash,
150 }
151
152 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
153 if reconfig:
154 logging.debug('cache: forcing regen due to reconfig')
155 return
156
157 try:
158 file_data = osutils.ReadFile(cls._CACHE_FILE)
159 except IOError as e:
160 if e.errno != errno.ENOENT:
161 logging.warning('cache: reading failed: %s', e)
162 osutils.SafeUnlink(cls._CACHE_FILE)
163 return
164
165 try:
166 data = json.loads(file_data)
167 except ValueError as e:
168 logging.warning('cache: ignoring invalid content: %s', e)
169 return
170
171 if crossdev_version != data.get('crossdev_version'):
172 logging.debug('cache: rebuilding after crossdev upgrade')
173 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
174 logging.debug('cache: rebuilding after cros_setup_toolchains change')
175 else:
176 logging.debug('cache: content is up-to-date!')
177 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800178
179 @classmethod
180 def Save(cls):
181 """Store crossdev cache on disk."""
182 # Save the cache from the successful run.
183 with open(cls._CACHE_FILE, 'w') as f:
184 json.dump(cls._CACHE, f)
185
186 @classmethod
187 def GetConfig(cls, target):
188 """Returns a map of crossdev provided variables about a tuple."""
189 CACHE_ATTR = '_target_tuple_map'
190
191 val = cls._CACHE.setdefault(CACHE_ATTR, {})
192 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400193 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400194 conf = {
195 'crosspkgs': [],
196 'target': toolchain.GetHostTuple(),
197 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400198 if target == 'host':
199 packages_list = HOST_PACKAGES
200 else:
201 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400202 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400203 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400204 else:
205 # Build the crossdev command.
206 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
207 if target in TARGET_COMPILER_RT_ENABLED:
208 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
209 if target in TARGET_GO_ENABLED:
210 cmd.extend(CROSSDEV_GO_ARGS)
211 if target in TARGET_LLVM_PKGS_ENABLED:
212 for pkg in LLVM_PKGS_TABLE:
213 cmd.extend(LLVM_PKGS_TABLE[pkg])
214 cmd.extend(['-t', target])
215 # Catch output of crossdev.
216 out = cros_build_lib.RunCommand(
217 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
218 # List of tuples split at the first '=', converted into dict.
219 conf = dict((k, cros_build_lib.ShellUnquote(v))
220 for k, v in (x.split('=', 1) for x in out))
221 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800222
Mike Frysinger66bfde52017-09-12 16:42:57 -0400223 manual_pkgs = cls.MANUAL_PKGS
224
225 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400226 conf[pkg + '_pn'] = pkg
227 conf[pkg + '_category'] = cat
228 if pkg not in conf['crosspkgs']:
229 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800230
231 val[target] = conf
232
David James66a09c42012-11-05 13:31:38 -0800233 return val[target]
234
235 @classmethod
236 def UpdateTargets(cls, targets, usepkg, config_only=False):
237 """Calls crossdev to initialize a cross target.
238
239 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700240 targets: The list of targets to initialize using crossdev.
241 usepkg: Copies the commandline opts.
242 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800243 """
244 configured_targets = cls._CACHE.setdefault('configured_targets', [])
245
246 cmdbase = ['crossdev', '--show-fail-log']
247 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
248 # Pick stable by default, and override as necessary.
249 cmdbase.extend(['-P', '--oneshot'])
250 if usepkg:
251 cmdbase.extend(['-P', '--getbinpkg',
252 '-P', '--usepkgonly',
253 '--without-headers'])
254
Christopher Wileyb22c0712015-06-02 10:37:03 -0700255 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800256 cmdbase.extend(['--overlays', overlays])
257 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
258
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700259 # Build target by the alphabetical order to make sure
260 # armv7a-cros-linux-gnueabihf builds after armv7a-cros-linux-gnueabi
261 # because some dependency issue. This can be reverted once we
262 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
263 for target in sorted(targets):
David James66a09c42012-11-05 13:31:38 -0800264 if config_only and target in configured_targets:
265 continue
266
267 cmd = cmdbase + ['-t', target]
268
269 for pkg in GetTargetPackages(target):
270 if pkg == 'gdb':
271 # Gdb does not have selectable versions.
272 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700273 elif pkg == 'ex_compiler-rt':
274 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700275 elif pkg == 'ex_go':
276 # Go does not have selectable versions.
277 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700278 elif pkg in LLVM_PKGS_TABLE:
279 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800280 elif pkg in cls.MANUAL_PKGS:
281 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700282 else:
283 # The first of the desired versions is the "primary" one.
284 version = GetDesiredPackageVersions(target, pkg)[0]
285 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800286
287 cmd.extend(targets[target]['crossdev'].split())
288 if config_only:
289 # In this case we want to just quietly reinit
290 cmd.append('--init-target')
291 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
292 else:
293 cros_build_lib.RunCommand(cmd)
294
295 configured_targets.append(target)
296
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100297
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100298def GetTargetPackages(target):
299 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800300 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100301 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800302 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100303
304
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100305# Portage helper functions:
306def GetPortagePackage(target, package):
307 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800308 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100309 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400310 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100311 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100312 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100313 category = conf['category']
314 # Portage package:
315 pn = conf[package + '_pn']
316 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500317 assert category
318 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100319 return '%s/%s' % (category, pn)
320
321
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700322def PortageTrees(root):
323 """Return the portage trees for a given root."""
324 if root == '/':
325 return portage.db['/']
326 # The portage logic requires the path always end in a slash.
327 root = root.rstrip('/') + '/'
328 return portage.create_trees(target_root=root, config_root=root)[root]
329
330
331def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100332 """Extracts the list of current versions of a target, package pair.
333
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500334 Args:
335 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700336 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100337
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500338 Returns:
339 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100340 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500342 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700343 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100344 version = portage.versions.cpv_getversion(pkg)
345 versions.append(version)
346 return versions
347
348
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700349def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100350 """Extracts the current stable version for a given package.
351
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500352 Args:
353 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
354 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700355 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100356
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500357 Returns:
358 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100359 """
David James90239b92012-11-05 15:31:34 -0800360 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500361 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700362 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800363 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100364
365
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700366def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100367 """Resolves keywords in a given version list for a particular package.
368
369 Resolving means replacing PACKAGE_STABLE with the actual number.
370
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500371 Args:
372 target: The target to operate on (e.g. i686-pc-linux-gnu)
373 package: The target/package to operate on (e.g. gcc)
374 versions: List of versions to resolve
375 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700376 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100377
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500378 Returns:
379 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100380 """
381 resolved = []
David James90239b92012-11-05 15:31:34 -0800382 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700383 if not installed:
384 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100385 for version in versions:
386 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700387 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400388 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100389 resolved.append(version)
390 return resolved
391
392
393def GetDesiredPackageVersions(target, package):
394 """Produces the list of desired versions for each target, package pair.
395
396 The first version in the list is implicitly treated as primary, ie.
397 the version that will be initialized by crossdev and selected.
398
399 If the version is PACKAGE_STABLE, it really means the current version which
400 is emerged by using the package atom with no particular version key.
401 Since crossdev unmasks all packages by default, this will actually
402 mean 'unstable' in most cases.
403
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500404 Args:
405 target: The target to operate on (e.g. i686-pc-linux-gnu)
406 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100407
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500408 Returns:
409 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100410 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400411 if package in GetTargetPackages(target):
412 return [PACKAGE_STABLE]
413 else:
414 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415
416
417def TargetIsInitialized(target):
418 """Verifies if the given list of targets has been correctly initialized.
419
420 This determines whether we have to call crossdev while emerging
421 toolchain packages or can do it using emerge. Emerge is naturally
422 preferred, because all packages can be updated in a single pass.
423
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500424 Args:
425 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100426
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500427 Returns:
428 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100429 """
430 # Check if packages for the given target all have a proper version.
431 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100432 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800433 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100434 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400435 if not (GetStablePackageVersion(atom, True) and
436 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 return False
438 return True
439 except cros_build_lib.RunCommandError:
440 # Fails - The target has likely never been initialized before.
441 return False
442
443
444def RemovePackageMask(target):
445 """Removes a package.mask file for the given platform.
446
447 The pre-existing package.mask files can mess with the keywords.
448
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500449 Args:
450 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100451 """
452 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700453 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100454
455
Zdenek Behan508dcce2011-12-05 15:39:32 +0100456# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700457def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500458 """Rebuild libtool as needed
459
460 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
461 gcc, libtool will break. We can't use binary packages either as those will
462 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700463
464 Args:
465 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500466 """
467 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700468 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500469 for line in f:
470 # Look for a line like:
471 # sys_lib_search_path_spec="..."
472 # It'll be a list of paths and gcc will be one of them.
473 if line.startswith('sys_lib_search_path_spec='):
474 line = line.rstrip()
475 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400476 root_path = os.path.join(root, path.lstrip(os.path.sep))
477 logging.debug('Libtool: checking %s', root_path)
478 if not os.path.exists(root_path):
479 logging.info('Rebuilding libtool after gcc upgrade')
480 logging.info(' %s', line)
481 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500482 needs_update = True
483 break
484
485 if needs_update:
486 break
487
488 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700489 cmd = [EMERGE_CMD, '--oneshot']
490 if root != '/':
491 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
492 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500493 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400494 else:
495 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500496
497
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700498def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100499 """Determines which packages need update/unmerge and defers to portage.
500
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500501 Args:
502 targets: The list of targets to update
503 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700504 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100505 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100506 # For each target, we do two things. Figure out the list of updates,
507 # and figure out the appropriate keywords/masks. Crossdev will initialize
508 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400509 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800510 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100511 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400512 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100513 # Record the highest needed version for each target, for masking purposes.
514 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100515 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100516 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400517 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100518 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700519 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200521 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400522 if pkg in NEW_PACKAGES and usepkg:
523 # Skip this binary package (for now).
524 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100525 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400526 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100527
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400528 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400530 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800531 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532
Mike Frysinger3bba5032016-09-20 14:15:04 -0400533 logging.info('Updating packages:')
534 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100536 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100537 if usepkg:
538 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700539 if root != '/':
540 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541
542 cmd.extend(packages)
543 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800544 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100545
546
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700547def CleanTargets(targets, root='/'):
548 """Unmerges old packages that are assumed unnecessary.
549
550 Args:
551 targets: The list of targets to clean up.
552 root: The install root in which we want packages cleaned up.
553 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100554 unmergemap = {}
555 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400556 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100557 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400558 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100559 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700560 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700562 # NOTE: This refers to installed packages (vartree) rather than the
563 # Portage version (porttree and/or bintree) when determining the current
564 # version. While this isn't the most accurate thing to do, it is probably
565 # a good simple compromise, which should have the desired result of
566 # uninstalling everything but the latest installed version. In
567 # particular, using the bintree (--usebinpkg) requires a non-trivial
568 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200569 desired_num = VersionListToNumeric(target, package, desired, True)
570 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400571 logging.warning('Error detecting stable version for %s, '
572 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200573 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100574 unmergemap[pkg] = set(current).difference(desired_num)
575
576 # Cleaning doesn't care about consistency and rebuilding package.* files.
577 packages = []
578 for pkg, vers in unmergemap.iteritems():
579 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
580
581 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400582 logging.info('Cleaning packages:')
583 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100584 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700585 if root != '/':
586 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100587 cmd.extend(packages)
588 cros_build_lib.RunCommand(cmd)
589 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400590 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100591
592
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700593def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100594 """Runs gcc-config and binutils-config to select the desired.
595
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500596 Args:
597 targets: The targets to select
598 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700599 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100600 """
601 for package in ['gcc', 'binutils']:
602 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400603 # See if this package is part of this target.
604 if package not in GetTargetPackages(target):
605 logging.debug('%s: %s is not used', target, package)
606 continue
607
Zdenek Behan508dcce2011-12-05 15:39:32 +0100608 # Pick the first version in the numbered list as the selected one.
609 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700610 desired_num = VersionListToNumeric(target, package, desired, True,
611 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100612 desired = desired_num[0]
613 # *-config does not play revisions, strip them, keep just PV.
614 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
615
Mike Frysinger785b0c32017-09-13 01:35:59 -0400616 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100617 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800618 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100619
620 # And finally, attach target to it.
621 desired = '%s-%s' % (target, desired)
622
623 # Target specific hacks
624 if package in suffixes:
625 if target in suffixes[package]:
626 desired += suffixes[package][target]
627
David James7ec5efc2012-11-06 09:39:49 -0800628 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700629 if root != '/':
630 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800631 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500632 result = cros_build_lib.RunCommand(
633 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
634 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700635
636 # Do not reconfig when the current is live or nothing needs to be done.
637 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100638 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500639 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700640 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100641
642
Mike Frysinger35247af2012-11-16 18:58:06 -0500643def ExpandTargets(targets_wanted):
644 """Expand any possible toolchain aliases into full targets
645
646 This will expand 'all' and 'sdk' into the respective toolchain tuples.
647
648 Args:
649 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500650
Mike Frysinger35247af2012-11-16 18:58:06 -0500651 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300652 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500653 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500654 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700655 if targets_wanted == set(['boards']):
656 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300657 return {}
658
659 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500660 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300661 return all_targets
662 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500663 # Filter out all the non-sdk toolchains as we don't want to mess
664 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300665 return toolchain.FilterToolchains(all_targets, 'sdk', True)
666
667 # Verify user input.
668 nonexistent = targets_wanted.difference(all_targets)
669 if nonexistent:
670 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
671 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500672
673
David Jamesf8c672f2012-11-06 13:38:11 -0800674def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700675 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100676 """Performs all steps to create a synchronized toolchain enviroment.
677
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500678 Args:
679 usepkg: Use prebuilt packages
680 deleteold: Unmerge deprecated packages
681 hostonly: Only setup the host toolchain
682 reconfig: Reload crossdev config and reselect toolchains
683 targets_wanted: All the targets to update
684 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700685 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100686 """
David Jamesf8c672f2012-11-06 13:38:11 -0800687 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100688 if not hostonly:
689 # For hostonly, we can skip most of the below logic, much of which won't
690 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500691 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400692
Don Garrettc0c74002015-10-09 12:58:19 -0700693 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300694 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400695 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800696 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100697
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100698 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400699 for target in targets:
700 if TargetIsInitialized(target):
701 reconfig_targets[target] = targets[target]
702 else:
703 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100704 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400705 logging.info('The following targets need to be re-initialized:')
706 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800707 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200708 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800709 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100710
Mike Frysinger66814c32017-10-09 18:11:46 -0400711 # If we're building a subset of toolchains for a board, we might not have
712 # all the tuples that the packages expect. We don't define the "full" set
713 # of tuples currently other than "whatever the full sdk has normally".
714 if usepkg or set(('all', 'sdk')) & targets_wanted:
715 # Since we have cross-compilers now, we can update these packages.
716 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400717
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100718 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400719 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100720
721 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700722 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
723 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800724
725 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700726 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100727
Mike Frysingerc880a962013-11-08 13:59:06 -0500728 # Now that we've cleared out old versions, see if we need to rebuild
729 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700730 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500731
Zdenek Behan508dcce2011-12-05 15:39:32 +0100732
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700733def ShowConfig(name):
734 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500735
736 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700737 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500738 """
Don Garrettc0c74002015-10-09 12:58:19 -0700739 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500740 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400741 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400742 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800743 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400744 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500745
746
Mike Frysinger35247af2012-11-16 18:58:06 -0500747def GeneratePathWrapper(root, wrappath, path):
748 """Generate a shell script to execute another shell script
749
750 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
751 argv[0] won't be pointing to the correct path, generate a shell script that
752 just executes another program with its full path.
753
754 Args:
755 root: The root tree to generate scripts inside of
756 wrappath: The full path (inside |root|) to create the wrapper
757 path: The target program which this wrapper will execute
758 """
759 replacements = {
760 'path': path,
761 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
762 }
763 wrapper = """#!/bin/sh
764base=$(realpath "$0")
765basedir=${base%%/*}
766exec "${basedir}/%(relroot)s%(path)s" "$@"
767""" % replacements
768 root_wrapper = root + wrappath
769 if os.path.islink(root_wrapper):
770 os.unlink(root_wrapper)
771 else:
772 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
773 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400774 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500775
776
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700777def FixClangXXWrapper(root, path):
778 """Fix wrapper shell scripts and symlinks for invoking clang++
779
780 In a typical installation, clang++ symlinks to clang, which symlinks to the
781 elf executable. The executable distinguishes between clang and clang++ based
782 on argv[0].
783
784 When invoked through the LdsoWrapper, argv[0] always contains the path to the
785 executable elf file, making clang/clang++ invocations indistinguishable.
786
787 This function detects if the elf executable being wrapped is clang-X.Y, and
788 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
789
790 The calling sequence now becomes:
791 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
792 the Ldsowrapper).
793 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
794 to the original clang-3.9 elf.
795 -) The difference this time is that inside the elf file execution, $0 is
796 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
797
798 Args:
799 root: The root tree to generate scripts / symlinks inside of
800 path: The target elf for which LdsoWrapper was created
801 """
802 if re.match(r'/usr/bin/clang-\d+\.\d+$', path):
803 logging.info('fixing clang++ invocation for %s', path)
804 clangdir = os.path.dirname(root + path)
805 clang = os.path.basename(path)
806 clangxx = clang.replace('clang', 'clang++')
807
808 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
809 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
810
811 # Create a hardlink clang++-X.Y pointing to clang-X.Y
812 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
813
814 # Adjust the clang++ symlink to point to clang++-X.Y
815 os.unlink(os.path.join(clangdir, 'clang++'))
816 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
817
818
Mike Frysinger35247af2012-11-16 18:58:06 -0500819def FileIsCrosSdkElf(elf):
820 """Determine if |elf| is an ELF that we execute in the cros_sdk
821
822 We don't need this to be perfect, just quick. It makes sure the ELF
823 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
824
825 Args:
826 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500827
Mike Frysinger35247af2012-11-16 18:58:06 -0500828 Returns:
829 True if we think |elf| is a native ELF
830 """
831 with open(elf) as f:
832 data = f.read(20)
833 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
834 return (data[0:4] == '\x7fELF' and
835 data[4] == '\x02' and
836 data[5] == '\x01' and
837 data[18] == '\x3e')
838
839
840def IsPathPackagable(ptype, path):
841 """Should the specified file be included in a toolchain package?
842
843 We only need to handle files as we'll create dirs as we need them.
844
845 Further, trim files that won't be useful:
846 - non-english translations (.mo) since it'd require env vars
847 - debug files since these are for the host compiler itself
848 - info/man pages as they're big, and docs are online, and the
849 native docs should work fine for the most part (`man gcc`)
850
851 Args:
852 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
853 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500854
Mike Frysinger35247af2012-11-16 18:58:06 -0500855 Returns:
856 True if we want to include this path in the package
857 """
858 return not (ptype in ('dir',) or
859 path.startswith('/usr/lib/debug/') or
860 os.path.splitext(path)[1] == '.mo' or
861 ('/man/' in path or '/info/' in path))
862
863
864def ReadlinkRoot(path, root):
865 """Like os.readlink(), but relative to a |root|
866
867 Args:
868 path: The symlink to read
869 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500870
Mike Frysinger35247af2012-11-16 18:58:06 -0500871 Returns:
872 A fully resolved symlink path
873 """
874 while os.path.islink(root + path):
875 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
876 return path
877
878
879def _GetFilesForTarget(target, root='/'):
880 """Locate all the files to package for |target|
881
882 This does not cover ELF dependencies.
883
884 Args:
885 target: The toolchain target name
886 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500887
Mike Frysinger35247af2012-11-16 18:58:06 -0500888 Returns:
889 A tuple of a set of all packable paths, and a set of all paths which
890 are also native ELFs
891 """
892 paths = set()
893 elfs = set()
894
895 # Find all the files owned by the packages for this target.
896 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500897
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700898 # Skip Go compiler from redistributable packages.
899 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
900 # into it. Due to this, the toolchain cannot be unpacked anywhere
901 # else and be readily useful. To enable packaging Go, we need to:
902 # -) Tweak the wrappers/environment to override GOROOT
903 # automatically based on the unpack location.
904 # -) Make sure the ELF dependency checking and wrapping logic
905 # below skips the Go toolchain executables and libraries.
906 # -) Make sure the packaging process maintains the relative
907 # timestamps of precompiled standard library packages.
908 # (see dev-lang/go ebuild for details).
909 if pkg == 'ex_go':
910 continue
911
Mike Frysinger35247af2012-11-16 18:58:06 -0500912 atom = GetPortagePackage(target, pkg)
913 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700914 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700915 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500916
917 # pylint: disable=E1101
918 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
919 settings=portage.settings)
920 contents = dblink.getcontents()
921 for obj in contents:
922 ptype = contents[obj][0]
923 if not IsPathPackagable(ptype, obj):
924 continue
925
926 if ptype == 'obj':
927 # For native ELFs, we need to pull in their dependencies too.
928 if FileIsCrosSdkElf(obj):
929 elfs.add(obj)
930 paths.add(obj)
931
932 return paths, elfs
933
934
935def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500936 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500937 """Link in all packable files and their runtime dependencies
938
939 This also wraps up executable ELFs with helper scripts.
940
941 Args:
942 output_dir: The output directory to store files
943 paths: All the files to include
944 elfs: All the files which are ELFs (a subset of |paths|)
945 ldpaths: A dict of static ldpath information
946 path_rewrite_func: User callback to rewrite paths in output_dir
947 root: The root path to pull all packages/files from
948 """
949 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400950 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500951 for path in paths:
952 new_path = path_rewrite_func(path)
953 dst = output_dir + new_path
954 osutils.SafeMakedirs(os.path.dirname(dst))
955
956 # Is this a symlink which we have to rewrite or wrap?
957 # Delay wrap check until after we have created all paths.
958 src = root + path
959 if os.path.islink(src):
960 tgt = os.readlink(src)
961 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -0400962 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -0500963
964 # Rewrite absolute links to relative and then generate the symlink
965 # ourselves. All other symlinks can be hardlinked below.
966 if tgt[0] == '/':
967 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
968 os.symlink(tgt, dst)
969 continue
970
971 os.link(src, dst)
972
Mike Frysinger35247af2012-11-16 18:58:06 -0500973 # Locate all the dependencies for all the ELFs. Stick them all in the
974 # top level "lib" dir to make the wrapper simpler. This exact path does
975 # not matter since we execute ldso directly, and we tell the ldso the
976 # exact path to search for its libraries.
977 libdir = os.path.join(output_dir, 'lib')
978 osutils.SafeMakedirs(libdir)
979 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -0400980 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -0500981 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400982 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500983 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -0700984 # Do not create wrapper for libc. crbug.com/766827
985 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -0500986 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400987 interp = os.path.join('/lib', os.path.basename(interp))
988 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
989 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700990 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -0500991
Mike Frysinger221bd822017-09-29 02:51:47 -0400992 # Wrap any symlinks to the wrapper.
993 if elf in sym_paths:
994 link = sym_paths[elf]
995 GeneratePathWrapper(output_dir, link, elf)
996
Mike Frysinger35247af2012-11-16 18:58:06 -0500997 for lib, lib_data in e['libs'].iteritems():
998 if lib in donelibs:
999 continue
1000
1001 src = path = lib_data['path']
1002 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001003 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001004 continue
1005 donelibs.add(lib)
1006
1007 # Needed libs are the SONAME, but that is usually a symlink, not a
1008 # real file. So link in the target rather than the symlink itself.
1009 # We have to walk all the possible symlinks (SONAME could point to a
1010 # symlink which points to a symlink), and we have to handle absolute
1011 # ourselves (since we have a "root" argument).
1012 dst = os.path.join(libdir, os.path.basename(path))
1013 src = ReadlinkRoot(src, root)
1014
1015 os.link(root + src, dst)
1016
1017
1018def _EnvdGetVar(envd, var):
1019 """Given a Gentoo env.d file, extract a var from it
1020
1021 Args:
1022 envd: The env.d file to load (may be a glob path)
1023 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001024
Mike Frysinger35247af2012-11-16 18:58:06 -05001025 Returns:
1026 The value of |var|
1027 """
1028 envds = glob.glob(envd)
1029 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1030 envd = envds[0]
1031 return cros_build_lib.LoadKeyValueFile(envd)[var]
1032
1033
1034def _ProcessBinutilsConfig(target, output_dir):
1035 """Do what binutils-config would have done"""
1036 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001037
1038 # Locate the bin dir holding the gold linker.
1039 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1040 target, 'binutils-bin')
1041 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001042 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001043 if not srcpath:
1044 # Maybe this target doesn't support gold.
1045 globpath = os.path.join(binutils_bin_path, '*')
1046 srcpath = glob.glob(globpath)
1047 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1048 % globpath)
1049 srcpath = srcpath[0]
1050 ld_path = os.path.join(srcpath, 'ld')
1051 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1052 ld_path = os.path.join(srcpath, 'ld.bfd')
1053 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1054 ld_path = os.path.join(srcpath, 'ld.gold')
1055 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1056 % ld_path)
1057
1058 # Nope, no gold support to be found.
1059 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001060 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001061 else:
1062 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001063 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001064
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001065 # Package the binutils-bin directory without the '-gold' suffix
1066 # if gold is not enabled as the default linker for this target.
1067 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1068 if not gold_supported:
1069 srcpath = srcpath[:-len('-gold')]
1070 ld_path = os.path.join(srcpath, 'ld')
1071 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1072
Mike Frysinger78b7a812014-11-26 19:45:23 -05001073 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001074 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1075 for prog in os.listdir(output_dir + srcpath):
1076 # Skip binaries already wrapped.
1077 if not prog.endswith('.real'):
1078 GeneratePathWrapper(output_dir, binpath + prog,
1079 os.path.join(srcpath, prog))
1080 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1081 os.path.join(srcpath, prog))
1082
David James27ac4ae2012-12-03 23:16:15 -08001083 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001084 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1085 if gold_supported:
1086 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001087 else:
1088 # If gold is not enabled as the default linker and 2 env.d
1089 # files exist, pick the one without the '-gold' suffix.
1090 envds = sorted(glob.glob(envd))
1091 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1092 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001093 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1094 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1095 output_dir + libpath)
1096
1097
1098def _ProcessGccConfig(target, output_dir):
1099 """Do what gcc-config would have done"""
1100 binpath = '/bin'
1101 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1102 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1103 for prog in os.listdir(output_dir + srcpath):
1104 # Skip binaries already wrapped.
1105 if (not prog.endswith('.real') and
1106 not prog.endswith('.elf') and
1107 prog.startswith(target)):
1108 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1109 os.path.join(srcpath, prog))
1110 return srcpath
1111
1112
Frank Henigman179ec7c2015-02-06 03:01:09 -05001113def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1114 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001115 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001116 for sysroot_wrapper in glob.glob(os.path.join(
1117 output_dir + srcpath, 'sysroot_wrapper*')):
1118 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
Douglas Andersoncc828a52017-10-13 13:07:25 -07001119
1120 # In order to optimize startup time in the chroot we run python a little
1121 # differently there. Put it back to the more portable way here.
1122 # See http://crbug.com/773138 for some details.
1123 if contents[0] == '#!/usr/bin/python2 -S':
1124 contents[0] = '#!/usr/bin/env python2'
1125
Frank Henigman179ec7c2015-02-06 03:01:09 -05001126 for num in xrange(len(contents)):
1127 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001128 assert 'True' in contents[num]
1129 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001130 break
1131 # Can't update the wrapper in place since it's a hardlink to a file in /.
1132 os.unlink(sysroot_wrapper)
1133 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1134 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001135
1136
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001137def _CreateMainLibDir(target, output_dir):
1138 """Create some lib dirs so that compiler can get the right Gcc paths"""
1139 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1140 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1141
1142
Mike Frysinger35247af2012-11-16 18:58:06 -05001143def _ProcessDistroCleanups(target, output_dir):
1144 """Clean up the tree and remove all distro-specific requirements
1145
1146 Args:
1147 target: The toolchain target name
1148 output_dir: The output directory to clean up
1149 """
1150 _ProcessBinutilsConfig(target, output_dir)
1151 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001152 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001153 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001154
1155 osutils.RmDir(os.path.join(output_dir, 'etc'))
1156
1157
1158def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1159 """Setup a tree from the packages for the specified target
1160
1161 This populates a path with all the files from toolchain packages so that
1162 a tarball can easily be generated from the result.
1163
1164 Args:
1165 target: The target to create a packagable root from
1166 output_dir: The output directory to place all the files
1167 ldpaths: A dict of static ldpath information
1168 root: The root path to pull all packages/files from
1169 """
1170 # Find all the files owned by the packages for this target.
1171 paths, elfs = _GetFilesForTarget(target, root=root)
1172
1173 # Link in all the package's files, any ELF dependencies, and wrap any
1174 # executable ELFs with helper scripts.
1175 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001176 """Move /usr/bin to /bin so people can just use that toplevel dir
1177
1178 Note we do not apply this to clang - there is correlation between clang's
1179 search path for libraries / inclusion and its installation path.
1180 """
1181 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1182 return path[4:]
1183 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001184 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1185 path_rewrite_func=MoveUsrBinToBin, root=root)
1186
1187 # The packages, when part of the normal distro, have helper scripts
1188 # that setup paths and such. Since we are making this standalone, we
1189 # need to preprocess all that ourselves.
1190 _ProcessDistroCleanups(target, output_dir)
1191
1192
1193def CreatePackages(targets_wanted, output_dir, root='/'):
1194 """Create redistributable cross-compiler packages for the specified targets
1195
1196 This creates toolchain packages that should be usable in conjunction with
1197 a downloaded sysroot (created elsewhere).
1198
1199 Tarballs (one per target) will be created in $PWD.
1200
1201 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001202 targets_wanted: The targets to package up.
1203 output_dir: The directory to put the packages in.
1204 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001205 """
Ralph Nathan03047282015-03-23 11:09:32 -07001206 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001207 osutils.SafeMakedirs(output_dir)
1208 ldpaths = lddtree.LoadLdpaths(root)
1209 targets = ExpandTargets(targets_wanted)
1210
Mike Frysinger221bd822017-09-29 02:51:47 -04001211 with osutils.TempDir(prefix='create-packages') as tempdir:
1212 logging.debug('Using tempdir: %s', tempdir)
1213
Mike Frysinger35247af2012-11-16 18:58:06 -05001214 # We have to split the root generation from the compression stages. This is
1215 # because we hardlink in all the files (to avoid overhead of reading/writing
1216 # the copies multiple times). But tar gets angry if a file's hardlink count
1217 # changes from when it starts reading a file to when it finishes.
1218 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1219 for target in targets:
1220 output_target_dir = os.path.join(tempdir, target)
1221 queue.put([target, output_target_dir, ldpaths, root])
1222
1223 # Build the tarball.
1224 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1225 for target in targets:
1226 tar_file = os.path.join(output_dir, target + '.tar.xz')
1227 queue.put([tar_file, os.path.join(tempdir, target)])
1228
1229
Mike Frysinger07534cf2017-09-12 17:40:21 -04001230def GetParser():
1231 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001232 parser = commandline.ArgumentParser(description=__doc__)
1233 parser.add_argument('-u', '--nousepkg',
1234 action='store_false', dest='usepkg', default=True,
1235 help='Use prebuilt packages if possible')
1236 parser.add_argument('-d', '--deleteold',
1237 action='store_true', dest='deleteold', default=False,
1238 help='Unmerge deprecated packages')
1239 parser.add_argument('-t', '--targets',
1240 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001241 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001242 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001243 "allowed. Defaults to 'sdk'.")
1244 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1245 help='Comma separated list of boards whose toolchains we '
1246 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001247 parser.add_argument('--hostonly',
1248 dest='hostonly', default=False, action='store_true',
1249 help='Only setup the host toolchain. '
1250 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001251 parser.add_argument('--show-board-cfg', '--show-cfg',
1252 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001253 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001254 parser.add_argument('--show-packages', default=None,
1255 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001256 parser.add_argument('--create-packages',
1257 action='store_true', default=False,
1258 help='Build redistributable packages')
1259 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1260 help='Output directory')
1261 parser.add_argument('--reconfig', default=False, action='store_true',
1262 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001263 parser.add_argument('--sysroot', type='path',
1264 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001265 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001266
Mike Frysinger07534cf2017-09-12 17:40:21 -04001267
1268def main(argv):
1269 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001270 options = parser.parse_args(argv)
1271 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001272
Mike Frysinger35247af2012-11-16 18:58:06 -05001273 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001274 conflicting_options = (
1275 options.cfg_name,
1276 options.show_packages,
1277 options.create_packages,
1278 )
1279 if sum(bool(x) for x in conflicting_options) > 1:
1280 parser.error('conflicting options: create-packages & show-packages & '
1281 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001282
Gilad Arnold8195b532015-04-07 10:56:30 +03001283 targets_wanted = set(options.targets.split(','))
1284 boards_wanted = (set(options.include_boards.split(','))
1285 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001286
Rahul Chaudhry4d416582017-10-25 12:31:58 -07001287 # pylint: disable=global-statement
1288 global TARGET_GO_ENABLED
1289 if options.usepkg:
1290 # For bootstrapping, disable the new toolchain (aarch64-cros-linux-gnu/go)
1291 # if using binary packages. Allow the sdk bot to catch up and make binary
1292 # packages available.
1293 TARGET_GO_ENABLED = TARGET_GO_ENABLED[:-1]
1294
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001295 if options.cfg_name:
1296 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001297 elif options.show_packages is not None:
1298 cros_build_lib.AssertInsideChroot()
1299 target = options.show_packages
1300 Crossdev.Load(False)
1301 for package in GetTargetPackages(target):
1302 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001303 elif options.create_packages:
1304 cros_build_lib.AssertInsideChroot()
1305 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001306 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001307 else:
1308 cros_build_lib.AssertInsideChroot()
1309 # This has to be always run as root.
1310 if os.geteuid() != 0:
1311 cros_build_lib.Die('this script must be run as root')
1312
1313 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001314 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001315 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001316 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001317 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001318 Crossdev.Save()
1319
1320 return 0