blob: 71523c3264f6bdcd14de93863f178bef63bb1bee [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
Mike Frysinger499ca392020-04-28 07:35:54 -040017import sys
Zdenek Behan508dcce2011-12-05 15:39:32 +010018
Aviv Keshetb7519e12016-10-04 00:50:00 -070019from chromite.lib import constants
Mike Frysinger506e75f2012-12-17 14:21:13 -050020from chromite.lib import commandline
Brian Harring503f3ab2012-03-09 21:39:41 -080021from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070022from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070023from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050024from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080025from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040026from chromite.utils import key_value_store
Mike Frysinger35247af2012-11-16 18:58:06 -050027
28# Needs to be after chromite imports.
Mike Frysingerabb7d812020-05-15 00:13:10 -040029import lddtree # pylint: disable=wrong-import-order
Zdenek Behan508dcce2011-12-05 15:39:32 +010030
Mike Frysinger31596002012-12-03 23:54:24 -050031if cros_build_lib.IsInsideChroot():
32 # Only import portage after we've checked that we're inside the chroot.
33 # Outside may not have portage, in which case the above may not happen.
34 # We'll check in main() if the operation needs portage.
Mike Frysinger27e21b72018-07-12 14:20:21 -040035 # pylint: disable=import-error
Mike Frysinger31596002012-12-03 23:54:24 -050036 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010037
38
Mike Frysinger499ca392020-04-28 07:35:54 -040039assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
40
41
Matt Tennantf1e30972012-03-02 16:30:07 -080042EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010043PACKAGE_STABLE = '[stable]'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010044
45CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070046ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010047STABLE_OVERLAY = '/usr/local/portage/stable'
48CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010049
50
Mike Frysinger66bfde52017-09-12 16:42:57 -040051# The exact list of host toolchain packages we care about. These are the
52# packages that bots/devs install only from binpkgs and rely on the SDK bot
53# (chromiumos-sdk) to validate+uprev.
54#
Mike Frysinger66bfde52017-09-12 16:42:57 -040055# We don't use crossdev to manage the host toolchain for us, especially since
56# we diverge significantly now (with llvm/clang/etc...), and we don't need or
57# want crossdev managing /etc/portage config files for the sdk
58HOST_PACKAGES = (
59 'dev-lang/go',
Yunlian Jiang2cb91dc2018-03-08 10:56:27 -080060 'dev-libs/elfutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040061 'sys-devel/binutils',
Mike Frysinger66bfde52017-09-12 16:42:57 -040062 'sys-devel/gcc',
63 'sys-devel/llvm',
64 'sys-kernel/linux-headers',
65 'sys-libs/glibc',
66 'sys-libs/libcxx',
67 'sys-libs/libcxxabi',
Manoj Guptade64cb22019-03-31 18:48:58 -070068 'sys-libs/llvm-libunwind',
Mike Frysinger66bfde52017-09-12 16:42:57 -040069)
70
Mike Frysinger785b0c32017-09-13 01:35:59 -040071# These packages are also installed into the host SDK. However, they require
72# the cross-compilers to be installed first (because they need them to actually
73# build), so we have to delay their installation.
74HOST_POST_CROSS_PACKAGES = (
Manoj Gupta65f88442018-04-12 22:42:19 -070075 'dev-lang/rust',
Bob Haarmane75b16b2020-09-29 20:48:53 +000076 'dev-lang/rust-bootstrap',
Mike Frysinger61a24392017-10-17 17:14:27 -040077 'virtual/target-sdk-post-cross',
Patrick Georgi043ce6e2019-02-20 22:27:09 +010078 'dev-embedded/coreboot-sdk',
Mike Frysinger785b0c32017-09-13 01:35:59 -040079)
80
81# New packages that we're in the process of adding to the SDK. Since the SDK
82# bot hasn't had a chance to run yet, there are no binary packages available,
83# so we have to list them here and wait. Once it completes, entries here can
84# be removed so they'll end up on bots & dev's systems.
85NEW_PACKAGES = (
Bob Haarmane75b16b2020-09-29 20:48:53 +000086 'dev-lang/rust-bootstrap',
Mike Frysinger785b0c32017-09-13 01:35:59 -040087)
88
Rahul Chaudhry4b803052015-05-13 15:25:56 -070089# Enable the Go compiler for these targets.
90TARGET_GO_ENABLED = (
91 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070092 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -070093 'armv7a-cros-linux-gnueabihf',
Rahul Chaudhry4d416582017-10-25 12:31:58 -070094 'aarch64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070095)
96CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
97
Manoj Gupta1b5642e2017-03-08 16:44:12 -080098# Enable llvm's compiler-rt for these targets.
99TARGET_COMPILER_RT_ENABLED = (
100 'armv7a-cros-linux-gnueabi',
Yunlian Jiang1b77ee42017-10-06 13:44:29 -0700101 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700102 'aarch64-cros-linux-gnu',
Manoj Gupta21f3a082018-03-06 21:25:39 -0800103 'armv7m-cros-eabi',
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800104)
105CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
106
Manoj Gupta946abb42017-04-12 14:27:19 -0700107TARGET_LLVM_PKGS_ENABLED = (
108 'armv7a-cros-linux-gnueabi',
Yunlian Jiang16c1a422017-10-04 10:33:22 -0700109 'armv7a-cros-linux-gnueabihf',
Manoj Gupta946abb42017-04-12 14:27:19 -0700110 'aarch64-cros-linux-gnu',
Manoj Gupta93713122020-10-29 17:52:16 -0700111 'i686-pc-linux-gnu',
Manoj Gupta946abb42017-04-12 14:27:19 -0700112 'x86_64-cros-linux-gnu',
113)
114
115LLVM_PKGS_TABLE = {
Yunlian Jiangbb2a3d32017-04-26 14:44:09 -0700116 'ex_libcxxabi' : ['--ex-pkg', 'sys-libs/libcxxabi'],
117 'ex_libcxx' : ['--ex-pkg', 'sys-libs/libcxx'],
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700118 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
Manoj Gupta946abb42017-04-12 14:27:19 -0700119}
120
David James66a09c42012-11-05 13:31:38 -0800121class Crossdev(object):
122 """Class for interacting with crossdev and caching its output."""
123
124 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
125 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800126 # Packages that needs separate handling, in addition to what we have from
127 # crossdev.
128 MANUAL_PKGS = {
George Burgess IVca1d7612020-10-01 00:38:32 -0700129 'rust': 'dev-lang',
Han Shene23782f2016-02-18 12:20:00 -0800130 'llvm': 'sys-devel',
Manoj Gupta20cfc6c2017-07-19 15:49:55 -0700131 'libcxxabi': 'sys-libs',
132 'libcxx': 'sys-libs',
Yunlian Jiangda3ce5f2018-04-25 14:10:01 -0700133 'elfutils': 'dev-libs',
Chirantan Ekbotee694cad2018-07-12 15:07:24 -0700134 'llvm-libunwind': 'sys-libs',
Han Shene23782f2016-02-18 12:20:00 -0800135 }
David James66a09c42012-11-05 13:31:38 -0800136
137 @classmethod
138 def Load(cls, reconfig):
Mike Frysinger3ed47722017-08-08 14:59:08 -0400139 """Load crossdev cache from disk.
140
141 We invalidate the cache when crossdev updates or this script changes.
142 """
David James90239b92012-11-05 15:31:34 -0800143 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
Mike Frysinger3ed47722017-08-08 14:59:08 -0400144 # If we run the compiled/cached .pyc file, we'll read/hash that when we
145 # really always want to track the source .py file.
146 script = os.path.abspath(__file__)
147 if script.endswith('.pyc'):
148 script = script[:-1]
Mike Frysingerb3202be2019-11-15 22:25:59 -0500149 setup_toolchains_hash = hashlib.md5(
150 osutils.ReadFile(script, mode='rb')).hexdigest()
Mike Frysinger3ed47722017-08-08 14:59:08 -0400151
152 cls._CACHE = {
153 'crossdev_version': crossdev_version,
154 'setup_toolchains_hash': setup_toolchains_hash,
155 }
156
157 logging.debug('cache: checking file: %s', cls._CACHE_FILE)
158 if reconfig:
159 logging.debug('cache: forcing regen due to reconfig')
160 return
161
162 try:
163 file_data = osutils.ReadFile(cls._CACHE_FILE)
164 except IOError as e:
165 if e.errno != errno.ENOENT:
166 logging.warning('cache: reading failed: %s', e)
167 osutils.SafeUnlink(cls._CACHE_FILE)
168 return
169
170 try:
171 data = json.loads(file_data)
172 except ValueError as e:
173 logging.warning('cache: ignoring invalid content: %s', e)
174 return
175
176 if crossdev_version != data.get('crossdev_version'):
177 logging.debug('cache: rebuilding after crossdev upgrade')
178 elif setup_toolchains_hash != data.get('setup_toolchains_hash'):
179 logging.debug('cache: rebuilding after cros_setup_toolchains change')
180 else:
181 logging.debug('cache: content is up-to-date!')
182 cls._CACHE = data
David James66a09c42012-11-05 13:31:38 -0800183
184 @classmethod
185 def Save(cls):
186 """Store crossdev cache on disk."""
187 # Save the cache from the successful run.
188 with open(cls._CACHE_FILE, 'w') as f:
189 json.dump(cls._CACHE, f)
190
191 @classmethod
192 def GetConfig(cls, target):
193 """Returns a map of crossdev provided variables about a tuple."""
194 CACHE_ATTR = '_target_tuple_map'
195
196 val = cls._CACHE.setdefault(CACHE_ATTR, {})
197 if not target in val:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400198 if target.startswith('host'):
Mike Frysinger66bfde52017-09-12 16:42:57 -0400199 conf = {
200 'crosspkgs': [],
201 'target': toolchain.GetHostTuple(),
202 }
Mike Frysinger785b0c32017-09-13 01:35:59 -0400203 if target == 'host':
204 packages_list = HOST_PACKAGES
205 else:
206 packages_list = HOST_POST_CROSS_PACKAGES
Mike Frysinger66bfde52017-09-12 16:42:57 -0400207 manual_pkgs = dict((pkg, cat) for cat, pkg in
Mike Frysinger785b0c32017-09-13 01:35:59 -0400208 [x.split('/') for x in packages_list])
Mike Frysinger66bfde52017-09-12 16:42:57 -0400209 else:
210 # Build the crossdev command.
211 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
212 if target in TARGET_COMPILER_RT_ENABLED:
213 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
214 if target in TARGET_GO_ENABLED:
215 cmd.extend(CROSSDEV_GO_ARGS)
216 if target in TARGET_LLVM_PKGS_ENABLED:
217 for pkg in LLVM_PKGS_TABLE:
218 cmd.extend(LLVM_PKGS_TABLE[pkg])
219 cmd.extend(['-t', target])
220 # Catch output of crossdev.
Mike Frysinger45602c72019-09-22 02:15:11 -0400221 out = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500222 cmd, print_cmd=False, stdout=True,
Mike Frysingerb3202be2019-11-15 22:25:59 -0500223 encoding='utf-8').stdout.splitlines()
Mike Frysinger66bfde52017-09-12 16:42:57 -0400224 # List of tuples split at the first '=', converted into dict.
225 conf = dict((k, cros_build_lib.ShellUnquote(v))
226 for k, v in (x.split('=', 1) for x in out))
227 conf['crosspkgs'] = conf['crosspkgs'].split()
Han Shene23782f2016-02-18 12:20:00 -0800228
Mike Frysinger66bfde52017-09-12 16:42:57 -0400229 manual_pkgs = cls.MANUAL_PKGS
230
231 for pkg, cat in manual_pkgs.items():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400232 conf[pkg + '_pn'] = pkg
233 conf[pkg + '_category'] = cat
234 if pkg not in conf['crosspkgs']:
235 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800236
237 val[target] = conf
238
David James66a09c42012-11-05 13:31:38 -0800239 return val[target]
240
241 @classmethod
242 def UpdateTargets(cls, targets, usepkg, config_only=False):
243 """Calls crossdev to initialize a cross target.
244
245 Args:
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000246 targets: The list of targets to initialize using crossdev.
Don Garrett25f309a2014-03-19 14:02:12 -0700247 usepkg: Copies the commandline opts.
248 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800249 """
250 configured_targets = cls._CACHE.setdefault('configured_targets', [])
251
252 cmdbase = ['crossdev', '--show-fail-log']
253 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
254 # Pick stable by default, and override as necessary.
255 cmdbase.extend(['-P', '--oneshot'])
256 if usepkg:
257 cmdbase.extend(['-P', '--getbinpkg',
258 '-P', '--usepkgonly',
259 '--without-headers'])
260
Christopher Wileyb22c0712015-06-02 10:37:03 -0700261 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800262 cmdbase.extend(['--overlays', overlays])
263 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
264
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000265 # Build target by the reversed alphabetical order to make sure
266 # armv7a-cros-linux-gnueabihf builds before armv7a-cros-linux-gnueabi
267 # because some dependency issue. This can be reverted once we
268 # migrated to armv7a-cros-linux-gnueabihf. crbug.com/711369
269 for target in sorted(targets, reverse=True):
270 if config_only and target in configured_targets:
271 continue
David James66a09c42012-11-05 13:31:38 -0800272
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000273 cmd = cmdbase + ['-t', target]
274
275 for pkg in GetTargetPackages(target):
276 if pkg == 'gdb':
277 # Gdb does not have selectable versions.
278 cmd.append('--ex-gdb')
279 elif pkg == 'ex_compiler-rt':
280 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
281 elif pkg == 'ex_go':
282 # Go does not have selectable versions.
283 cmd.extend(CROSSDEV_GO_ARGS)
284 elif pkg in LLVM_PKGS_TABLE:
285 cmd.extend(LLVM_PKGS_TABLE[pkg])
286 elif pkg in cls.MANUAL_PKGS:
287 pass
288 else:
289 # The first of the desired versions is the "primary" one.
290 version = GetDesiredPackageVersions(target, pkg)[0]
291 cmd.extend(['--%s' % pkg, version])
292
293 cmd.extend(targets[target]['crossdev'].split())
294 if config_only:
295 # In this case we want to just quietly reinit
296 cmd.append('--init-target')
Mike Frysinger0282d222019-12-17 17:15:48 -0500297 cros_build_lib.run(cmd, print_cmd=False, stdout=True)
David James66a09c42012-11-05 13:31:38 -0800298 else:
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000299 cros_build_lib.run(cmd)
David James66a09c42012-11-05 13:31:38 -0800300
Nicolas Boichat3fecf392019-11-25 02:58:28 +0000301 configured_targets.append(target)
David James66a09c42012-11-05 13:31:38 -0800302
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100303
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100304def GetTargetPackages(target):
305 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800306 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100307 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800308 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100309
310
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100311# Portage helper functions:
312def GetPortagePackage(target, package):
313 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800314 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100315 # Portage category:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400316 if target.startswith('host') or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100317 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100318 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100319 category = conf['category']
320 # Portage package:
321 pn = conf[package + '_pn']
322 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500323 assert category
324 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100325 return '%s/%s' % (category, pn)
326
327
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700328def PortageTrees(root):
329 """Return the portage trees for a given root."""
330 if root == '/':
331 return portage.db['/']
332 # The portage logic requires the path always end in a slash.
333 root = root.rstrip('/') + '/'
334 return portage.create_trees(target_root=root, config_root=root)[root]
335
336
337def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100338 """Extracts the list of current versions of a target, package pair.
339
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500340 Args:
341 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700342 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100343
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500344 Returns:
345 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100346 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347 versions = []
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700348 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100349 version = portage.versions.cpv_getversion(pkg)
350 versions.append(version)
351 return versions
352
353
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700354def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100355 """Extracts the current stable version for a given package.
356
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500357 Args:
358 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
359 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700360 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100361
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500362 Returns:
363 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100364 """
David James90239b92012-11-05 15:31:34 -0800365 pkgtype = 'vartree' if installed else 'porttree'
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700366 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800367 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100368
369
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700370def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100371 """Resolves keywords in a given version list for a particular package.
372
373 Resolving means replacing PACKAGE_STABLE with the actual number.
374
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500375 Args:
376 target: The target to operate on (e.g. i686-pc-linux-gnu)
377 package: The target/package to operate on (e.g. gcc)
378 versions: List of versions to resolve
379 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700380 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100381
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500382 Returns:
383 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100384 """
385 resolved = []
David James90239b92012-11-05 15:31:34 -0800386 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700387 if not installed:
388 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100389 for version in versions:
390 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700391 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400392 else:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100393 resolved.append(version)
394 return resolved
395
396
397def GetDesiredPackageVersions(target, package):
398 """Produces the list of desired versions for each target, package pair.
399
400 The first version in the list is implicitly treated as primary, ie.
401 the version that will be initialized by crossdev and selected.
402
403 If the version is PACKAGE_STABLE, it really means the current version which
404 is emerged by using the package atom with no particular version key.
405 Since crossdev unmasks all packages by default, this will actually
406 mean 'unstable' in most cases.
407
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500408 Args:
409 target: The target to operate on (e.g. i686-pc-linux-gnu)
410 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100411
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500412 Returns:
413 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100414 """
Mike Frysingerd23be272017-09-12 17:18:44 -0400415 if package in GetTargetPackages(target):
416 return [PACKAGE_STABLE]
417 else:
418 return []
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419
420
421def TargetIsInitialized(target):
422 """Verifies if the given list of targets has been correctly initialized.
423
424 This determines whether we have to call crossdev while emerging
425 toolchain packages or can do it using emerge. Emerge is naturally
426 preferred, because all packages can be updated in a single pass.
427
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500428 Args:
429 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100430
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500431 Returns:
432 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433 """
434 # Check if packages for the given target all have a proper version.
435 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100436 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800437 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100438 # Do we even want this package && is it initialized?
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400439 if not (GetStablePackageVersion(atom, True) and
440 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100441 return False
442 return True
443 except cros_build_lib.RunCommandError:
444 # Fails - The target has likely never been initialized before.
445 return False
446
447
448def RemovePackageMask(target):
449 """Removes a package.mask file for the given platform.
450
451 The pre-existing package.mask files can mess with the keywords.
452
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500453 Args:
454 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455 """
456 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700457 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100458
459
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700461def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500462 """Rebuild libtool as needed
463
464 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
465 gcc, libtool will break. We can't use binary packages either as those will
466 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700467
468 Args:
469 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500470 """
471 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700472 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500473 for line in f:
474 # Look for a line like:
475 # sys_lib_search_path_spec="..."
476 # It'll be a list of paths and gcc will be one of them.
477 if line.startswith('sys_lib_search_path_spec='):
478 line = line.rstrip()
479 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400480 root_path = os.path.join(root, path.lstrip(os.path.sep))
481 logging.debug('Libtool: checking %s', root_path)
482 if not os.path.exists(root_path):
483 logging.info('Rebuilding libtool after gcc upgrade')
484 logging.info(' %s', line)
485 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500486 needs_update = True
487 break
488
489 if needs_update:
490 break
491
492 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700493 cmd = [EMERGE_CMD, '--oneshot']
494 if root != '/':
495 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
496 cmd.append('sys-devel/libtool')
Mike Frysinger45602c72019-09-22 02:15:11 -0400497 cros_build_lib.run(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400498 else:
499 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500500
501
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700502def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100503 """Determines which packages need update/unmerge and defers to portage.
504
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500505 Args:
506 targets: The list of targets to update
507 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700508 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100509 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510 # For each target, we do two things. Figure out the list of updates,
511 # and figure out the appropriate keywords/masks. Crossdev will initialize
512 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400513 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800514 mergemap = {}
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000515 # Used to keep track of post-cross packages. These are allowed to have
516 # implicit dependencies on toolchain packages, and therefore need to
517 # be built last.
518 post_cross_pkgs = set()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100519 for target in targets:
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000520 is_post_cross_target = target.endswith('-post-cross')
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)
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000536 if is_post_cross_target:
537 post_cross_pkgs.add(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538
Mike Frysinger7f0e1982017-09-12 17:08:50 -0400539 packages = [pkg for pkg, vers in mergemap.items() if vers]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400541 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800542 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100543
Mike Frysinger3bba5032016-09-20 14:15:04 -0400544 logging.info('Updating packages:')
545 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100546
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100547 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100548 if usepkg:
549 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700550 if root != '/':
551 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100552
ChromeOS Developereb8a5c42021-01-12 00:05:02 +0000553 if usepkg:
554 # Since we are not building from source, we can handle
555 # all packages in one go.
556 cmd.extend(packages)
557 cros_build_lib.run(cmd)
558 else:
559 post_cross_cmd = cmd[:]
560 cmd.extend([pkg for pkg in packages if pkg not in post_cross_pkgs])
561 cros_build_lib.run(cmd)
562 post_cross_items = [pkg for pkg in packages if pkg in post_cross_pkgs]
563 if len(post_cross_items) > 0:
564 cros_build_lib.run(post_cross_cmd + post_cross_items)
David Jamesf8c672f2012-11-06 13:38:11 -0800565 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100566
567
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700568def CleanTargets(targets, root='/'):
569 """Unmerges old packages that are assumed unnecessary.
570
571 Args:
572 targets: The list of targets to clean up.
573 root: The install root in which we want packages cleaned up.
574 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100575 unmergemap = {}
576 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400577 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100578 for package in GetTargetPackages(target):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400579 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100580 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700581 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100582 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700583 # NOTE: This refers to installed packages (vartree) rather than the
584 # Portage version (porttree and/or bintree) when determining the current
585 # version. While this isn't the most accurate thing to do, it is probably
586 # a good simple compromise, which should have the desired result of
587 # uninstalling everything but the latest installed version. In
588 # particular, using the bintree (--usebinpkg) requires a non-trivial
589 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200590 desired_num = VersionListToNumeric(target, package, desired, True)
591 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400592 logging.warning('Error detecting stable version for %s, '
593 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200594 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595 unmergemap[pkg] = set(current).difference(desired_num)
596
597 # Cleaning doesn't care about consistency and rebuilding package.* files.
598 packages = []
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400599 for pkg, vers in unmergemap.items():
Zdenek Behan508dcce2011-12-05 15:39:32 +0100600 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
601
602 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400603 logging.info('Cleaning packages:')
604 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100605 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700606 if root != '/':
607 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100608 cmd.extend(packages)
Mike Frysinger45602c72019-09-22 02:15:11 -0400609 cros_build_lib.run(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100610 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400611 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100612
613
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200614def SelectActiveToolchains(targets, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100615 """Runs gcc-config and binutils-config to select the desired.
616
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500617 Args:
618 targets: The targets to select
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700619 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100620 """
621 for package in ['gcc', 'binutils']:
622 for target in targets:
Mike Frysinger785b0c32017-09-13 01:35:59 -0400623 # See if this package is part of this target.
624 if package not in GetTargetPackages(target):
625 logging.debug('%s: %s is not used', target, package)
626 continue
627
Zdenek Behan508dcce2011-12-05 15:39:32 +0100628 # Pick the first version in the numbered list as the selected one.
629 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700630 desired_num = VersionListToNumeric(target, package, desired, True,
631 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100632 desired = desired_num[0]
633 # *-config does not play revisions, strip them, keep just PV.
634 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
635
Mike Frysinger785b0c32017-09-13 01:35:59 -0400636 if target.startswith('host'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100637 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800638 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100639
640 # And finally, attach target to it.
641 desired = '%s-%s' % (target, desired)
642
David James7ec5efc2012-11-06 09:39:49 -0800643 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700644 if root != '/':
645 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800646 cmd = ['%s-config' % package, '-c', target]
Mike Frysinger45602c72019-09-22 02:15:11 -0400647 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500648 cmd, print_cmd=False, stdout=True, encoding='utf-8',
Mike Frysingerb3202be2019-11-15 22:25:59 -0500649 extra_env=extra_env)
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500650 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700651
652 # Do not reconfig when the current is live or nothing needs to be done.
653 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100654 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500655 cmd = [package + '-config', desired]
Mike Frysinger45602c72019-09-22 02:15:11 -0400656 cros_build_lib.run(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100657
658
Mike Frysinger35247af2012-11-16 18:58:06 -0500659def ExpandTargets(targets_wanted):
660 """Expand any possible toolchain aliases into full targets
661
662 This will expand 'all' and 'sdk' into the respective toolchain tuples.
663
664 Args:
665 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500666
Mike Frysinger35247af2012-11-16 18:58:06 -0500667 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300668 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500669 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500670 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700671 if targets_wanted == set(['boards']):
672 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300673 return {}
674
675 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500676 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300677 return all_targets
678 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500679 # Filter out all the non-sdk toolchains as we don't want to mess
680 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300681 return toolchain.FilterToolchains(all_targets, 'sdk', True)
682
683 # Verify user input.
684 nonexistent = targets_wanted.difference(all_targets)
685 if nonexistent:
Mike Frysingercd790662019-10-17 00:23:13 -0400686 raise ValueError('Invalid targets: %s' % (','.join(nonexistent),))
Gilad Arnold8195b532015-04-07 10:56:30 +0300687 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500688
689
David Jamesf8c672f2012-11-06 13:38:11 -0800690def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700691 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100692 """Performs all steps to create a synchronized toolchain enviroment.
693
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500694 Args:
695 usepkg: Use prebuilt packages
696 deleteold: Unmerge deprecated packages
697 hostonly: Only setup the host toolchain
698 reconfig: Reload crossdev config and reselect toolchains
699 targets_wanted: All the targets to update
700 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700701 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100702 """
David Jamesf8c672f2012-11-06 13:38:11 -0800703 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100704 if not hostonly:
705 # For hostonly, we can skip most of the below logic, much of which won't
706 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500707 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400708
Don Garrettc0c74002015-10-09 12:58:19 -0700709 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300710 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400711 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800712 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100713
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100714 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400715 for target in targets:
716 if TargetIsInitialized(target):
717 reconfig_targets[target] = targets[target]
718 else:
719 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100720 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400721 logging.info('The following targets need to be re-initialized:')
722 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800723 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200724 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800725 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100726
Mike Frysinger66814c32017-10-09 18:11:46 -0400727 # If we're building a subset of toolchains for a board, we might not have
728 # all the tuples that the packages expect. We don't define the "full" set
729 # of tuples currently other than "whatever the full sdk has normally".
730 if usepkg or set(('all', 'sdk')) & targets_wanted:
731 # Since we have cross-compilers now, we can update these packages.
732 targets['host-post-cross'] = {}
Mike Frysinger785b0c32017-09-13 01:35:59 -0400733
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100734 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400735 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100736
737 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700738 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
Adrian Ratiu78e534d2021-01-06 19:58:38 +0200739 SelectActiveToolchains(targets, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800740
741 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700742 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100743
Mike Frysingerc880a962013-11-08 13:59:06 -0500744 # Now that we've cleared out old versions, see if we need to rebuild
745 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700746 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500747
Zdenek Behan508dcce2011-12-05 15:39:32 +0100748
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700749def ShowConfig(name):
750 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500751
752 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700753 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500754 """
Don Garrettc0c74002015-10-09 12:58:19 -0700755 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500756 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400757 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400758 print(','.join(
Mike Frysinger818d9632019-08-24 14:43:05 -0400759 list(toolchain.FilterToolchains(toolchains, 'default', True)) +
760 list(toolchain.FilterToolchains(toolchains, 'default', False))))
Mike Frysinger35247af2012-11-16 18:58:06 -0500761
762
Mike Frysinger35247af2012-11-16 18:58:06 -0500763def GeneratePathWrapper(root, wrappath, path):
764 """Generate a shell script to execute another shell script
765
766 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
767 argv[0] won't be pointing to the correct path, generate a shell script that
768 just executes another program with its full path.
769
770 Args:
771 root: The root tree to generate scripts inside of
772 wrappath: The full path (inside |root|) to create the wrapper
773 path: The target program which this wrapper will execute
774 """
775 replacements = {
776 'path': path,
777 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
778 }
Takuto Ikuta58403972018-08-16 18:52:51 +0900779
780 # Do not use exec here, because exec invokes script with absolute path in
781 # argv0. Keeping relativeness allows us to remove abs path from compile result
782 # and leads directory independent build cache sharing in some distributed
783 # build system.
Mike Frysinger35247af2012-11-16 18:58:06 -0500784 wrapper = """#!/bin/sh
Takuto Ikuta58403972018-08-16 18:52:51 +0900785basedir=$(dirname "$0")
786"${basedir}/%(relroot)s%(path)s" "$@"
787exit "$?"
Mike Frysinger35247af2012-11-16 18:58:06 -0500788""" % replacements
789 root_wrapper = root + wrappath
790 if os.path.islink(root_wrapper):
791 os.unlink(root_wrapper)
792 else:
793 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
794 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400795 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500796
797
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700798def FixClangXXWrapper(root, path):
799 """Fix wrapper shell scripts and symlinks for invoking clang++
800
801 In a typical installation, clang++ symlinks to clang, which symlinks to the
802 elf executable. The executable distinguishes between clang and clang++ based
803 on argv[0].
804
805 When invoked through the LdsoWrapper, argv[0] always contains the path to the
806 executable elf file, making clang/clang++ invocations indistinguishable.
807
808 This function detects if the elf executable being wrapped is clang-X.Y, and
809 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
810
811 The calling sequence now becomes:
812 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
813 the Ldsowrapper).
814 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
815 to the original clang-3.9 elf.
816 -) The difference this time is that inside the elf file execution, $0 is
817 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
818
Manoj Guptaae268142018-04-27 23:28:36 -0700819 Update: Starting since clang 7, the clang and clang++ are symlinks to
820 clang-7 binary, not clang-7.0. The pattern match is extended to handle
821 both clang-7 and clang-7.0 cases for now. (https://crbug.com/837889)
822
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700823 Args:
824 root: The root tree to generate scripts / symlinks inside of
825 path: The target elf for which LdsoWrapper was created
826 """
Manoj Guptaae268142018-04-27 23:28:36 -0700827 if re.match(r'/usr/bin/clang-\d+(\.\d+)*$', path):
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700828 logging.info('fixing clang++ invocation for %s', path)
829 clangdir = os.path.dirname(root + path)
830 clang = os.path.basename(path)
831 clangxx = clang.replace('clang', 'clang++')
832
833 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
834 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
835
836 # Create a hardlink clang++-X.Y pointing to clang-X.Y
837 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
838
839 # Adjust the clang++ symlink to point to clang++-X.Y
840 os.unlink(os.path.join(clangdir, 'clang++'))
841 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
842
843
Mike Frysinger35247af2012-11-16 18:58:06 -0500844def FileIsCrosSdkElf(elf):
845 """Determine if |elf| is an ELF that we execute in the cros_sdk
846
847 We don't need this to be perfect, just quick. It makes sure the ELF
848 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
849
850 Args:
851 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500852
Mike Frysinger35247af2012-11-16 18:58:06 -0500853 Returns:
854 True if we think |elf| is a native ELF
855 """
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500856 with open(elf, 'rb') as f:
Mike Frysinger35247af2012-11-16 18:58:06 -0500857 data = f.read(20)
858 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
Mike Frysinger4a12bf12019-12-04 04:20:10 -0500859 return (data[0:4] == b'\x7fELF' and
Mike Frysingerdd34dfe2019-12-10 16:25:47 -0500860 data[4:5] == b'\x02' and
861 data[5:6] == b'\x01' and
862 data[18:19] == b'\x3e')
Mike Frysinger35247af2012-11-16 18:58:06 -0500863
864
865def IsPathPackagable(ptype, path):
866 """Should the specified file be included in a toolchain package?
867
868 We only need to handle files as we'll create dirs as we need them.
869
870 Further, trim files that won't be useful:
871 - non-english translations (.mo) since it'd require env vars
872 - debug files since these are for the host compiler itself
873 - info/man pages as they're big, and docs are online, and the
874 native docs should work fine for the most part (`man gcc`)
875
876 Args:
877 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
878 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500879
Mike Frysinger35247af2012-11-16 18:58:06 -0500880 Returns:
881 True if we want to include this path in the package
882 """
883 return not (ptype in ('dir',) or
884 path.startswith('/usr/lib/debug/') or
885 os.path.splitext(path)[1] == '.mo' or
886 ('/man/' in path or '/info/' in path))
887
888
889def ReadlinkRoot(path, root):
890 """Like os.readlink(), but relative to a |root|
891
892 Args:
893 path: The symlink to read
894 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500895
Mike Frysinger35247af2012-11-16 18:58:06 -0500896 Returns:
897 A fully resolved symlink path
898 """
899 while os.path.islink(root + path):
900 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
901 return path
902
903
904def _GetFilesForTarget(target, root='/'):
905 """Locate all the files to package for |target|
906
907 This does not cover ELF dependencies.
908
909 Args:
910 target: The toolchain target name
911 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500912
Mike Frysinger35247af2012-11-16 18:58:06 -0500913 Returns:
914 A tuple of a set of all packable paths, and a set of all paths which
915 are also native ELFs
916 """
917 paths = set()
918 elfs = set()
919
920 # Find all the files owned by the packages for this target.
921 for pkg in GetTargetPackages(target):
Mike Frysinger35247af2012-11-16 18:58:06 -0500922
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700923 # Skip Go compiler from redistributable packages.
924 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
925 # into it. Due to this, the toolchain cannot be unpacked anywhere
926 # else and be readily useful. To enable packaging Go, we need to:
927 # -) Tweak the wrappers/environment to override GOROOT
928 # automatically based on the unpack location.
929 # -) Make sure the ELF dependency checking and wrapping logic
930 # below skips the Go toolchain executables and libraries.
931 # -) Make sure the packaging process maintains the relative
932 # timestamps of precompiled standard library packages.
933 # (see dev-lang/go ebuild for details).
934 if pkg == 'ex_go':
935 continue
936
Yunlian Jiang36f35242018-04-27 10:18:40 -0700937 # Use armv7a-cros-linux-gnueabi/compiler-rt for
938 # armv7a-cros-linux-gnueabihf/compiler-rt.
939 # Currently the armv7a-cros-linux-gnueabi is actually
940 # the same as armv7a-cros-linux-gnueabihf with different names.
941 # Because of that, for compiler-rt, it generates the same binary in
942 # the same location. To avoid the installation conflict, we do not
943 # install anything for 'armv7a-cros-linux-gnueabihf'. This would cause
944 # problem if other people try to use standalone armv7a-cros-linux-gnueabihf
945 # toolchain.
946 if 'compiler-rt' in pkg and 'armv7a-cros-linux-gnueabi' in target:
947 atom = GetPortagePackage(target, pkg)
948 cat, pn = atom.split('/')
949 ver = GetInstalledPackageVersions(atom, root=root)[0]
Yunlian Jiang36f35242018-04-27 10:18:40 -0700950 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
951 settings=portage.settings)
952 contents = dblink.getcontents()
953 if not contents:
954 if 'hf' in target:
955 new_target = 'armv7a-cros-linux-gnueabi'
956 else:
957 new_target = 'armv7a-cros-linux-gnueabihf'
958 atom = GetPortagePackage(new_target, pkg)
959 else:
960 atom = GetPortagePackage(target, pkg)
961
Mike Frysinger35247af2012-11-16 18:58:06 -0500962 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700963 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700964 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500965
Mike Frysinger35247af2012-11-16 18:58:06 -0500966 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
967 settings=portage.settings)
968 contents = dblink.getcontents()
969 for obj in contents:
970 ptype = contents[obj][0]
971 if not IsPathPackagable(ptype, obj):
972 continue
973
974 if ptype == 'obj':
975 # For native ELFs, we need to pull in their dependencies too.
976 if FileIsCrosSdkElf(obj):
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500977 logging.debug('Adding ELF %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500978 elfs.add(obj)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -0500979 logging.debug('Adding path %s', obj)
Mike Frysinger35247af2012-11-16 18:58:06 -0500980 paths.add(obj)
981
982 return paths, elfs
983
984
985def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500986 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500987 """Link in all packable files and their runtime dependencies
988
989 This also wraps up executable ELFs with helper scripts.
990
991 Args:
992 output_dir: The output directory to store files
993 paths: All the files to include
994 elfs: All the files which are ELFs (a subset of |paths|)
995 ldpaths: A dict of static ldpath information
996 path_rewrite_func: User callback to rewrite paths in output_dir
997 root: The root path to pull all packages/files from
998 """
999 # Link in all the files.
Mike Frysinger221bd822017-09-29 02:51:47 -04001000 sym_paths = {}
Mike Frysinger35247af2012-11-16 18:58:06 -05001001 for path in paths:
1002 new_path = path_rewrite_func(path)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001003 logging.debug('Transformed %s to %s', path, new_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001004 dst = output_dir + new_path
1005 osutils.SafeMakedirs(os.path.dirname(dst))
1006
1007 # Is this a symlink which we have to rewrite or wrap?
1008 # Delay wrap check until after we have created all paths.
1009 src = root + path
1010 if os.path.islink(src):
1011 tgt = os.readlink(src)
1012 if os.path.sep in tgt:
Mike Frysinger221bd822017-09-29 02:51:47 -04001013 sym_paths[lddtree.normpath(ReadlinkRoot(src, root))] = new_path
Mike Frysinger35247af2012-11-16 18:58:06 -05001014
1015 # Rewrite absolute links to relative and then generate the symlink
1016 # ourselves. All other symlinks can be hardlinked below.
1017 if tgt[0] == '/':
1018 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
1019 os.symlink(tgt, dst)
1020 continue
1021
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001022 logging.debug('Linking path %s -> %s', src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001023 os.link(src, dst)
1024
Mike Frysinger35247af2012-11-16 18:58:06 -05001025 # Locate all the dependencies for all the ELFs. Stick them all in the
1026 # top level "lib" dir to make the wrapper simpler. This exact path does
1027 # not matter since we execute ldso directly, and we tell the ldso the
1028 # exact path to search for its libraries.
1029 libdir = os.path.join(output_dir, 'lib')
1030 osutils.SafeMakedirs(libdir)
1031 donelibs = set()
Mike Frysinger9fe02342019-12-12 17:52:53 -05001032 basenamelibs = set()
Mike Frysinger4331fc62017-09-28 14:03:25 -04001033 glibc_re = re.compile(r'/lib(c|pthread)-[0-9.]+\.so$')
Mike Frysinger35247af2012-11-16 18:58:06 -05001034 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -04001035 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001036 logging.debug('Parsed elf %s data: %s', elf, e)
Mike Frysinger35247af2012-11-16 18:58:06 -05001037 interp = e['interp']
Yunlian Jiang196e8f42017-09-20 12:29:47 -07001038 # Do not create wrapper for libc. crbug.com/766827
1039 if interp and not glibc_re.search(elf):
Mike Frysinger35247af2012-11-16 18:58:06 -05001040 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -04001041 interp = os.path.join('/lib', os.path.basename(interp))
1042 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
1043 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -07001044 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -05001045
Mike Frysinger221bd822017-09-29 02:51:47 -04001046 # Wrap any symlinks to the wrapper.
1047 if elf in sym_paths:
1048 link = sym_paths[elf]
1049 GeneratePathWrapper(output_dir, link, elf)
1050
Mike Frysinger9fe02342019-12-12 17:52:53 -05001051 # TODO(crbug.com/917193): Drop this hack once libopcodes linkage is fixed.
1052 if os.path.basename(elf).startswith('libopcodes-'):
1053 continue
Mike Frysinger35247af2012-11-16 18:58:06 -05001054
Mike Frysinger9fe02342019-12-12 17:52:53 -05001055 for lib, lib_data in e['libs'].items():
Mike Frysinger35247af2012-11-16 18:58:06 -05001056 src = path = lib_data['path']
1057 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -07001058 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001059 continue
Mike Frysinger9fe02342019-12-12 17:52:53 -05001060
1061 # No need to try and copy the same source lib multiple times.
1062 if path in donelibs:
1063 continue
1064 donelibs.add(path)
1065
1066 # Die if we try to normalize different source libs with the same basename.
1067 if lib in basenamelibs:
1068 logging.error('Multiple sources detected for %s:\n new: %s\n old: %s',
1069 os.path.join('/lib', lib), path,
1070 ' '.join(x for x in donelibs
1071 if x != path and os.path.basename(x) == lib))
1072 # TODO(crbug.com/917193): Make this fatal.
1073 # cros_build_lib.Die('Unable to resolve lib conflicts')
1074 continue
1075 basenamelibs.add(lib)
Mike Frysinger35247af2012-11-16 18:58:06 -05001076
1077 # Needed libs are the SONAME, but that is usually a symlink, not a
1078 # real file. So link in the target rather than the symlink itself.
1079 # We have to walk all the possible symlinks (SONAME could point to a
1080 # symlink which points to a symlink), and we have to handle absolute
1081 # ourselves (since we have a "root" argument).
1082 dst = os.path.join(libdir, os.path.basename(path))
1083 src = ReadlinkRoot(src, root)
1084
Mike Frysinger60a2f6c2019-12-04 04:18:58 -05001085 logging.debug('Linking lib %s -> %s', root + src, dst)
Mike Frysinger35247af2012-11-16 18:58:06 -05001086 os.link(root + src, dst)
1087
1088
1089def _EnvdGetVar(envd, var):
1090 """Given a Gentoo env.d file, extract a var from it
1091
1092 Args:
1093 envd: The env.d file to load (may be a glob path)
1094 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -05001095
Mike Frysinger35247af2012-11-16 18:58:06 -05001096 Returns:
1097 The value of |var|
1098 """
1099 envds = glob.glob(envd)
1100 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
1101 envd = envds[0]
Mike Frysingere652ba12019-09-08 00:57:43 -04001102 return key_value_store.LoadFile(envd)[var]
Mike Frysinger35247af2012-11-16 18:58:06 -05001103
1104
1105def _ProcessBinutilsConfig(target, output_dir):
1106 """Do what binutils-config would have done"""
1107 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001108
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001109 # Locate the bin dir holding the linker and perform some sanity checks
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001110 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1111 target, 'binutils-bin')
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001112 globpath = os.path.join(binutils_bin_path, '*')
Mike Frysinger35247af2012-11-16 18:58:06 -05001113 srcpath = glob.glob(globpath)
Adrian Ratiu78e534d2021-01-06 19:58:38 +02001114 assert len(srcpath) == 1, ('%s: matched more than one path. Is Gold enabled?'
1115 % globpath)
1116 srcpath = srcpath[0]
1117 ld_path = os.path.join(srcpath, 'ld')
1118 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1119 ld_path = os.path.join(srcpath, 'ld.bfd')
1120 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001121
Mike Frysinger78b7a812014-11-26 19:45:23 -05001122 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001123 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1124 for prog in os.listdir(output_dir + srcpath):
1125 # Skip binaries already wrapped.
1126 if not prog.endswith('.real'):
1127 GeneratePathWrapper(output_dir, binpath + prog,
1128 os.path.join(srcpath, prog))
1129 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1130 os.path.join(srcpath, prog))
1131
David James27ac4ae2012-12-03 23:16:15 -08001132 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001133 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
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.
Tobias Boschddd16492019-08-14 09:29:54 -07001157
Tobias Boschddd16492019-08-14 09:29:54 -07001158 # Use the version of the wrapper that does not use ccache.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001159 for sysroot_wrapper in glob.glob(os.path.join(
Tobias Boschddd16492019-08-14 09:29:54 -07001160 output_dir + srcpath, 'sysroot_wrapper*.ccache')):
1161 # Can't update the wrapper in place to not affect the chroot,
1162 # but only the extracted toolchain.
1163 os.unlink(sysroot_wrapper)
1164 shutil.copy(sysroot_wrapper[:-6] + 'noccache', sysroot_wrapper)
Tobias Bosch7bc907a2019-10-09 11:52:40 -07001165 shutil.copy(sysroot_wrapper[:-6] + 'noccache.elf', sysroot_wrapper + '.elf')
Mike Frysinger35247af2012-11-16 18:58:06 -05001166
1167
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001168def _CreateMainLibDir(target, output_dir):
1169 """Create some lib dirs so that compiler can get the right Gcc paths"""
1170 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'lib'))
1171 osutils.SafeMakedirs(os.path.join(output_dir, 'usr', target, 'usr/lib'))
1172
1173
Mike Frysinger35247af2012-11-16 18:58:06 -05001174def _ProcessDistroCleanups(target, output_dir):
1175 """Clean up the tree and remove all distro-specific requirements
1176
1177 Args:
1178 target: The toolchain target name
1179 output_dir: The output directory to clean up
1180 """
1181 _ProcessBinutilsConfig(target, output_dir)
1182 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001183 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Yunlian Jiang5ad6b512017-09-20 09:27:45 -07001184 _CreateMainLibDir(target, output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001185
1186 osutils.RmDir(os.path.join(output_dir, 'etc'))
1187
1188
1189def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1190 """Setup a tree from the packages for the specified target
1191
1192 This populates a path with all the files from toolchain packages so that
1193 a tarball can easily be generated from the result.
1194
1195 Args:
1196 target: The target to create a packagable root from
1197 output_dir: The output directory to place all the files
1198 ldpaths: A dict of static ldpath information
1199 root: The root path to pull all packages/files from
1200 """
1201 # Find all the files owned by the packages for this target.
1202 paths, elfs = _GetFilesForTarget(target, root=root)
1203
1204 # Link in all the package's files, any ELF dependencies, and wrap any
1205 # executable ELFs with helper scripts.
1206 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001207 """Move /usr/bin to /bin so people can just use that toplevel dir
1208
George Burgess IVca1d7612020-10-01 00:38:32 -07001209 Note we do not apply this to clang or rust - there is correlation between
1210 clang's search path for libraries / inclusion and its installation path.
Han Shen699ea192016-03-02 10:42:47 -08001211 """
George Burgess IVca1d7612020-10-01 00:38:32 -07001212 if (path.startswith('/usr/bin/') and
1213 not any(x in path for x in ('clang', 'rust', 'cargo'))):
Han Shen699ea192016-03-02 10:42:47 -08001214 return path[4:]
1215 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001216 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1217 path_rewrite_func=MoveUsrBinToBin, root=root)
1218
1219 # The packages, when part of the normal distro, have helper scripts
1220 # that setup paths and such. Since we are making this standalone, we
1221 # need to preprocess all that ourselves.
1222 _ProcessDistroCleanups(target, output_dir)
1223
1224
1225def CreatePackages(targets_wanted, output_dir, root='/'):
1226 """Create redistributable cross-compiler packages for the specified targets
1227
1228 This creates toolchain packages that should be usable in conjunction with
1229 a downloaded sysroot (created elsewhere).
1230
1231 Tarballs (one per target) will be created in $PWD.
1232
1233 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001234 targets_wanted: The targets to package up.
1235 output_dir: The directory to put the packages in.
1236 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001237 """
Ralph Nathan03047282015-03-23 11:09:32 -07001238 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001239 osutils.SafeMakedirs(output_dir)
1240 ldpaths = lddtree.LoadLdpaths(root)
1241 targets = ExpandTargets(targets_wanted)
1242
Mike Frysinger221bd822017-09-29 02:51:47 -04001243 with osutils.TempDir(prefix='create-packages') as tempdir:
1244 logging.debug('Using tempdir: %s', tempdir)
1245
Mike Frysinger35247af2012-11-16 18:58:06 -05001246 # We have to split the root generation from the compression stages. This is
1247 # because we hardlink in all the files (to avoid overhead of reading/writing
1248 # the copies multiple times). But tar gets angry if a file's hardlink count
1249 # changes from when it starts reading a file to when it finishes.
1250 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1251 for target in targets:
1252 output_target_dir = os.path.join(tempdir, target)
1253 queue.put([target, output_target_dir, ldpaths, root])
1254
1255 # Build the tarball.
1256 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1257 for target in targets:
1258 tar_file = os.path.join(output_dir, target + '.tar.xz')
1259 queue.put([tar_file, os.path.join(tempdir, target)])
1260
1261
Mike Frysinger07534cf2017-09-12 17:40:21 -04001262def GetParser():
1263 """Return a command line parser."""
Mike Frysinger0c808452014-11-06 17:30:23 -05001264 parser = commandline.ArgumentParser(description=__doc__)
1265 parser.add_argument('-u', '--nousepkg',
1266 action='store_false', dest='usepkg', default=True,
1267 help='Use prebuilt packages if possible')
1268 parser.add_argument('-d', '--deleteold',
1269 action='store_true', dest='deleteold', default=False,
1270 help='Unmerge deprecated packages')
1271 parser.add_argument('-t', '--targets',
1272 dest='targets', default='sdk',
Mike Frysinger80de5012019-08-01 14:10:53 -04001273 help='Comma separated list of tuples. Special keywords '
Don Garrettc0c74002015-10-09 12:58:19 -07001274 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001275 "allowed. Defaults to 'sdk'.")
1276 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1277 help='Comma separated list of boards whose toolchains we '
1278 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001279 parser.add_argument('--hostonly',
1280 dest='hostonly', default=False, action='store_true',
1281 help='Only setup the host toolchain. '
1282 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001283 parser.add_argument('--show-board-cfg', '--show-cfg',
1284 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001285 help='Board to list toolchains tuples for')
Mike Frysinger91f52882017-09-13 00:16:58 -04001286 parser.add_argument('--show-packages', default=None,
1287 help='List all packages the specified target uses')
Mike Frysinger0c808452014-11-06 17:30:23 -05001288 parser.add_argument('--create-packages',
1289 action='store_true', default=False,
1290 help='Build redistributable packages')
1291 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1292 help='Output directory')
1293 parser.add_argument('--reconfig', default=False, action='store_true',
1294 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001295 parser.add_argument('--sysroot', type='path',
1296 help='The sysroot in which to install the toolchains')
Mike Frysinger07534cf2017-09-12 17:40:21 -04001297 return parser
Zdenek Behan508dcce2011-12-05 15:39:32 +01001298
Mike Frysinger07534cf2017-09-12 17:40:21 -04001299
1300def main(argv):
1301 parser = GetParser()
Mike Frysinger0c808452014-11-06 17:30:23 -05001302 options = parser.parse_args(argv)
1303 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001304
Mike Frysinger35247af2012-11-16 18:58:06 -05001305 # Figure out what we're supposed to do and reject conflicting options.
Mike Frysinger91f52882017-09-13 00:16:58 -04001306 conflicting_options = (
1307 options.cfg_name,
1308 options.show_packages,
1309 options.create_packages,
1310 )
1311 if sum(bool(x) for x in conflicting_options) > 1:
1312 parser.error('conflicting options: create-packages & show-packages & '
1313 'show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001314
Gilad Arnold8195b532015-04-07 10:56:30 +03001315 targets_wanted = set(options.targets.split(','))
1316 boards_wanted = (set(options.include_boards.split(','))
1317 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001318
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001319 if options.cfg_name:
1320 ShowConfig(options.cfg_name)
Mike Frysinger91f52882017-09-13 00:16:58 -04001321 elif options.show_packages is not None:
1322 cros_build_lib.AssertInsideChroot()
1323 target = options.show_packages
1324 Crossdev.Load(False)
1325 for package in GetTargetPackages(target):
1326 print(GetPortagePackage(target, package))
Mike Frysinger35247af2012-11-16 18:58:06 -05001327 elif options.create_packages:
1328 cros_build_lib.AssertInsideChroot()
1329 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001330 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001331 else:
1332 cros_build_lib.AssertInsideChroot()
1333 # This has to be always run as root.
1334 if os.geteuid() != 0:
1335 cros_build_lib.Die('this script must be run as root')
1336
1337 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001338 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001339 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001340 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001341 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001342 Crossdev.Save()
1343
1344 return 0