blob: a3fe06ab49ab648eb30c57af6825881d6bbd9fe0 [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'],
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700110 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700111}
112
Zdenek Behan508dcce2011-12-05 15:39:32 +0100113# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
114CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500115 'binutils' : {
Manoj Guptaa91e38e2018-11-15 11:07:48 -0800116 'aarch64-cros-linux-gnu' : '-gold',
Mike Frysinger8a83c622015-05-28 00:35:05 -0400117 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -0800118 'armv7a-cros-linux-gnueabi': '-gold',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700119 'armv7a-cros-linux-gnueabihf': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500120 'i686-pc-linux-gnu' : '-gold',
121 'x86_64-cros-linux-gnu' : '-gold',
122 },
Zdenek Behan508dcce2011-12-05 15:39:32 +0100123}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100124
125
David James66a09c42012-11-05 13:31:38 -0800126class Crossdev(object):
127 """Class for interacting with crossdev and caching its output."""
128
129 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
130 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800131 # Packages that needs separate handling, in addition to what we have from
132 # crossdev.
133 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700134 'clang': 'sys-devel',
Yunlian Jiang18ae9982017-11-03 09:15:31 -0700135 'lld': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800136 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700137 'libcxxabi': 'sys-libs',
138 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700139 'elfutils': 'dev-libs',
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700140 'llvm-libunwind': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800141 }
David James66a09c42012-11-05 13:31:38 -0800142
143 @classmethod
144 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400145 """Load crossdev cache from disk.
146
147 We invalidate the cache when crossdev updates or this script changes.
148 """
David James90239b92012-11-05 15:31:34 -0800149 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400150 # If we run the compiled/cached .pyc file, we'll read/hash that when we
151 # really always want to track the source .py file.
152 script = os.path.abspath(__file__)
153 if script.endswith('.pyc'):
154 script = script[:-1]
155 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
156
157 cls._CACHE = {
158 'crossdev_version': crossdev_version,
159 'setup_toolchains_hash': setup_toolchains_hash,
160 }
161
162 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
163 if reconfig:
164 logging.debug('cache: forcing regen due to reconfig')
165 return
166
167 try:
168 file_data = osutils.ReadFile(cls._CACHE_FILE)
169 except IOError as e:
170 if e.errno != errno.ENOENT:
171 logging.warning('cache: reading failed: %s', e)
172 osutils.SafeUnlink(cls._CACHE_FILE)
173 return
174
175 try:
176 data = json.loads(file_data)
177 except ValueError as e:
178 logging.warning('cache: ignoring invalid content: %s', e)
179 return
180
181 if crossdev_version != data.get('crossdev_version'):
182 logging.debug('cache: rebuilding after crossdev upgrade')
183 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
184 logging.debug('cache: rebuilding after cros_setup_toolchains change')
185 else:
186 logging.debug('cache: content is up-to-date!')
187 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800188
189 @classmethod
190 def Save(cls):
191 """Store crossdev cache on disk."""
192 # Save the cache from the successful run.
193 with open(cls._CACHE_FILE, 'w') as f:
194 json.dump(cls._CACHE, f)
195
196 @classmethod
197 def GetConfig(cls, target):
198 """Returns a map of crossdev provided variables about a tuple."""
199 CACHE_ATTR = '_target_tuple_map'
200
201 val = cls._CACHE.setdefault(CACHE_ATTR, {})
202 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400203 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400204 conf = {
205 'crosspkgs': [],
206 'target': toolchain.GetHostTuple(),
207 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400208 if target == 'host':
209 packages_list = HOST_PACKAGES
210 else:
211 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400212 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400213 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400214 else:
215 # Build the crossdev command.
216 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
217 if target in TARGET_COMPILER_RT_ENABLED:
218 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
219 if target in TARGET_GO_ENABLED:
220 cmd.extend(CROSSDEV_GO_ARGS)
221 if target in TARGET_LLVM_PKGS_ENABLED:
222 for pkg in LLVM_PKGS_TABLE:
223 cmd.extend(LLVM_PKGS_TABLE[pkg])
224 cmd.extend(['-t', target])
225 # Catch output of crossdev.
226 out = cros_build_lib.RunCommand(
227 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
228 # List of tuples split at the first '=', converted into dict.
229 conf = dict((k, cros_build_lib.ShellUnquote(v))
230 for k, v in (x.split('=', 1) for x in out))
231 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800232
Mike Frysinger66bfde52017-09-12 16:42:57 -0400233 manual_pkgs = cls.MANUAL_PKGS
234
235 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400236 conf[pkg + '_pn'] = pkg
237 conf[pkg + '_category'] = cat
238 if pkg not in conf['crosspkgs']:
239 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800240
241 val[target] = conf
242
David James66a09c42012-11-05 13:31:38 -0800243 return val[target]
244
245 @classmethod
246 def UpdateTargets(cls, targets, usepkg, config_only=False):
247 """Calls crossdev to initialize a cross target.
248
249 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700250 targets: The list of targets to initialize using crossdev.
251 usepkg: Copies the commandline opts.
252 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800253 """
254 configured_targets = cls._CACHE.setdefault('configured_targets', [])
255
256 cmdbase = ['crossdev', '--show-fail-log']
257 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
258 # Pick stable by default, and override as necessary.
259 cmdbase.extend(['-P', '--oneshot'])
260 if usepkg:
261 cmdbase.extend(['-P', '--getbinpkg',
262 '-P', '--usepkgonly',
263 '--without-headers'])
264
Christopher Wileyb22c0712015-06-02 10:37:03 -0700265 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800266 cmdbase.extend(['--overlays', overlays])
267 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
268
Yunlian Jiang4ff84172018-05-12 13:03:01 -0700269 # Build target by the reversed alphabetical order to make sure
270 # armv7a-cros-linux-gnueabihf builds before armv7a-cros-linux-gnueabi
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700271 # because some dependency issue. This can be reverted once we
272 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
Yunlian Jiang4ff84172018-05-12 13:03:01 -0700273 for target in sorted(targets, reverse=True):
David James66a09c42012-11-05 13:31:38 -0800274 if config_only and target in configured_targets:
275 continue
276
277 cmd = cmdbase + ['-t', target]
278
279 for pkg in GetTargetPackages(target):
280 if pkg == 'gdb':
281 # Gdb does not have selectable versions.
282 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700283 elif pkg == 'ex_compiler-rt':
284 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700285 elif pkg == 'ex_go':
286 # Go does not have selectable versions.
287 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700288 elif pkg in LLVM_PKGS_TABLE:
289 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800290 elif pkg in cls.MANUAL_PKGS:
291 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700292 else:
293 # The first of the desired versions is the "primary" one.
294 version = GetDesiredPackageVersions(target, pkg)[0]
295 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800296
297 cmd.extend(targets[target]['crossdev'].split())
298 if config_only:
299 # In this case we want to just quietly reinit
300 cmd.append('--init-target')
301 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
302 else:
303 cros_build_lib.RunCommand(cmd)
304
305 configured_targets.append(target)
306
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100307
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100308def GetTargetPackages(target):
309 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800310 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100311 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800312 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100313
314
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100315# Portage helper functions:
316def GetPortagePackage(target, package):
317 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800318 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100319 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400320 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100321 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100322 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100323 category = conf['category']
324 # Portage package:
325 pn = conf[package + '_pn']
326 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500327 assert category
328 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100329 return '%s/%s' % (category, pn)
330
331
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700332def PortageTrees(root):
333 """Return the portage trees for a given root."""
334 if root == '/':
335 return portage.db['/']
336 # The portage logic requires the path always end in a slash.
337 root = root.rstrip('/') + '/'
338 return portage.create_trees(target_root=root, config_root=root)[root]
339
340
341def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100342 """Extracts the list of current versions of a target, package pair.
343
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500344 Args:
345 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700346 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500348 Returns:
349 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100350 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100351 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700352 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100353 version = portage.versions.cpv_getversion(pkg)
354 versions.append(version)
355 return versions
356
357
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700358def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100359 """Extracts the current stable version for a given package.
360
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500361 Args:
362 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
363 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700364 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100365
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500366 Returns:
367 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100368 """
David James90239b92012-11-05 15:31:34 -0800369 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700370 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800371 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100372
373
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700374def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100375 """Resolves keywords in a given version list for a particular package.
376
377 Resolving means replacing PACKAGE_STABLE with the actual number.
378
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500379 Args:
380 target: The target to operate on (e.g. i686-pc-linux-gnu)
381 package: The target/package to operate on (e.g. gcc)
382 versions: List of versions to resolve
383 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700384 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100385
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500386 Returns:
387 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100388 """
389 resolved = []
David James90239b92012-11-05 15:31:34 -0800390 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700391 if not installed:
392 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100393 for version in versions:
394 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700395 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400396 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100397 resolved.append(version)
398 return resolved
399
400
401def GetDesiredPackageVersions(target, package):
402 """Produces the list of desired versions for each target, package pair.
403
404 The first version in the list is implicitly treated as primary, ie.
405 the version that will be initialized by crossdev and selected.
406
407 If the version is PACKAGE_STABLE, it really means the current version which
408 is emerged by using the package atom with no particular version key.
409 Since crossdev unmasks all packages by default, this will actually
410 mean 'unstable' in most cases.
411
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500412 Args:
413 target: The target to operate on (e.g. i686-pc-linux-gnu)
414 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500416 Returns:
417 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100418 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400419 if package in GetTargetPackages(target):
420 return [PACKAGE_STABLE]
421 else:
422 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100423
424
425def TargetIsInitialized(target):
426 """Verifies if the given list of targets has been correctly initialized.
427
428 This determines whether we have to call crossdev while emerging
429 toolchain packages or can do it using emerge. Emerge is naturally
430 preferred, because all packages can be updated in a single pass.
431
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500432 Args:
433 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100434
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500435 Returns:
436 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 """
438 # Check if packages for the given target all have a proper version.
439 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100440 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800441 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100442 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400443 if not (GetStablePackageVersion(atom, True) and
444 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100445 return False
446 return True
447 except cros_build_lib.RunCommandError:
448 # Fails - The target has likely never been initialized before.
449 return False
450
451
452def RemovePackageMask(target):
453 """Removes a package.mask file for the given platform.
454
455 The pre-existing package.mask files can mess with the keywords.
456
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500457 Args:
458 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100459 """
460 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700461 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100462
463
Zdenek Behan508dcce2011-12-05 15:39:32 +0100464# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700465def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500466 """Rebuild libtool as needed
467
468 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
469 gcc, libtool will break. We can't use binary packages either as those will
470 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700471
472 Args:
473 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500474 """
475 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700476 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500477 for line in f:
478 # Look for a line like:
479 # sys_lib_search_path_spec="..."
480 # It'll be a list of paths and gcc will be one of them.
481 if line.startswith('sys_lib_search_path_spec='):
482 line = line.rstrip()
483 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400484 root_path = os.path.join(root, path.lstrip(os.path.sep))
485 logging.debug('Libtool: checking %s', root_path)
486 if not os.path.exists(root_path):
487 logging.info('Rebuilding libtool after gcc upgrade')
488 logging.info(' %s', line)
489 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500490 needs_update = True
491 break
492
493 if needs_update:
494 break
495
496 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700497 cmd = [EMERGE_CMD, '--oneshot']
498 if root != '/':
499 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
500 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500501 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400502 else:
503 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500504
505
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700506def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100507 """Determines which packages need update/unmerge and defers to portage.
508
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500509 Args:
510 targets: The list of targets to update
511 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700512 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100513 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100514 # For each target, we do two things. Figure out the list of updates,
515 # and figure out the appropriate keywords/masks. Crossdev will initialize
516 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400517 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800518 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100519 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400520 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100521 # Record the highest needed version for each target, for masking purposes.
522 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100523 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100524 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400525 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100526 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700527 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200529 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400530 if pkg in NEW_PACKAGES and usepkg:
531 # Skip this binary package (for now).
532 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100533 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400534 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400536 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100537 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400538 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800539 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540
Mike Frysinger3bba5032016-09-20 14:15:04 -0400541 logging.info('Updating packages:')
542 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100544 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100545 if usepkg:
546 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700547 if root != '/':
548 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100549
550 cmd.extend(packages)
551 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800552 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100553
554
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700555def CleanTargets(targets, root='/'):
556 """Unmerges old packages that are assumed unnecessary.
557
558 Args:
559 targets: The list of targets to clean up.
560 root: The install root in which we want packages cleaned up.
561 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100562 unmergemap = {}
563 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400564 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100565 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400566 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100567 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700568 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100569 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700570 # NOTE: This refers to installed packages (vartree) rather than the
571 # Portage version (porttree and/or bintree) when determining the current
572 # version. While this isn't the most accurate thing to do, it is probably
573 # a good simple compromise, which should have the desired result of
574 # uninstalling everything but the latest installed version. In
575 # particular, using the bintree (--usebinpkg) requires a non-trivial
576 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200577 desired_num = VersionListToNumeric(target, package, desired, True)
578 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400579 logging.warning('Error detecting stable version for %s, '
580 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200581 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100582 unmergemap[pkg] = set(current).difference(desired_num)
583
584 # Cleaning doesn't care about consistency and rebuilding package.* files.
585 packages = []
586 for pkg, vers in unmergemap.iteritems():
587 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
588
589 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400590 logging.info('Cleaning packages:')
591 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100592 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700593 if root != '/':
594 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595 cmd.extend(packages)
596 cros_build_lib.RunCommand(cmd)
597 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400598 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100599
600
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700601def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100602 """Runs gcc-config and binutils-config to select the desired.
603
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500604 Args:
605 targets: The targets to select
606 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700607 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100608 """
609 for package in ['gcc', 'binutils']:
610 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400611 # See if this package is part of this target.
612 if package not in GetTargetPackages(target):
613 logging.debug('%s: %s is not used', target, package)
614 continue
615
Zdenek Behan508dcce2011-12-05 15:39:32 +0100616 # Pick the first version in the numbered list as the selected one.
617 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700618 desired_num = VersionListToNumeric(target, package, desired, True,
619 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100620 desired = desired_num[0]
621 # *-config does not play revisions, strip them, keep just PV.
622 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
623
Mike Frysinger785b0c32017-09-13 01:35:59 -0400624 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100625 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800626 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100627
628 # And finally, attach target to it.
629 desired = '%s-%s' % (target, desired)
630
631 # Target specific hacks
632 if package in suffixes:
633 if target in suffixes[package]:
634 desired += suffixes[package][target]
635
David James7ec5efc2012-11-06 09:39:49 -0800636 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700637 if root != '/':
638 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800639 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500640 result = cros_build_lib.RunCommand(
641 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
642 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700643
644 # Do not reconfig when the current is live or nothing needs to be done.
645 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100646 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500647 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700648 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100649
650
Mike Frysinger35247af2012-11-16 18:58:06 -0500651def ExpandTargets(targets_wanted):
652 """Expand any possible toolchain aliases into full targets
653
654 This will expand 'all' and 'sdk' into the respective toolchain tuples.
655
656 Args:
657 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500658
Mike Frysinger35247af2012-11-16 18:58:06 -0500659 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300660 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500661 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500662 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700663 if targets_wanted == set(['boards']):
664 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300665 return {}
666
667 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500668 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300669 return all_targets
670 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500671 # Filter out all the non-sdk toolchains as we don't want to mess
672 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300673 return toolchain.FilterToolchains(all_targets, 'sdk', True)
674
675 # Verify user input.
676 nonexistent = targets_wanted.difference(all_targets)
677 if nonexistent:
678 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
679 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500680
681
David Jamesf8c672f2012-11-06 13:38:11 -0800682def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700683 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100684 """Performs all steps to create a synchronized toolchain enviroment.
685
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500686 Args:
687 usepkg: Use prebuilt packages
688 deleteold: Unmerge deprecated packages
689 hostonly: Only setup the host toolchain
690 reconfig: Reload crossdev config and reselect toolchains
691 targets_wanted: All the targets to update
692 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700693 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100694 """
David Jamesf8c672f2012-11-06 13:38:11 -0800695 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100696 if not hostonly:
697 # For hostonly, we can skip most of the below logic, much of which won't
698 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500699 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400700
Don Garrettc0c74002015-10-09 12:58:19 -0700701 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300702 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400703 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800704 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100705
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100706 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400707 for target in targets:
708 if TargetIsInitialized(target):
709 reconfig_targets[target] = targets[target]
710 else:
711 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100712 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400713 logging.info('The following targets need to be re-initialized:')
714 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800715 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200716 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800717 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100718
Mike Frysinger66814c32017-10-09 18:11:46 -0400719 # If we're building a subset of toolchains for a board, we might not have
720 # all the tuples that the packages expect. We don't define the "full" set
721 # of tuples currently other than "whatever the full sdk has normally".
722 if usepkg or set(('all', 'sdk')) & targets_wanted:
723 # Since we have cross-compilers now, we can update these packages.
724 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400725
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100726 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400727 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100728
729 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700730 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
731 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800732
733 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700734 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100735
Mike Frysingerc880a962013-11-08 13:59:06 -0500736 # Now that we've cleared out old versions, see if we need to rebuild
737 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700738 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500739
Zdenek Behan508dcce2011-12-05 15:39:32 +0100740
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700741def ShowConfig(name):
742 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500743
744 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700745 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500746 """
Don Garrettc0c74002015-10-09 12:58:19 -0700747 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500748 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400749 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400750 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800751 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400752 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500753
754
Mike Frysinger35247af2012-11-16 18:58:06 -0500755def GeneratePathWrapper(root, wrappath, path):
756 """Generate a shell script to execute another shell script
757
758 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
759 argv[0] won't be pointing to the correct path, generate a shell script that
760 just executes another program with its full path.
761
762 Args:
763 root: The root tree to generate scripts inside of
764 wrappath: The full path (inside |root|) to create the wrapper
765 path: The target program which this wrapper will execute
766 """
767 replacements = {
768 'path': path,
769 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
770 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900771
772 # Do not use exec here, because exec invokes script with absolute path in
773 # argv0. Keeping relativeness allows us to remove abs path from compile result
774 # and leads directory independent build cache sharing in some distributed
775 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500776 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900777basedir=$(dirname "$0")
778"${basedir}/%(relroot)s%(path)s" "$@"
779exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500780""" % replacements
781 root_wrapper = root + wrappath
782 if os.path.islink(root_wrapper):
783 os.unlink(root_wrapper)
784 else:
785 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
786 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400787 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500788
789
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700790def FixClangXXWrapper(root, path):
791 """Fix wrapper shell scripts and symlinks for invoking clang++
792
793 In a typical installation, clang++ symlinks to clang, which symlinks to the
794 elf executable. The executable distinguishes between clang and clang++ based
795 on argv[0].
796
797 When invoked through the LdsoWrapper, argv[0] always contains the path to the
798 executable elf file, making clang/clang++ invocations indistinguishable.
799
800 This function detects if the elf executable being wrapped is clang-X.Y, and
801 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
802
803 The calling sequence now becomes:
804 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
805 the Ldsowrapper).
806 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
807 to the original clang-3.9 elf.
808 -) The difference this time is that inside the elf file execution, $0 is
809 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
810
Manoj Guptaae268142018-04-27 23:28:36 -0700811 Update: Starting since clang 7, the clang and clang++ are symlinks to
812 clang-7 binary, not clang-7.0. The pattern match is extended to handle
813 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
814
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700815 Args:
816 root: The root tree to generate scripts / symlinks inside of
817 path: The target elf for which LdsoWrapper was created
818 """
Manoj Guptaae268142018-04-27 23:28:36 -0700819 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700820 logging.info('fixing clang++ invocation for %s', path)
821 clangdir = os.path.dirname(root + path)
822 clang = os.path.basename(path)
823 clangxx = clang.replace('clang', 'clang++')
824
825 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
826 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
827
828 # Create a hardlink clang++-X.Y pointing to clang-X.Y
829 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
830
831 # Adjust the clang++ symlink to point to clang++-X.Y
832 os.unlink(os.path.join(clangdir, 'clang++'))
833 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
834
835
Mike Frysinger35247af2012-11-16 18:58:06 -0500836def FileIsCrosSdkElf(elf):
837 """Determine if |elf| is an ELF that we execute in the cros_sdk
838
839 We don't need this to be perfect, just quick. It makes sure the ELF
840 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
841
842 Args:
843 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500844
Mike Frysinger35247af2012-11-16 18:58:06 -0500845 Returns:
846 True if we think |elf| is a native ELF
847 """
848 with open(elf) as f:
849 data = f.read(20)
850 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
851 return (data[0:4] == '\x7fELF' and
852 data[4] == '\x02' and
853 data[5] == '\x01' and
854 data[18] == '\x3e')
855
856
857def IsPathPackagable(ptype, path):
858 """Should the specified file be included in a toolchain package?
859
860 We only need to handle files as we'll create dirs as we need them.
861
862 Further, trim files that won't be useful:
863 - non-english translations (.mo) since it'd require env vars
864 - debug files since these are for the host compiler itself
865 - info/man pages as they're big, and docs are online, and the
866 native docs should work fine for the most part (`man gcc`)
867
868 Args:
869 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
870 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500871
Mike Frysinger35247af2012-11-16 18:58:06 -0500872 Returns:
873 True if we want to include this path in the package
874 """
875 return not (ptype in ('dir',) or
876 path.startswith('/usr/lib/debug/') or
877 os.path.splitext(path)[1] == '.mo' or
878 ('/man/' in path or '/info/' in path))
879
880
881def ReadlinkRoot(path, root):
882 """Like os.readlink(), but relative to a |root|
883
884 Args:
885 path: The symlink to read
886 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500887
Mike Frysinger35247af2012-11-16 18:58:06 -0500888 Returns:
889 A fully resolved symlink path
890 """
891 while os.path.islink(root + path):
892 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
893 return path
894
895
896def _GetFilesForTarget(target, root='/'):
897 """Locate all the files to package for |target|
898
899 This does not cover ELF dependencies.
900
901 Args:
902 target: The toolchain target name
903 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500904
Mike Frysinger35247af2012-11-16 18:58:06 -0500905 Returns:
906 A tuple of a set of all packable paths, and a set of all paths which
907 are also native ELFs
908 """
909 paths = set()
910 elfs = set()
911
912 # Find all the files owned by the packages for this target.
913 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500914
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700915 # Skip Go compiler from redistributable packages.
916 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
917 # into it. Due to this, the toolchain cannot be unpacked anywhere
918 # else and be readily useful. To enable packaging Go, we need to:
919 # -) Tweak the wrappers/environment to override GOROOT
920 # automatically based on the unpack location.
921 # -) Make sure the ELF dependency checking and wrapping logic
922 # below skips the Go toolchain executables and libraries.
923 # -) Make sure the packaging process maintains the relative
924 # timestamps of precompiled standard library packages.
925 # (see dev-lang/go ebuild for details).
926 if pkg == 'ex_go':
927 continue
928
Yunlian Jiang36f35242018-04-27 10:18:40 -0700929 # Use armv7a-cros-linux-gnueabi/compiler-rt for
930 # armv7a-cros-linux-gnueabihf/compiler-rt.
931 # Currently the armv7a-cros-linux-gnueabi is actually
932 # the same as armv7a-cros-linux-gnueabihf with different names.
933 # Because of that, for compiler-rt, it generates the same binary in
934 # the same location. To avoid the installation conflict, we do not
935 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
936 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
937 # toolchain.
938 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
939 atom = GetPortagePackage(target, pkg)
940 cat, pn = atom.split('/')
941 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700942 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
943 settings=portage.settings)
944 contents = dblink.getcontents()
945 if not contents:
946 if 'hf' in target:
947 new_target = 'armv7a-cros-linux-gnueabi'
948 else:
949 new_target = 'armv7a-cros-linux-gnueabihf'
950 atom = GetPortagePackage(new_target, pkg)
951 else:
952 atom = GetPortagePackage(target, pkg)
953
Mike Frysinger35247af2012-11-16 18:58:06 -0500954 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700955 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700956 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500957
Mike Frysinger35247af2012-11-16 18:58:06 -0500958 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
959 settings=portage.settings)
960 contents = dblink.getcontents()
961 for obj in contents:
962 ptype = contents[obj][0]
963 if not IsPathPackagable(ptype, obj):
964 continue
965
966 if ptype == 'obj':
967 # For native ELFs, we need to pull in their dependencies too.
968 if FileIsCrosSdkElf(obj):
969 elfs.add(obj)
970 paths.add(obj)
971
972 return paths, elfs
973
974
975def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500976 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500977 """Link in all packable files and their runtime dependencies
978
979 This also wraps up executable ELFs with helper scripts.
980
981 Args:
982 output_dir: The output directory to store files
983 paths: All the files to include
984 elfs: All the files which are ELFs (a subset of |paths|)
985 ldpaths: A dict of static ldpath information
986 path_rewrite_func: User callback to rewrite paths in output_dir
987 root: The root path to pull all packages/files from
988 """
989 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400990 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500991 for path in paths:
992 new_path = path_rewrite_func(path)
993 dst = output_dir + new_path
994 osutils.SafeMakedirs(os.path.dirname(dst))
995
996 # Is this a symlink which we have to rewrite or wrap?
997 # Delay wrap check until after we have created all paths.
998 src = root + path
999 if os.path.islink(src):
1000 tgt = os.readlink(src)
1001 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001002 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001003
1004 # Rewrite absolute links to relative and then generate the symlink
1005 # ourselves. All other symlinks can be hardlinked below.
1006 if tgt[0] == '/':
1007 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1008 os.symlink(tgt, dst)
1009 continue
1010
1011 os.link(src, dst)
1012
Mike Frysinger35247af2012-11-16 18:58:06 -05001013 # Locate all the dependencies for all the ELFs. Stick them all in the
1014 # top level "lib" dir to make the wrapper simpler. This exact path does
1015 # not matter since we execute ldso directly, and we tell the ldso the
1016 # exact path to search for its libraries.
1017 libdir = os.path.join(output_dir, 'lib')
1018 osutils.SafeMakedirs(libdir)
1019 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001020 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001021 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001022 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -05001023 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -07001024 # Do not create wrapper for libc. crbug.com/766827
1025 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -05001026 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -04001027 interp = os.path.join('/lib', os.path.basename(interp))
1028 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1029 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -07001030 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -05001031
Mike Frysinger221bd822017-09-29 02:51:47 -04001032 # Wrap any symlinks to the wrapper.
1033 if elf in sym_paths:
1034 link = sym_paths[elf]
1035 GeneratePathWrapper(output_dir, link, elf)
1036
Mike Frysinger35247af2012-11-16 18:58:06 -05001037 for lib, lib_data in e['libs'].iteritems():
1038 if lib in donelibs:
1039 continue
1040
1041 src = path = lib_data['path']
1042 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001043 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001044 continue
1045 donelibs.add(lib)
1046
1047 # Needed libs are the SONAME, but that is usually a symlink, not a
1048 # real file. So link in the target rather than the symlink itself.
1049 # We have to walk all the possible symlinks (SONAME could point to a
1050 # symlink which points to a symlink), and we have to handle absolute
1051 # ourselves (since we have a "root" argument).
1052 dst = os.path.join(libdir, os.path.basename(path))
1053 src = ReadlinkRoot(src, root)
1054
1055 os.link(root + src, dst)
1056
1057
1058def _EnvdGetVar(envd, var):
1059 """Given a Gentoo env.d file, extract a var from it
1060
1061 Args:
1062 envd: The env.d file to load (may be a glob path)
1063 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001064
Mike Frysinger35247af2012-11-16 18:58:06 -05001065 Returns:
1066 The value of |var|
1067 """
1068 envds = glob.glob(envd)
1069 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1070 envd = envds[0]
1071 return cros_build_lib.LoadKeyValueFile(envd)[var]
1072
1073
1074def _ProcessBinutilsConfig(target, output_dir):
1075 """Do what binutils-config would have done"""
1076 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001077
1078 # Locate the bin dir holding the gold linker.
1079 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1080 target, 'binutils-bin')
1081 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001082 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001083 if not srcpath:
1084 # Maybe this target doesn't support gold.
1085 globpath = os.path.join(binutils_bin_path, '*')
1086 srcpath = glob.glob(globpath)
1087 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1088 % globpath)
1089 srcpath = srcpath[0]
1090 ld_path = os.path.join(srcpath, 'ld')
1091 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1092 ld_path = os.path.join(srcpath, 'ld.bfd')
1093 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1094 ld_path = os.path.join(srcpath, 'ld.gold')
1095 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1096 % ld_path)
1097
1098 # Nope, no gold support to be found.
1099 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001100 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001101 else:
1102 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001103 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001104
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001105 # Package the binutils-bin directory without the '-gold' suffix
1106 # if gold is not enabled as the default linker for this target.
1107 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1108 if not gold_supported:
1109 srcpath = srcpath[:-len('-gold')]
1110 ld_path = os.path.join(srcpath, 'ld')
1111 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1112
Mike Frysinger78b7a812014-11-26 19:45:23 -05001113 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001114 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1115 for prog in os.listdir(output_dir + srcpath):
1116 # Skip binaries already wrapped.
1117 if not prog.endswith('.real'):
1118 GeneratePathWrapper(output_dir, binpath + prog,
1119 os.path.join(srcpath, prog))
1120 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1121 os.path.join(srcpath, prog))
1122
David James27ac4ae2012-12-03 23:16:15 -08001123 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001124 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1125 if gold_supported:
1126 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001127 else:
1128 # If gold is not enabled as the default linker and 2 env.d
1129 # files exist, pick the one without the '-gold' suffix.
1130 envds = sorted(glob.glob(envd))
1131 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1132 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001133 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1134 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1135 output_dir + libpath)
1136
1137
1138def _ProcessGccConfig(target, output_dir):
1139 """Do what gcc-config would have done"""
1140 binpath = '/bin'
1141 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1142 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1143 for prog in os.listdir(output_dir + srcpath):
1144 # Skip binaries already wrapped.
1145 if (not prog.endswith('.real') and
1146 not prog.endswith('.elf') and
1147 prog.startswith(target)):
1148 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1149 os.path.join(srcpath, prog))
1150 return srcpath
1151
1152
Frank Henigman179ec7c2015-02-06 03:01:09 -05001153def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1154 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001155 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001156 for sysroot_wrapper in glob.glob(os.path.join(
1157 output_dir + srcpath, 'sysroot_wrapper*')):
1158 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
Douglas Andersoncc828a52017-10-13 13:07:25 -07001159
1160 # In order to optimize startup time in the chroot we run python a little
1161 # differently there. Put it back to the more portable way here.
Mike Frysingerdcad4e02018-08-03 16:20:02 -04001162 # See https://crbug.com/773138 for some details.
Douglas Andersoncc828a52017-10-13 13:07:25 -07001163 if contents[0] == '#!/usr/bin/python2 -S':
1164 contents[0] = '#!/usr/bin/env python2'
1165
Frank Henigman179ec7c2015-02-06 03:01:09 -05001166 for num in xrange(len(contents)):
1167 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001168 assert 'True' in contents[num]
1169 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001170 break
1171 # Can't update the wrapper in place since it's a hardlink to a file in /.
1172 os.unlink(sysroot_wrapper)
1173 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1174 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001175
1176
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001177def _CreateMainLibDir(target, output_dir):
1178 """Create some lib dirs so that compiler can get the right Gcc paths"""
1179 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1180 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1181
1182
Mike Frysinger35247af2012-11-16 18:58:06 -05001183def _ProcessDistroCleanups(target, output_dir):
1184 """Clean up the tree and remove all distro-specific requirements
1185
1186 Args:
1187 target: The toolchain target name
1188 output_dir: The output directory to clean up
1189 """
1190 _ProcessBinutilsConfig(target, output_dir)
1191 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001192 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001193 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001194
1195 osutils.RmDir(os.path.join(output_dir, 'etc'))
1196
1197
1198def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1199 """Setup a tree from the packages for the specified target
1200
1201 This populates a path with all the files from toolchain packages so that
1202 a tarball can easily be generated from the result.
1203
1204 Args:
1205 target: The target to create a packagable root from
1206 output_dir: The output directory to place all the files
1207 ldpaths: A dict of static ldpath information
1208 root: The root path to pull all packages/files from
1209 """
1210 # Find all the files owned by the packages for this target.
1211 paths, elfs = _GetFilesForTarget(target, root=root)
1212
1213 # Link in all the package's files, any ELF dependencies, and wrap any
1214 # executable ELFs with helper scripts.
1215 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001216 """Move /usr/bin to /bin so people can just use that toplevel dir
1217
1218 Note we do not apply this to clang - there is correlation between clang's
1219 search path for libraries / inclusion and its installation path.
1220 """
1221 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1222 return path[4:]
1223 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001224 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1225 path_rewrite_func=MoveUsrBinToBin, root=root)
1226
1227 # The packages, when part of the normal distro, have helper scripts
1228 # that setup paths and such. Since we are making this standalone, we
1229 # need to preprocess all that ourselves.
1230 _ProcessDistroCleanups(target, output_dir)
1231
1232
1233def CreatePackages(targets_wanted, output_dir, root='/'):
1234 """Create redistributable cross-compiler packages for the specified targets
1235
1236 This creates toolchain packages that should be usable in conjunction with
1237 a downloaded sysroot (created elsewhere).
1238
1239 Tarballs (one per target) will be created in $PWD.
1240
1241 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001242 targets_wanted: The targets to package up.
1243 output_dir: The directory to put the packages in.
1244 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001245 """
Ralph Nathan03047282015-03-23 11:09:32 -07001246 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001247 osutils.SafeMakedirs(output_dir)
1248 ldpaths = lddtree.LoadLdpaths(root)
1249 targets = ExpandTargets(targets_wanted)
1250
Mike Frysinger221bd822017-09-29 02:51:47 -04001251 with osutils.TempDir(prefix='create-packages') as tempdir:
1252 logging.debug('Using tempdir: %s', tempdir)
1253
Mike Frysinger35247af2012-11-16 18:58:06 -05001254 # We have to split the root generation from the compression stages. This is
1255 # because we hardlink in all the files (to avoid overhead of reading/writing
1256 # the copies multiple times). But tar gets angry if a file's hardlink count
1257 # changes from when it starts reading a file to when it finishes.
1258 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1259 for target in targets:
1260 output_target_dir = os.path.join(tempdir, target)
1261 queue.put([target, output_target_dir, ldpaths, root])
1262
1263 # Build the tarball.
1264 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1265 for target in targets:
1266 tar_file = os.path.join(output_dir, target + '.tar.xz')
1267 queue.put([tar_file, os.path.join(tempdir, target)])
1268
1269
Mike Frysinger07534cf2017-09-12 17:40:21 -04001270def GetParser():
1271 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001272 parser = commandline.ArgumentParser(description=__doc__)
1273 parser.add_argument('-u', '--nousepkg',
1274 action='store_false', dest='usepkg', default=True,
1275 help='Use prebuilt packages if possible')
1276 parser.add_argument('-d', '--deleteold',
1277 action='store_true', dest='deleteold', default=False,
1278 help='Unmerge deprecated packages')
1279 parser.add_argument('-t', '--targets',
1280 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001281 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001282 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001283 "allowed. Defaults to 'sdk'.")
1284 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1285 help='Comma separated list of boards whose toolchains we '
1286 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001287 parser.add_argument('--hostonly',
1288 dest='hostonly', default=False, action='store_true',
1289 help='Only setup the host toolchain. '
1290 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001291 parser.add_argument('--show-board-cfg', '--show-cfg',
1292 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001293 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001294 parser.add_argument('--show-packages', default=None,
1295 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001296 parser.add_argument('--create-packages',
1297 action='store_true', default=False,
1298 help='Build redistributable packages')
1299 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1300 help='Output directory')
1301 parser.add_argument('--reconfig', default=False, action='store_true',
1302 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001303 parser.add_argument('--sysroot', type='path',
1304 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001305 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001306
Mike Frysinger07534cf2017-09-12 17:40:21 -04001307
1308def main(argv):
1309 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001310 options = parser.parse_args(argv)
1311 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001312
Mike Frysinger35247af2012-11-16 18:58:06 -05001313 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001314 conflicting_options = (
1315 options.cfg_name,
1316 options.show_packages,
1317 options.create_packages,
1318 )
1319 if sum(bool(x) for x in conflicting_options) > 1:
1320 parser.error('conflicting options: create-packages & show-packages & '
1321 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001322
Gilad Arnold8195b532015-04-07 10:56:30 +03001323 targets_wanted = set(options.targets.split(','))
1324 boards_wanted = (set(options.include_boards.split(','))
1325 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001326
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001327 if options.cfg_name:
1328 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001329 elif options.show_packages is not None:
1330 cros_build_lib.AssertInsideChroot()
1331 target = options.show_packages
1332 Crossdev.Load(False)
1333 for package in GetTargetPackages(target):
1334 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001335 elif options.create_packages:
1336 cros_build_lib.AssertInsideChroot()
1337 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001338 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001339 else:
1340 cros_build_lib.AssertInsideChroot()
1341 # This has to be always run as root.
1342 if os.geteuid() != 0:
1343 cros_build_lib.Die('this script must be run as root')
1344
1345 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001346 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001347 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001348 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001349 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001350 Crossdev.Save()
1351
1352 return 0