blob: 80836df9523bb6a3a92758c64710c9dbf2c4338c [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 = (
Manoj Gupta65f88442018-04-12 22:42:19 -070070 'dev-lang/rust',
71 'dev-util/cargo',
Mike Frysinger61a24392017-10-17 17:14:27 -040072 'virtual/target-sdk-post-cross',
Mike Frysinger785b0c32017-09-13 01:35:59 -040073)
74
75# New packages that we're in the process of adding to the SDK. Since the SDK
76# bot hasn't had a chance to run yet, there are no binary packages available,
77# so we have to list them here and wait. Once it completes, entries here can
78# be removed so they'll end up on bots & dev's systems.
79NEW_PACKAGES = (
Mike Frysinger785b0c32017-09-13 01:35:59 -040080)
81
Rahul Chaudhry4b803052015-05-13 15:25:56 -070082# Enable the Go compiler for these targets.
83TARGET_GO_ENABLED = (
84 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070085 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070086 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070087 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070088)
89CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
90
Manoj Gupta1b5642e2017-03-08 16:44:12 -080091# Enable llvm's compiler-rt for these targets.
92TARGET_COMPILER_RT_ENABLED = (
93 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070094 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070095 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -080096 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080097)
98CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
99
Manoj Gupta946abb42017-04-12 14:27:19 -0700100TARGET_LLVM_PKGS_ENABLED = (
101 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700102 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700103 'aarch64-cros-linux-gnu',
104 'x86_64-cros-linux-gnu',
105)
106
107LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700108 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
109 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700110}
111
Zdenek Behan508dcce2011-12-05 15:39:32 +0100112# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
113CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500114 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -0400115 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -0800116 'armv7a-cros-linux-gnueabi': '-gold',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700117 'armv7a-cros-linux-gnueabihf': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500118 'i686-pc-linux-gnu' : '-gold',
119 'x86_64-cros-linux-gnu' : '-gold',
120 },
Zdenek Behan508dcce2011-12-05 15:39:32 +0100121}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100122
123
David James66a09c42012-11-05 13:31:38 -0800124class Crossdev(object):
125 """Class for interacting with crossdev and caching its output."""
126
127 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
128 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800129 # Packages that needs separate handling, in addition to what we have from
130 # crossdev.
131 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700132 'clang': 'sys-devel',
Yunlian Jiang18ae9982017-11-03 09:15:31 -0700133 'lld': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800134 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700135 'libcxxabi': 'sys-libs',
136 'libcxx': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800137 }
David James66a09c42012-11-05 13:31:38 -0800138
139 @classmethod
140 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400141 """Load crossdev cache from disk.
142
143 We invalidate the cache when crossdev updates or this script changes.
144 """
David James90239b92012-11-05 15:31:34 -0800145 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400146 # If we run the compiled/cached .pyc file, we'll read/hash that when we
147 # really always want to track the source .py file.
148 script = os.path.abspath(__file__)
149 if script.endswith('.pyc'):
150 script = script[:-1]
151 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
152
153 cls._CACHE = {
154 'crossdev_version': crossdev_version,
155 'setup_toolchains_hash': setup_toolchains_hash,
156 }
157
158 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
159 if reconfig:
160 logging.debug('cache: forcing regen due to reconfig')
161 return
162
163 try:
164 file_data = osutils.ReadFile(cls._CACHE_FILE)
165 except IOError as e:
166 if e.errno != errno.ENOENT:
167 logging.warning('cache: reading failed: %s', e)
168 osutils.SafeUnlink(cls._CACHE_FILE)
169 return
170
171 try:
172 data = json.loads(file_data)
173 except ValueError as e:
174 logging.warning('cache: ignoring invalid content: %s', e)
175 return
176
177 if crossdev_version != data.get('crossdev_version'):
178 logging.debug('cache: rebuilding after crossdev upgrade')
179 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
180 logging.debug('cache: rebuilding after cros_setup_toolchains change')
181 else:
182 logging.debug('cache: content is up-to-date!')
183 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800184
185 @classmethod
186 def Save(cls):
187 """Store crossdev cache on disk."""
188 # Save the cache from the successful run.
189 with open(cls._CACHE_FILE, 'w') as f:
190 json.dump(cls._CACHE, f)
191
192 @classmethod
193 def GetConfig(cls, target):
194 """Returns a map of crossdev provided variables about a tuple."""
195 CACHE_ATTR = '_target_tuple_map'
196
197 val = cls._CACHE.setdefault(CACHE_ATTR, {})
198 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400199 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400200 conf = {
201 'crosspkgs': [],
202 'target': toolchain.GetHostTuple(),
203 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400204 if target == 'host':
205 packages_list = HOST_PACKAGES
206 else:
207 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400208 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400209 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400210 else:
211 # Build the crossdev command.
212 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
213 if target in TARGET_COMPILER_RT_ENABLED:
214 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
215 if target in TARGET_GO_ENABLED:
216 cmd.extend(CROSSDEV_GO_ARGS)
217 if target in TARGET_LLVM_PKGS_ENABLED:
218 for pkg in LLVM_PKGS_TABLE:
219 cmd.extend(LLVM_PKGS_TABLE[pkg])
220 cmd.extend(['-t', target])
221 # Catch output of crossdev.
222 out = cros_build_lib.RunCommand(
223 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
224 # List of tuples split at the first '=', converted into dict.
225 conf = dict((k, cros_build_lib.ShellUnquote(v))
226 for k, v in (x.split('=', 1) for x in out))
227 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800228
Mike Frysinger66bfde52017-09-12 16:42:57 -0400229 manual_pkgs = cls.MANUAL_PKGS
230
231 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400232 conf[pkg + '_pn'] = pkg
233 conf[pkg + '_category'] = cat
234 if pkg not in conf['crosspkgs']:
235 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800236
237 val[target] = conf
238
David James66a09c42012-11-05 13:31:38 -0800239 return val[target]
240
241 @classmethod
242 def UpdateTargets(cls, targets, usepkg, config_only=False):
243 """Calls crossdev to initialize a cross target.
244
245 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700246 targets: The list of targets to initialize using crossdev.
247 usepkg: Copies the commandline opts.
248 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800249 """
250 configured_targets = cls._CACHE.setdefault('configured_targets', [])
251
252 cmdbase = ['crossdev', '--show-fail-log']
253 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
254 # Pick stable by default, and override as necessary.
255 cmdbase.extend(['-P', '--oneshot'])
256 if usepkg:
257 cmdbase.extend(['-P', '--getbinpkg',
258 '-P', '--usepkgonly',
259 '--without-headers'])
260
Christopher Wileyb22c0712015-06-02 10:37:03 -0700261 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800262 cmdbase.extend(['--overlays', overlays])
263 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
264
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700265 # Build target by the alphabetical order to make sure
266 # armv7a-cros-linux-gnueabihf builds after armv7a-cros-linux-gnueabi
267 # because some dependency issue. This can be reverted once we
268 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
269 for target in sorted(targets):
David James66a09c42012-11-05 13:31:38 -0800270 if config_only and target in configured_targets:
271 continue
272
273 cmd = cmdbase + ['-t', target]
274
275 for pkg in GetTargetPackages(target):
276 if pkg == 'gdb':
277 # Gdb does not have selectable versions.
278 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700279 elif pkg == 'ex_compiler-rt':
280 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700281 elif pkg == 'ex_go':
282 # Go does not have selectable versions.
283 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700284 elif pkg in LLVM_PKGS_TABLE:
285 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800286 elif pkg in cls.MANUAL_PKGS:
287 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700288 else:
289 # The first of the desired versions is the "primary" one.
290 version = GetDesiredPackageVersions(target, pkg)[0]
291 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800292
293 cmd.extend(targets[target]['crossdev'].split())
294 if config_only:
295 # In this case we want to just quietly reinit
296 cmd.append('--init-target')
297 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
298 else:
299 cros_build_lib.RunCommand(cmd)
300
301 configured_targets.append(target)
302
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100303
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100304def GetTargetPackages(target):
305 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800306 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100307 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800308 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100309
310
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100311# Portage helper functions:
312def GetPortagePackage(target, package):
313 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800314 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100315 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400316 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100317 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100318 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100319 category = conf['category']
320 # Portage package:
321 pn = conf[package + '_pn']
322 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500323 assert category
324 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100325 return '%s/%s' % (category, pn)
326
327
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700328def PortageTrees(root):
329 """Return the portage trees for a given root."""
330 if root == '/':
331 return portage.db['/']
332 # The portage logic requires the path always end in a slash.
333 root = root.rstrip('/') + '/'
334 return portage.create_trees(target_root=root, config_root=root)[root]
335
336
337def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100338 """Extracts the list of current versions of a target, package pair.
339
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500340 Args:
341 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700342 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100343
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500344 Returns:
345 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100346 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500348 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700349 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100350 version = portage.versions.cpv_getversion(pkg)
351 versions.append(version)
352 return versions
353
354
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700355def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100356 """Extracts the current stable version for a given package.
357
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500358 Args:
359 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
360 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700361 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100362
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500363 Returns:
364 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100365 """
David James90239b92012-11-05 15:31:34 -0800366 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500367 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700368 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800369 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100370
371
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700372def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100373 """Resolves keywords in a given version list for a particular package.
374
375 Resolving means replacing PACKAGE_STABLE with the actual number.
376
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500377 Args:
378 target: The target to operate on (e.g. i686-pc-linux-gnu)
379 package: The target/package to operate on (e.g. gcc)
380 versions: List of versions to resolve
381 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700382 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100383
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500384 Returns:
385 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100386 """
387 resolved = []
David James90239b92012-11-05 15:31:34 -0800388 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700389 if not installed:
390 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100391 for version in versions:
392 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700393 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400394 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100395 resolved.append(version)
396 return resolved
397
398
399def GetDesiredPackageVersions(target, package):
400 """Produces the list of desired versions for each target, package pair.
401
402 The first version in the list is implicitly treated as primary, ie.
403 the version that will be initialized by crossdev and selected.
404
405 If the version is PACKAGE_STABLE, it really means the current version which
406 is emerged by using the package atom with no particular version key.
407 Since crossdev unmasks all packages by default, this will actually
408 mean 'unstable' in most cases.
409
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500410 Args:
411 target: The target to operate on (e.g. i686-pc-linux-gnu)
412 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100413
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500414 Returns:
415 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100416 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400417 if package in GetTargetPackages(target):
418 return [PACKAGE_STABLE]
419 else:
420 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100421
422
423def TargetIsInitialized(target):
424 """Verifies if the given list of targets has been correctly initialized.
425
426 This determines whether we have to call crossdev while emerging
427 toolchain packages or can do it using emerge. Emerge is naturally
428 preferred, because all packages can be updated in a single pass.
429
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500430 Args:
431 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100432
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500433 Returns:
434 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100435 """
436 # Check if packages for the given target all have a proper version.
437 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100438 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800439 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100440 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400441 if not (GetStablePackageVersion(atom, True) and
442 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100443 return False
444 return True
445 except cros_build_lib.RunCommandError:
446 # Fails - The target has likely never been initialized before.
447 return False
448
449
450def RemovePackageMask(target):
451 """Removes a package.mask file for the given platform.
452
453 The pre-existing package.mask files can mess with the keywords.
454
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500455 Args:
456 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100457 """
458 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700459 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460
461
Zdenek Behan508dcce2011-12-05 15:39:32 +0100462# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700463def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500464 """Rebuild libtool as needed
465
466 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
467 gcc, libtool will break. We can't use binary packages either as those will
468 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700469
470 Args:
471 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500472 """
473 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700474 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500475 for line in f:
476 # Look for a line like:
477 # sys_lib_search_path_spec="..."
478 # It'll be a list of paths and gcc will be one of them.
479 if line.startswith('sys_lib_search_path_spec='):
480 line = line.rstrip()
481 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400482 root_path = os.path.join(root, path.lstrip(os.path.sep))
483 logging.debug('Libtool: checking %s', root_path)
484 if not os.path.exists(root_path):
485 logging.info('Rebuilding libtool after gcc upgrade')
486 logging.info(' %s', line)
487 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500488 needs_update = True
489 break
490
491 if needs_update:
492 break
493
494 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700495 cmd = [EMERGE_CMD, '--oneshot']
496 if root != '/':
497 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
498 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500499 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400500 else:
501 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500502
503
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700504def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100505 """Determines which packages need update/unmerge and defers to portage.
506
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500507 Args:
508 targets: The list of targets to update
509 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700510 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100511 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100512 # For each target, we do two things. Figure out the list of updates,
513 # and figure out the appropriate keywords/masks. Crossdev will initialize
514 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400515 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800516 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400518 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100519 # Record the highest needed version for each target, for masking purposes.
520 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100521 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100522 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400523 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100524 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700525 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100526 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200527 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400528 if pkg in NEW_PACKAGES and usepkg:
529 # Skip this binary package (for now).
530 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100531 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400532 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100533
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400534 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400536 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800537 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538
Mike Frysinger3bba5032016-09-20 14:15:04 -0400539 logging.info('Updating packages:')
540 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100542 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543 if usepkg:
544 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700545 if root != '/':
546 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100547
548 cmd.extend(packages)
549 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800550 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100551
552
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700553def CleanTargets(targets, root='/'):
554 """Unmerges old packages that are assumed unnecessary.
555
556 Args:
557 targets: The list of targets to clean up.
558 root: The install root in which we want packages cleaned up.
559 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100560 unmergemap = {}
561 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400562 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100563 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400564 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100565 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700566 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100567 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700568 # NOTE: This refers to installed packages (vartree) rather than the
569 # Portage version (porttree and/or bintree) when determining the current
570 # version. While this isn't the most accurate thing to do, it is probably
571 # a good simple compromise, which should have the desired result of
572 # uninstalling everything but the latest installed version. In
573 # particular, using the bintree (--usebinpkg) requires a non-trivial
574 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200575 desired_num = VersionListToNumeric(target, package, desired, True)
576 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400577 logging.warning('Error detecting stable version for %s, '
578 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200579 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100580 unmergemap[pkg] = set(current).difference(desired_num)
581
582 # Cleaning doesn't care about consistency and rebuilding package.* files.
583 packages = []
584 for pkg, vers in unmergemap.iteritems():
585 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
586
587 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400588 logging.info('Cleaning packages:')
589 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100590 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700591 if root != '/':
592 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593 cmd.extend(packages)
594 cros_build_lib.RunCommand(cmd)
595 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400596 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100597
598
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700599def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100600 """Runs gcc-config and binutils-config to select the desired.
601
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500602 Args:
603 targets: The targets to select
604 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700605 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100606 """
607 for package in ['gcc', 'binutils']:
608 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400609 # See if this package is part of this target.
610 if package not in GetTargetPackages(target):
611 logging.debug('%s: %s is not used', target, package)
612 continue
613
Zdenek Behan508dcce2011-12-05 15:39:32 +0100614 # Pick the first version in the numbered list as the selected one.
615 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700616 desired_num = VersionListToNumeric(target, package, desired, True,
617 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100618 desired = desired_num[0]
619 # *-config does not play revisions, strip them, keep just PV.
620 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
621
Mike Frysinger785b0c32017-09-13 01:35:59 -0400622 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100623 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800624 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100625
626 # And finally, attach target to it.
627 desired = '%s-%s' % (target, desired)
628
629 # Target specific hacks
630 if package in suffixes:
631 if target in suffixes[package]:
632 desired += suffixes[package][target]
633
David James7ec5efc2012-11-06 09:39:49 -0800634 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700635 if root != '/':
636 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800637 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500638 result = cros_build_lib.RunCommand(
639 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
640 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700641
642 # Do not reconfig when the current is live or nothing needs to be done.
643 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100644 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500645 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700646 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100647
648
Mike Frysinger35247af2012-11-16 18:58:06 -0500649def ExpandTargets(targets_wanted):
650 """Expand any possible toolchain aliases into full targets
651
652 This will expand 'all' and 'sdk' into the respective toolchain tuples.
653
654 Args:
655 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500656
Mike Frysinger35247af2012-11-16 18:58:06 -0500657 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300658 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500659 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500660 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700661 if targets_wanted == set(['boards']):
662 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300663 return {}
664
665 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500666 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300667 return all_targets
668 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500669 # Filter out all the non-sdk toolchains as we don't want to mess
670 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300671 return toolchain.FilterToolchains(all_targets, 'sdk', True)
672
673 # Verify user input.
674 nonexistent = targets_wanted.difference(all_targets)
675 if nonexistent:
676 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
677 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500678
679
David Jamesf8c672f2012-11-06 13:38:11 -0800680def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700681 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100682 """Performs all steps to create a synchronized toolchain enviroment.
683
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500684 Args:
685 usepkg: Use prebuilt packages
686 deleteold: Unmerge deprecated packages
687 hostonly: Only setup the host toolchain
688 reconfig: Reload crossdev config and reselect toolchains
689 targets_wanted: All the targets to update
690 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700691 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100692 """
David Jamesf8c672f2012-11-06 13:38:11 -0800693 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100694 if not hostonly:
695 # For hostonly, we can skip most of the below logic, much of which won't
696 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500697 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400698
Don Garrettc0c74002015-10-09 12:58:19 -0700699 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300700 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400701 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800702 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100703
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100704 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400705 for target in targets:
706 if TargetIsInitialized(target):
707 reconfig_targets[target] = targets[target]
708 else:
709 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100710 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400711 logging.info('The following targets need to be re-initialized:')
712 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800713 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200714 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800715 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100716
Mike Frysinger66814c32017-10-09 18:11:46 -0400717 # If we're building a subset of toolchains for a board, we might not have
718 # all the tuples that the packages expect. We don't define the "full" set
719 # of tuples currently other than "whatever the full sdk has normally".
720 if usepkg or set(('all', 'sdk')) & targets_wanted:
721 # Since we have cross-compilers now, we can update these packages.
722 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400723
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100724 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400725 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100726
727 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700728 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
729 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800730
731 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700732 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100733
Mike Frysingerc880a962013-11-08 13:59:06 -0500734 # Now that we've cleared out old versions, see if we need to rebuild
735 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700736 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500737
Zdenek Behan508dcce2011-12-05 15:39:32 +0100738
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700739def ShowConfig(name):
740 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500741
742 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700743 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500744 """
Don Garrettc0c74002015-10-09 12:58:19 -0700745 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500746 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400747 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400748 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800749 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400750 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500751
752
Mike Frysinger35247af2012-11-16 18:58:06 -0500753def GeneratePathWrapper(root, wrappath, path):
754 """Generate a shell script to execute another shell script
755
756 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
757 argv[0] won't be pointing to the correct path, generate a shell script that
758 just executes another program with its full path.
759
760 Args:
761 root: The root tree to generate scripts inside of
762 wrappath: The full path (inside |root|) to create the wrapper
763 path: The target program which this wrapper will execute
764 """
765 replacements = {
766 'path': path,
767 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
768 }
769 wrapper = """#!/bin/sh
770base=$(realpath "$0")
771basedir=${base%%/*}
772exec "${basedir}/%(relroot)s%(path)s" "$@"
773""" % replacements
774 root_wrapper = root + wrappath
775 if os.path.islink(root_wrapper):
776 os.unlink(root_wrapper)
777 else:
778 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
779 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400780 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500781
782
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700783def FixClangXXWrapper(root, path):
784 """Fix wrapper shell scripts and symlinks for invoking clang++
785
786 In a typical installation, clang++ symlinks to clang, which symlinks to the
787 elf executable. The executable distinguishes between clang and clang++ based
788 on argv[0].
789
790 When invoked through the LdsoWrapper, argv[0] always contains the path to the
791 executable elf file, making clang/clang++ invocations indistinguishable.
792
793 This function detects if the elf executable being wrapped is clang-X.Y, and
794 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
795
796 The calling sequence now becomes:
797 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
798 the Ldsowrapper).
799 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
800 to the original clang-3.9 elf.
801 -) The difference this time is that inside the elf file execution, $0 is
802 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
803
Manoj Guptaae268142018-04-27 23:28:36 -0700804 Update: Starting since clang 7, the clang and clang++ are symlinks to
805 clang-7 binary, not clang-7.0. The pattern match is extended to handle
806 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
807
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700808 Args:
809 root: The root tree to generate scripts / symlinks inside of
810 path: The target elf for which LdsoWrapper was created
811 """
Manoj Guptaae268142018-04-27 23:28:36 -0700812 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700813 logging.info('fixing clang++ invocation for %s', path)
814 clangdir = os.path.dirname(root + path)
815 clang = os.path.basename(path)
816 clangxx = clang.replace('clang', 'clang++')
817
818 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
819 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
820
821 # Create a hardlink clang++-X.Y pointing to clang-X.Y
822 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
823
824 # Adjust the clang++ symlink to point to clang++-X.Y
825 os.unlink(os.path.join(clangdir, 'clang++'))
826 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
827
828
Mike Frysinger35247af2012-11-16 18:58:06 -0500829def FileIsCrosSdkElf(elf):
830 """Determine if |elf| is an ELF that we execute in the cros_sdk
831
832 We don't need this to be perfect, just quick. It makes sure the ELF
833 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
834
835 Args:
836 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500837
Mike Frysinger35247af2012-11-16 18:58:06 -0500838 Returns:
839 True if we think |elf| is a native ELF
840 """
841 with open(elf) as f:
842 data = f.read(20)
843 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
844 return (data[0:4] == '\x7fELF' and
845 data[4] == '\x02' and
846 data[5] == '\x01' and
847 data[18] == '\x3e')
848
849
850def IsPathPackagable(ptype, path):
851 """Should the specified file be included in a toolchain package?
852
853 We only need to handle files as we'll create dirs as we need them.
854
855 Further, trim files that won't be useful:
856 - non-english translations (.mo) since it'd require env vars
857 - debug files since these are for the host compiler itself
858 - info/man pages as they're big, and docs are online, and the
859 native docs should work fine for the most part (`man gcc`)
860
861 Args:
862 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
863 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500864
Mike Frysinger35247af2012-11-16 18:58:06 -0500865 Returns:
866 True if we want to include this path in the package
867 """
868 return not (ptype in ('dir',) or
869 path.startswith('/usr/lib/debug/') or
870 os.path.splitext(path)[1] == '.mo' or
871 ('/man/' in path or '/info/' in path))
872
873
874def ReadlinkRoot(path, root):
875 """Like os.readlink(), but relative to a |root|
876
877 Args:
878 path: The symlink to read
879 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500880
Mike Frysinger35247af2012-11-16 18:58:06 -0500881 Returns:
882 A fully resolved symlink path
883 """
884 while os.path.islink(root + path):
885 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
886 return path
887
888
889def _GetFilesForTarget(target, root='/'):
890 """Locate all the files to package for |target|
891
892 This does not cover ELF dependencies.
893
894 Args:
895 target: The toolchain target name
896 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500897
Mike Frysinger35247af2012-11-16 18:58:06 -0500898 Returns:
899 A tuple of a set of all packable paths, and a set of all paths which
900 are also native ELFs
901 """
902 paths = set()
903 elfs = set()
904
905 # Find all the files owned by the packages for this target.
906 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500907
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700908 # Skip Go compiler from redistributable packages.
909 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
910 # into it. Due to this, the toolchain cannot be unpacked anywhere
911 # else and be readily useful. To enable packaging Go, we need to:
912 # -) Tweak the wrappers/environment to override GOROOT
913 # automatically based on the unpack location.
914 # -) Make sure the ELF dependency checking and wrapping logic
915 # below skips the Go toolchain executables and libraries.
916 # -) Make sure the packaging process maintains the relative
917 # timestamps of precompiled standard library packages.
918 # (see dev-lang/go ebuild for details).
919 if pkg == 'ex_go':
920 continue
921
Yunlian Jiang36f35242018-04-27 10:18:40 -0700922 # Use armv7a-cros-linux-gnueabi/compiler-rt for
923 # armv7a-cros-linux-gnueabihf/compiler-rt.
924 # Currently the armv7a-cros-linux-gnueabi is actually
925 # the same as armv7a-cros-linux-gnueabihf with different names.
926 # Because of that, for compiler-rt, it generates the same binary in
927 # the same location. To avoid the installation conflict, we do not
928 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
929 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
930 # toolchain.
931 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
932 atom = GetPortagePackage(target, pkg)
933 cat, pn = atom.split('/')
934 ver = GetInstalledPackageVersions(atom, root=root)[0]
935 # pylint: disable=E1101
936 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
937 settings=portage.settings)
938 contents = dblink.getcontents()
939 if not contents:
940 if 'hf' in target:
941 new_target = 'armv7a-cros-linux-gnueabi'
942 else:
943 new_target = 'armv7a-cros-linux-gnueabihf'
944 atom = GetPortagePackage(new_target, pkg)
945 else:
946 atom = GetPortagePackage(target, pkg)
947
Mike Frysinger35247af2012-11-16 18:58:06 -0500948 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700949 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700950 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500951
952 # pylint: disable=E1101
953 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
954 settings=portage.settings)
955 contents = dblink.getcontents()
956 for obj in contents:
957 ptype = contents[obj][0]
958 if not IsPathPackagable(ptype, obj):
959 continue
960
961 if ptype == 'obj':
962 # For native ELFs, we need to pull in their dependencies too.
963 if FileIsCrosSdkElf(obj):
964 elfs.add(obj)
965 paths.add(obj)
966
967 return paths, elfs
968
969
970def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500971 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500972 """Link in all packable files and their runtime dependencies
973
974 This also wraps up executable ELFs with helper scripts.
975
976 Args:
977 output_dir: The output directory to store files
978 paths: All the files to include
979 elfs: All the files which are ELFs (a subset of |paths|)
980 ldpaths: A dict of static ldpath information
981 path_rewrite_func: User callback to rewrite paths in output_dir
982 root: The root path to pull all packages/files from
983 """
984 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400985 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500986 for path in paths:
987 new_path = path_rewrite_func(path)
988 dst = output_dir + new_path
989 osutils.SafeMakedirs(os.path.dirname(dst))
990
991 # Is this a symlink which we have to rewrite or wrap?
992 # Delay wrap check until after we have created all paths.
993 src = root + path
994 if os.path.islink(src):
995 tgt = os.readlink(src)
996 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -0400997 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -0500998
999 # Rewrite absolute links to relative and then generate the symlink
1000 # ourselves. All other symlinks can be hardlinked below.
1001 if tgt[0] == '/':
1002 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1003 os.symlink(tgt, dst)
1004 continue
1005
1006 os.link(src, dst)
1007
Mike Frysinger35247af2012-11-16 18:58:06 -05001008 # Locate all the dependencies for all the ELFs. Stick them all in the
1009 # top level "lib" dir to make the wrapper simpler. This exact path does
1010 # not matter since we execute ldso directly, and we tell the ldso the
1011 # exact path to search for its libraries.
1012 libdir = os.path.join(output_dir, 'lib')
1013 osutils.SafeMakedirs(libdir)
1014 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001015 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001016 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001017 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -05001018 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -07001019 # Do not create wrapper for libc. crbug.com/766827
1020 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -05001021 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -04001022 interp = os.path.join('/lib', os.path.basename(interp))
1023 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1024 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -07001025 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -05001026
Mike Frysinger221bd822017-09-29 02:51:47 -04001027 # Wrap any symlinks to the wrapper.
1028 if elf in sym_paths:
1029 link = sym_paths[elf]
1030 GeneratePathWrapper(output_dir, link, elf)
1031
Mike Frysinger35247af2012-11-16 18:58:06 -05001032 for lib, lib_data in e['libs'].iteritems():
1033 if lib in donelibs:
1034 continue
1035
1036 src = path = lib_data['path']
1037 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001038 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001039 continue
1040 donelibs.add(lib)
1041
1042 # Needed libs are the SONAME, but that is usually a symlink, not a
1043 # real file. So link in the target rather than the symlink itself.
1044 # We have to walk all the possible symlinks (SONAME could point to a
1045 # symlink which points to a symlink), and we have to handle absolute
1046 # ourselves (since we have a "root" argument).
1047 dst = os.path.join(libdir, os.path.basename(path))
1048 src = ReadlinkRoot(src, root)
1049
1050 os.link(root + src, dst)
1051
1052
1053def _EnvdGetVar(envd, var):
1054 """Given a Gentoo env.d file, extract a var from it
1055
1056 Args:
1057 envd: The env.d file to load (may be a glob path)
1058 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001059
Mike Frysinger35247af2012-11-16 18:58:06 -05001060 Returns:
1061 The value of |var|
1062 """
1063 envds = glob.glob(envd)
1064 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1065 envd = envds[0]
1066 return cros_build_lib.LoadKeyValueFile(envd)[var]
1067
1068
1069def _ProcessBinutilsConfig(target, output_dir):
1070 """Do what binutils-config would have done"""
1071 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001072
1073 # Locate the bin dir holding the gold linker.
1074 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1075 target, 'binutils-bin')
1076 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001077 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001078 if not srcpath:
1079 # Maybe this target doesn't support gold.
1080 globpath = os.path.join(binutils_bin_path, '*')
1081 srcpath = glob.glob(globpath)
1082 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1083 % globpath)
1084 srcpath = srcpath[0]
1085 ld_path = os.path.join(srcpath, 'ld')
1086 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1087 ld_path = os.path.join(srcpath, 'ld.bfd')
1088 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1089 ld_path = os.path.join(srcpath, 'ld.gold')
1090 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1091 % ld_path)
1092
1093 # Nope, no gold support to be found.
1094 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001095 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001096 else:
1097 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001098 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001099
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001100 # Package the binutils-bin directory without the '-gold' suffix
1101 # if gold is not enabled as the default linker for this target.
1102 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1103 if not gold_supported:
1104 srcpath = srcpath[:-len('-gold')]
1105 ld_path = os.path.join(srcpath, 'ld')
1106 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1107
Mike Frysinger78b7a812014-11-26 19:45:23 -05001108 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001109 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1110 for prog in os.listdir(output_dir + srcpath):
1111 # Skip binaries already wrapped.
1112 if not prog.endswith('.real'):
1113 GeneratePathWrapper(output_dir, binpath + prog,
1114 os.path.join(srcpath, prog))
1115 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1116 os.path.join(srcpath, prog))
1117
David James27ac4ae2012-12-03 23:16:15 -08001118 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001119 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1120 if gold_supported:
1121 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001122 else:
1123 # If gold is not enabled as the default linker and 2 env.d
1124 # files exist, pick the one without the '-gold' suffix.
1125 envds = sorted(glob.glob(envd))
1126 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1127 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001128 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1129 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1130 output_dir + libpath)
1131
1132
1133def _ProcessGccConfig(target, output_dir):
1134 """Do what gcc-config would have done"""
1135 binpath = '/bin'
1136 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1137 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1138 for prog in os.listdir(output_dir + srcpath):
1139 # Skip binaries already wrapped.
1140 if (not prog.endswith('.real') and
1141 not prog.endswith('.elf') and
1142 prog.startswith(target)):
1143 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1144 os.path.join(srcpath, prog))
1145 return srcpath
1146
1147
Frank Henigman179ec7c2015-02-06 03:01:09 -05001148def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1149 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001150 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001151 for sysroot_wrapper in glob.glob(os.path.join(
1152 output_dir + srcpath, 'sysroot_wrapper*')):
1153 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
Douglas Andersoncc828a52017-10-13 13:07:25 -07001154
1155 # In order to optimize startup time in the chroot we run python a little
1156 # differently there. Put it back to the more portable way here.
1157 # See http://crbug.com/773138 for some details.
1158 if contents[0] == '#!/usr/bin/python2 -S':
1159 contents[0] = '#!/usr/bin/env python2'
1160
Frank Henigman179ec7c2015-02-06 03:01:09 -05001161 for num in xrange(len(contents)):
1162 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001163 assert 'True' in contents[num]
1164 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001165 break
1166 # Can't update the wrapper in place since it's a hardlink to a file in /.
1167 os.unlink(sysroot_wrapper)
1168 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1169 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001170
1171
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001172def _CreateMainLibDir(target, output_dir):
1173 """Create some lib dirs so that compiler can get the right Gcc paths"""
1174 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1175 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1176
1177
Mike Frysinger35247af2012-11-16 18:58:06 -05001178def _ProcessDistroCleanups(target, output_dir):
1179 """Clean up the tree and remove all distro-specific requirements
1180
1181 Args:
1182 target: The toolchain target name
1183 output_dir: The output directory to clean up
1184 """
1185 _ProcessBinutilsConfig(target, output_dir)
1186 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001187 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001188 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001189
1190 osutils.RmDir(os.path.join(output_dir, 'etc'))
1191
1192
1193def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1194 """Setup a tree from the packages for the specified target
1195
1196 This populates a path with all the files from toolchain packages so that
1197 a tarball can easily be generated from the result.
1198
1199 Args:
1200 target: The target to create a packagable root from
1201 output_dir: The output directory to place all the files
1202 ldpaths: A dict of static ldpath information
1203 root: The root path to pull all packages/files from
1204 """
1205 # Find all the files owned by the packages for this target.
1206 paths, elfs = _GetFilesForTarget(target, root=root)
1207
1208 # Link in all the package's files, any ELF dependencies, and wrap any
1209 # executable ELFs with helper scripts.
1210 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001211 """Move /usr/bin to /bin so people can just use that toplevel dir
1212
1213 Note we do not apply this to clang - there is correlation between clang's
1214 search path for libraries / inclusion and its installation path.
1215 """
1216 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1217 return path[4:]
1218 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001219 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1220 path_rewrite_func=MoveUsrBinToBin, root=root)
1221
1222 # The packages, when part of the normal distro, have helper scripts
1223 # that setup paths and such. Since we are making this standalone, we
1224 # need to preprocess all that ourselves.
1225 _ProcessDistroCleanups(target, output_dir)
1226
1227
1228def CreatePackages(targets_wanted, output_dir, root='/'):
1229 """Create redistributable cross-compiler packages for the specified targets
1230
1231 This creates toolchain packages that should be usable in conjunction with
1232 a downloaded sysroot (created elsewhere).
1233
1234 Tarballs (one per target) will be created in $PWD.
1235
1236 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001237 targets_wanted: The targets to package up.
1238 output_dir: The directory to put the packages in.
1239 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001240 """
Ralph Nathan03047282015-03-23 11:09:32 -07001241 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001242 osutils.SafeMakedirs(output_dir)
1243 ldpaths = lddtree.LoadLdpaths(root)
1244 targets = ExpandTargets(targets_wanted)
1245
Mike Frysinger221bd822017-09-29 02:51:47 -04001246 with osutils.TempDir(prefix='create-packages') as tempdir:
1247 logging.debug('Using tempdir: %s', tempdir)
1248
Mike Frysinger35247af2012-11-16 18:58:06 -05001249 # We have to split the root generation from the compression stages. This is
1250 # because we hardlink in all the files (to avoid overhead of reading/writing
1251 # the copies multiple times). But tar gets angry if a file's hardlink count
1252 # changes from when it starts reading a file to when it finishes.
1253 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1254 for target in targets:
1255 output_target_dir = os.path.join(tempdir, target)
1256 queue.put([target, output_target_dir, ldpaths, root])
1257
1258 # Build the tarball.
1259 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1260 for target in targets:
1261 tar_file = os.path.join(output_dir, target + '.tar.xz')
1262 queue.put([tar_file, os.path.join(tempdir, target)])
1263
1264
Mike Frysinger07534cf2017-09-12 17:40:21 -04001265def GetParser():
1266 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001267 parser = commandline.ArgumentParser(description=__doc__)
1268 parser.add_argument('-u', '--nousepkg',
1269 action='store_false', dest='usepkg', default=True,
1270 help='Use prebuilt packages if possible')
1271 parser.add_argument('-d', '--deleteold',
1272 action='store_true', dest='deleteold', default=False,
1273 help='Unmerge deprecated packages')
1274 parser.add_argument('-t', '--targets',
1275 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001276 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001277 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001278 "allowed. Defaults to 'sdk'.")
1279 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1280 help='Comma separated list of boards whose toolchains we '
1281 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001282 parser.add_argument('--hostonly',
1283 dest='hostonly', default=False, action='store_true',
1284 help='Only setup the host toolchain. '
1285 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001286 parser.add_argument('--show-board-cfg', '--show-cfg',
1287 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001288 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001289 parser.add_argument('--show-packages', default=None,
1290 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001291 parser.add_argument('--create-packages',
1292 action='store_true', default=False,
1293 help='Build redistributable packages')
1294 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1295 help='Output directory')
1296 parser.add_argument('--reconfig', default=False, action='store_true',
1297 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001298 parser.add_argument('--sysroot', type='path',
1299 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001300 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001301
Mike Frysinger07534cf2017-09-12 17:40:21 -04001302
1303def main(argv):
1304 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001305 options = parser.parse_args(argv)
1306 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001307
Mike Frysinger35247af2012-11-16 18:58:06 -05001308 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001309 conflicting_options = (
1310 options.cfg_name,
1311 options.show_packages,
1312 options.create_packages,
1313 )
1314 if sum(bool(x) for x in conflicting_options) > 1:
1315 parser.error('conflicting options: create-packages & show-packages & '
1316 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001317
Gilad Arnold8195b532015-04-07 10:56:30 +03001318 targets_wanted = set(options.targets.split(','))
1319 boards_wanted = (set(options.include_boards.split(','))
1320 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001321
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001322 if options.cfg_name:
1323 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001324 elif options.show_packages is not None:
1325 cros_build_lib.AssertInsideChroot()
1326 target = options.show_packages
1327 Crossdev.Load(False)
1328 for package in GetTargetPackages(target):
1329 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001330 elif options.create_packages:
1331 cros_build_lib.AssertInsideChroot()
1332 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001333 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001334 else:
1335 cros_build_lib.AssertInsideChroot()
1336 # This has to be always run as root.
1337 if os.geteuid() != 0:
1338 cros_build_lib.Die('this script must be run as root')
1339
1340 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001341 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001342 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001343 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001344 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001345 Crossdev.Save()
1346
1347 return 0