blob: 3fdf44038c666199072b3cf6ef7a8682e948b64f [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.
Mike Frysinger27e21b72018-07-12 14:20:21 -040032 # pylint: disable=import-error
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',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700137 'elfutils': 'dev-libs',
Han Shene23782f2016-02-18 12:20:00 -0800138 }
David James66a09c42012-11-05 13:31:38 -0800139
140 @classmethod
141 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400142 """Load crossdev cache from disk.
143
144 We invalidate the cache when crossdev updates or this script changes.
145 """
David James90239b92012-11-05 15:31:34 -0800146 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400147 # If we run the compiled/cached .pyc file, we'll read/hash that when we
148 # really always want to track the source .py file.
149 script = os.path.abspath(__file__)
150 if script.endswith('.pyc'):
151 script = script[:-1]
152 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
153
154 cls._CACHE = {
155 'crossdev_version': crossdev_version,
156 'setup_toolchains_hash': setup_toolchains_hash,
157 }
158
159 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
160 if reconfig:
161 logging.debug('cache: forcing regen due to reconfig')
162 return
163
164 try:
165 file_data = osutils.ReadFile(cls._CACHE_FILE)
166 except IOError as e:
167 if e.errno != errno.ENOENT:
168 logging.warning('cache: reading failed: %s', e)
169 osutils.SafeUnlink(cls._CACHE_FILE)
170 return
171
172 try:
173 data = json.loads(file_data)
174 except ValueError as e:
175 logging.warning('cache: ignoring invalid content: %s', e)
176 return
177
178 if crossdev_version != data.get('crossdev_version'):
179 logging.debug('cache: rebuilding after crossdev upgrade')
180 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
181 logging.debug('cache: rebuilding after cros_setup_toolchains change')
182 else:
183 logging.debug('cache: content is up-to-date!')
184 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800185
186 @classmethod
187 def Save(cls):
188 """Store crossdev cache on disk."""
189 # Save the cache from the successful run.
190 with open(cls._CACHE_FILE, 'w') as f:
191 json.dump(cls._CACHE, f)
192
193 @classmethod
194 def GetConfig(cls, target):
195 """Returns a map of crossdev provided variables about a tuple."""
196 CACHE_ATTR = '_target_tuple_map'
197
198 val = cls._CACHE.setdefault(CACHE_ATTR, {})
199 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400200 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400201 conf = {
202 'crosspkgs': [],
203 'target': toolchain.GetHostTuple(),
204 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400205 if target == 'host':
206 packages_list = HOST_PACKAGES
207 else:
208 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400209 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400210 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400211 else:
212 # Build the crossdev command.
213 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
214 if target in TARGET_COMPILER_RT_ENABLED:
215 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
216 if target in TARGET_GO_ENABLED:
217 cmd.extend(CROSSDEV_GO_ARGS)
218 if target in TARGET_LLVM_PKGS_ENABLED:
219 for pkg in LLVM_PKGS_TABLE:
220 cmd.extend(LLVM_PKGS_TABLE[pkg])
221 cmd.extend(['-t', target])
222 # Catch output of crossdev.
223 out = cros_build_lib.RunCommand(
224 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
225 # List of tuples split at the first '=', converted into dict.
226 conf = dict((k, cros_build_lib.ShellUnquote(v))
227 for k, v in (x.split('=', 1) for x in out))
228 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800229
Mike Frysinger66bfde52017-09-12 16:42:57 -0400230 manual_pkgs = cls.MANUAL_PKGS
231
232 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400233 conf[pkg + '_pn'] = pkg
234 conf[pkg + '_category'] = cat
235 if pkg not in conf['crosspkgs']:
236 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800237
238 val[target] = conf
239
David James66a09c42012-11-05 13:31:38 -0800240 return val[target]
241
242 @classmethod
243 def UpdateTargets(cls, targets, usepkg, config_only=False):
244 """Calls crossdev to initialize a cross target.
245
246 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700247 targets: The list of targets to initialize using crossdev.
248 usepkg: Copies the commandline opts.
249 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800250 """
251 configured_targets = cls._CACHE.setdefault('configured_targets', [])
252
253 cmdbase = ['crossdev', '--show-fail-log']
254 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
255 # Pick stable by default, and override as necessary.
256 cmdbase.extend(['-P', '--oneshot'])
257 if usepkg:
258 cmdbase.extend(['-P', '--getbinpkg',
259 '-P', '--usepkgonly',
260 '--without-headers'])
261
Christopher Wileyb22c0712015-06-02 10:37:03 -0700262 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800263 cmdbase.extend(['--overlays', overlays])
264 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
265
Yunlian Jiang4ff84172018-05-12 13:03:01 -0700266 # Build target by the reversed alphabetical order to make sure
267 # armv7a-cros-linux-gnueabihf builds before armv7a-cros-linux-gnueabi
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700268 # because some dependency issue. This can be reverted once we
269 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
Yunlian Jiang4ff84172018-05-12 13:03:01 -0700270 for target in sorted(targets, reverse=True):
David James66a09c42012-11-05 13:31:38 -0800271 if config_only and target in configured_targets:
272 continue
273
274 cmd = cmdbase + ['-t', target]
275
276 for pkg in GetTargetPackages(target):
277 if pkg == 'gdb':
278 # Gdb does not have selectable versions.
279 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700280 elif pkg == 'ex_compiler-rt':
281 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700282 elif pkg == 'ex_go':
283 # Go does not have selectable versions.
284 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700285 elif pkg in LLVM_PKGS_TABLE:
286 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800287 elif pkg in cls.MANUAL_PKGS:
288 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700289 else:
290 # The first of the desired versions is the "primary" one.
291 version = GetDesiredPackageVersions(target, pkg)[0]
292 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800293
294 cmd.extend(targets[target]['crossdev'].split())
295 if config_only:
296 # In this case we want to just quietly reinit
297 cmd.append('--init-target')
298 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
299 else:
300 cros_build_lib.RunCommand(cmd)
301
302 configured_targets.append(target)
303
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100304
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100305def GetTargetPackages(target):
306 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800307 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100308 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800309 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100310
311
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100312# Portage helper functions:
313def GetPortagePackage(target, package):
314 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800315 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100316 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400317 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100318 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100319 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100320 category = conf['category']
321 # Portage package:
322 pn = conf[package + '_pn']
323 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500324 assert category
325 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100326 return '%s/%s' % (category, pn)
327
328
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700329def PortageTrees(root):
330 """Return the portage trees for a given root."""
331 if root == '/':
332 return portage.db['/']
333 # The portage logic requires the path always end in a slash.
334 root = root.rstrip('/') + '/'
335 return portage.create_trees(target_root=root, config_root=root)[root]
336
337
338def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100339 """Extracts the list of current versions of a target, package pair.
340
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500341 Args:
342 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700343 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100344
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500345 Returns:
346 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100348 versions = []
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'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700367 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800368 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369
370
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700371def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100372 """Resolves keywords in a given version list for a particular package.
373
374 Resolving means replacing PACKAGE_STABLE with the actual number.
375
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500376 Args:
377 target: The target to operate on (e.g. i686-pc-linux-gnu)
378 package: The target/package to operate on (e.g. gcc)
379 versions: List of versions to resolve
380 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700381 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100382
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500383 Returns:
384 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100385 """
386 resolved = []
David James90239b92012-11-05 15:31:34 -0800387 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700388 if not installed:
389 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100390 for version in versions:
391 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700392 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400393 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100394 resolved.append(version)
395 return resolved
396
397
398def GetDesiredPackageVersions(target, package):
399 """Produces the list of desired versions for each target, package pair.
400
401 The first version in the list is implicitly treated as primary, ie.
402 the version that will be initialized by crossdev and selected.
403
404 If the version is PACKAGE_STABLE, it really means the current version which
405 is emerged by using the package atom with no particular version key.
406 Since crossdev unmasks all packages by default, this will actually
407 mean 'unstable' in most cases.
408
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500409 Args:
410 target: The target to operate on (e.g. i686-pc-linux-gnu)
411 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100412
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500413 Returns:
414 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400416 if package in GetTargetPackages(target):
417 return [PACKAGE_STABLE]
418 else:
419 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100420
421
422def TargetIsInitialized(target):
423 """Verifies if the given list of targets has been correctly initialized.
424
425 This determines whether we have to call crossdev while emerging
426 toolchain packages or can do it using emerge. Emerge is naturally
427 preferred, because all packages can be updated in a single pass.
428
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500429 Args:
430 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100431
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500432 Returns:
433 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100434 """
435 # Check if packages for the given target all have a proper version.
436 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100437 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800438 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100439 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400440 if not (GetStablePackageVersion(atom, True) and
441 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442 return False
443 return True
444 except cros_build_lib.RunCommandError:
445 # Fails - The target has likely never been initialized before.
446 return False
447
448
449def RemovePackageMask(target):
450 """Removes a package.mask file for the given platform.
451
452 The pre-existing package.mask files can mess with the keywords.
453
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500454 Args:
455 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100456 """
457 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700458 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100459
460
Zdenek Behan508dcce2011-12-05 15:39:32 +0100461# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700462def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500463 """Rebuild libtool as needed
464
465 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
466 gcc, libtool will break. We can't use binary packages either as those will
467 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700468
469 Args:
470 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500471 """
472 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700473 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500474 for line in f:
475 # Look for a line like:
476 # sys_lib_search_path_spec="..."
477 # It'll be a list of paths and gcc will be one of them.
478 if line.startswith('sys_lib_search_path_spec='):
479 line = line.rstrip()
480 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400481 root_path = os.path.join(root, path.lstrip(os.path.sep))
482 logging.debug('Libtool: checking %s', root_path)
483 if not os.path.exists(root_path):
484 logging.info('Rebuilding libtool after gcc upgrade')
485 logging.info(' %s', line)
486 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500487 needs_update = True
488 break
489
490 if needs_update:
491 break
492
493 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700494 cmd = [EMERGE_CMD, '--oneshot']
495 if root != '/':
496 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
497 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500498 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400499 else:
500 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500501
502
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700503def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100504 """Determines which packages need update/unmerge and defers to portage.
505
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500506 Args:
507 targets: The list of targets to update
508 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700509 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100511 # For each target, we do two things. Figure out the list of updates,
512 # and figure out the appropriate keywords/masks. Crossdev will initialize
513 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400514 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800515 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100516 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400517 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100518 # Record the highest needed version for each target, for masking purposes.
519 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100520 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100521 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400522 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100523 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700524 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100525 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200526 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400527 if pkg in NEW_PACKAGES and usepkg:
528 # Skip this binary package (for now).
529 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100530 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400531 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400533 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400535 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800536 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100537
Mike Frysinger3bba5032016-09-20 14:15:04 -0400538 logging.info('Updating packages:')
539 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100541 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100542 if usepkg:
543 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700544 if root != '/':
545 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100546
547 cmd.extend(packages)
548 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800549 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100550
551
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700552def CleanTargets(targets, root='/'):
553 """Unmerges old packages that are assumed unnecessary.
554
555 Args:
556 targets: The list of targets to clean up.
557 root: The install root in which we want packages cleaned up.
558 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100559 unmergemap = {}
560 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400561 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100562 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400563 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100564 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700565 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100566 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700567 # NOTE: This refers to installed packages (vartree) rather than the
568 # Portage version (porttree and/or bintree) when determining the current
569 # version. While this isn't the most accurate thing to do, it is probably
570 # a good simple compromise, which should have the desired result of
571 # uninstalling everything but the latest installed version. In
572 # particular, using the bintree (--usebinpkg) requires a non-trivial
573 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200574 desired_num = VersionListToNumeric(target, package, desired, True)
575 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400576 logging.warning('Error detecting stable version for %s, '
577 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200578 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100579 unmergemap[pkg] = set(current).difference(desired_num)
580
581 # Cleaning doesn't care about consistency and rebuilding package.* files.
582 packages = []
583 for pkg, vers in unmergemap.iteritems():
584 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
585
586 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400587 logging.info('Cleaning packages:')
588 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100589 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700590 if root != '/':
591 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100592 cmd.extend(packages)
593 cros_build_lib.RunCommand(cmd)
594 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400595 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100596
597
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700598def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100599 """Runs gcc-config and binutils-config to select the desired.
600
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500601 Args:
602 targets: The targets to select
603 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700604 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100605 """
606 for package in ['gcc', 'binutils']:
607 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400608 # See if this package is part of this target.
609 if package not in GetTargetPackages(target):
610 logging.debug('%s: %s is not used', target, package)
611 continue
612
Zdenek Behan508dcce2011-12-05 15:39:32 +0100613 # Pick the first version in the numbered list as the selected one.
614 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700615 desired_num = VersionListToNumeric(target, package, desired, True,
616 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100617 desired = desired_num[0]
618 # *-config does not play revisions, strip them, keep just PV.
619 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
620
Mike Frysinger785b0c32017-09-13 01:35:59 -0400621 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100622 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800623 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100624
625 # And finally, attach target to it.
626 desired = '%s-%s' % (target, desired)
627
628 # Target specific hacks
629 if package in suffixes:
630 if target in suffixes[package]:
631 desired += suffixes[package][target]
632
David James7ec5efc2012-11-06 09:39:49 -0800633 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700634 if root != '/':
635 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800636 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500637 result = cros_build_lib.RunCommand(
638 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
639 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700640
641 # Do not reconfig when the current is live or nothing needs to be done.
642 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100643 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500644 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700645 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100646
647
Mike Frysinger35247af2012-11-16 18:58:06 -0500648def ExpandTargets(targets_wanted):
649 """Expand any possible toolchain aliases into full targets
650
651 This will expand 'all' and 'sdk' into the respective toolchain tuples.
652
653 Args:
654 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500655
Mike Frysinger35247af2012-11-16 18:58:06 -0500656 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300657 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500658 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500659 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700660 if targets_wanted == set(['boards']):
661 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300662 return {}
663
664 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500665 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300666 return all_targets
667 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500668 # Filter out all the non-sdk toolchains as we don't want to mess
669 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300670 return toolchain.FilterToolchains(all_targets, 'sdk', True)
671
672 # Verify user input.
673 nonexistent = targets_wanted.difference(all_targets)
674 if nonexistent:
675 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
676 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500677
678
David Jamesf8c672f2012-11-06 13:38:11 -0800679def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700680 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100681 """Performs all steps to create a synchronized toolchain enviroment.
682
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500683 Args:
684 usepkg: Use prebuilt packages
685 deleteold: Unmerge deprecated packages
686 hostonly: Only setup the host toolchain
687 reconfig: Reload crossdev config and reselect toolchains
688 targets_wanted: All the targets to update
689 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700690 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100691 """
David Jamesf8c672f2012-11-06 13:38:11 -0800692 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100693 if not hostonly:
694 # For hostonly, we can skip most of the below logic, much of which won't
695 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500696 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400697
Don Garrettc0c74002015-10-09 12:58:19 -0700698 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300699 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400700 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800701 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100702
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100703 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400704 for target in targets:
705 if TargetIsInitialized(target):
706 reconfig_targets[target] = targets[target]
707 else:
708 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100709 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400710 logging.info('The following targets need to be re-initialized:')
711 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800712 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200713 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800714 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100715
Mike Frysinger66814c32017-10-09 18:11:46 -0400716 # If we're building a subset of toolchains for a board, we might not have
717 # all the tuples that the packages expect. We don't define the "full" set
718 # of tuples currently other than "whatever the full sdk has normally".
719 if usepkg or set(('all', 'sdk')) & targets_wanted:
720 # Since we have cross-compilers now, we can update these packages.
721 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400722
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100723 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400724 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100725
726 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700727 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
728 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800729
730 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700731 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100732
Mike Frysingerc880a962013-11-08 13:59:06 -0500733 # Now that we've cleared out old versions, see if we need to rebuild
734 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700735 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500736
Zdenek Behan508dcce2011-12-05 15:39:32 +0100737
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700738def ShowConfig(name):
739 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500740
741 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700742 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500743 """
Don Garrettc0c74002015-10-09 12:58:19 -0700744 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500745 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400746 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400747 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800748 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400749 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500750
751
Mike Frysinger35247af2012-11-16 18:58:06 -0500752def GeneratePathWrapper(root, wrappath, path):
753 """Generate a shell script to execute another shell script
754
755 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
756 argv[0] won't be pointing to the correct path, generate a shell script that
757 just executes another program with its full path.
758
759 Args:
760 root: The root tree to generate scripts inside of
761 wrappath: The full path (inside |root|) to create the wrapper
762 path: The target program which this wrapper will execute
763 """
764 replacements = {
765 'path': path,
766 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
767 }
768 wrapper = """#!/bin/sh
769base=$(realpath "$0")
770basedir=${base%%/*}
771exec "${basedir}/%(relroot)s%(path)s" "$@"
772""" % replacements
773 root_wrapper = root + wrappath
774 if os.path.islink(root_wrapper):
775 os.unlink(root_wrapper)
776 else:
777 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
778 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400779 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500780
781
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700782def FixClangXXWrapper(root, path):
783 """Fix wrapper shell scripts and symlinks for invoking clang++
784
785 In a typical installation, clang++ symlinks to clang, which symlinks to the
786 elf executable. The executable distinguishes between clang and clang++ based
787 on argv[0].
788
789 When invoked through the LdsoWrapper, argv[0] always contains the path to the
790 executable elf file, making clang/clang++ invocations indistinguishable.
791
792 This function detects if the elf executable being wrapped is clang-X.Y, and
793 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
794
795 The calling sequence now becomes:
796 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
797 the Ldsowrapper).
798 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
799 to the original clang-3.9 elf.
800 -) The difference this time is that inside the elf file execution, $0 is
801 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
802
Manoj Guptaae268142018-04-27 23:28:36 -0700803 Update: Starting since clang 7, the clang and clang++ are symlinks to
804 clang-7 binary, not clang-7.0. The pattern match is extended to handle
805 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
806
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700807 Args:
808 root: The root tree to generate scripts / symlinks inside of
809 path: The target elf for which LdsoWrapper was created
810 """
Manoj Guptaae268142018-04-27 23:28:36 -0700811 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700812 logging.info('fixing clang++ invocation for %s', path)
813 clangdir = os.path.dirname(root + path)
814 clang = os.path.basename(path)
815 clangxx = clang.replace('clang', 'clang++')
816
817 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
818 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
819
820 # Create a hardlink clang++-X.Y pointing to clang-X.Y
821 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
822
823 # Adjust the clang++ symlink to point to clang++-X.Y
824 os.unlink(os.path.join(clangdir, 'clang++'))
825 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
826
827
Mike Frysinger35247af2012-11-16 18:58:06 -0500828def FileIsCrosSdkElf(elf):
829 """Determine if |elf| is an ELF that we execute in the cros_sdk
830
831 We don't need this to be perfect, just quick. It makes sure the ELF
832 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
833
834 Args:
835 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500836
Mike Frysinger35247af2012-11-16 18:58:06 -0500837 Returns:
838 True if we think |elf| is a native ELF
839 """
840 with open(elf) as f:
841 data = f.read(20)
842 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
843 return (data[0:4] == '\x7fELF' and
844 data[4] == '\x02' and
845 data[5] == '\x01' and
846 data[18] == '\x3e')
847
848
849def IsPathPackagable(ptype, path):
850 """Should the specified file be included in a toolchain package?
851
852 We only need to handle files as we'll create dirs as we need them.
853
854 Further, trim files that won't be useful:
855 - non-english translations (.mo) since it'd require env vars
856 - debug files since these are for the host compiler itself
857 - info/man pages as they're big, and docs are online, and the
858 native docs should work fine for the most part (`man gcc`)
859
860 Args:
861 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
862 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500863
Mike Frysinger35247af2012-11-16 18:58:06 -0500864 Returns:
865 True if we want to include this path in the package
866 """
867 return not (ptype in ('dir',) or
868 path.startswith('/usr/lib/debug/') or
869 os.path.splitext(path)[1] == '.mo' or
870 ('/man/' in path or '/info/' in path))
871
872
873def ReadlinkRoot(path, root):
874 """Like os.readlink(), but relative to a |root|
875
876 Args:
877 path: The symlink to read
878 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500879
Mike Frysinger35247af2012-11-16 18:58:06 -0500880 Returns:
881 A fully resolved symlink path
882 """
883 while os.path.islink(root + path):
884 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
885 return path
886
887
888def _GetFilesForTarget(target, root='/'):
889 """Locate all the files to package for |target|
890
891 This does not cover ELF dependencies.
892
893 Args:
894 target: The toolchain target name
895 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500896
Mike Frysinger35247af2012-11-16 18:58:06 -0500897 Returns:
898 A tuple of a set of all packable paths, and a set of all paths which
899 are also native ELFs
900 """
901 paths = set()
902 elfs = set()
903
904 # Find all the files owned by the packages for this target.
905 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500906
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700907 # Skip Go compiler from redistributable packages.
908 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
909 # into it. Due to this, the toolchain cannot be unpacked anywhere
910 # else and be readily useful. To enable packaging Go, we need to:
911 # -) Tweak the wrappers/environment to override GOROOT
912 # automatically based on the unpack location.
913 # -) Make sure the ELF dependency checking and wrapping logic
914 # below skips the Go toolchain executables and libraries.
915 # -) Make sure the packaging process maintains the relative
916 # timestamps of precompiled standard library packages.
917 # (see dev-lang/go ebuild for details).
918 if pkg == 'ex_go':
919 continue
920
Yunlian Jiang36f35242018-04-27 10:18:40 -0700921 # Use armv7a-cros-linux-gnueabi/compiler-rt for
922 # armv7a-cros-linux-gnueabihf/compiler-rt.
923 # Currently the armv7a-cros-linux-gnueabi is actually
924 # the same as armv7a-cros-linux-gnueabihf with different names.
925 # Because of that, for compiler-rt, it generates the same binary in
926 # the same location. To avoid the installation conflict, we do not
927 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
928 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
929 # toolchain.
930 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
931 atom = GetPortagePackage(target, pkg)
932 cat, pn = atom.split('/')
933 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700934 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
935 settings=portage.settings)
936 contents = dblink.getcontents()
937 if not contents:
938 if 'hf' in target:
939 new_target = 'armv7a-cros-linux-gnueabi'
940 else:
941 new_target = 'armv7a-cros-linux-gnueabihf'
942 atom = GetPortagePackage(new_target, pkg)
943 else:
944 atom = GetPortagePackage(target, pkg)
945
Mike Frysinger35247af2012-11-16 18:58:06 -0500946 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700947 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700948 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500949
Mike Frysinger35247af2012-11-16 18:58:06 -0500950 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
951 settings=portage.settings)
952 contents = dblink.getcontents()
953 for obj in contents:
954 ptype = contents[obj][0]
955 if not IsPathPackagable(ptype, obj):
956 continue
957
958 if ptype == 'obj':
959 # For native ELFs, we need to pull in their dependencies too.
960 if FileIsCrosSdkElf(obj):
961 elfs.add(obj)
962 paths.add(obj)
963
964 return paths, elfs
965
966
967def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500968 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500969 """Link in all packable files and their runtime dependencies
970
971 This also wraps up executable ELFs with helper scripts.
972
973 Args:
974 output_dir: The output directory to store files
975 paths: All the files to include
976 elfs: All the files which are ELFs (a subset of |paths|)
977 ldpaths: A dict of static ldpath information
978 path_rewrite_func: User callback to rewrite paths in output_dir
979 root: The root path to pull all packages/files from
980 """
981 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400982 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500983 for path in paths:
984 new_path = path_rewrite_func(path)
985 dst = output_dir + new_path
986 osutils.SafeMakedirs(os.path.dirname(dst))
987
988 # Is this a symlink which we have to rewrite or wrap?
989 # Delay wrap check until after we have created all paths.
990 src = root + path
991 if os.path.islink(src):
992 tgt = os.readlink(src)
993 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -0400994 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -0500995
996 # Rewrite absolute links to relative and then generate the symlink
997 # ourselves. All other symlinks can be hardlinked below.
998 if tgt[0] == '/':
999 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1000 os.symlink(tgt, dst)
1001 continue
1002
1003 os.link(src, dst)
1004
Mike Frysinger35247af2012-11-16 18:58:06 -05001005 # Locate all the dependencies for all the ELFs. Stick them all in the
1006 # top level "lib" dir to make the wrapper simpler. This exact path does
1007 # not matter since we execute ldso directly, and we tell the ldso the
1008 # exact path to search for its libraries.
1009 libdir = os.path.join(output_dir, 'lib')
1010 osutils.SafeMakedirs(libdir)
1011 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001012 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001013 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001014 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -05001015 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -07001016 # Do not create wrapper for libc. crbug.com/766827
1017 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -05001018 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -04001019 interp = os.path.join('/lib', os.path.basename(interp))
1020 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1021 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -07001022 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -05001023
Mike Frysinger221bd822017-09-29 02:51:47 -04001024 # Wrap any symlinks to the wrapper.
1025 if elf in sym_paths:
1026 link = sym_paths[elf]
1027 GeneratePathWrapper(output_dir, link, elf)
1028
Mike Frysinger35247af2012-11-16 18:58:06 -05001029 for lib, lib_data in e['libs'].iteritems():
1030 if lib in donelibs:
1031 continue
1032
1033 src = path = lib_data['path']
1034 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001035 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001036 continue
1037 donelibs.add(lib)
1038
1039 # Needed libs are the SONAME, but that is usually a symlink, not a
1040 # real file. So link in the target rather than the symlink itself.
1041 # We have to walk all the possible symlinks (SONAME could point to a
1042 # symlink which points to a symlink), and we have to handle absolute
1043 # ourselves (since we have a "root" argument).
1044 dst = os.path.join(libdir, os.path.basename(path))
1045 src = ReadlinkRoot(src, root)
1046
1047 os.link(root + src, dst)
1048
1049
1050def _EnvdGetVar(envd, var):
1051 """Given a Gentoo env.d file, extract a var from it
1052
1053 Args:
1054 envd: The env.d file to load (may be a glob path)
1055 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001056
Mike Frysinger35247af2012-11-16 18:58:06 -05001057 Returns:
1058 The value of |var|
1059 """
1060 envds = glob.glob(envd)
1061 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1062 envd = envds[0]
1063 return cros_build_lib.LoadKeyValueFile(envd)[var]
1064
1065
1066def _ProcessBinutilsConfig(target, output_dir):
1067 """Do what binutils-config would have done"""
1068 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001069
1070 # Locate the bin dir holding the gold linker.
1071 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1072 target, 'binutils-bin')
1073 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001074 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001075 if not srcpath:
1076 # Maybe this target doesn't support gold.
1077 globpath = os.path.join(binutils_bin_path, '*')
1078 srcpath = glob.glob(globpath)
1079 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1080 % globpath)
1081 srcpath = srcpath[0]
1082 ld_path = os.path.join(srcpath, 'ld')
1083 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1084 ld_path = os.path.join(srcpath, 'ld.bfd')
1085 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1086 ld_path = os.path.join(srcpath, 'ld.gold')
1087 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1088 % ld_path)
1089
1090 # Nope, no gold support to be found.
1091 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001092 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001093 else:
1094 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001095 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001096
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001097 # Package the binutils-bin directory without the '-gold' suffix
1098 # if gold is not enabled as the default linker for this target.
1099 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1100 if not gold_supported:
1101 srcpath = srcpath[:-len('-gold')]
1102 ld_path = os.path.join(srcpath, 'ld')
1103 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1104
Mike Frysinger78b7a812014-11-26 19:45:23 -05001105 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001106 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1107 for prog in os.listdir(output_dir + srcpath):
1108 # Skip binaries already wrapped.
1109 if not prog.endswith('.real'):
1110 GeneratePathWrapper(output_dir, binpath + prog,
1111 os.path.join(srcpath, prog))
1112 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1113 os.path.join(srcpath, prog))
1114
David James27ac4ae2012-12-03 23:16:15 -08001115 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001116 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1117 if gold_supported:
1118 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001119 else:
1120 # If gold is not enabled as the default linker and 2 env.d
1121 # files exist, pick the one without the '-gold' suffix.
1122 envds = sorted(glob.glob(envd))
1123 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1124 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001125 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1126 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1127 output_dir + libpath)
1128
1129
1130def _ProcessGccConfig(target, output_dir):
1131 """Do what gcc-config would have done"""
1132 binpath = '/bin'
1133 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1134 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1135 for prog in os.listdir(output_dir + srcpath):
1136 # Skip binaries already wrapped.
1137 if (not prog.endswith('.real') and
1138 not prog.endswith('.elf') and
1139 prog.startswith(target)):
1140 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1141 os.path.join(srcpath, prog))
1142 return srcpath
1143
1144
Frank Henigman179ec7c2015-02-06 03:01:09 -05001145def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1146 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001147 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001148 for sysroot_wrapper in glob.glob(os.path.join(
1149 output_dir + srcpath, 'sysroot_wrapper*')):
1150 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
Douglas Andersoncc828a52017-10-13 13:07:25 -07001151
1152 # In order to optimize startup time in the chroot we run python a little
1153 # differently there. Put it back to the more portable way here.
1154 # See http://crbug.com/773138 for some details.
1155 if contents[0] == '#!/usr/bin/python2 -S':
1156 contents[0] = '#!/usr/bin/env python2'
1157
Frank Henigman179ec7c2015-02-06 03:01:09 -05001158 for num in xrange(len(contents)):
1159 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001160 assert 'True' in contents[num]
1161 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001162 break
1163 # Can't update the wrapper in place since it's a hardlink to a file in /.
1164 os.unlink(sysroot_wrapper)
1165 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1166 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001167
1168
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001169def _CreateMainLibDir(target, output_dir):
1170 """Create some lib dirs so that compiler can get the right Gcc paths"""
1171 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1172 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1173
1174
Mike Frysinger35247af2012-11-16 18:58:06 -05001175def _ProcessDistroCleanups(target, output_dir):
1176 """Clean up the tree and remove all distro-specific requirements
1177
1178 Args:
1179 target: The toolchain target name
1180 output_dir: The output directory to clean up
1181 """
1182 _ProcessBinutilsConfig(target, output_dir)
1183 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001184 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001185 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001186
1187 osutils.RmDir(os.path.join(output_dir, 'etc'))
1188
1189
1190def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1191 """Setup a tree from the packages for the specified target
1192
1193 This populates a path with all the files from toolchain packages so that
1194 a tarball can easily be generated from the result.
1195
1196 Args:
1197 target: The target to create a packagable root from
1198 output_dir: The output directory to place all the files
1199 ldpaths: A dict of static ldpath information
1200 root: The root path to pull all packages/files from
1201 """
1202 # Find all the files owned by the packages for this target.
1203 paths, elfs = _GetFilesForTarget(target, root=root)
1204
1205 # Link in all the package's files, any ELF dependencies, and wrap any
1206 # executable ELFs with helper scripts.
1207 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001208 """Move /usr/bin to /bin so people can just use that toplevel dir
1209
1210 Note we do not apply this to clang - there is correlation between clang's
1211 search path for libraries / inclusion and its installation path.
1212 """
1213 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1214 return path[4:]
1215 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001216 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1217 path_rewrite_func=MoveUsrBinToBin, root=root)
1218
1219 # The packages, when part of the normal distro, have helper scripts
1220 # that setup paths and such. Since we are making this standalone, we
1221 # need to preprocess all that ourselves.
1222 _ProcessDistroCleanups(target, output_dir)
1223
1224
1225def CreatePackages(targets_wanted, output_dir, root='/'):
1226 """Create redistributable cross-compiler packages for the specified targets
1227
1228 This creates toolchain packages that should be usable in conjunction with
1229 a downloaded sysroot (created elsewhere).
1230
1231 Tarballs (one per target) will be created in $PWD.
1232
1233 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001234 targets_wanted: The targets to package up.
1235 output_dir: The directory to put the packages in.
1236 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001237 """
Ralph Nathan03047282015-03-23 11:09:32 -07001238 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001239 osutils.SafeMakedirs(output_dir)
1240 ldpaths = lddtree.LoadLdpaths(root)
1241 targets = ExpandTargets(targets_wanted)
1242
Mike Frysinger221bd822017-09-29 02:51:47 -04001243 with osutils.TempDir(prefix='create-packages') as tempdir:
1244 logging.debug('Using tempdir: %s', tempdir)
1245
Mike Frysinger35247af2012-11-16 18:58:06 -05001246 # We have to split the root generation from the compression stages. This is
1247 # because we hardlink in all the files (to avoid overhead of reading/writing
1248 # the copies multiple times). But tar gets angry if a file's hardlink count
1249 # changes from when it starts reading a file to when it finishes.
1250 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1251 for target in targets:
1252 output_target_dir = os.path.join(tempdir, target)
1253 queue.put([target, output_target_dir, ldpaths, root])
1254
1255 # Build the tarball.
1256 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1257 for target in targets:
1258 tar_file = os.path.join(output_dir, target + '.tar.xz')
1259 queue.put([tar_file, os.path.join(tempdir, target)])
1260
1261
Mike Frysinger07534cf2017-09-12 17:40:21 -04001262def GetParser():
1263 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001264 parser = commandline.ArgumentParser(description=__doc__)
1265 parser.add_argument('-u', '--nousepkg',
1266 action='store_false', dest='usepkg', default=True,
1267 help='Use prebuilt packages if possible')
1268 parser.add_argument('-d', '--deleteold',
1269 action='store_true', dest='deleteold', default=False,
1270 help='Unmerge deprecated packages')
1271 parser.add_argument('-t', '--targets',
1272 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001273 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001274 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001275 "allowed. Defaults to 'sdk'.")
1276 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1277 help='Comma separated list of boards whose toolchains we '
1278 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001279 parser.add_argument('--hostonly',
1280 dest='hostonly', default=False, action='store_true',
1281 help='Only setup the host toolchain. '
1282 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001283 parser.add_argument('--show-board-cfg', '--show-cfg',
1284 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001285 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001286 parser.add_argument('--show-packages', default=None,
1287 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001288 parser.add_argument('--create-packages',
1289 action='store_true', default=False,
1290 help='Build redistributable packages')
1291 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1292 help='Output directory')
1293 parser.add_argument('--reconfig', default=False, action='store_true',
1294 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001295 parser.add_argument('--sysroot', type='path',
1296 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001297 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001298
Mike Frysinger07534cf2017-09-12 17:40:21 -04001299
1300def main(argv):
1301 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001302 options = parser.parse_args(argv)
1303 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001304
Mike Frysinger35247af2012-11-16 18:58:06 -05001305 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001306 conflicting_options = (
1307 options.cfg_name,
1308 options.show_packages,
1309 options.create_packages,
1310 )
1311 if sum(bool(x) for x in conflicting_options) > 1:
1312 parser.error('conflicting options: create-packages & show-packages & '
1313 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001314
Gilad Arnold8195b532015-04-07 10:56:30 +03001315 targets_wanted = set(options.targets.split(','))
1316 boards_wanted = (set(options.include_boards.split(','))
1317 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001318
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001319 if options.cfg_name:
1320 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001321 elif options.show_packages is not None:
1322 cros_build_lib.AssertInsideChroot()
1323 target = options.show_packages
1324 Crossdev.Load(False)
1325 for package in GetTargetPackages(target):
1326 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001327 elif options.create_packages:
1328 cros_build_lib.AssertInsideChroot()
1329 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001330 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001331 else:
1332 cros_build_lib.AssertInsideChroot()
1333 # This has to be always run as root.
1334 if os.geteuid() != 0:
1335 cros_build_lib.Die('this script must be run as root')
1336
1337 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001338 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001339 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001340 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001341 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001342 Crossdev.Save()
1343
1344 return 0