blob: ba5dda451e789956bca4b07b878d5b4ccecd7005 [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',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010073 'dev-embedded/coreboot-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040074)
75
76# New packages that we're in the process of adding to the SDK. Since the SDK
77# bot hasn't had a chance to run yet, there are no binary packages available,
78# so we have to list them here and wait. Once it completes, entries here can
79# be removed so they'll end up on bots & dev's systems.
80NEW_PACKAGES = (
Mike Frysinger785b0c32017-09-13 01:35:59 -040081)
82
Rahul Chaudhry4b803052015-05-13 15:25:56 -070083# Enable the Go compiler for these targets.
84TARGET_GO_ENABLED = (
85 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070086 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070087 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070088 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070089)
90CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
91
Manoj Gupta1b5642e2017-03-08 16:44:12 -080092# Enable llvm's compiler-rt for these targets.
93TARGET_COMPILER_RT_ENABLED = (
94 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070095 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070096 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -080097 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080098)
99CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
100
Manoj Gupta946abb42017-04-12 14:27:19 -0700101TARGET_LLVM_PKGS_ENABLED = (
102 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700103 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700104 'aarch64-cros-linux-gnu',
105 'x86_64-cros-linux-gnu',
106)
107
108LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700109 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
110 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700111 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700112}
113
Zdenek Behan508dcce2011-12-05 15:39:32 +0100114# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
115CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500116 'binutils' : {
Manoj Guptaa91e38e2018-11-15 11:07:48 -0800117 'aarch64-cros-linux-gnu' : '-gold',
Mike Frysinger8a83c622015-05-28 00:35:05 -0400118 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -0800119 'armv7a-cros-linux-gnueabi': '-gold',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700120 'armv7a-cros-linux-gnueabihf': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500121 'i686-pc-linux-gnu' : '-gold',
122 'x86_64-cros-linux-gnu' : '-gold',
123 },
Zdenek Behan508dcce2011-12-05 15:39:32 +0100124}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100125
126
David James66a09c42012-11-05 13:31:38 -0800127class Crossdev(object):
128 """Class for interacting with crossdev and caching its output."""
129
130 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
131 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800132 # Packages that needs separate handling, in addition to what we have from
133 # crossdev.
134 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700135 'clang': 'sys-devel',
Yunlian Jiang18ae9982017-11-03 09:15:31 -0700136 'lld': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800137 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700138 'libcxxabi': 'sys-libs',
139 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700140 'elfutils': 'dev-libs',
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700141 'llvm-libunwind': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800142 }
David James66a09c42012-11-05 13:31:38 -0800143
144 @classmethod
145 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400146 """Load crossdev cache from disk.
147
148 We invalidate the cache when crossdev updates or this script changes.
149 """
David James90239b92012-11-05 15:31:34 -0800150 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400151 # If we run the compiled/cached .pyc file, we'll read/hash that when we
152 # really always want to track the source .py file.
153 script = os.path.abspath(__file__)
154 if script.endswith('.pyc'):
155 script = script[:-1]
156 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
157
158 cls._CACHE = {
159 'crossdev_version': crossdev_version,
160 'setup_toolchains_hash': setup_toolchains_hash,
161 }
162
163 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
164 if reconfig:
165 logging.debug('cache: forcing regen due to reconfig')
166 return
167
168 try:
169 file_data = osutils.ReadFile(cls._CACHE_FILE)
170 except IOError as e:
171 if e.errno != errno.ENOENT:
172 logging.warning('cache: reading failed: %s', e)
173 osutils.SafeUnlink(cls._CACHE_FILE)
174 return
175
176 try:
177 data = json.loads(file_data)
178 except ValueError as e:
179 logging.warning('cache: ignoring invalid content: %s', e)
180 return
181
182 if crossdev_version != data.get('crossdev_version'):
183 logging.debug('cache: rebuilding after crossdev upgrade')
184 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
185 logging.debug('cache: rebuilding after cros_setup_toolchains change')
186 else:
187 logging.debug('cache: content is up-to-date!')
188 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800189
190 @classmethod
191 def Save(cls):
192 """Store crossdev cache on disk."""
193 # Save the cache from the successful run.
194 with open(cls._CACHE_FILE, 'w') as f:
195 json.dump(cls._CACHE, f)
196
197 @classmethod
198 def GetConfig(cls, target):
199 """Returns a map of crossdev provided variables about a tuple."""
200 CACHE_ATTR = '_target_tuple_map'
201
202 val = cls._CACHE.setdefault(CACHE_ATTR, {})
203 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400204 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400205 conf = {
206 'crosspkgs': [],
207 'target': toolchain.GetHostTuple(),
208 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400209 if target == 'host':
210 packages_list = HOST_PACKAGES
211 else:
212 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400213 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400214 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400215 else:
216 # Build the crossdev command.
217 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
218 if target in TARGET_COMPILER_RT_ENABLED:
219 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
220 if target in TARGET_GO_ENABLED:
221 cmd.extend(CROSSDEV_GO_ARGS)
222 if target in TARGET_LLVM_PKGS_ENABLED:
223 for pkg in LLVM_PKGS_TABLE:
224 cmd.extend(LLVM_PKGS_TABLE[pkg])
225 cmd.extend(['-t', target])
226 # Catch output of crossdev.
227 out = cros_build_lib.RunCommand(
228 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
229 # List of tuples split at the first '=', converted into dict.
230 conf = dict((k, cros_build_lib.ShellUnquote(v))
231 for k, v in (x.split('=', 1) for x in out))
232 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800233
Mike Frysinger66bfde52017-09-12 16:42:57 -0400234 manual_pkgs = cls.MANUAL_PKGS
235
236 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400237 conf[pkg + '_pn'] = pkg
238 conf[pkg + '_category'] = cat
239 if pkg not in conf['crosspkgs']:
240 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800241
242 val[target] = conf
243
David James66a09c42012-11-05 13:31:38 -0800244 return val[target]
245
246 @classmethod
247 def UpdateTargets(cls, targets, usepkg, config_only=False):
248 """Calls crossdev to initialize a cross target.
249
250 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700251 targets: The list of targets to initialize using crossdev.
252 usepkg: Copies the commandline opts.
253 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800254 """
255 configured_targets = cls._CACHE.setdefault('configured_targets', [])
256
257 cmdbase = ['crossdev', '--show-fail-log']
258 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
259 # Pick stable by default, and override as necessary.
260 cmdbase.extend(['-P', '--oneshot'])
261 if usepkg:
262 cmdbase.extend(['-P', '--getbinpkg',
263 '-P', '--usepkgonly',
264 '--without-headers'])
265
Christopher Wileyb22c0712015-06-02 10:37:03 -0700266 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800267 cmdbase.extend(['--overlays', overlays])
268 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
269
Yunlian Jiang4ff84172018-05-12 13:03:01 -0700270 # Build target by the reversed alphabetical order to make sure
271 # armv7a-cros-linux-gnueabihf builds before armv7a-cros-linux-gnueabi
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700272 # because some dependency issue. This can be reverted once we
273 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
Yunlian Jiang4ff84172018-05-12 13:03:01 -0700274 for target in sorted(targets, reverse=True):
David James66a09c42012-11-05 13:31:38 -0800275 if config_only and target in configured_targets:
276 continue
277
278 cmd = cmdbase + ['-t', target]
279
280 for pkg in GetTargetPackages(target):
281 if pkg == 'gdb':
282 # Gdb does not have selectable versions.
283 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700284 elif pkg == 'ex_compiler-rt':
285 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700286 elif pkg == 'ex_go':
287 # Go does not have selectable versions.
288 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700289 elif pkg in LLVM_PKGS_TABLE:
290 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800291 elif pkg in cls.MANUAL_PKGS:
292 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700293 else:
294 # The first of the desired versions is the "primary" one.
295 version = GetDesiredPackageVersions(target, pkg)[0]
296 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800297
298 cmd.extend(targets[target]['crossdev'].split())
299 if config_only:
300 # In this case we want to just quietly reinit
301 cmd.append('--init-target')
302 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
303 else:
304 cros_build_lib.RunCommand(cmd)
305
306 configured_targets.append(target)
307
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100308
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100309def GetTargetPackages(target):
310 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800311 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100312 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800313 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100314
315
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100316# Portage helper functions:
317def GetPortagePackage(target, package):
318 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800319 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100320 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400321 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100322 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100323 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100324 category = conf['category']
325 # Portage package:
326 pn = conf[package + '_pn']
327 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500328 assert category
329 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100330 return '%s/%s' % (category, pn)
331
332
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700333def PortageTrees(root):
334 """Return the portage trees for a given root."""
335 if root == '/':
336 return portage.db['/']
337 # The portage logic requires the path always end in a slash.
338 root = root.rstrip('/') + '/'
339 return portage.create_trees(target_root=root, config_root=root)[root]
340
341
342def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100343 """Extracts the list of current versions of a target, package pair.
344
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500345 Args:
346 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700347 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100348
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500349 Returns:
350 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100351 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100352 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700353 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100354 version = portage.versions.cpv_getversion(pkg)
355 versions.append(version)
356 return versions
357
358
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700359def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360 """Extracts the current stable version for a given package.
361
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500362 Args:
363 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
364 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700365 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100366
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500367 Returns:
368 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369 """
David James90239b92012-11-05 15:31:34 -0800370 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700371 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800372 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100373
374
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700375def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100376 """Resolves keywords in a given version list for a particular package.
377
378 Resolving means replacing PACKAGE_STABLE with the actual number.
379
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500380 Args:
381 target: The target to operate on (e.g. i686-pc-linux-gnu)
382 package: The target/package to operate on (e.g. gcc)
383 versions: List of versions to resolve
384 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700385 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100386
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500387 Returns:
388 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100389 """
390 resolved = []
David James90239b92012-11-05 15:31:34 -0800391 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700392 if not installed:
393 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100394 for version in versions:
395 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700396 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400397 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100398 resolved.append(version)
399 return resolved
400
401
402def GetDesiredPackageVersions(target, package):
403 """Produces the list of desired versions for each target, package pair.
404
405 The first version in the list is implicitly treated as primary, ie.
406 the version that will be initialized by crossdev and selected.
407
408 If the version is PACKAGE_STABLE, it really means the current version which
409 is emerged by using the package atom with no particular version key.
410 Since crossdev unmasks all packages by default, this will actually
411 mean 'unstable' in most cases.
412
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500413 Args:
414 target: The target to operate on (e.g. i686-pc-linux-gnu)
415 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100416
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500417 Returns:
418 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400420 if package in GetTargetPackages(target):
421 return [PACKAGE_STABLE]
422 else:
423 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100424
425
426def TargetIsInitialized(target):
427 """Verifies if the given list of targets has been correctly initialized.
428
429 This determines whether we have to call crossdev while emerging
430 toolchain packages or can do it using emerge. Emerge is naturally
431 preferred, because all packages can be updated in a single pass.
432
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500433 Args:
434 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100435
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500436 Returns:
437 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100438 """
439 # Check if packages for the given target all have a proper version.
440 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100441 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800442 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100443 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400444 if not (GetStablePackageVersion(atom, True) and
445 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100446 return False
447 return True
448 except cros_build_lib.RunCommandError:
449 # Fails - The target has likely never been initialized before.
450 return False
451
452
453def RemovePackageMask(target):
454 """Removes a package.mask file for the given platform.
455
456 The pre-existing package.mask files can mess with the keywords.
457
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500458 Args:
459 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460 """
461 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700462 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100463
464
Zdenek Behan508dcce2011-12-05 15:39:32 +0100465# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700466def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500467 """Rebuild libtool as needed
468
469 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
470 gcc, libtool will break. We can't use binary packages either as those will
471 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700472
473 Args:
474 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500475 """
476 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700477 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500478 for line in f:
479 # Look for a line like:
480 # sys_lib_search_path_spec="..."
481 # It'll be a list of paths and gcc will be one of them.
482 if line.startswith('sys_lib_search_path_spec='):
483 line = line.rstrip()
484 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400485 root_path = os.path.join(root, path.lstrip(os.path.sep))
486 logging.debug('Libtool: checking %s', root_path)
487 if not os.path.exists(root_path):
488 logging.info('Rebuilding libtool after gcc upgrade')
489 logging.info(' %s', line)
490 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500491 needs_update = True
492 break
493
494 if needs_update:
495 break
496
497 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700498 cmd = [EMERGE_CMD, '--oneshot']
499 if root != '/':
500 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
501 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500502 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400503 else:
504 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500505
506
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700507def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100508 """Determines which packages need update/unmerge and defers to portage.
509
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500510 Args:
511 targets: The list of targets to update
512 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700513 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100514 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100515 # For each target, we do two things. Figure out the list of updates,
516 # and figure out the appropriate keywords/masks. Crossdev will initialize
517 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400518 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800519 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400521 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100522 # Record the highest needed version for each target, for masking purposes.
523 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100524 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100525 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400526 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100527 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700528 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200530 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400531 if pkg in NEW_PACKAGES and usepkg:
532 # Skip this binary package (for now).
533 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100534 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400535 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400537 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400539 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800540 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541
Mike Frysinger3bba5032016-09-20 14:15:04 -0400542 logging.info('Updating packages:')
543 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100544
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100545 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100546 if usepkg:
547 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700548 if root != '/':
549 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100550
551 cmd.extend(packages)
552 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800553 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100554
555
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700556def CleanTargets(targets, root='/'):
557 """Unmerges old packages that are assumed unnecessary.
558
559 Args:
560 targets: The list of targets to clean up.
561 root: The install root in which we want packages cleaned up.
562 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100563 unmergemap = {}
564 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400565 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100566 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400567 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100568 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700569 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100570 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700571 # NOTE: This refers to installed packages (vartree) rather than the
572 # Portage version (porttree and/or bintree) when determining the current
573 # version. While this isn't the most accurate thing to do, it is probably
574 # a good simple compromise, which should have the desired result of
575 # uninstalling everything but the latest installed version. In
576 # particular, using the bintree (--usebinpkg) requires a non-trivial
577 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200578 desired_num = VersionListToNumeric(target, package, desired, True)
579 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400580 logging.warning('Error detecting stable version for %s, '
581 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200582 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100583 unmergemap[pkg] = set(current).difference(desired_num)
584
585 # Cleaning doesn't care about consistency and rebuilding package.* files.
586 packages = []
587 for pkg, vers in unmergemap.iteritems():
588 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
589
590 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400591 logging.info('Cleaning packages:')
592 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100593 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700594 if root != '/':
595 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100596 cmd.extend(packages)
597 cros_build_lib.RunCommand(cmd)
598 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400599 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100600
601
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700602def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603 """Runs gcc-config and binutils-config to select the desired.
604
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500605 Args:
606 targets: The targets to select
607 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700608 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100609 """
610 for package in ['gcc', 'binutils']:
611 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400612 # See if this package is part of this target.
613 if package not in GetTargetPackages(target):
614 logging.debug('%s: %s is not used', target, package)
615 continue
616
Zdenek Behan508dcce2011-12-05 15:39:32 +0100617 # Pick the first version in the numbered list as the selected one.
618 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700619 desired_num = VersionListToNumeric(target, package, desired, True,
620 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100621 desired = desired_num[0]
622 # *-config does not play revisions, strip them, keep just PV.
623 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
624
Mike Frysinger785b0c32017-09-13 01:35:59 -0400625 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100626 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800627 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100628
629 # And finally, attach target to it.
630 desired = '%s-%s' % (target, desired)
631
632 # Target specific hacks
633 if package in suffixes:
634 if target in suffixes[package]:
635 desired += suffixes[package][target]
636
David James7ec5efc2012-11-06 09:39:49 -0800637 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700638 if root != '/':
639 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800640 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500641 result = cros_build_lib.RunCommand(
642 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
643 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700644
645 # Do not reconfig when the current is live or nothing needs to be done.
646 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100647 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500648 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700649 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100650
651
Mike Frysinger35247af2012-11-16 18:58:06 -0500652def ExpandTargets(targets_wanted):
653 """Expand any possible toolchain aliases into full targets
654
655 This will expand 'all' and 'sdk' into the respective toolchain tuples.
656
657 Args:
658 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500659
Mike Frysinger35247af2012-11-16 18:58:06 -0500660 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300661 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500662 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500663 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700664 if targets_wanted == set(['boards']):
665 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300666 return {}
667
668 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500669 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300670 return all_targets
671 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500672 # Filter out all the non-sdk toolchains as we don't want to mess
673 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300674 return toolchain.FilterToolchains(all_targets, 'sdk', True)
675
676 # Verify user input.
677 nonexistent = targets_wanted.difference(all_targets)
678 if nonexistent:
679 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
680 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500681
682
David Jamesf8c672f2012-11-06 13:38:11 -0800683def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700684 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100685 """Performs all steps to create a synchronized toolchain enviroment.
686
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500687 Args:
688 usepkg: Use prebuilt packages
689 deleteold: Unmerge deprecated packages
690 hostonly: Only setup the host toolchain
691 reconfig: Reload crossdev config and reselect toolchains
692 targets_wanted: All the targets to update
693 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700694 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100695 """
David Jamesf8c672f2012-11-06 13:38:11 -0800696 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100697 if not hostonly:
698 # For hostonly, we can skip most of the below logic, much of which won't
699 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500700 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400701
Don Garrettc0c74002015-10-09 12:58:19 -0700702 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300703 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400704 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800705 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100706
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100707 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400708 for target in targets:
709 if TargetIsInitialized(target):
710 reconfig_targets[target] = targets[target]
711 else:
712 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100713 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400714 logging.info('The following targets need to be re-initialized:')
715 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800716 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200717 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800718 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100719
Mike Frysinger66814c32017-10-09 18:11:46 -0400720 # If we're building a subset of toolchains for a board, we might not have
721 # all the tuples that the packages expect. We don't define the "full" set
722 # of tuples currently other than "whatever the full sdk has normally".
723 if usepkg or set(('all', 'sdk')) & targets_wanted:
724 # Since we have cross-compilers now, we can update these packages.
725 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400726
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100727 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400728 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100729
730 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700731 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
732 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800733
734 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700735 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100736
Mike Frysingerc880a962013-11-08 13:59:06 -0500737 # Now that we've cleared out old versions, see if we need to rebuild
738 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700739 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500740
Zdenek Behan508dcce2011-12-05 15:39:32 +0100741
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700742def ShowConfig(name):
743 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500744
745 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700746 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500747 """
Don Garrettc0c74002015-10-09 12:58:19 -0700748 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500749 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400750 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400751 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800752 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400753 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500754
755
Mike Frysinger35247af2012-11-16 18:58:06 -0500756def GeneratePathWrapper(root, wrappath, path):
757 """Generate a shell script to execute another shell script
758
759 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
760 argv[0] won't be pointing to the correct path, generate a shell script that
761 just executes another program with its full path.
762
763 Args:
764 root: The root tree to generate scripts inside of
765 wrappath: The full path (inside |root|) to create the wrapper
766 path: The target program which this wrapper will execute
767 """
768 replacements = {
769 'path': path,
770 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
771 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900772
773 # Do not use exec here, because exec invokes script with absolute path in
774 # argv0. Keeping relativeness allows us to remove abs path from compile result
775 # and leads directory independent build cache sharing in some distributed
776 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500777 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900778basedir=$(dirname "$0")
779"${basedir}/%(relroot)s%(path)s" "$@"
780exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500781""" % replacements
782 root_wrapper = root + wrappath
783 if os.path.islink(root_wrapper):
784 os.unlink(root_wrapper)
785 else:
786 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
787 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400788 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500789
790
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700791def FixClangXXWrapper(root, path):
792 """Fix wrapper shell scripts and symlinks for invoking clang++
793
794 In a typical installation, clang++ symlinks to clang, which symlinks to the
795 elf executable. The executable distinguishes between clang and clang++ based
796 on argv[0].
797
798 When invoked through the LdsoWrapper, argv[0] always contains the path to the
799 executable elf file, making clang/clang++ invocations indistinguishable.
800
801 This function detects if the elf executable being wrapped is clang-X.Y, and
802 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
803
804 The calling sequence now becomes:
805 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
806 the Ldsowrapper).
807 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
808 to the original clang-3.9 elf.
809 -) The difference this time is that inside the elf file execution, $0 is
810 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
811
Manoj Guptaae268142018-04-27 23:28:36 -0700812 Update: Starting since clang 7, the clang and clang++ are symlinks to
813 clang-7 binary, not clang-7.0. The pattern match is extended to handle
814 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
815
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700816 Args:
817 root: The root tree to generate scripts / symlinks inside of
818 path: The target elf for which LdsoWrapper was created
819 """
Manoj Guptaae268142018-04-27 23:28:36 -0700820 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700821 logging.info('fixing clang++ invocation for %s', path)
822 clangdir = os.path.dirname(root + path)
823 clang = os.path.basename(path)
824 clangxx = clang.replace('clang', 'clang++')
825
826 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
827 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
828
829 # Create a hardlink clang++-X.Y pointing to clang-X.Y
830 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
831
832 # Adjust the clang++ symlink to point to clang++-X.Y
833 os.unlink(os.path.join(clangdir, 'clang++'))
834 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
835
836
Mike Frysinger35247af2012-11-16 18:58:06 -0500837def FileIsCrosSdkElf(elf):
838 """Determine if |elf| is an ELF that we execute in the cros_sdk
839
840 We don't need this to be perfect, just quick. It makes sure the ELF
841 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
842
843 Args:
844 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500845
Mike Frysinger35247af2012-11-16 18:58:06 -0500846 Returns:
847 True if we think |elf| is a native ELF
848 """
849 with open(elf) as f:
850 data = f.read(20)
851 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
852 return (data[0:4] == '\x7fELF' and
853 data[4] == '\x02' and
854 data[5] == '\x01' and
855 data[18] == '\x3e')
856
857
858def IsPathPackagable(ptype, path):
859 """Should the specified file be included in a toolchain package?
860
861 We only need to handle files as we'll create dirs as we need them.
862
863 Further, trim files that won't be useful:
864 - non-english translations (.mo) since it'd require env vars
865 - debug files since these are for the host compiler itself
866 - info/man pages as they're big, and docs are online, and the
867 native docs should work fine for the most part (`man gcc`)
868
869 Args:
870 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
871 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500872
Mike Frysinger35247af2012-11-16 18:58:06 -0500873 Returns:
874 True if we want to include this path in the package
875 """
876 return not (ptype in ('dir',) or
877 path.startswith('/usr/lib/debug/') or
878 os.path.splitext(path)[1] == '.mo' or
879 ('/man/' in path or '/info/' in path))
880
881
882def ReadlinkRoot(path, root):
883 """Like os.readlink(), but relative to a |root|
884
885 Args:
886 path: The symlink to read
887 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500888
Mike Frysinger35247af2012-11-16 18:58:06 -0500889 Returns:
890 A fully resolved symlink path
891 """
892 while os.path.islink(root + path):
893 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
894 return path
895
896
897def _GetFilesForTarget(target, root='/'):
898 """Locate all the files to package for |target|
899
900 This does not cover ELF dependencies.
901
902 Args:
903 target: The toolchain target name
904 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500905
Mike Frysinger35247af2012-11-16 18:58:06 -0500906 Returns:
907 A tuple of a set of all packable paths, and a set of all paths which
908 are also native ELFs
909 """
910 paths = set()
911 elfs = set()
912
913 # Find all the files owned by the packages for this target.
914 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500915
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700916 # Skip Go compiler from redistributable packages.
917 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
918 # into it. Due to this, the toolchain cannot be unpacked anywhere
919 # else and be readily useful. To enable packaging Go, we need to:
920 # -) Tweak the wrappers/environment to override GOROOT
921 # automatically based on the unpack location.
922 # -) Make sure the ELF dependency checking and wrapping logic
923 # below skips the Go toolchain executables and libraries.
924 # -) Make sure the packaging process maintains the relative
925 # timestamps of precompiled standard library packages.
926 # (see dev-lang/go ebuild for details).
927 if pkg == 'ex_go':
928 continue
929
Yunlian Jiang36f35242018-04-27 10:18:40 -0700930 # Use armv7a-cros-linux-gnueabi/compiler-rt for
931 # armv7a-cros-linux-gnueabihf/compiler-rt.
932 # Currently the armv7a-cros-linux-gnueabi is actually
933 # the same as armv7a-cros-linux-gnueabihf with different names.
934 # Because of that, for compiler-rt, it generates the same binary in
935 # the same location. To avoid the installation conflict, we do not
936 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
937 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
938 # toolchain.
939 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
940 atom = GetPortagePackage(target, pkg)
941 cat, pn = atom.split('/')
942 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700943 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
944 settings=portage.settings)
945 contents = dblink.getcontents()
946 if not contents:
947 if 'hf' in target:
948 new_target = 'armv7a-cros-linux-gnueabi'
949 else:
950 new_target = 'armv7a-cros-linux-gnueabihf'
951 atom = GetPortagePackage(new_target, pkg)
952 else:
953 atom = GetPortagePackage(target, pkg)
954
Mike Frysinger35247af2012-11-16 18:58:06 -0500955 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700956 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700957 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500958
Mike Frysinger35247af2012-11-16 18:58:06 -0500959 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
960 settings=portage.settings)
961 contents = dblink.getcontents()
962 for obj in contents:
963 ptype = contents[obj][0]
964 if not IsPathPackagable(ptype, obj):
965 continue
966
967 if ptype == 'obj':
968 # For native ELFs, we need to pull in their dependencies too.
969 if FileIsCrosSdkElf(obj):
970 elfs.add(obj)
971 paths.add(obj)
972
973 return paths, elfs
974
975
976def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500977 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500978 """Link in all packable files and their runtime dependencies
979
980 This also wraps up executable ELFs with helper scripts.
981
982 Args:
983 output_dir: The output directory to store files
984 paths: All the files to include
985 elfs: All the files which are ELFs (a subset of |paths|)
986 ldpaths: A dict of static ldpath information
987 path_rewrite_func: User callback to rewrite paths in output_dir
988 root: The root path to pull all packages/files from
989 """
990 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400991 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500992 for path in paths:
993 new_path = path_rewrite_func(path)
994 dst = output_dir + new_path
995 osutils.SafeMakedirs(os.path.dirname(dst))
996
997 # Is this a symlink which we have to rewrite or wrap?
998 # Delay wrap check until after we have created all paths.
999 src = root + path
1000 if os.path.islink(src):
1001 tgt = os.readlink(src)
1002 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001003 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001004
1005 # Rewrite absolute links to relative and then generate the symlink
1006 # ourselves. All other symlinks can be hardlinked below.
1007 if tgt[0] == '/':
1008 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1009 os.symlink(tgt, dst)
1010 continue
1011
1012 os.link(src, dst)
1013
Mike Frysinger35247af2012-11-16 18:58:06 -05001014 # Locate all the dependencies for all the ELFs. Stick them all in the
1015 # top level "lib" dir to make the wrapper simpler. This exact path does
1016 # not matter since we execute ldso directly, and we tell the ldso the
1017 # exact path to search for its libraries.
1018 libdir = os.path.join(output_dir, 'lib')
1019 osutils.SafeMakedirs(libdir)
1020 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001021 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001022 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001023 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -05001024 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -07001025 # Do not create wrapper for libc. crbug.com/766827
1026 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -05001027 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -04001028 interp = os.path.join('/lib', os.path.basename(interp))
1029 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1030 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -07001031 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -05001032
Mike Frysinger221bd822017-09-29 02:51:47 -04001033 # Wrap any symlinks to the wrapper.
1034 if elf in sym_paths:
1035 link = sym_paths[elf]
1036 GeneratePathWrapper(output_dir, link, elf)
1037
Mike Frysinger35247af2012-11-16 18:58:06 -05001038 for lib, lib_data in e['libs'].iteritems():
1039 if lib in donelibs:
1040 continue
1041
1042 src = path = lib_data['path']
1043 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001044 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001045 continue
1046 donelibs.add(lib)
1047
1048 # Needed libs are the SONAME, but that is usually a symlink, not a
1049 # real file. So link in the target rather than the symlink itself.
1050 # We have to walk all the possible symlinks (SONAME could point to a
1051 # symlink which points to a symlink), and we have to handle absolute
1052 # ourselves (since we have a "root" argument).
1053 dst = os.path.join(libdir, os.path.basename(path))
1054 src = ReadlinkRoot(src, root)
1055
1056 os.link(root + src, dst)
1057
1058
1059def _EnvdGetVar(envd, var):
1060 """Given a Gentoo env.d file, extract a var from it
1061
1062 Args:
1063 envd: The env.d file to load (may be a glob path)
1064 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001065
Mike Frysinger35247af2012-11-16 18:58:06 -05001066 Returns:
1067 The value of |var|
1068 """
1069 envds = glob.glob(envd)
1070 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1071 envd = envds[0]
1072 return cros_build_lib.LoadKeyValueFile(envd)[var]
1073
1074
1075def _ProcessBinutilsConfig(target, output_dir):
1076 """Do what binutils-config would have done"""
1077 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001078
1079 # Locate the bin dir holding the gold linker.
1080 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1081 target, 'binutils-bin')
1082 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001083 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001084 if not srcpath:
1085 # Maybe this target doesn't support gold.
1086 globpath = os.path.join(binutils_bin_path, '*')
1087 srcpath = glob.glob(globpath)
1088 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1089 % globpath)
1090 srcpath = srcpath[0]
1091 ld_path = os.path.join(srcpath, 'ld')
1092 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1093 ld_path = os.path.join(srcpath, 'ld.bfd')
1094 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1095 ld_path = os.path.join(srcpath, 'ld.gold')
1096 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1097 % ld_path)
1098
1099 # Nope, no gold support to be found.
1100 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001101 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001102 else:
1103 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001104 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001105
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001106 # Package the binutils-bin directory without the '-gold' suffix
1107 # if gold is not enabled as the default linker for this target.
1108 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1109 if not gold_supported:
1110 srcpath = srcpath[:-len('-gold')]
1111 ld_path = os.path.join(srcpath, 'ld')
1112 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1113
Mike Frysinger78b7a812014-11-26 19:45:23 -05001114 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001115 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1116 for prog in os.listdir(output_dir + srcpath):
1117 # Skip binaries already wrapped.
1118 if not prog.endswith('.real'):
1119 GeneratePathWrapper(output_dir, binpath + prog,
1120 os.path.join(srcpath, prog))
1121 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1122 os.path.join(srcpath, prog))
1123
David James27ac4ae2012-12-03 23:16:15 -08001124 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001125 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1126 if gold_supported:
1127 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001128 else:
1129 # If gold is not enabled as the default linker and 2 env.d
1130 # files exist, pick the one without the '-gold' suffix.
1131 envds = sorted(glob.glob(envd))
1132 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1133 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001134 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1135 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1136 output_dir + libpath)
1137
1138
1139def _ProcessGccConfig(target, output_dir):
1140 """Do what gcc-config would have done"""
1141 binpath = '/bin'
1142 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1143 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1144 for prog in os.listdir(output_dir + srcpath):
1145 # Skip binaries already wrapped.
1146 if (not prog.endswith('.real') and
1147 not prog.endswith('.elf') and
1148 prog.startswith(target)):
1149 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1150 os.path.join(srcpath, prog))
1151 return srcpath
1152
1153
Frank Henigman179ec7c2015-02-06 03:01:09 -05001154def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1155 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001156 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001157 for sysroot_wrapper in glob.glob(os.path.join(
1158 output_dir + srcpath, 'sysroot_wrapper*')):
1159 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
Douglas Andersoncc828a52017-10-13 13:07:25 -07001160
1161 # In order to optimize startup time in the chroot we run python a little
1162 # differently there. Put it back to the more portable way here.
Mike Frysingerdcad4e02018-08-03 16:20:02 -04001163 # See https://crbug.com/773138 for some details.
Douglas Andersoncc828a52017-10-13 13:07:25 -07001164 if contents[0] == '#!/usr/bin/python2 -S':
1165 contents[0] = '#!/usr/bin/env python2'
1166
Frank Henigman179ec7c2015-02-06 03:01:09 -05001167 for num in xrange(len(contents)):
1168 if '@CCACHE_DEFAULT@' in contents[num]:
Caroline Ticece9e9232017-06-02 09:38:42 -07001169 assert 'True' in contents[num]
1170 contents[num] = contents[num].replace('True', 'False')
Frank Henigman179ec7c2015-02-06 03:01:09 -05001171 break
1172 # Can't update the wrapper in place since it's a hardlink to a file in /.
1173 os.unlink(sysroot_wrapper)
1174 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1175 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001176
1177
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001178def _CreateMainLibDir(target, output_dir):
1179 """Create some lib dirs so that compiler can get the right Gcc paths"""
1180 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1181 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1182
1183
Mike Frysinger35247af2012-11-16 18:58:06 -05001184def _ProcessDistroCleanups(target, output_dir):
1185 """Clean up the tree and remove all distro-specific requirements
1186
1187 Args:
1188 target: The toolchain target name
1189 output_dir: The output directory to clean up
1190 """
1191 _ProcessBinutilsConfig(target, output_dir)
1192 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001193 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001194 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001195
1196 osutils.RmDir(os.path.join(output_dir, 'etc'))
1197
1198
1199def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1200 """Setup a tree from the packages for the specified target
1201
1202 This populates a path with all the files from toolchain packages so that
1203 a tarball can easily be generated from the result.
1204
1205 Args:
1206 target: The target to create a packagable root from
1207 output_dir: The output directory to place all the files
1208 ldpaths: A dict of static ldpath information
1209 root: The root path to pull all packages/files from
1210 """
1211 # Find all the files owned by the packages for this target.
1212 paths, elfs = _GetFilesForTarget(target, root=root)
1213
1214 # Link in all the package's files, any ELF dependencies, and wrap any
1215 # executable ELFs with helper scripts.
1216 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001217 """Move /usr/bin to /bin so people can just use that toplevel dir
1218
1219 Note we do not apply this to clang - there is correlation between clang's
1220 search path for libraries / inclusion and its installation path.
1221 """
1222 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1223 return path[4:]
1224 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001225 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1226 path_rewrite_func=MoveUsrBinToBin, root=root)
1227
1228 # The packages, when part of the normal distro, have helper scripts
1229 # that setup paths and such. Since we are making this standalone, we
1230 # need to preprocess all that ourselves.
1231 _ProcessDistroCleanups(target, output_dir)
1232
1233
1234def CreatePackages(targets_wanted, output_dir, root='/'):
1235 """Create redistributable cross-compiler packages for the specified targets
1236
1237 This creates toolchain packages that should be usable in conjunction with
1238 a downloaded sysroot (created elsewhere).
1239
1240 Tarballs (one per target) will be created in $PWD.
1241
1242 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001243 targets_wanted: The targets to package up.
1244 output_dir: The directory to put the packages in.
1245 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001246 """
Ralph Nathan03047282015-03-23 11:09:32 -07001247 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001248 osutils.SafeMakedirs(output_dir)
1249 ldpaths = lddtree.LoadLdpaths(root)
1250 targets = ExpandTargets(targets_wanted)
1251
Mike Frysinger221bd822017-09-29 02:51:47 -04001252 with osutils.TempDir(prefix='create-packages') as tempdir:
1253 logging.debug('Using tempdir: %s', tempdir)
1254
Mike Frysinger35247af2012-11-16 18:58:06 -05001255 # We have to split the root generation from the compression stages. This is
1256 # because we hardlink in all the files (to avoid overhead of reading/writing
1257 # the copies multiple times). But tar gets angry if a file's hardlink count
1258 # changes from when it starts reading a file to when it finishes.
1259 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1260 for target in targets:
1261 output_target_dir = os.path.join(tempdir, target)
1262 queue.put([target, output_target_dir, ldpaths, root])
1263
1264 # Build the tarball.
1265 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1266 for target in targets:
1267 tar_file = os.path.join(output_dir, target + '.tar.xz')
1268 queue.put([tar_file, os.path.join(tempdir, target)])
1269
1270
Mike Frysinger07534cf2017-09-12 17:40:21 -04001271def GetParser():
1272 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001273 parser = commandline.ArgumentParser(description=__doc__)
1274 parser.add_argument('-u', '--nousepkg',
1275 action='store_false', dest='usepkg', default=True,
1276 help='Use prebuilt packages if possible')
1277 parser.add_argument('-d', '--deleteold',
1278 action='store_true', dest='deleteold', default=False,
1279 help='Unmerge deprecated packages')
1280 parser.add_argument('-t', '--targets',
1281 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001282 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001283 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001284 "allowed. Defaults to 'sdk'.")
1285 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1286 help='Comma separated list of boards whose toolchains we '
1287 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001288 parser.add_argument('--hostonly',
1289 dest='hostonly', default=False, action='store_true',
1290 help='Only setup the host toolchain. '
1291 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001292 parser.add_argument('--show-board-cfg', '--show-cfg',
1293 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001294 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001295 parser.add_argument('--show-packages', default=None,
1296 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001297 parser.add_argument('--create-packages',
1298 action='store_true', default=False,
1299 help='Build redistributable packages')
1300 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1301 help='Output directory')
1302 parser.add_argument('--reconfig', default=False, action='store_true',
1303 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001304 parser.add_argument('--sysroot', type='path',
1305 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001306 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001307
Mike Frysinger07534cf2017-09-12 17:40:21 -04001308
1309def main(argv):
1310 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001311 options = parser.parse_args(argv)
1312 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001313
Mike Frysinger35247af2012-11-16 18:58:06 -05001314 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001315 conflicting_options = (
1316 options.cfg_name,
1317 options.show_packages,
1318 options.create_packages,
1319 )
1320 if sum(bool(x) for x in conflicting_options) > 1:
1321 parser.error('conflicting options: create-packages & show-packages & '
1322 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001323
Gilad Arnold8195b532015-04-07 10:56:30 +03001324 targets_wanted = set(options.targets.split(','))
1325 boards_wanted = (set(options.include_boards.split(','))
1326 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001327
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001328 if options.cfg_name:
1329 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001330 elif options.show_packages is not None:
1331 cros_build_lib.AssertInsideChroot()
1332 target = options.show_packages
1333 Crossdev.Load(False)
1334 for package in GetTargetPackages(target):
1335 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001336 elif options.create_packages:
1337 cros_build_lib.AssertInsideChroot()
1338 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001339 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001340 else:
1341 cros_build_lib.AssertInsideChroot()
1342 # This has to be always run as root.
1343 if os.geteuid() != 0:
1344 cros_build_lib.Die('this script must be run as root')
1345
1346 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001347 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001348 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001349 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001350 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001351 Crossdev.Save()
1352
1353 return 0