blob: 24f8a97cf87d81e3868a7da92b99a631928ce45d [file] [log] [blame]
Zdenek Behan508dcce2011-12-05 15:39:32 +01001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Mike Frysinger750c5f52014-09-16 16:16:57 -04005"""This script manages the installed toolchains in the chroot."""
Zdenek Behan508dcce2011-12-05 15:39:32 +01006
Mike Frysinger383367e2014-09-16 15:06:17 -04007from __future__ import print_function
8
Zdenek Behan508dcce2011-12-05 15:39:32 +01009import copy
Mike Frysinger35247af2012-11-16 18:58:06 -050010import glob
Mike Frysinger7ccee992012-06-01 21:27:59 -040011import json
Zdenek Behan508dcce2011-12-05 15:39:32 +010012import os
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -070013import re
Zdenek Behan508dcce2011-12-05 15:39:32 +010014
Aviv Keshetb7519e12016-10-04 00:50:00 -070015from chromite.lib import constants
Mike Frysinger506e75f2012-12-17 14:21:13 -050016from chromite.lib import commandline
Brian Harring503f3ab2012-03-09 21:39:41 -080017from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070018from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070019from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050020from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080021from chromite.lib import toolchain
Mike Frysinger35247af2012-11-16 18:58:06 -050022
23# Needs to be after chromite imports.
24import lddtree
Zdenek Behan508dcce2011-12-05 15:39:32 +010025
Mike Frysinger31596002012-12-03 23:54:24 -050026if cros_build_lib.IsInsideChroot():
27 # Only import portage after we've checked that we're inside the chroot.
28 # Outside may not have portage, in which case the above may not happen.
29 # We'll check in main() if the operation needs portage.
Don Garrett25f309a2014-03-19 14:02:12 -070030 # pylint: disable=F0401
Mike Frysinger31596002012-12-03 23:54:24 -050031 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010032
33
Matt Tennantf1e30972012-03-02 16:30:07 -080034EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010035PACKAGE_STABLE = '[stable]'
36PACKAGE_NONE = '[none]'
37SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010038
39CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
Christopher Wileyb22c0712015-06-02 10:37:03 -070040ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010041STABLE_OVERLAY = '/usr/local/portage/stable'
42CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010043
44
45# TODO: The versions are stored here very much like in setup_board.
46# The goal for future is to differentiate these using a config file.
47# This is done essentially by messing with GetDesiredPackageVersions()
48DEFAULT_VERSION = PACKAGE_STABLE
49DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010050}
51TARGET_VERSION_MAP = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050052 'host' : {
53 'gdb' : PACKAGE_NONE,
Rahul Chaudhry4b803052015-05-13 15:25:56 -070054 'ex_go' : PACKAGE_NONE,
Manoj Gupta1b5642e2017-03-08 16:44:12 -080055 'ex_compiler-rt': PACKAGE_NONE,
Manoj Gupta946abb42017-04-12 14:27:19 -070056 'ex_llvm-libunwind': PACKAGE_NONE,
Mike Frysingerd6e2df02014-11-26 02:55:04 -050057 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010058}
Rahul Chaudhry4b803052015-05-13 15:25:56 -070059
60# Enable the Go compiler for these targets.
61TARGET_GO_ENABLED = (
62 'x86_64-cros-linux-gnu',
Rahul Chaudhry4b803052015-05-13 15:25:56 -070063 'armv7a-cros-linux-gnueabi',
64)
65CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
66
Manoj Gupta1b5642e2017-03-08 16:44:12 -080067# Enable llvm's compiler-rt for these targets.
68TARGET_COMPILER_RT_ENABLED = (
69 'armv7a-cros-linux-gnueabi',
Manoj Gupta946abb42017-04-12 14:27:19 -070070 'aarch64-cros-linux-gnu',
Manoj Gupta1b5642e2017-03-08 16:44:12 -080071)
72CROSSDEV_COMPILER_RT_ARGS = ['--ex-pkg', 'sys-libs/compiler-rt']
73
Manoj Gupta946abb42017-04-12 14:27:19 -070074TARGET_LLVM_PKGS_ENABLED = (
75 'armv7a-cros-linux-gnueabi',
76 'aarch64-cros-linux-gnu',
77 'x86_64-cros-linux-gnu',
78)
79
80LLVM_PKGS_TABLE = {
81 'ex_llvm-libunwind' : ['--ex-pkg', 'sys-libs/llvm-libunwind'],
82}
83
Zdenek Behan508dcce2011-12-05 15:39:32 +010084# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
85CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050086 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -040087 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -080088 'armv7a-cros-linux-gnueabi': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -050089 'i686-pc-linux-gnu' : '-gold',
90 'x86_64-cros-linux-gnu' : '-gold',
91 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010092}
Zdenek Behan508dcce2011-12-05 15:39:32 +010093# Global per-run cache that will be filled ondemand in by GetPackageMap()
94# function as needed.
95target_version_map = {
96}
97
98
David James66a09c42012-11-05 13:31:38 -080099class Crossdev(object):
100 """Class for interacting with crossdev and caching its output."""
101
102 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
103 _CACHE = {}
Han Shene23782f2016-02-18 12:20:00 -0800104 # Packages that needs separate handling, in addition to what we have from
105 # crossdev.
106 MANUAL_PKGS = {
107 'llvm': 'sys-devel',
108 }
David James66a09c42012-11-05 13:31:38 -0800109
110 @classmethod
111 def Load(cls, reconfig):
112 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -0800113 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
114 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -0800115 if os.path.exists(cls._CACHE_FILE) and not reconfig:
116 with open(cls._CACHE_FILE) as f:
117 data = json.load(f)
David James90239b92012-11-05 15:31:34 -0800118 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -0800119 cls._CACHE = data
120
121 @classmethod
122 def Save(cls):
123 """Store crossdev cache on disk."""
124 # Save the cache from the successful run.
125 with open(cls._CACHE_FILE, 'w') as f:
126 json.dump(cls._CACHE, f)
127
128 @classmethod
129 def GetConfig(cls, target):
130 """Returns a map of crossdev provided variables about a tuple."""
131 CACHE_ATTR = '_target_tuple_map'
132
133 val = cls._CACHE.setdefault(CACHE_ATTR, {})
134 if not target in val:
135 # Find out the crossdev tuple.
136 target_tuple = target
137 if target == 'host':
David James27ac4ae2012-12-03 23:16:15 -0800138 target_tuple = toolchain.GetHostTuple()
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700139 # Build the crossdev command.
140 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
141 if target in TARGET_GO_ENABLED:
142 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800143 if target in TARGET_COMPILER_RT_ENABLED:
144 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700145 if target in TARGET_LLVM_PKGS_ENABLED:
146 for pkg in LLVM_PKGS_TABLE:
147 cmd.extend(LLVM_PKGS_TABLE[pkg])
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700148 cmd.extend(['-t', target_tuple])
David James66a09c42012-11-05 13:31:38 -0800149 # Catch output of crossdev.
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700150 out = cros_build_lib.RunCommand(cmd, print_cmd=False,
151 redirect_stdout=True).output.splitlines()
David James66a09c42012-11-05 13:31:38 -0800152 # List of tuples split at the first '=', converted into dict.
Han Shene23782f2016-02-18 12:20:00 -0800153 conf = dict((k, cros_build_lib.ShellUnquote(v))
154 for k, v in (x.split('=', 1) for x in out))
155 conf['crosspkgs'] = conf['crosspkgs'].split()
156
157 for pkg, cat in cls.MANUAL_PKGS.iteritems():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400158 conf[pkg + '_pn'] = pkg
159 conf[pkg + '_category'] = cat
160 if pkg not in conf['crosspkgs']:
161 conf['crosspkgs'].append(pkg)
Han Shene23782f2016-02-18 12:20:00 -0800162
163 val[target] = conf
164
David James66a09c42012-11-05 13:31:38 -0800165 return val[target]
166
167 @classmethod
168 def UpdateTargets(cls, targets, usepkg, config_only=False):
169 """Calls crossdev to initialize a cross target.
170
171 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700172 targets: The list of targets to initialize using crossdev.
173 usepkg: Copies the commandline opts.
174 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800175 """
176 configured_targets = cls._CACHE.setdefault('configured_targets', [])
177
178 cmdbase = ['crossdev', '--show-fail-log']
179 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
180 # Pick stable by default, and override as necessary.
181 cmdbase.extend(['-P', '--oneshot'])
182 if usepkg:
183 cmdbase.extend(['-P', '--getbinpkg',
184 '-P', '--usepkgonly',
185 '--without-headers'])
186
Christopher Wileyb22c0712015-06-02 10:37:03 -0700187 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800188 cmdbase.extend(['--overlays', overlays])
189 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
190
191 for target in targets:
192 if config_only and target in configured_targets:
193 continue
194
195 cmd = cmdbase + ['-t', target]
196
197 for pkg in GetTargetPackages(target):
198 if pkg == 'gdb':
199 # Gdb does not have selectable versions.
200 cmd.append('--ex-gdb')
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700201 elif pkg == 'ex_go':
202 # Go does not have selectable versions.
203 cmd.extend(CROSSDEV_GO_ARGS)
Manoj Gupta1b5642e2017-03-08 16:44:12 -0800204 elif pkg == 'ex_compiler-rt':
205 cmd.extend(CROSSDEV_COMPILER_RT_ARGS)
Manoj Gupta946abb42017-04-12 14:27:19 -0700206 elif pkg in LLVM_PKGS_TABLE:
207 cmd.extend(LLVM_PKGS_TABLE[pkg])
Han Shene23782f2016-02-18 12:20:00 -0800208 elif pkg in cls.MANUAL_PKGS:
209 pass
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700210 else:
211 # The first of the desired versions is the "primary" one.
212 version = GetDesiredPackageVersions(target, pkg)[0]
213 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800214
215 cmd.extend(targets[target]['crossdev'].split())
216 if config_only:
217 # In this case we want to just quietly reinit
218 cmd.append('--init-target')
219 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
220 else:
221 cros_build_lib.RunCommand(cmd)
222
223 configured_targets.append(target)
224
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100225
Zdenek Behan508dcce2011-12-05 15:39:32 +0100226def GetPackageMap(target):
227 """Compiles a package map for the given target from the constants.
228
229 Uses a cache in target_version_map, that is dynamically filled in as needed,
230 since here everything is static data and the structuring is for ease of
231 configurability only.
232
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500233 Args:
234 target: The target for which to return a version map
Zdenek Behan508dcce2011-12-05 15:39:32 +0100235
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500236 Returns:
237 A map between packages and desired versions in internal format
238 (using the PACKAGE_* constants)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100239 """
240 if target in target_version_map:
241 return target_version_map[target]
242
243 # Start from copy of the global defaults.
244 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
245
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100246 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100247 # prefer any specific overrides
248 if pkg in TARGET_VERSION_MAP.get(target, {}):
249 result[pkg] = TARGET_VERSION_MAP[target][pkg]
250 else:
251 # finally, if not already set, set a sane default
252 result.setdefault(pkg, DEFAULT_VERSION)
253 target_version_map[target] = result
254 return result
255
256
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100257def GetTargetPackages(target):
258 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800259 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100260 # Undesired packages are denoted by empty ${pkg}_pn variable.
Han Shene23782f2016-02-18 12:20:00 -0800261 return [x for x in conf['crosspkgs'] if conf.get(x+'_pn')]
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100262
263
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100264# Portage helper functions:
265def GetPortagePackage(target, package):
266 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800267 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100268 # Portage category:
Han Shene23782f2016-02-18 12:20:00 -0800269 if target == 'host' or package in Crossdev.MANUAL_PKGS:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100270 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100271 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100272 category = conf['category']
273 # Portage package:
274 pn = conf[package + '_pn']
275 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500276 assert category
277 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100278 return '%s/%s' % (category, pn)
279
280
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100281def IsPackageDisabled(target, package):
282 """Returns if the given package is not used for the target."""
283 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
284
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100285
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700286def PortageTrees(root):
287 """Return the portage trees for a given root."""
288 if root == '/':
289 return portage.db['/']
290 # The portage logic requires the path always end in a slash.
291 root = root.rstrip('/') + '/'
292 return portage.create_trees(target_root=root, config_root=root)[root]
293
294
295def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100296 """Extracts the list of current versions of a target, package pair.
297
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500298 Args:
299 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700300 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100301
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500302 Returns:
303 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100304 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100305 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500306 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700307 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100308 version = portage.versions.cpv_getversion(pkg)
309 versions.append(version)
310 return versions
311
312
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700313def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100314 """Extracts the current stable version for a given package.
315
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500316 Args:
317 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
318 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700319 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100320
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500321 Returns:
322 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100323 """
David James90239b92012-11-05 15:31:34 -0800324 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500325 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700326 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800327 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100328
329
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700330def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100331 """Resolves keywords in a given version list for a particular package.
332
333 Resolving means replacing PACKAGE_STABLE with the actual number.
334
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500335 Args:
336 target: The target to operate on (e.g. i686-pc-linux-gnu)
337 package: The target/package to operate on (e.g. gcc)
338 versions: List of versions to resolve
339 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700340 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500342 Returns:
343 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100344 """
345 resolved = []
David James90239b92012-11-05 15:31:34 -0800346 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700347 if not installed:
348 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100349 for version in versions:
350 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700351 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100352 elif version != PACKAGE_NONE:
353 resolved.append(version)
354 return resolved
355
356
357def GetDesiredPackageVersions(target, package):
358 """Produces the list of desired versions for each target, package pair.
359
360 The first version in the list is implicitly treated as primary, ie.
361 the version that will be initialized by crossdev and selected.
362
363 If the version is PACKAGE_STABLE, it really means the current version which
364 is emerged by using the package atom with no particular version key.
365 Since crossdev unmasks all packages by default, this will actually
366 mean 'unstable' in most cases.
367
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500368 Args:
369 target: The target to operate on (e.g. i686-pc-linux-gnu)
370 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100371
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500372 Returns:
373 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100374 """
375 packagemap = GetPackageMap(target)
376
377 versions = []
378 if package in packagemap:
379 versions.append(packagemap[package])
380
381 return versions
382
383
384def TargetIsInitialized(target):
385 """Verifies if the given list of targets has been correctly initialized.
386
387 This determines whether we have to call crossdev while emerging
388 toolchain packages or can do it using emerge. Emerge is naturally
389 preferred, because all packages can be updated in a single pass.
390
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500391 Args:
392 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100393
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500394 Returns:
395 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100396 """
397 # Check if packages for the given target all have a proper version.
398 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100399 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800400 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100401 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800402 if not IsPackageDisabled(target, package) and not (
403 GetStablePackageVersion(atom, True) and
404 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100405 return False
406 return True
407 except cros_build_lib.RunCommandError:
408 # Fails - The target has likely never been initialized before.
409 return False
410
411
412def RemovePackageMask(target):
413 """Removes a package.mask file for the given platform.
414
415 The pre-existing package.mask files can mess with the keywords.
416
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500417 Args:
418 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419 """
420 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700421 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100422
423
Zdenek Behan508dcce2011-12-05 15:39:32 +0100424# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700425def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500426 """Rebuild libtool as needed
427
428 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
429 gcc, libtool will break. We can't use binary packages either as those will
430 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700431
432 Args:
433 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500434 """
435 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700436 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500437 for line in f:
438 # Look for a line like:
439 # sys_lib_search_path_spec="..."
440 # It'll be a list of paths and gcc will be one of them.
441 if line.startswith('sys_lib_search_path_spec='):
442 line = line.rstrip()
443 for path in line.split('=', 1)[1].strip('"').split():
Mike Frysinger3bba5032016-09-20 14:15:04 -0400444 root_path = os.path.join(root, path.lstrip(os.path.sep))
445 logging.debug('Libtool: checking %s', root_path)
446 if not os.path.exists(root_path):
447 logging.info('Rebuilding libtool after gcc upgrade')
448 logging.info(' %s', line)
449 logging.info(' missing path: %s', path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500450 needs_update = True
451 break
452
453 if needs_update:
454 break
455
456 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700457 cmd = [EMERGE_CMD, '--oneshot']
458 if root != '/':
459 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
460 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500461 cros_build_lib.RunCommand(cmd)
Mike Frysinger3bba5032016-09-20 14:15:04 -0400462 else:
463 logging.debug('Libtool is up-to-date; no need to rebuild')
Mike Frysingerc880a962013-11-08 13:59:06 -0500464
465
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700466def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100467 """Determines which packages need update/unmerge and defers to portage.
468
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500469 Args:
470 targets: The list of targets to update
471 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700472 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100473 """
David James90239b92012-11-05 15:31:34 -0800474 # Remove keyword files created by old versions of cros_setup_toolchains.
475 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100476
477 # For each target, we do two things. Figure out the list of updates,
478 # and figure out the appropriate keywords/masks. Crossdev will initialize
479 # these, but they need to be regenerated on every update.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400480 logging.info('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800481 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100482 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400483 logging.debug('Updating target %s', target)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100484 # Record the highest needed version for each target, for masking purposes.
485 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100486 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100487 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100488 if IsPackageDisabled(target, package):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400489 logging.debug(' Skipping disabled package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100490 continue
Mike Frysinger3bba5032016-09-20 14:15:04 -0400491 logging.debug(' Updating package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100492 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700493 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100494 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200495 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100496 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100497
Zdenek Behan508dcce2011-12-05 15:39:32 +0100498 packages = []
499 for pkg in mergemap:
500 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200501 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800502 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100503
504 if not packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400505 logging.info('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800506 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100507
Mike Frysinger3bba5032016-09-20 14:15:04 -0400508 logging.info('Updating packages:')
509 logging.info('%s', packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100511 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100512 if usepkg:
513 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700514 if root != '/':
515 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100516
517 cmd.extend(packages)
518 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800519 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520
521
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700522def CleanTargets(targets, root='/'):
523 """Unmerges old packages that are assumed unnecessary.
524
525 Args:
526 targets: The list of targets to clean up.
527 root: The install root in which we want packages cleaned up.
528 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529 unmergemap = {}
530 for target in targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400531 logging.debug('Cleaning target %s', target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100532 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100533 if IsPackageDisabled(target, package):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400534 logging.debug(' Skipping disabled package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100535 continue
Mike Frysinger3bba5032016-09-20 14:15:04 -0400536 logging.debug(' Cleaning package %s', package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100537 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700538 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700540 # NOTE: This refers to installed packages (vartree) rather than the
541 # Portage version (porttree and/or bintree) when determining the current
542 # version. While this isn't the most accurate thing to do, it is probably
543 # a good simple compromise, which should have the desired result of
544 # uninstalling everything but the latest installed version. In
545 # particular, using the bintree (--usebinpkg) requires a non-trivial
546 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200547 desired_num = VersionListToNumeric(target, package, desired, True)
548 if not set(desired_num).issubset(current):
Mike Frysinger3bba5032016-09-20 14:15:04 -0400549 logging.warning('Error detecting stable version for %s, '
550 'skipping clean!', pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200551 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100552 unmergemap[pkg] = set(current).difference(desired_num)
553
554 # Cleaning doesn't care about consistency and rebuilding package.* files.
555 packages = []
556 for pkg, vers in unmergemap.iteritems():
557 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
558
559 if packages:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400560 logging.info('Cleaning packages:')
561 logging.info('%s', packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100562 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700563 if root != '/':
564 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100565 cmd.extend(packages)
566 cros_build_lib.RunCommand(cmd)
567 else:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400568 logging.info('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100569
570
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700571def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100572 """Runs gcc-config and binutils-config to select the desired.
573
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500574 Args:
575 targets: The targets to select
576 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700577 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100578 """
579 for package in ['gcc', 'binutils']:
580 for target in targets:
581 # Pick the first version in the numbered list as the selected one.
582 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700583 desired_num = VersionListToNumeric(target, package, desired, True,
584 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100585 desired = desired_num[0]
586 # *-config does not play revisions, strip them, keep just PV.
587 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
588
589 if target == 'host':
590 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800591 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100592
593 # And finally, attach target to it.
594 desired = '%s-%s' % (target, desired)
595
596 # Target specific hacks
597 if package in suffixes:
598 if target in suffixes[package]:
599 desired += suffixes[package][target]
600
David James7ec5efc2012-11-06 09:39:49 -0800601 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700602 if root != '/':
603 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800604 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500605 result = cros_build_lib.RunCommand(
606 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
607 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700608
609 # Do not reconfig when the current is live or nothing needs to be done.
610 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100611 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500612 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700613 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100614
615
Mike Frysinger35247af2012-11-16 18:58:06 -0500616def ExpandTargets(targets_wanted):
617 """Expand any possible toolchain aliases into full targets
618
619 This will expand 'all' and 'sdk' into the respective toolchain tuples.
620
621 Args:
622 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500623
Mike Frysinger35247af2012-11-16 18:58:06 -0500624 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300625 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500626 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500627 targets_wanted = set(targets_wanted)
Don Garrettc0c74002015-10-09 12:58:19 -0700628 if targets_wanted == set(['boards']):
629 # Only pull targets from the included boards.
Gilad Arnold8195b532015-04-07 10:56:30 +0300630 return {}
631
632 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500633 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300634 return all_targets
635 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500636 # Filter out all the non-sdk toolchains as we don't want to mess
637 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300638 return toolchain.FilterToolchains(all_targets, 'sdk', True)
639
640 # Verify user input.
641 nonexistent = targets_wanted.difference(all_targets)
642 if nonexistent:
643 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
644 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500645
646
David Jamesf8c672f2012-11-06 13:38:11 -0800647def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Don Garrettc0c74002015-10-09 12:58:19 -0700648 targets_wanted, boards_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100649 """Performs all steps to create a synchronized toolchain enviroment.
650
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500651 Args:
652 usepkg: Use prebuilt packages
653 deleteold: Unmerge deprecated packages
654 hostonly: Only setup the host toolchain
655 reconfig: Reload crossdev config and reselect toolchains
656 targets_wanted: All the targets to update
657 boards_wanted: Load targets from these boards
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700658 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100659 """
David Jamesf8c672f2012-11-06 13:38:11 -0800660 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100661 if not hostonly:
662 # For hostonly, we can skip most of the below logic, much of which won't
663 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500664 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400665
Don Garrettc0c74002015-10-09 12:58:19 -0700666 # Now re-add any targets that might be from this board. This is to
Gilad Arnold8195b532015-04-07 10:56:30 +0300667 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400668 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800669 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100670
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100671 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400672 for target in targets:
673 if TargetIsInitialized(target):
674 reconfig_targets[target] = targets[target]
675 else:
676 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100677 if crossdev_targets:
Mike Frysinger3bba5032016-09-20 14:15:04 -0400678 logging.info('The following targets need to be re-initialized:')
679 logging.info('%s', crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800680 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200681 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800682 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100683
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100684 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400685 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100686
687 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700688 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
689 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800690
691 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700692 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100693
Mike Frysingerc880a962013-11-08 13:59:06 -0500694 # Now that we've cleared out old versions, see if we need to rebuild
695 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700696 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500697
Zdenek Behan508dcce2011-12-05 15:39:32 +0100698
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700699def ShowConfig(name):
700 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500701
702 Args:
Don Garrettc0c74002015-10-09 12:58:19 -0700703 name: The board name to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500704 """
Don Garrettc0c74002015-10-09 12:58:19 -0700705 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500706 # Make sure we display the default toolchain first.
Mike Frysinger3bba5032016-09-20 14:15:04 -0400707 # Note: Do not use logging here as this is meant to be used by other tools.
Mike Frysinger383367e2014-09-16 15:06:17 -0400708 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800709 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400710 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500711
712
Mike Frysinger35247af2012-11-16 18:58:06 -0500713def GeneratePathWrapper(root, wrappath, path):
714 """Generate a shell script to execute another shell script
715
716 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
717 argv[0] won't be pointing to the correct path, generate a shell script that
718 just executes another program with its full path.
719
720 Args:
721 root: The root tree to generate scripts inside of
722 wrappath: The full path (inside |root|) to create the wrapper
723 path: The target program which this wrapper will execute
724 """
725 replacements = {
726 'path': path,
727 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
728 }
729 wrapper = """#!/bin/sh
730base=$(realpath "$0")
731basedir=${base%%/*}
732exec "${basedir}/%(relroot)s%(path)s" "$@"
733""" % replacements
734 root_wrapper = root + wrappath
735 if os.path.islink(root_wrapper):
736 os.unlink(root_wrapper)
737 else:
738 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
739 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400740 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500741
742
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700743def FixClangXXWrapper(root, path):
744 """Fix wrapper shell scripts and symlinks for invoking clang++
745
746 In a typical installation, clang++ symlinks to clang, which symlinks to the
747 elf executable. The executable distinguishes between clang and clang++ based
748 on argv[0].
749
750 When invoked through the LdsoWrapper, argv[0] always contains the path to the
751 executable elf file, making clang/clang++ invocations indistinguishable.
752
753 This function detects if the elf executable being wrapped is clang-X.Y, and
754 fixes wrappers/symlinks as necessary so that clang++ will work correctly.
755
756 The calling sequence now becomes:
757 -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
758 the Ldsowrapper).
759 -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
760 to the original clang-3.9 elf.
761 -) The difference this time is that inside the elf file execution, $0 is
762 set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
763
764 Args:
765 root: The root tree to generate scripts / symlinks inside of
766 path: The target elf for which LdsoWrapper was created
767 """
768 if re.match(r'/usr/bin/clang-\d+\.\d+$', path):
769 logging.info('fixing clang++ invocation for %s', path)
770 clangdir = os.path.dirname(root + path)
771 clang = os.path.basename(path)
772 clangxx = clang.replace('clang', 'clang++')
773
774 # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
775 os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
776
777 # Create a hardlink clang++-X.Y pointing to clang-X.Y
778 os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
779
780 # Adjust the clang++ symlink to point to clang++-X.Y
781 os.unlink(os.path.join(clangdir, 'clang++'))
782 os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
783
784
Mike Frysinger35247af2012-11-16 18:58:06 -0500785def FileIsCrosSdkElf(elf):
786 """Determine if |elf| is an ELF that we execute in the cros_sdk
787
788 We don't need this to be perfect, just quick. It makes sure the ELF
789 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
790
791 Args:
792 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500793
Mike Frysinger35247af2012-11-16 18:58:06 -0500794 Returns:
795 True if we think |elf| is a native ELF
796 """
797 with open(elf) as f:
798 data = f.read(20)
799 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
800 return (data[0:4] == '\x7fELF' and
801 data[4] == '\x02' and
802 data[5] == '\x01' and
803 data[18] == '\x3e')
804
805
806def IsPathPackagable(ptype, path):
807 """Should the specified file be included in a toolchain package?
808
809 We only need to handle files as we'll create dirs as we need them.
810
811 Further, trim files that won't be useful:
812 - non-english translations (.mo) since it'd require env vars
813 - debug files since these are for the host compiler itself
814 - info/man pages as they're big, and docs are online, and the
815 native docs should work fine for the most part (`man gcc`)
816
817 Args:
818 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
819 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500820
Mike Frysinger35247af2012-11-16 18:58:06 -0500821 Returns:
822 True if we want to include this path in the package
823 """
824 return not (ptype in ('dir',) or
825 path.startswith('/usr/lib/debug/') or
826 os.path.splitext(path)[1] == '.mo' or
827 ('/man/' in path or '/info/' in path))
828
829
830def ReadlinkRoot(path, root):
831 """Like os.readlink(), but relative to a |root|
832
833 Args:
834 path: The symlink to read
835 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500836
Mike Frysinger35247af2012-11-16 18:58:06 -0500837 Returns:
838 A fully resolved symlink path
839 """
840 while os.path.islink(root + path):
841 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
842 return path
843
844
845def _GetFilesForTarget(target, root='/'):
846 """Locate all the files to package for |target|
847
848 This does not cover ELF dependencies.
849
850 Args:
851 target: The toolchain target name
852 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500853
Mike Frysinger35247af2012-11-16 18:58:06 -0500854 Returns:
855 A tuple of a set of all packable paths, and a set of all paths which
856 are also native ELFs
857 """
858 paths = set()
859 elfs = set()
860
861 # Find all the files owned by the packages for this target.
862 for pkg in GetTargetPackages(target):
863 # Ignore packages that are part of the target sysroot.
864 if pkg in ('kernel', 'libc'):
865 continue
866
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700867 # Skip Go compiler from redistributable packages.
868 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
869 # into it. Due to this, the toolchain cannot be unpacked anywhere
870 # else and be readily useful. To enable packaging Go, we need to:
871 # -) Tweak the wrappers/environment to override GOROOT
872 # automatically based on the unpack location.
873 # -) Make sure the ELF dependency checking and wrapping logic
874 # below skips the Go toolchain executables and libraries.
875 # -) Make sure the packaging process maintains the relative
876 # timestamps of precompiled standard library packages.
877 # (see dev-lang/go ebuild for details).
878 if pkg == 'ex_go':
879 continue
880
Mike Frysinger35247af2012-11-16 18:58:06 -0500881 atom = GetPortagePackage(target, pkg)
882 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700883 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700884 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500885
886 # pylint: disable=E1101
887 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
888 settings=portage.settings)
889 contents = dblink.getcontents()
890 for obj in contents:
891 ptype = contents[obj][0]
892 if not IsPathPackagable(ptype, obj):
893 continue
894
895 if ptype == 'obj':
896 # For native ELFs, we need to pull in their dependencies too.
897 if FileIsCrosSdkElf(obj):
898 elfs.add(obj)
899 paths.add(obj)
900
901 return paths, elfs
902
903
904def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500905 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500906 """Link in all packable files and their runtime dependencies
907
908 This also wraps up executable ELFs with helper scripts.
909
910 Args:
911 output_dir: The output directory to store files
912 paths: All the files to include
913 elfs: All the files which are ELFs (a subset of |paths|)
914 ldpaths: A dict of static ldpath information
915 path_rewrite_func: User callback to rewrite paths in output_dir
916 root: The root path to pull all packages/files from
917 """
918 # Link in all the files.
919 sym_paths = []
920 for path in paths:
921 new_path = path_rewrite_func(path)
922 dst = output_dir + new_path
923 osutils.SafeMakedirs(os.path.dirname(dst))
924
925 # Is this a symlink which we have to rewrite or wrap?
926 # Delay wrap check until after we have created all paths.
927 src = root + path
928 if os.path.islink(src):
929 tgt = os.readlink(src)
930 if os.path.sep in tgt:
931 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
932
933 # Rewrite absolute links to relative and then generate the symlink
934 # ourselves. All other symlinks can be hardlinked below.
935 if tgt[0] == '/':
936 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
937 os.symlink(tgt, dst)
938 continue
939
940 os.link(src, dst)
941
942 # Now see if any of the symlinks need to be wrapped.
943 for sym, tgt in sym_paths:
944 if tgt in elfs:
945 GeneratePathWrapper(output_dir, sym, tgt)
946
947 # Locate all the dependencies for all the ELFs. Stick them all in the
948 # top level "lib" dir to make the wrapper simpler. This exact path does
949 # not matter since we execute ldso directly, and we tell the ldso the
950 # exact path to search for its libraries.
951 libdir = os.path.join(output_dir, 'lib')
952 osutils.SafeMakedirs(libdir)
953 donelibs = set()
954 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400955 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500956 interp = e['interp']
957 if interp:
958 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400959 interp = os.path.join('/lib', os.path.basename(interp))
960 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
961 libpaths=e['rpath'] + e['runpath'])
Rahul Chaudhrya8127bb2016-07-22 15:53:17 -0700962 FixClangXXWrapper(output_dir, path_rewrite_func(elf))
Mike Frysinger35247af2012-11-16 18:58:06 -0500963
964 for lib, lib_data in e['libs'].iteritems():
965 if lib in donelibs:
966 continue
967
968 src = path = lib_data['path']
969 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -0700970 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -0500971 continue
972 donelibs.add(lib)
973
974 # Needed libs are the SONAME, but that is usually a symlink, not a
975 # real file. So link in the target rather than the symlink itself.
976 # We have to walk all the possible symlinks (SONAME could point to a
977 # symlink which points to a symlink), and we have to handle absolute
978 # ourselves (since we have a "root" argument).
979 dst = os.path.join(libdir, os.path.basename(path))
980 src = ReadlinkRoot(src, root)
981
982 os.link(root + src, dst)
983
984
985def _EnvdGetVar(envd, var):
986 """Given a Gentoo env.d file, extract a var from it
987
988 Args:
989 envd: The env.d file to load (may be a glob path)
990 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -0500991
Mike Frysinger35247af2012-11-16 18:58:06 -0500992 Returns:
993 The value of |var|
994 """
995 envds = glob.glob(envd)
996 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
997 envd = envds[0]
998 return cros_build_lib.LoadKeyValueFile(envd)[var]
999
1000
1001def _ProcessBinutilsConfig(target, output_dir):
1002 """Do what binutils-config would have done"""
1003 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001004
1005 # Locate the bin dir holding the gold linker.
1006 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
1007 target, 'binutils-bin')
1008 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -05001009 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001010 if not srcpath:
1011 # Maybe this target doesn't support gold.
1012 globpath = os.path.join(binutils_bin_path, '*')
1013 srcpath = glob.glob(globpath)
1014 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
1015 % globpath)
1016 srcpath = srcpath[0]
1017 ld_path = os.path.join(srcpath, 'ld')
1018 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1019 ld_path = os.path.join(srcpath, 'ld.bfd')
1020 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1021 ld_path = os.path.join(srcpath, 'ld.gold')
1022 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
1023 % ld_path)
1024
1025 # Nope, no gold support to be found.
1026 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -07001027 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001028 else:
1029 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
Mike Frysinger78b7a812014-11-26 19:45:23 -05001030 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001031
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001032 # Package the binutils-bin directory without the '-gold' suffix
1033 # if gold is not enabled as the default linker for this target.
1034 gold_supported = CONFIG_TARGET_SUFFIXES['binutils'].get(target) == '-gold'
1035 if not gold_supported:
1036 srcpath = srcpath[:-len('-gold')]
1037 ld_path = os.path.join(srcpath, 'ld')
1038 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
1039
Mike Frysinger78b7a812014-11-26 19:45:23 -05001040 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -05001041 gccpath = os.path.join('/usr', 'libexec', 'gcc')
1042 for prog in os.listdir(output_dir + srcpath):
1043 # Skip binaries already wrapped.
1044 if not prog.endswith('.real'):
1045 GeneratePathWrapper(output_dir, binpath + prog,
1046 os.path.join(srcpath, prog))
1047 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
1048 os.path.join(srcpath, prog))
1049
David James27ac4ae2012-12-03 23:16:15 -08001050 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -05001051 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
1052 if gold_supported:
1053 envd += '-gold'
Rahul Chaudhry4891b4d2017-03-08 10:31:27 -08001054 else:
1055 # If gold is not enabled as the default linker and 2 env.d
1056 # files exist, pick the one without the '-gold' suffix.
1057 envds = sorted(glob.glob(envd))
1058 if len(envds) == 2 and envds[1] == envds[0] + '-gold':
1059 envd = envds[0]
Mike Frysinger35247af2012-11-16 18:58:06 -05001060 srcpath = _EnvdGetVar(envd, 'LIBPATH')
1061 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
1062 output_dir + libpath)
1063
1064
1065def _ProcessGccConfig(target, output_dir):
1066 """Do what gcc-config would have done"""
1067 binpath = '/bin'
1068 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
1069 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
1070 for prog in os.listdir(output_dir + srcpath):
1071 # Skip binaries already wrapped.
1072 if (not prog.endswith('.real') and
1073 not prog.endswith('.elf') and
1074 prog.startswith(target)):
1075 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
1076 os.path.join(srcpath, prog))
1077 return srcpath
1078
1079
Frank Henigman179ec7c2015-02-06 03:01:09 -05001080def _ProcessSysrootWrappers(_target, output_dir, srcpath):
1081 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -05001082 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -05001083 for sysroot_wrapper in glob.glob(os.path.join(
1084 output_dir + srcpath, 'sysroot_wrapper*')):
1085 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
1086 for num in xrange(len(contents)):
1087 if '@CCACHE_DEFAULT@' in contents[num]:
1088 contents[num] = 'use_ccache = False'
1089 break
1090 # Can't update the wrapper in place since it's a hardlink to a file in /.
1091 os.unlink(sysroot_wrapper)
1092 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
1093 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -05001094
1095
1096def _ProcessDistroCleanups(target, output_dir):
1097 """Clean up the tree and remove all distro-specific requirements
1098
1099 Args:
1100 target: The toolchain target name
1101 output_dir: The output directory to clean up
1102 """
1103 _ProcessBinutilsConfig(target, output_dir)
1104 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -05001105 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Mike Frysinger35247af2012-11-16 18:58:06 -05001106
1107 osutils.RmDir(os.path.join(output_dir, 'etc'))
1108
1109
1110def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1111 """Setup a tree from the packages for the specified target
1112
1113 This populates a path with all the files from toolchain packages so that
1114 a tarball can easily be generated from the result.
1115
1116 Args:
1117 target: The target to create a packagable root from
1118 output_dir: The output directory to place all the files
1119 ldpaths: A dict of static ldpath information
1120 root: The root path to pull all packages/files from
1121 """
1122 # Find all the files owned by the packages for this target.
1123 paths, elfs = _GetFilesForTarget(target, root=root)
1124
1125 # Link in all the package's files, any ELF dependencies, and wrap any
1126 # executable ELFs with helper scripts.
1127 def MoveUsrBinToBin(path):
Han Shen699ea192016-03-02 10:42:47 -08001128 """Move /usr/bin to /bin so people can just use that toplevel dir
1129
1130 Note we do not apply this to clang - there is correlation between clang's
1131 search path for libraries / inclusion and its installation path.
1132 """
1133 if path.startswith('/usr/bin/') and path.find('clang') == -1:
1134 return path[4:]
1135 return path
Mike Frysinger35247af2012-11-16 18:58:06 -05001136 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1137 path_rewrite_func=MoveUsrBinToBin, root=root)
1138
1139 # The packages, when part of the normal distro, have helper scripts
1140 # that setup paths and such. Since we are making this standalone, we
1141 # need to preprocess all that ourselves.
1142 _ProcessDistroCleanups(target, output_dir)
1143
1144
1145def CreatePackages(targets_wanted, output_dir, root='/'):
1146 """Create redistributable cross-compiler packages for the specified targets
1147
1148 This creates toolchain packages that should be usable in conjunction with
1149 a downloaded sysroot (created elsewhere).
1150
1151 Tarballs (one per target) will be created in $PWD.
1152
1153 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001154 targets_wanted: The targets to package up.
1155 output_dir: The directory to put the packages in.
1156 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001157 """
Ralph Nathan03047282015-03-23 11:09:32 -07001158 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001159 osutils.SafeMakedirs(output_dir)
1160 ldpaths = lddtree.LoadLdpaths(root)
1161 targets = ExpandTargets(targets_wanted)
1162
David James4bc13702013-03-26 08:08:04 -07001163 with osutils.TempDir() as tempdir:
Mike Frysinger35247af2012-11-16 18:58:06 -05001164 # We have to split the root generation from the compression stages. This is
1165 # because we hardlink in all the files (to avoid overhead of reading/writing
1166 # the copies multiple times). But tar gets angry if a file's hardlink count
1167 # changes from when it starts reading a file to when it finishes.
1168 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1169 for target in targets:
1170 output_target_dir = os.path.join(tempdir, target)
1171 queue.put([target, output_target_dir, ldpaths, root])
1172
1173 # Build the tarball.
1174 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1175 for target in targets:
1176 tar_file = os.path.join(output_dir, target + '.tar.xz')
1177 queue.put([tar_file, os.path.join(tempdir, target)])
1178
1179
Brian Harring30675052012-02-29 12:18:22 -08001180def main(argv):
Mike Frysinger0c808452014-11-06 17:30:23 -05001181 parser = commandline.ArgumentParser(description=__doc__)
1182 parser.add_argument('-u', '--nousepkg',
1183 action='store_false', dest='usepkg', default=True,
1184 help='Use prebuilt packages if possible')
1185 parser.add_argument('-d', '--deleteold',
1186 action='store_true', dest='deleteold', default=False,
1187 help='Unmerge deprecated packages')
1188 parser.add_argument('-t', '--targets',
1189 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001190 help="Comma separated list of tuples. Special keywords "
Don Garrettc0c74002015-10-09 12:58:19 -07001191 "'host', 'sdk', 'boards', and 'all' are "
Gilad Arnold8195b532015-04-07 10:56:30 +03001192 "allowed. Defaults to 'sdk'.")
1193 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1194 help='Comma separated list of boards whose toolchains we '
1195 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001196 parser.add_argument('--hostonly',
1197 dest='hostonly', default=False, action='store_true',
1198 help='Only setup the host toolchain. '
1199 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001200 parser.add_argument('--show-board-cfg', '--show-cfg',
1201 dest='cfg_name', default=None,
Don Garrettc0c74002015-10-09 12:58:19 -07001202 help='Board to list toolchains tuples for')
Mike Frysinger0c808452014-11-06 17:30:23 -05001203 parser.add_argument('--create-packages',
1204 action='store_true', default=False,
1205 help='Build redistributable packages')
1206 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1207 help='Output directory')
1208 parser.add_argument('--reconfig', default=False, action='store_true',
1209 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001210 parser.add_argument('--sysroot', type='path',
1211 help='The sysroot in which to install the toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001212
Mike Frysinger0c808452014-11-06 17:30:23 -05001213 options = parser.parse_args(argv)
1214 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001215
Mike Frysinger35247af2012-11-16 18:58:06 -05001216 # Figure out what we're supposed to do and reject conflicting options.
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001217 if options.cfg_name and options.create_packages:
Mike Frysinger35247af2012-11-16 18:58:06 -05001218 parser.error('conflicting options: create-packages & show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001219
Gilad Arnold8195b532015-04-07 10:56:30 +03001220 targets_wanted = set(options.targets.split(','))
1221 boards_wanted = (set(options.include_boards.split(','))
1222 if options.include_boards else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001223
Manoj Gupta946abb42017-04-12 14:27:19 -07001224 # pylint: disable=global-statement
1225 # Disable installing llvm pkgs till binary package is available
1226 global TARGET_LLVM_PKGS_ENABLED
1227 global TARGET_COMPILER_RT_ENABLED
1228 if options.usepkg:
1229 TARGET_LLVM_PKGS_ENABLED = ()
1230 TARGET_COMPILER_RT_ENABLED = ('armv7a-cros-linux-gnueabi',)
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001231 if options.cfg_name:
1232 ShowConfig(options.cfg_name)
Mike Frysinger35247af2012-11-16 18:58:06 -05001233 elif options.create_packages:
1234 cros_build_lib.AssertInsideChroot()
1235 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001236 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001237 else:
1238 cros_build_lib.AssertInsideChroot()
1239 # This has to be always run as root.
1240 if os.geteuid() != 0:
1241 cros_build_lib.Die('this script must be run as root')
1242
1243 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001244 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001245 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001246 options.reconfig, targets_wanted, boards_wanted,
Don Garrettc0c74002015-10-09 12:58:19 -07001247 root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001248 Crossdev.Save()
1249
1250 return 0