blob: 99bb484f87c648945a2ebcfbbb5f7af9d914550c [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
Tobias Boschddd16492019-08-14 09:29:54 -070016import shutil
Zdenek Behan508dcce2011-12-05 15:39:32 +010017
Aviv Keshetb7519e12016-10-04 00:50:00 -070018from chromite.lib import constants
Mike Frysinger506e75f2012-12-17 14:21:13 -050019from chromite.lib import commandline
Brian Harring503f3ab2012-03-09 21:39:41 -080020from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070021from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070022from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050023from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080024from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040025from chromite.utils import key_value_store
Mike Frysinger35247af2012-11-16 18:58:06 -050026
27# Needs to be after chromite imports.
28import lddtree
Zdenek Behan508dcce2011-12-05 15:39:32 +010029
Mike Frysinger31596002012-12-03 23:54:24 -050030if cros_build_lib.IsInsideChroot():
31 # Only import portage after we've checked that we're inside the chroot.
32 # Outside may not have portage, in which case the above may not happen.
33 # We'll check in main() if the operation needs portage.
Mike Frysinger27e21b72018-07-12 14:20:21 -040034 # pylint: disable=import-error
Mike Frysinger31596002012-12-03 23:54:24 -050035 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010036
37
Matt Tennantf1e30972012-03-02 16:30:07 -080038EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010039PACKAGE_STABLE = '[stable]'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010040
41CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070042ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010043STABLE_OVERLAY = '/usr/local/portage/stable'
44CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010045
46
Mike Frysinger66bfde52017-09-12 16:42:57 -040047# The exact list of host toolchain packages we care about. These are the
48# packages that bots/devs install only from binpkgs and rely on the SDK bot
49# (chromiumos-sdk) to validate+uprev.
50#
Mike Frysinger66bfde52017-09-12 16:42:57 -040051# We don't use crossdev to manage the host toolchain for us, especially since
52# we diverge significantly now (with llvm/clang/etc...), and we don't need or
53# want crossdev managing /etc/portage config files for the sdk
54HOST_PACKAGES = (
55 'dev-lang/go',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080056 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040057 'sys-devel/binutils',
58 'sys-devel/clang',
59 'sys-devel/gcc',
Yunlian Jiangf5721f32017-10-31 11:43:11 -070060 'sys-devel/lld',
Mike Frysinger66bfde52017-09-12 16:42:57 -040061 'sys-devel/llvm',
62 'sys-kernel/linux-headers',
63 'sys-libs/glibc',
64 'sys-libs/libcxx',
65 'sys-libs/libcxxabi',
Manoj Guptade64cb22019-03-31 18:48:58 -070066 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040067)
68
Mike Frysinger785b0c32017-09-13 01:35:59 -040069# These packages are also installed into the host SDK. However, they require
70# the cross-compilers to be installed first (because they need them to actually
71# build), so we have to delay their installation.
72HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070073 'dev-lang/rust',
74 'dev-util/cargo',
Mike Frysinger61a24392017-10-17 17:14:27 -040075 'virtual/target-sdk-post-cross',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010076 'dev-embedded/coreboot-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040077)
78
79# New packages that we're in the process of adding to the SDK. Since the SDK
80# bot hasn't had a chance to run yet, there are no binary packages available,
81# so we have to list them here and wait. Once it completes, entries here can
82# be removed so they'll end up on bots & dev's systems.
83NEW_PACKAGES = (
Mike Frysinger785b0c32017-09-13 01:35:59 -040084)
85
Rahul Chaudhry4b803052015-05-13 15:25:56 -070086# Enable the Go compiler for these targets.
87TARGET_GO_ENABLED = (
88 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070089 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070090 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070091 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070092)
93CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
94
Manoj Gupta1b5642e2017-03-08 16:44:12 -080095# Enable llvm's compiler-rt for these targets.
96TARGET_COMPILER_RT_ENABLED = (
97 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -070098 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -070099 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -0800100 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800101)
102CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
103
Manoj Gupta946abb42017-04-12 14:27:19 -0700104TARGET_LLVM_PKGS_ENABLED = (
105 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700106 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700107 'aarch64-cros-linux-gnu',
108 'x86_64-cros-linux-gnu',
109)
110
111LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700112 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
113 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700114 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700115}
116
Zdenek Behan508dcce2011-12-05 15:39:32 +0100117# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
118CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500119 'binutils' : {
Manoj Guptaa91e38e2018-11-15 11:07:48 -0800120 'aarch64-cros-linux-gnu' : '-gold',
Mike Frysinger8a83c622015-05-28 00:35:05 -0400121 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -0800122 'armv7a-cros-linux-gnueabi': '-gold',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700123 'armv7a-cros-linux-gnueabihf': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500124 'i686-pc-linux-gnu' : '-gold',
125 'x86_64-cros-linux-gnu' : '-gold',
126 },
Zdenek Behan508dcce2011-12-05 15:39:32 +0100127}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100128
129
David James66a09c42012-11-05 13:31:38 -0800130class Crossdev(object):
131 """Class for interacting with crossdev and caching its output."""
132
133 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
134 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800135 # Packages that needs separate handling, in addition to what we have from
136 # crossdev.
137 MANUAL_PKGS = {
Manoj Guptaf0a602e2017-08-02 10:25:45 -0700138 'clang': 'sys-devel',
Yunlian Jiang18ae9982017-11-03 09:15:31 -0700139 'lld': 'sys-devel',
Han Shene23782f2016-02-18 12:20:00 -0800140 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700141 'libcxxabi': 'sys-libs',
142 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700143 'elfutils': 'dev-libs',
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700144 'llvm-libunwind': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800145 }
David James66a09c42012-11-05 13:31:38 -0800146
147 @classmethod
148 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400149 """Load crossdev cache from disk.
150
151 We invalidate the cache when crossdev updates or this script changes.
152 """
David James90239b92012-11-05 15:31:34 -0800153 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400154 # If we run the compiled/cached .pyc file, we'll read/hash that when we
155 # really always want to track the source .py file.
156 script = os.path.abspath(__file__)
157 if script.endswith('.pyc'):
158 script = script[:-1]
159 setup_toolchains_hash = hashlib.md5(osutils.ReadFile(script)).hexdigest()
160
161 cls._CACHE = {
162 'crossdev_version': crossdev_version,
163 'setup_toolchains_hash': setup_toolchains_hash,
164 }
165
166 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
167 if reconfig:
168 logging.debug('cache: forcing regen due to reconfig')
169 return
170
171 try:
172 file_data = osutils.ReadFile(cls._CACHE_FILE)
173 except IOError as e:
174 if e.errno != errno.ENOENT:
175 logging.warning('cache: reading failed: %s', e)
176 osutils.SafeUnlink(cls._CACHE_FILE)
177 return
178
179 try:
180 data = json.loads(file_data)
181 except ValueError as e:
182 logging.warning('cache: ignoring invalid content: %s', e)
183 return
184
185 if crossdev_version != data.get('crossdev_version'):
186 logging.debug('cache: rebuilding after crossdev upgrade')
187 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
188 logging.debug('cache: rebuilding after cros_setup_toolchains change')
189 else:
190 logging.debug('cache: content is up-to-date!')
191 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800192
193 @classmethod
194 def Save(cls):
195 """Store crossdev cache on disk."""
196 # Save the cache from the successful run.
197 with open(cls._CACHE_FILE, 'w') as f:
198 json.dump(cls._CACHE, f)
199
200 @classmethod
201 def GetConfig(cls, target):
202 """Returns a map of crossdev provided variables about a tuple."""
203 CACHE_ATTR = '_target_tuple_map'
204
205 val = cls._CACHE.setdefault(CACHE_ATTR, {})
206 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400207 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400208 conf = {
209 'crosspkgs': [],
210 'target': toolchain.GetHostTuple(),
211 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400212 if target == 'host':
213 packages_list = HOST_PACKAGES
214 else:
215 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400216 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400217 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400218 else:
219 # Build the crossdev command.
220 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
221 if target in TARGET_COMPILER_RT_ENABLED:
222 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
223 if target in TARGET_GO_ENABLED:
224 cmd.extend(CROSSDEV_GO_ARGS)
225 if target in TARGET_LLVM_PKGS_ENABLED:
226 for pkg in LLVM_PKGS_TABLE:
227 cmd.extend(LLVM_PKGS_TABLE[pkg])
228 cmd.extend(['-t', target])
229 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400230 out = cros_build_lib.run(
Mike Frysinger66bfde52017-09-12 16:42:57 -0400231 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
232 # List of tuples split at the first '=', converted into dict.
233 conf = dict((k, cros_build_lib.ShellUnquote(v))
234 for k, v in (x.split('=', 1) for x in out))
235 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800236
Mike Frysinger66bfde52017-09-12 16:42:57 -0400237 manual_pkgs = cls.MANUAL_PKGS
238
239 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400240 conf[pkg + '_pn'] = pkg
241 conf[pkg + '_category'] = cat
242 if pkg not in conf['crosspkgs']:
243 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800244
245 val[target] = conf
246
David James66a09c42012-11-05 13:31:38 -0800247 return val[target]
248
249 @classmethod
250 def UpdateTargets(cls, targets, usepkg, config_only=False):
251 """Calls crossdev to initialize a cross target.
252
253 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700254 targets: The list of targets to initialize using crossdev.
255 usepkg: Copies the commandline opts.
256 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800257 """
258 configured_targets = cls._CACHE.setdefault('configured_targets', [])
259
260 cmdbase = ['crossdev', '--show-fail-log']
261 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
262 # Pick stable by default, and override as necessary.
263 cmdbase.extend(['-P', '--oneshot'])
264 if usepkg:
265 cmdbase.extend(['-P', '--getbinpkg',
266 '-P', '--usepkgonly',
267 '--without-headers'])
268
Christopher Wileyb22c0712015-06-02 10:37:03 -0700269 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800270 cmdbase.extend(['--overlays', overlays])
271 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
272
Yunlian Jiang4ff84172018-05-12 13:03:01 -0700273 # Build target by the reversed alphabetical order to make sure
274 # armv7a-cros-linux-gnueabihf builds before armv7a-cros-linux-gnueabi
Yunlian Jiang85c606a2017-10-10 20:58:53 -0700275 # because some dependency issue. This can be reverted once we
276 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
Yunlian Jiang4ff84172018-05-12 13:03:01 -0700277 for target in sorted(targets, reverse=True):
David James66a09c42012-11-05 13:31:38 -0800278 if config_only and target in configured_targets:
279 continue
280
281 cmd = cmdbase + ['-t', target]
282
283 for pkg in GetTargetPackages(target):
284 if pkg == 'gdb':
285 # Gdb does not have selectable versions.
286 cmd.append('--ex-gdb')
Manoj Guptab8181562017-05-21 10:18:59 -0700287 elif pkg == 'ex_compiler-rt':
288 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700289 elif pkg == 'ex_go':
290 # Go does not have selectable versions.
291 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700292 elif pkg in LLVM_PKGS_TABLE:
293 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800294 elif pkg in cls.MANUAL_PKGS:
295 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700296 else:
297 # The first of the desired versions is the "primary" one.
298 version = GetDesiredPackageVersions(target, pkg)[0]
299 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800300
301 cmd.extend(targets[target]['crossdev'].split())
302 if config_only:
303 # In this case we want to just quietly reinit
304 cmd.append('--init-target')
Mike Frysinger45602c72019-09-22 02:15:11 -0400305 cros_build_lib.run(cmd, print_cmd=False, redirect_stdout=True)
David James66a09c42012-11-05 13:31:38 -0800306 else:
Mike Frysinger45602c72019-09-22 02:15:11 -0400307 cros_build_lib.run(cmd)
David James66a09c42012-11-05 13:31:38 -0800308
309 configured_targets.append(target)
310
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100311
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100312def GetTargetPackages(target):
313 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800314 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100315 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800316 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100317
318
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100319# Portage helper functions:
320def GetPortagePackage(target, package):
321 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800322 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100323 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400324 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100325 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100326 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100327 category = conf['category']
328 # Portage package:
329 pn = conf[package + '_pn']
330 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500331 assert category
332 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100333 return '%s/%s' % (category, pn)
334
335
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700336def PortageTrees(root):
337 """Return the portage trees for a given root."""
338 if root == '/':
339 return portage.db['/']
340 # The portage logic requires the path always end in a slash.
341 root = root.rstrip('/') + '/'
342 return portage.create_trees(target_root=root, config_root=root)[root]
343
344
345def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100346 """Extracts the list of current versions of a target, package pair.
347
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500348 Args:
349 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700350 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100351
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500352 Returns:
353 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100354 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100355 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700356 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100357 version = portage.versions.cpv_getversion(pkg)
358 versions.append(version)
359 return versions
360
361
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700362def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100363 """Extracts the current stable version for a given package.
364
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500365 Args:
366 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
367 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700368 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100369
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500370 Returns:
371 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100372 """
David James90239b92012-11-05 15:31:34 -0800373 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700374 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800375 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100376
377
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700378def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100379 """Resolves keywords in a given version list for a particular package.
380
381 Resolving means replacing PACKAGE_STABLE with the actual number.
382
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500383 Args:
384 target: The target to operate on (e.g. i686-pc-linux-gnu)
385 package: The target/package to operate on (e.g. gcc)
386 versions: List of versions to resolve
387 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700388 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100389
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500390 Returns:
391 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100392 """
393 resolved = []
David James90239b92012-11-05 15:31:34 -0800394 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700395 if not installed:
396 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100397 for version in versions:
398 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700399 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400400 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100401 resolved.append(version)
402 return resolved
403
404
405def GetDesiredPackageVersions(target, package):
406 """Produces the list of desired versions for each target, package pair.
407
408 The first version in the list is implicitly treated as primary, ie.
409 the version that will be initialized by crossdev and selected.
410
411 If the version is PACKAGE_STABLE, it really means the current version which
412 is emerged by using the package atom with no particular version key.
413 Since crossdev unmasks all packages by default, this will actually
414 mean 'unstable' in most cases.
415
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500416 Args:
417 target: The target to operate on (e.g. i686-pc-linux-gnu)
418 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500420 Returns:
421 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100422 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400423 if package in GetTargetPackages(target):
424 return [PACKAGE_STABLE]
425 else:
426 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100427
428
429def TargetIsInitialized(target):
430 """Verifies if the given list of targets has been correctly initialized.
431
432 This determines whether we have to call crossdev while emerging
433 toolchain packages or can do it using emerge. Emerge is naturally
434 preferred, because all packages can be updated in a single pass.
435
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500436 Args:
437 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100438
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500439 Returns:
440 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100441 """
442 # Check if packages for the given target all have a proper version.
443 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100444 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800445 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100446 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400447 if not (GetStablePackageVersion(atom, True) and
448 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100449 return False
450 return True
451 except cros_build_lib.RunCommandError:
452 # Fails - The target has likely never been initialized before.
453 return False
454
455
456def RemovePackageMask(target):
457 """Removes a package.mask file for the given platform.
458
459 The pre-existing package.mask files can mess with the keywords.
460
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500461 Args:
462 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100463 """
464 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700465 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100466
467
Zdenek Behan508dcce2011-12-05 15:39:32 +0100468# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700469def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500470 """Rebuild libtool as needed
471
472 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
473 gcc, libtool will break. We can't use binary packages either as those will
474 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700475
476 Args:
477 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500478 """
479 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700480 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500481 for line in f:
482 # Look for a line like:
483 # sys_lib_search_path_spec="..."
484 # It'll be a list of paths and gcc will be one of them.
485 if line.startswith('sys_lib_search_path_spec='):
486 line = line.rstrip()
487 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400488 root_path = os.path.join(root, path.lstrip(os.path.sep))
489 logging.debug('Libtool: checking %s', root_path)
490 if not os.path.exists(root_path):
491 logging.info('Rebuilding libtool after gcc upgrade')
492 logging.info(' %s', line)
493 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500494 needs_update = True
495 break
496
497 if needs_update:
498 break
499
500 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700501 cmd = [EMERGE_CMD, '--oneshot']
502 if root != '/':
503 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
504 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400505 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400506 else:
507 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500508
509
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700510def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100511 """Determines which packages need update/unmerge and defers to portage.
512
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500513 Args:
514 targets: The list of targets to update
515 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700516 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100518 # For each target, we do two things. Figure out the list of updates,
519 # and figure out the appropriate keywords/masks. Crossdev will initialize
520 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400521 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800522 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100523 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400524 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100525 # Record the highest needed version for each target, for masking purposes.
526 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100527 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528 # Portage name for the package
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400529 logging.debug(' Checking package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100530 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700531 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200533 desired_num = VersionListToNumeric(target, package, desired, False)
Mike Frysinger785b0c32017-09-13 01:35:59 -0400534 if pkg in NEW_PACKAGES and usepkg:
535 # Skip this binary package (for now).
536 continue
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100537 mergemap[pkg] = set(desired_num).difference(current)
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400538 logging.debug(' %s -> %s', current, desired_num)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400540 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400542 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800543 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100544
Mike Frysinger3bba5032016-09-20 14:15:04 -0400545 logging.info('Updating packages:')
546 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100547
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100548 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100549 if usepkg:
550 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700551 if root != '/':
552 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100553
554 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400555 cros_build_lib.run(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800556 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100557
558
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700559def CleanTargets(targets, root='/'):
560 """Unmerges old packages that are assumed unnecessary.
561
562 Args:
563 targets: The list of targets to clean up.
564 root: The install root in which we want packages cleaned up.
565 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100566 unmergemap = {}
567 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400568 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100569 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400570 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100571 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700572 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100573 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700574 # NOTE: This refers to installed packages (vartree) rather than the
575 # Portage version (porttree and/or bintree) when determining the current
576 # version. While this isn't the most accurate thing to do, it is probably
577 # a good simple compromise, which should have the desired result of
578 # uninstalling everything but the latest installed version. In
579 # particular, using the bintree (--usebinpkg) requires a non-trivial
580 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200581 desired_num = VersionListToNumeric(target, package, desired, True)
582 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400583 logging.warning('Error detecting stable version for %s, '
584 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200585 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100586 unmergemap[pkg] = set(current).difference(desired_num)
587
588 # Cleaning doesn't care about consistency and rebuilding package.* files.
589 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400590 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100591 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
592
593 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400594 logging.info('Cleaning packages:')
595 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100596 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700597 if root != '/':
598 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100599 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400600 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100601 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400602 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603
604
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700605def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100606 """Runs gcc-config and binutils-config to select the desired.
607
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500608 Args:
609 targets: The targets to select
610 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700611 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100612 """
613 for package in ['gcc', 'binutils']:
614 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400615 # See if this package is part of this target.
616 if package not in GetTargetPackages(target):
617 logging.debug('%s: %s is not used', target, package)
618 continue
619
Zdenek Behan508dcce2011-12-05 15:39:32 +0100620 # Pick the first version in the numbered list as the selected one.
621 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700622 desired_num = VersionListToNumeric(target, package, desired, True,
623 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100624 desired = desired_num[0]
625 # *-config does not play revisions, strip them, keep just PV.
626 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
627
Mike Frysinger785b0c32017-09-13 01:35:59 -0400628 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100629 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800630 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100631
632 # And finally, attach target to it.
633 desired = '%s-%s' % (target, desired)
634
635 # Target specific hacks
636 if package in suffixes:
637 if target in suffixes[package]:
638 desired += suffixes[package][target]
639
David James7ec5efc2012-11-06 09:39:49 -0800640 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700641 if root != '/':
642 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800643 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400644 result = cros_build_lib.run(
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500645 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
646 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700647
648 # Do not reconfig when the current is live or nothing needs to be done.
649 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100650 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500651 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400652 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100653
654
Mike Frysinger35247af2012-11-16 18:58:06 -0500655def ExpandTargets(targets_wanted):
656 """Expand any possible toolchain aliases into full targets
657
658 This will expand 'all' and 'sdk' into the respective toolchain tuples.
659
660 Args:
661 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500662
Mike Frysinger35247af2012-11-16 18:58:06 -0500663 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300664 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500665 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500666 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700667 if targets_wanted == set(['boards']):
668 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300669 return {}
670
671 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500672 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300673 return all_targets
674 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500675 # Filter out all the non-sdk toolchains as we don't want to mess
676 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300677 return toolchain.FilterToolchains(all_targets, 'sdk', True)
678
679 # Verify user input.
680 nonexistent = targets_wanted.difference(all_targets)
681 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400682 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300683 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500684
685
David Jamesf8c672f2012-11-06 13:38:11 -0800686def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700687 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100688 """Performs all steps to create a synchronized toolchain enviroment.
689
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500690 Args:
691 usepkg: Use prebuilt packages
692 deleteold: Unmerge deprecated packages
693 hostonly: Only setup the host toolchain
694 reconfig: Reload crossdev config and reselect toolchains
695 targets_wanted: All the targets to update
696 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700697 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100698 """
David Jamesf8c672f2012-11-06 13:38:11 -0800699 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100700 if not hostonly:
701 # For hostonly, we can skip most of the below logic, much of which won't
702 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500703 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400704
Don Garrettc0c74002015-10-09 12:58:19 -0700705 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300706 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400707 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800708 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100709
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100710 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400711 for target in targets:
712 if TargetIsInitialized(target):
713 reconfig_targets[target] = targets[target]
714 else:
715 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100716 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400717 logging.info('The following targets need to be re-initialized:')
718 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800719 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200720 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800721 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100722
Mike Frysinger66814c32017-10-09 18:11:46 -0400723 # If we're building a subset of toolchains for a board, we might not have
724 # all the tuples that the packages expect. We don't define the "full" set
725 # of tuples currently other than "whatever the full sdk has normally".
726 if usepkg or set(('all', 'sdk')) & targets_wanted:
727 # Since we have cross-compilers now, we can update these packages.
728 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400729
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100730 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400731 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100732
733 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700734 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
735 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800736
737 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700738 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100739
Mike Frysingerc880a962013-11-08 13:59:06 -0500740 # Now that we've cleared out old versions, see if we need to rebuild
741 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700742 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500743
Zdenek Behan508dcce2011-12-05 15:39:32 +0100744
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700745def ShowConfig(name):
746 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500747
748 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700749 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500750 """
Don Garrettc0c74002015-10-09 12:58:19 -0700751 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500752 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400753 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400754 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400755 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
756 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500757
758
Mike Frysinger35247af2012-11-16 18:58:06 -0500759def GeneratePathWrapper(root, wrappath, path):
760 """Generate a shell script to execute another shell script
761
762 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
763 argv[0] won't be pointing to the correct path, generate a shell script that
764 just executes another program with its full path.
765
766 Args:
767 root: The root tree to generate scripts inside of
768 wrappath: The full path (inside |root|) to create the wrapper
769 path: The target program which this wrapper will execute
770 """
771 replacements = {
772 'path': path,
773 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
774 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900775
776 # Do not use exec here, because exec invokes script with absolute path in
777 # argv0. Keeping relativeness allows us to remove abs path from compile result
778 # and leads directory independent build cache sharing in some distributed
779 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500780 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900781basedir=$(dirname "$0")
782"${basedir}/%(relroot)s%(path)s" "$@"
783exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500784""" % replacements
785 root_wrapper = root + wrappath
786 if os.path.islink(root_wrapper):
787 os.unlink(root_wrapper)
788 else:
789 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
790 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400791 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500792
793
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700794def FixClangXXWrapper(root, path):
795 """Fix wrapper shell scripts and symlinks for invoking clang++
796
797 In a typical installation, clang++ symlinks to clang, which symlinks to the
798 elf executable. The executable distinguishes between clang and clang++ based
799 on argv[0].
800
801 When invoked through the LdsoWrapper, argv[0] always contains the path to the
802 executable elf file, making clang/clang++ invocations indistinguishable.
803
804 This function detects if the elf executable being wrapped is clang-X.Y, and
805 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
806
807 The calling sequence now becomes:
808 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
809 the Ldsowrapper).
810 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
811 to the original clang-3.9 elf.
812 -) The difference this time is that inside the elf file execution, $0 is
813 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
814
Manoj Guptaae268142018-04-27 23:28:36 -0700815 Update: Starting since clang 7, the clang and clang++ are symlinks to
816 clang-7 binary, not clang-7.0. The pattern match is extended to handle
817 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
818
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700819 Args:
820 root: The root tree to generate scripts / symlinks inside of
821 path: The target elf for which LdsoWrapper was created
822 """
Manoj Guptaae268142018-04-27 23:28:36 -0700823 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700824 logging.info('fixing clang++ invocation for %s', path)
825 clangdir = os.path.dirname(root + path)
826 clang = os.path.basename(path)
827 clangxx = clang.replace('clang', 'clang++')
828
829 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
830 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
831
832 # Create a hardlink clang++-X.Y pointing to clang-X.Y
833 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
834
835 # Adjust the clang++ symlink to point to clang++-X.Y
836 os.unlink(os.path.join(clangdir, 'clang++'))
837 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
838
839
Mike Frysinger35247af2012-11-16 18:58:06 -0500840def FileIsCrosSdkElf(elf):
841 """Determine if |elf| is an ELF that we execute in the cros_sdk
842
843 We don't need this to be perfect, just quick. It makes sure the ELF
844 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
845
846 Args:
847 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500848
Mike Frysinger35247af2012-11-16 18:58:06 -0500849 Returns:
850 True if we think |elf| is a native ELF
851 """
852 with open(elf) as f:
853 data = f.read(20)
854 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
855 return (data[0:4] == '\x7fELF' and
856 data[4] == '\x02' and
857 data[5] == '\x01' and
858 data[18] == '\x3e')
859
860
861def IsPathPackagable(ptype, path):
862 """Should the specified file be included in a toolchain package?
863
864 We only need to handle files as we'll create dirs as we need them.
865
866 Further, trim files that won't be useful:
867 - non-english translations (.mo) since it'd require env vars
868 - debug files since these are for the host compiler itself
869 - info/man pages as they're big, and docs are online, and the
870 native docs should work fine for the most part (`man gcc`)
871
872 Args:
873 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
874 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500875
Mike Frysinger35247af2012-11-16 18:58:06 -0500876 Returns:
877 True if we want to include this path in the package
878 """
879 return not (ptype in ('dir',) or
880 path.startswith('/usr/lib/debug/') or
881 os.path.splitext(path)[1] == '.mo' or
882 ('/man/' in path or '/info/' in path))
883
884
885def ReadlinkRoot(path, root):
886 """Like os.readlink(), but relative to a |root|
887
888 Args:
889 path: The symlink to read
890 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500891
Mike Frysinger35247af2012-11-16 18:58:06 -0500892 Returns:
893 A fully resolved symlink path
894 """
895 while os.path.islink(root + path):
896 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
897 return path
898
899
900def _GetFilesForTarget(target, root='/'):
901 """Locate all the files to package for |target|
902
903 This does not cover ELF dependencies.
904
905 Args:
906 target: The toolchain target name
907 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500908
Mike Frysinger35247af2012-11-16 18:58:06 -0500909 Returns:
910 A tuple of a set of all packable paths, and a set of all paths which
911 are also native ELFs
912 """
913 paths = set()
914 elfs = set()
915
916 # Find all the files owned by the packages for this target.
917 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500918
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700919 # Skip Go compiler from redistributable packages.
920 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
921 # into it. Due to this, the toolchain cannot be unpacked anywhere
922 # else and be readily useful. To enable packaging Go, we need to:
923 # -) Tweak the wrappers/environment to override GOROOT
924 # automatically based on the unpack location.
925 # -) Make sure the ELF dependency checking and wrapping logic
926 # below skips the Go toolchain executables and libraries.
927 # -) Make sure the packaging process maintains the relative
928 # timestamps of precompiled standard library packages.
929 # (see dev-lang/go ebuild for details).
930 if pkg == 'ex_go':
931 continue
932
Yunlian Jiang36f35242018-04-27 10:18:40 -0700933 # Use armv7a-cros-linux-gnueabi/compiler-rt for
934 # armv7a-cros-linux-gnueabihf/compiler-rt.
935 # Currently the armv7a-cros-linux-gnueabi is actually
936 # the same as armv7a-cros-linux-gnueabihf with different names.
937 # Because of that, for compiler-rt, it generates the same binary in
938 # the same location. To avoid the installation conflict, we do not
939 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
940 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
941 # toolchain.
942 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
943 atom = GetPortagePackage(target, pkg)
944 cat, pn = atom.split('/')
945 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700946 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
947 settings=portage.settings)
948 contents = dblink.getcontents()
949 if not contents:
950 if 'hf' in target:
951 new_target = 'armv7a-cros-linux-gnueabi'
952 else:
953 new_target = 'armv7a-cros-linux-gnueabihf'
954 atom = GetPortagePackage(new_target, pkg)
955 else:
956 atom = GetPortagePackage(target, pkg)
957
Mike Frysinger35247af2012-11-16 18:58:06 -0500958 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700959 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700960 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500961
Mike Frysinger35247af2012-11-16 18:58:06 -0500962 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
963 settings=portage.settings)
964 contents = dblink.getcontents()
965 for obj in contents:
966 ptype = contents[obj][0]
967 if not IsPathPackagable(ptype, obj):
968 continue
969
970 if ptype == 'obj':
971 # For native ELFs, we need to pull in their dependencies too.
972 if FileIsCrosSdkElf(obj):
973 elfs.add(obj)
974 paths.add(obj)
975
976 return paths, elfs
977
978
979def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500980 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500981 """Link in all packable files and their runtime dependencies
982
983 This also wraps up executable ELFs with helper scripts.
984
985 Args:
986 output_dir: The output directory to store files
987 paths: All the files to include
988 elfs: All the files which are ELFs (a subset of |paths|)
989 ldpaths: A dict of static ldpath information
990 path_rewrite_func: User callback to rewrite paths in output_dir
991 root: The root path to pull all packages/files from
992 """
993 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -0400994 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500995 for path in paths:
996 new_path = path_rewrite_func(path)
997 dst = output_dir + new_path
998 osutils.SafeMakedirs(os.path.dirname(dst))
999
1000 # Is this a symlink which we have to rewrite or wrap?
1001 # Delay wrap check until after we have created all paths.
1002 src = root + path
1003 if os.path.islink(src):
1004 tgt = os.readlink(src)
1005 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001006 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001007
1008 # Rewrite absolute links to relative and then generate the symlink
1009 # ourselves. All other symlinks can be hardlinked below.
1010 if tgt[0] == '/':
1011 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1012 os.symlink(tgt, dst)
1013 continue
1014
1015 os.link(src, dst)
1016
Mike Frysinger35247af2012-11-16 18:58:06 -05001017 # Locate all the dependencies for all the ELFs. Stick them all in the
1018 # top level "lib" dir to make the wrapper simpler. This exact path does
1019 # not matter since we execute ldso directly, and we tell the ldso the
1020 # exact path to search for its libraries.
1021 libdir = os.path.join(output_dir, 'lib')
1022 osutils.SafeMakedirs(libdir)
1023 donelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001024 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001025 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001026 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -05001027 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -07001028 # Do not create wrapper for libc. crbug.com/766827
1029 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -05001030 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -04001031 interp = os.path.join('/lib', os.path.basename(interp))
1032 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1033 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -07001034 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -05001035
Mike Frysinger221bd822017-09-29 02:51:47 -04001036 # Wrap any symlinks to the wrapper.
1037 if elf in sym_paths:
1038 link = sym_paths[elf]
1039 GeneratePathWrapper(output_dir, link, elf)
1040
Mike Frysinger0bdbc102019-06-13 15:27:29 -04001041 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001042 if lib in donelibs:
1043 continue
1044
1045 src = path = lib_data['path']
1046 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001047 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001048 continue
1049 donelibs.add(lib)
1050
1051 # Needed libs are the SONAME, but that is usually a symlink, not a
1052 # real file. So link in the target rather than the symlink itself.
1053 # We have to walk all the possible symlinks (SONAME could point to a
1054 # symlink which points to a symlink), and we have to handle absolute
1055 # ourselves (since we have a "root" argument).
1056 dst = os.path.join(libdir, os.path.basename(path))
1057 src = ReadlinkRoot(src, root)
1058
1059 os.link(root + src, dst)
1060
1061
1062def _EnvdGetVar(envd, var):
1063 """Given a Gentoo env.d file, extract a var from it
1064
1065 Args:
1066 envd: The env.d file to load (may be a glob path)
1067 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001068
Mike Frysinger35247af2012-11-16 18:58:06 -05001069 Returns:
1070 The value of |var|
1071 """
1072 envds = glob.glob(envd)
1073 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1074 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001075 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001076
1077
1078def _ProcessBinutilsConfig(target, output_dir):
1079 """Do what binutils-config would have done"""
1080 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001081
1082 # Locate the bin dir holding the gold linker.
1083 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1084 target, 'binutils-bin')
1085 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001086 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001087 if not srcpath:
1088 # Maybe this target doesn't support gold.
1089 globpath = os.path.join(binutils_bin_path, '*')
1090 srcpath = glob.glob(globpath)
1091 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1092 % globpath)
1093 srcpath = srcpath[0]
1094 ld_path = os.path.join(srcpath, 'ld')
1095 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1096 ld_path = os.path.join(srcpath, 'ld.bfd')
1097 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1098 ld_path = os.path.join(srcpath, 'ld.gold')
1099 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1100 % ld_path)
1101
1102 # Nope, no gold support to be found.
1103 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001104 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001105 else:
1106 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001107 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001108
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001109 # Package the binutils-bin directory without the '-gold' suffix
1110 # if gold is not enabled as the default linker for this target.
1111 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1112 if not gold_supported:
1113 srcpath = srcpath[:-len('-gold')]
1114 ld_path = os.path.join(srcpath, 'ld')
1115 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1116
Mike Frysinger78b7a812014-11-26 19:45:23 -05001117 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001118 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1119 for prog in os.listdir(output_dir + srcpath):
1120 # Skip binaries already wrapped.
1121 if not prog.endswith('.real'):
1122 GeneratePathWrapper(output_dir, binpath + prog,
1123 os.path.join(srcpath, prog))
1124 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1125 os.path.join(srcpath, prog))
1126
David James27ac4ae2012-12-03 23:16:15 -08001127 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001128 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1129 if gold_supported:
1130 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001131 else:
1132 # If gold is not enabled as the default linker and 2 env.d
1133 # files exist, pick the one without the '-gold' suffix.
1134 envds = sorted(glob.glob(envd))
1135 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1136 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001137 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1138 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1139 output_dir + libpath)
1140
1141
1142def _ProcessGccConfig(target, output_dir):
1143 """Do what gcc-config would have done"""
1144 binpath = '/bin'
1145 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1146 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1147 for prog in os.listdir(output_dir + srcpath):
1148 # Skip binaries already wrapped.
1149 if (not prog.endswith('.real') and
1150 not prog.endswith('.elf') and
1151 prog.startswith(target)):
1152 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1153 os.path.join(srcpath, prog))
1154 return srcpath
1155
1156
Frank Henigman179ec7c2015-02-06 03:01:09 -05001157def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1158 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001159 # Disable ccache since we know it won't work outside of chroot.
Tobias Boschddd16492019-08-14 09:29:54 -07001160
Tobias Boschddd16492019-08-14 09:29:54 -07001161 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001162 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001163 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1164 # Can't update the wrapper in place to not affect the chroot,
1165 # but only the extracted toolchain.
1166 os.unlink(sysroot_wrapper)
1167 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001168 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001169
1170
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001171def _CreateMainLibDir(target, output_dir):
1172 """Create some lib dirs so that compiler can get the right Gcc paths"""
1173 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1174 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1175
1176
Mike Frysinger35247af2012-11-16 18:58:06 -05001177def _ProcessDistroCleanups(target, output_dir):
1178 """Clean up the tree and remove all distro-specific requirements
1179
1180 Args:
1181 target: The toolchain target name
1182 output_dir: The output directory to clean up
1183 """
1184 _ProcessBinutilsConfig(target, output_dir)
1185 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001186 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001187 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001188
1189 osutils.RmDir(os.path.join(output_dir, 'etc'))
1190
1191
1192def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1193 """Setup a tree from the packages for the specified target
1194
1195 This populates a path with all the files from toolchain packages so that
1196 a tarball can easily be generated from the result.
1197
1198 Args:
1199 target: The target to create a packagable root from
1200 output_dir: The output directory to place all the files
1201 ldpaths: A dict of static ldpath information
1202 root: The root path to pull all packages/files from
1203 """
1204 # Find all the files owned by the packages for this target.
1205 paths, elfs = _GetFilesForTarget(target, root=root)
1206
1207 # Link in all the package's files, any ELF dependencies, and wrap any
1208 # executable ELFs with helper scripts.
1209 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001210 """Move /usr/bin to /bin so people can just use that toplevel dir
1211
1212 Note we do not apply this to clang - there is correlation between clang's
1213 search path for libraries / inclusion and its installation path.
1214 """
1215 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1216 return path[4:]
1217 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001218 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1219 path_rewrite_func=MoveUsrBinToBin, root=root)
1220
1221 # The packages, when part of the normal distro, have helper scripts
1222 # that setup paths and such. Since we are making this standalone, we
1223 # need to preprocess all that ourselves.
1224 _ProcessDistroCleanups(target, output_dir)
1225
1226
1227def CreatePackages(targets_wanted, output_dir, root='/'):
1228 """Create redistributable cross-compiler packages for the specified targets
1229
1230 This creates toolchain packages that should be usable in conjunction with
1231 a downloaded sysroot (created elsewhere).
1232
1233 Tarballs (one per target) will be created in $PWD.
1234
1235 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001236 targets_wanted: The targets to package up.
1237 output_dir: The directory to put the packages in.
1238 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001239 """
Ralph Nathan03047282015-03-23 11:09:32 -07001240 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001241 osutils.SafeMakedirs(output_dir)
1242 ldpaths = lddtree.LoadLdpaths(root)
1243 targets = ExpandTargets(targets_wanted)
1244
Mike Frysinger221bd822017-09-29 02:51:47 -04001245 with osutils.TempDir(prefix='create-packages') as tempdir:
1246 logging.debug('Using tempdir: %s', tempdir)
1247
Mike Frysinger35247af2012-11-16 18:58:06 -05001248 # We have to split the root generation from the compression stages. This is
1249 # because we hardlink in all the files (to avoid overhead of reading/writing
1250 # the copies multiple times). But tar gets angry if a file's hardlink count
1251 # changes from when it starts reading a file to when it finishes.
1252 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1253 for target in targets:
1254 output_target_dir = os.path.join(tempdir, target)
1255 queue.put([target, output_target_dir, ldpaths, root])
1256
1257 # Build the tarball.
1258 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1259 for target in targets:
1260 tar_file = os.path.join(output_dir, target + '.tar.xz')
1261 queue.put([tar_file, os.path.join(tempdir, target)])
1262
1263
Mike Frysinger07534cf2017-09-12 17:40:21 -04001264def GetParser():
1265 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001266 parser = commandline.ArgumentParser(description=__doc__)
1267 parser.add_argument('-u', '--nousepkg',
1268 action='store_false', dest='usepkg', default=True,
1269 help='Use prebuilt packages if possible')
1270 parser.add_argument('-d', '--deleteold',
1271 action='store_true', dest='deleteold', default=False,
1272 help='Unmerge deprecated packages')
1273 parser.add_argument('-t', '--targets',
1274 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001275 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001276 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001277 "allowed. Defaults to 'sdk'.")
1278 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1279 help='Comma separated list of boards whose toolchains we '
1280 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001281 parser.add_argument('--hostonly',
1282 dest='hostonly', default=False, action='store_true',
1283 help='Only setup the host toolchain. '
1284 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001285 parser.add_argument('--show-board-cfg', '--show-cfg',
1286 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001287 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001288 parser.add_argument('--show-packages', default=None,
1289 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001290 parser.add_argument('--create-packages',
1291 action='store_true', default=False,
1292 help='Build redistributable packages')
1293 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1294 help='Output directory')
1295 parser.add_argument('--reconfig', default=False, action='store_true',
1296 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001297 parser.add_argument('--sysroot', type='path',
1298 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001299 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001300
Mike Frysinger07534cf2017-09-12 17:40:21 -04001301
1302def main(argv):
1303 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001304 options = parser.parse_args(argv)
1305 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001306
Mike Frysinger35247af2012-11-16 18:58:06 -05001307 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001308 conflicting_options = (
1309 options.cfg_name,
1310 options.show_packages,
1311 options.create_packages,
1312 )
1313 if sum(bool(x) for x in conflicting_options) > 1:
1314 parser.error('conflicting options: create-packages & show-packages & '
1315 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001316
Gilad Arnold8195b532015-04-07 10:56:30 +03001317 targets_wanted = set(options.targets.split(','))
1318 boards_wanted = (set(options.include_boards.split(','))
1319 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001320
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001321 if options.cfg_name:
1322 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001323 elif options.show_packages is not None:
1324 cros_build_lib.AssertInsideChroot()
1325 target = options.show_packages
1326 Crossdev.Load(False)
1327 for package in GetTargetPackages(target):
1328 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001329 elif options.create_packages:
1330 cros_build_lib.AssertInsideChroot()
1331 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001332 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001333 else:
1334 cros_build_lib.AssertInsideChroot()
1335 # This has to be always run as root.
1336 if os.geteuid() != 0:
1337 cros_build_lib.Die('this script must be run as root')
1338
1339 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001340 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001341 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001342 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001343 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001344 Crossdev.Save()
1345
1346 return 0