blob: 51fe906823f38dbc0ef7be6193ed358dd7039d70 [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
Zdenek Behan508dcce2011-12-05 15:39:32 +010013
Don Garrett88b8d782014-05-13 17:30:55 -070014from chromite.cbuildbot import constants
Mike Frysinger506e75f2012-12-17 14:21:13 -050015from chromite.lib import commandline
Brian Harring503f3ab2012-03-09 21:39:41 -080016from chromite.lib import cros_build_lib
Ralph Nathan03047282015-03-23 11:09:32 -070017from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070018from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050019from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080020from chromite.lib import toolchain
Bertrand SIMONNET3c77bc92015-03-20 14:28:54 -070021from chromite.lib import workspace_lib
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'
40STABLE_OVERLAY = '/usr/local/portage/stable'
41CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010042
43
44# TODO: The versions are stored here very much like in setup_board.
45# The goal for future is to differentiate these using a config file.
46# This is done essentially by messing with GetDesiredPackageVersions()
47DEFAULT_VERSION = PACKAGE_STABLE
48DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010049}
50TARGET_VERSION_MAP = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050051 'host' : {
52 'gdb' : PACKAGE_NONE,
Rahul Chaudhry4b803052015-05-13 15:25:56 -070053 'ex_go' : PACKAGE_NONE,
Mike Frysingerd6e2df02014-11-26 02:55:04 -050054 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010055}
Rahul Chaudhry4b803052015-05-13 15:25:56 -070056
57# Enable the Go compiler for these targets.
58TARGET_GO_ENABLED = (
59 'x86_64-cros-linux-gnu',
60 'i686-pc-linux-gnu',
61 'armv7a-cros-linux-gnueabi',
62)
63CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
64
Zdenek Behan508dcce2011-12-05 15:39:32 +010065# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
66CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050067 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -040068 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -080069 'armv7a-cros-linux-gnueabi': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -050070 'i686-pc-linux-gnu' : '-gold',
71 'x86_64-cros-linux-gnu' : '-gold',
72 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010073}
Zdenek Behan508dcce2011-12-05 15:39:32 +010074# Global per-run cache that will be filled ondemand in by GetPackageMap()
75# function as needed.
76target_version_map = {
77}
78
79
David James66a09c42012-11-05 13:31:38 -080080class Crossdev(object):
81 """Class for interacting with crossdev and caching its output."""
82
83 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
84 _CACHE = {}
85
86 @classmethod
87 def Load(cls, reconfig):
88 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -080089 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
90 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -080091 if os.path.exists(cls._CACHE_FILE) and not reconfig:
92 with open(cls._CACHE_FILE) as f:
93 data = json.load(f)
David James90239b92012-11-05 15:31:34 -080094 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -080095 cls._CACHE = data
96
97 @classmethod
98 def Save(cls):
99 """Store crossdev cache on disk."""
100 # Save the cache from the successful run.
101 with open(cls._CACHE_FILE, 'w') as f:
102 json.dump(cls._CACHE, f)
103
104 @classmethod
105 def GetConfig(cls, target):
106 """Returns a map of crossdev provided variables about a tuple."""
107 CACHE_ATTR = '_target_tuple_map'
108
109 val = cls._CACHE.setdefault(CACHE_ATTR, {})
110 if not target in val:
111 # Find out the crossdev tuple.
112 target_tuple = target
113 if target == 'host':
David James27ac4ae2012-12-03 23:16:15 -0800114 target_tuple = toolchain.GetHostTuple()
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700115 # Build the crossdev command.
116 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
117 if target in TARGET_GO_ENABLED:
118 cmd.extend(CROSSDEV_GO_ARGS)
119 cmd.extend(['-t', target_tuple])
David James66a09c42012-11-05 13:31:38 -0800120 # Catch output of crossdev.
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700121 out = cros_build_lib.RunCommand(cmd, print_cmd=False,
122 redirect_stdout=True).output.splitlines()
David James66a09c42012-11-05 13:31:38 -0800123 # List of tuples split at the first '=', converted into dict.
124 val[target] = dict([x.split('=', 1) for x in out])
125 return val[target]
126
127 @classmethod
128 def UpdateTargets(cls, targets, usepkg, config_only=False):
129 """Calls crossdev to initialize a cross target.
130
131 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700132 targets: The list of targets to initialize using crossdev.
133 usepkg: Copies the commandline opts.
134 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800135 """
136 configured_targets = cls._CACHE.setdefault('configured_targets', [])
137
138 cmdbase = ['crossdev', '--show-fail-log']
139 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
140 # Pick stable by default, and override as necessary.
141 cmdbase.extend(['-P', '--oneshot'])
142 if usepkg:
143 cmdbase.extend(['-P', '--getbinpkg',
144 '-P', '--usepkgonly',
145 '--without-headers'])
146
147 overlays = '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)
148 cmdbase.extend(['--overlays', overlays])
149 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
150
151 for target in targets:
152 if config_only and target in configured_targets:
153 continue
154
155 cmd = cmdbase + ['-t', target]
156
157 for pkg in GetTargetPackages(target):
158 if pkg == 'gdb':
159 # Gdb does not have selectable versions.
160 cmd.append('--ex-gdb')
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700161 elif pkg == 'ex_go':
162 # Go does not have selectable versions.
163 cmd.extend(CROSSDEV_GO_ARGS)
164 else:
165 # The first of the desired versions is the "primary" one.
166 version = GetDesiredPackageVersions(target, pkg)[0]
167 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800168
169 cmd.extend(targets[target]['crossdev'].split())
170 if config_only:
171 # In this case we want to just quietly reinit
172 cmd.append('--init-target')
173 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
174 else:
175 cros_build_lib.RunCommand(cmd)
176
177 configured_targets.append(target)
178
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100179
Zdenek Behan508dcce2011-12-05 15:39:32 +0100180def GetPackageMap(target):
181 """Compiles a package map for the given target from the constants.
182
183 Uses a cache in target_version_map, that is dynamically filled in as needed,
184 since here everything is static data and the structuring is for ease of
185 configurability only.
186
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500187 Args:
188 target: The target for which to return a version map
Zdenek Behan508dcce2011-12-05 15:39:32 +0100189
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500190 Returns:
191 A map between packages and desired versions in internal format
192 (using the PACKAGE_* constants)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100193 """
194 if target in target_version_map:
195 return target_version_map[target]
196
197 # Start from copy of the global defaults.
198 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
199
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100200 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100201 # prefer any specific overrides
202 if pkg in TARGET_VERSION_MAP.get(target, {}):
203 result[pkg] = TARGET_VERSION_MAP[target][pkg]
204 else:
205 # finally, if not already set, set a sane default
206 result.setdefault(pkg, DEFAULT_VERSION)
207 target_version_map[target] = result
208 return result
209
210
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100211def GetTargetPackages(target):
212 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800213 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100214 # Undesired packages are denoted by empty ${pkg}_pn variable.
215 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
216
217
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100218# Portage helper functions:
219def GetPortagePackage(target, package):
220 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800221 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100222 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100223 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100224 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100225 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100226 category = conf['category']
227 # Portage package:
228 pn = conf[package + '_pn']
229 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500230 assert category
231 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100232 return '%s/%s' % (category, pn)
233
234
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100235def IsPackageDisabled(target, package):
236 """Returns if the given package is not used for the target."""
237 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
238
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100239
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700240def PortageTrees(root):
241 """Return the portage trees for a given root."""
242 if root == '/':
243 return portage.db['/']
244 # The portage logic requires the path always end in a slash.
245 root = root.rstrip('/') + '/'
246 return portage.create_trees(target_root=root, config_root=root)[root]
247
248
249def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100250 """Extracts the list of current versions of a target, package pair.
251
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500252 Args:
253 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700254 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100255
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500256 Returns:
257 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100258 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100259 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500260 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700261 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100262 version = portage.versions.cpv_getversion(pkg)
263 versions.append(version)
264 return versions
265
266
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700267def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100268 """Extracts the current stable version for a given package.
269
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500270 Args:
271 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
272 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700273 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100274
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500275 Returns:
276 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100277 """
David James90239b92012-11-05 15:31:34 -0800278 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500279 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700280 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800281 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100282
283
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700284def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100285 """Resolves keywords in a given version list for a particular package.
286
287 Resolving means replacing PACKAGE_STABLE with the actual number.
288
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500289 Args:
290 target: The target to operate on (e.g. i686-pc-linux-gnu)
291 package: The target/package to operate on (e.g. gcc)
292 versions: List of versions to resolve
293 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700294 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100295
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500296 Returns:
297 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100298 """
299 resolved = []
David James90239b92012-11-05 15:31:34 -0800300 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700301 if not installed:
302 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100303 for version in versions:
304 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700305 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100306 elif version != PACKAGE_NONE:
307 resolved.append(version)
308 return resolved
309
310
311def GetDesiredPackageVersions(target, package):
312 """Produces the list of desired versions for each target, package pair.
313
314 The first version in the list is implicitly treated as primary, ie.
315 the version that will be initialized by crossdev and selected.
316
317 If the version is PACKAGE_STABLE, it really means the current version which
318 is emerged by using the package atom with no particular version key.
319 Since crossdev unmasks all packages by default, this will actually
320 mean 'unstable' in most cases.
321
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500322 Args:
323 target: The target to operate on (e.g. i686-pc-linux-gnu)
324 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100325
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500326 Returns:
327 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100328 """
329 packagemap = GetPackageMap(target)
330
331 versions = []
332 if package in packagemap:
333 versions.append(packagemap[package])
334
335 return versions
336
337
338def TargetIsInitialized(target):
339 """Verifies if the given list of targets has been correctly initialized.
340
341 This determines whether we have to call crossdev while emerging
342 toolchain packages or can do it using emerge. Emerge is naturally
343 preferred, because all packages can be updated in a single pass.
344
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500345 Args:
346 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500348 Returns:
349 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100350 """
351 # Check if packages for the given target all have a proper version.
352 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100353 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800354 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100355 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800356 if not IsPackageDisabled(target, package) and not (
357 GetStablePackageVersion(atom, True) and
358 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100359 return False
360 return True
361 except cros_build_lib.RunCommandError:
362 # Fails - The target has likely never been initialized before.
363 return False
364
365
366def RemovePackageMask(target):
367 """Removes a package.mask file for the given platform.
368
369 The pre-existing package.mask files can mess with the keywords.
370
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500371 Args:
372 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100373 """
374 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700375 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100376
377
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700379def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500380 """Rebuild libtool as needed
381
382 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
383 gcc, libtool will break. We can't use binary packages either as those will
384 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700385
386 Args:
387 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500388 """
389 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700390 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500391 for line in f:
392 # Look for a line like:
393 # sys_lib_search_path_spec="..."
394 # It'll be a list of paths and gcc will be one of them.
395 if line.startswith('sys_lib_search_path_spec='):
396 line = line.rstrip()
397 for path in line.split('=', 1)[1].strip('"').split():
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700398 if not os.path.exists(os.path.join(root, path.lstrip(os.path.sep))):
Mike Frysinger383367e2014-09-16 15:06:17 -0400399 print('Rebuilding libtool after gcc upgrade')
400 print(' %s' % line)
401 print(' missing path: %s' % path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500402 needs_update = True
403 break
404
405 if needs_update:
406 break
407
408 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700409 cmd = [EMERGE_CMD, '--oneshot']
410 if root != '/':
411 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
412 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500413 cros_build_lib.RunCommand(cmd)
414
415
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700416def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417 """Determines which packages need update/unmerge and defers to portage.
418
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500419 Args:
420 targets: The list of targets to update
421 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700422 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100423 """
David James90239b92012-11-05 15:31:34 -0800424 # Remove keyword files created by old versions of cros_setup_toolchains.
425 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100426
427 # For each target, we do two things. Figure out the list of updates,
428 # and figure out the appropriate keywords/masks. Crossdev will initialize
429 # these, but they need to be regenerated on every update.
Mike Frysinger383367e2014-09-16 15:06:17 -0400430 print('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800431 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100432 for target in targets:
433 # Record the highest needed version for each target, for masking purposes.
434 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100435 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100436 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100437 if IsPackageDisabled(target, package):
438 continue
439 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700440 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100441 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200442 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100443 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100444
Zdenek Behan508dcce2011-12-05 15:39:32 +0100445 packages = []
446 for pkg in mergemap:
447 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200448 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800449 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100450
451 if not packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400452 print('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800453 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100454
Mike Frysinger383367e2014-09-16 15:06:17 -0400455 print('Updating packages:')
456 print(packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100457
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100458 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100459 if usepkg:
460 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700461 if root != '/':
462 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100463
464 cmd.extend(packages)
465 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800466 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100467
468
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700469def CleanTargets(targets, root='/'):
470 """Unmerges old packages that are assumed unnecessary.
471
472 Args:
473 targets: The list of targets to clean up.
474 root: The install root in which we want packages cleaned up.
475 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100476 unmergemap = {}
477 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100478 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100479 if IsPackageDisabled(target, package):
480 continue
481 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700482 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100483 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700484 # NOTE: This refers to installed packages (vartree) rather than the
485 # Portage version (porttree and/or bintree) when determining the current
486 # version. While this isn't the most accurate thing to do, it is probably
487 # a good simple compromise, which should have the desired result of
488 # uninstalling everything but the latest installed version. In
489 # particular, using the bintree (--usebinpkg) requires a non-trivial
490 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200491 desired_num = VersionListToNumeric(target, package, desired, True)
492 if not set(desired_num).issubset(current):
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700493 print('Error detecting stable version for %s, skipping clean!' % pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200494 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100495 unmergemap[pkg] = set(current).difference(desired_num)
496
497 # Cleaning doesn't care about consistency and rebuilding package.* files.
498 packages = []
499 for pkg, vers in unmergemap.iteritems():
500 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
501
502 if packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400503 print('Cleaning packages:')
504 print(packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100505 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700506 if root != '/':
507 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100508 cmd.extend(packages)
509 cros_build_lib.RunCommand(cmd)
510 else:
Mike Frysinger383367e2014-09-16 15:06:17 -0400511 print('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100512
513
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700514def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100515 """Runs gcc-config and binutils-config to select the desired.
516
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500517 Args:
518 targets: The targets to select
519 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700520 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100521 """
522 for package in ['gcc', 'binutils']:
523 for target in targets:
524 # Pick the first version in the numbered list as the selected one.
525 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700526 desired_num = VersionListToNumeric(target, package, desired, True,
527 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528 desired = desired_num[0]
529 # *-config does not play revisions, strip them, keep just PV.
530 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
531
532 if target == 'host':
533 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800534 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535
536 # And finally, attach target to it.
537 desired = '%s-%s' % (target, desired)
538
539 # Target specific hacks
540 if package in suffixes:
541 if target in suffixes[package]:
542 desired += suffixes[package][target]
543
David James7ec5efc2012-11-06 09:39:49 -0800544 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700545 if root != '/':
546 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800547 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500548 result = cros_build_lib.RunCommand(
549 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
550 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700551
552 # Do not reconfig when the current is live or nothing needs to be done.
553 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100554 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500555 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700556 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100557
558
Mike Frysinger35247af2012-11-16 18:58:06 -0500559def ExpandTargets(targets_wanted):
560 """Expand any possible toolchain aliases into full targets
561
562 This will expand 'all' and 'sdk' into the respective toolchain tuples.
563
564 Args:
565 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500566
Mike Frysinger35247af2012-11-16 18:58:06 -0500567 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300568 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500569 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500570 targets_wanted = set(targets_wanted)
Gilad Arnold8195b532015-04-07 10:56:30 +0300571 if targets_wanted in (set(['boards']), set(['bricks'])):
572 # Only pull targets from the included boards/bricks.
573 return {}
574
575 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500576 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300577 return all_targets
578 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500579 # Filter out all the non-sdk toolchains as we don't want to mess
580 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300581 return toolchain.FilterToolchains(all_targets, 'sdk', True)
582
583 # Verify user input.
584 nonexistent = targets_wanted.difference(all_targets)
585 if nonexistent:
586 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
587 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500588
589
David Jamesf8c672f2012-11-06 13:38:11 -0800590def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700591 targets_wanted, boards_wanted, bricks_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100592 """Performs all steps to create a synchronized toolchain enviroment.
593
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500594 Args:
595 usepkg: Use prebuilt packages
596 deleteold: Unmerge deprecated packages
597 hostonly: Only setup the host toolchain
598 reconfig: Reload crossdev config and reselect toolchains
599 targets_wanted: All the targets to update
600 boards_wanted: Load targets from these boards
Gilad Arnold8195b532015-04-07 10:56:30 +0300601 bricks_wanted: Load targets from these bricks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700602 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603 """
David Jamesf8c672f2012-11-06 13:38:11 -0800604 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100605 if not hostonly:
606 # For hostonly, we can skip most of the below logic, much of which won't
607 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500608 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400609
Gilad Arnold8195b532015-04-07 10:56:30 +0300610 # Now re-add any targets that might be from this board/brick. This is to
611 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400612 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800613 targets.update(toolchain.GetToolchainsForBoard(board))
Gilad Arnold8195b532015-04-07 10:56:30 +0300614 for brick in bricks_wanted:
615 targets.update(toolchain.GetToolchainsForBrick(brick))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100616
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100617 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400618 for target in targets:
619 if TargetIsInitialized(target):
620 reconfig_targets[target] = targets[target]
621 else:
622 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100623 if crossdev_targets:
Mike Frysinger383367e2014-09-16 15:06:17 -0400624 print('The following targets need to be re-initialized:')
625 print(crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800626 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200627 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800628 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100629
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100630 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400631 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100632
633 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700634 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
635 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800636
637 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700638 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100639
Mike Frysingerc880a962013-11-08 13:59:06 -0500640 # Now that we've cleared out old versions, see if we need to rebuild
641 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700642 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500643
Zdenek Behan508dcce2011-12-05 15:39:32 +0100644
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700645def ShowConfig(name):
646 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500647
648 Args:
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700649 name: The board name or brick locator to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500650 """
Bertrand SIMONNET3c77bc92015-03-20 14:28:54 -0700651 if workspace_lib.IsLocator(name):
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700652 toolchains = toolchain.GetToolchainsForBrick(name)
653 else:
654 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500655 # Make sure we display the default toolchain first.
Mike Frysinger383367e2014-09-16 15:06:17 -0400656 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800657 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400658 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500659
660
Mike Frysinger35247af2012-11-16 18:58:06 -0500661def GeneratePathWrapper(root, wrappath, path):
662 """Generate a shell script to execute another shell script
663
664 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
665 argv[0] won't be pointing to the correct path, generate a shell script that
666 just executes another program with its full path.
667
668 Args:
669 root: The root tree to generate scripts inside of
670 wrappath: The full path (inside |root|) to create the wrapper
671 path: The target program which this wrapper will execute
672 """
673 replacements = {
674 'path': path,
675 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
676 }
677 wrapper = """#!/bin/sh
678base=$(realpath "$0")
679basedir=${base%%/*}
680exec "${basedir}/%(relroot)s%(path)s" "$@"
681""" % replacements
682 root_wrapper = root + wrappath
683 if os.path.islink(root_wrapper):
684 os.unlink(root_wrapper)
685 else:
686 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
687 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400688 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500689
690
691def FileIsCrosSdkElf(elf):
692 """Determine if |elf| is an ELF that we execute in the cros_sdk
693
694 We don't need this to be perfect, just quick. It makes sure the ELF
695 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
696
697 Args:
698 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500699
Mike Frysinger35247af2012-11-16 18:58:06 -0500700 Returns:
701 True if we think |elf| is a native ELF
702 """
703 with open(elf) as f:
704 data = f.read(20)
705 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
706 return (data[0:4] == '\x7fELF' and
707 data[4] == '\x02' and
708 data[5] == '\x01' and
709 data[18] == '\x3e')
710
711
712def IsPathPackagable(ptype, path):
713 """Should the specified file be included in a toolchain package?
714
715 We only need to handle files as we'll create dirs as we need them.
716
717 Further, trim files that won't be useful:
718 - non-english translations (.mo) since it'd require env vars
719 - debug files since these are for the host compiler itself
720 - info/man pages as they're big, and docs are online, and the
721 native docs should work fine for the most part (`man gcc`)
722
723 Args:
724 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
725 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500726
Mike Frysinger35247af2012-11-16 18:58:06 -0500727 Returns:
728 True if we want to include this path in the package
729 """
730 return not (ptype in ('dir',) or
731 path.startswith('/usr/lib/debug/') or
732 os.path.splitext(path)[1] == '.mo' or
733 ('/man/' in path or '/info/' in path))
734
735
736def ReadlinkRoot(path, root):
737 """Like os.readlink(), but relative to a |root|
738
739 Args:
740 path: The symlink to read
741 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500742
Mike Frysinger35247af2012-11-16 18:58:06 -0500743 Returns:
744 A fully resolved symlink path
745 """
746 while os.path.islink(root + path):
747 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
748 return path
749
750
751def _GetFilesForTarget(target, root='/'):
752 """Locate all the files to package for |target|
753
754 This does not cover ELF dependencies.
755
756 Args:
757 target: The toolchain target name
758 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500759
Mike Frysinger35247af2012-11-16 18:58:06 -0500760 Returns:
761 A tuple of a set of all packable paths, and a set of all paths which
762 are also native ELFs
763 """
764 paths = set()
765 elfs = set()
766
767 # Find all the files owned by the packages for this target.
768 for pkg in GetTargetPackages(target):
769 # Ignore packages that are part of the target sysroot.
770 if pkg in ('kernel', 'libc'):
771 continue
772
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700773 # Skip Go compiler from redistributable packages.
774 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
775 # into it. Due to this, the toolchain cannot be unpacked anywhere
776 # else and be readily useful. To enable packaging Go, we need to:
777 # -) Tweak the wrappers/environment to override GOROOT
778 # automatically based on the unpack location.
779 # -) Make sure the ELF dependency checking and wrapping logic
780 # below skips the Go toolchain executables and libraries.
781 # -) Make sure the packaging process maintains the relative
782 # timestamps of precompiled standard library packages.
783 # (see dev-lang/go ebuild for details).
784 if pkg == 'ex_go':
785 continue
786
Mike Frysinger35247af2012-11-16 18:58:06 -0500787 atom = GetPortagePackage(target, pkg)
788 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700789 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700790 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500791
792 # pylint: disable=E1101
793 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
794 settings=portage.settings)
795 contents = dblink.getcontents()
796 for obj in contents:
797 ptype = contents[obj][0]
798 if not IsPathPackagable(ptype, obj):
799 continue
800
801 if ptype == 'obj':
802 # For native ELFs, we need to pull in their dependencies too.
803 if FileIsCrosSdkElf(obj):
804 elfs.add(obj)
805 paths.add(obj)
806
807 return paths, elfs
808
809
810def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500811 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500812 """Link in all packable files and their runtime dependencies
813
814 This also wraps up executable ELFs with helper scripts.
815
816 Args:
817 output_dir: The output directory to store files
818 paths: All the files to include
819 elfs: All the files which are ELFs (a subset of |paths|)
820 ldpaths: A dict of static ldpath information
821 path_rewrite_func: User callback to rewrite paths in output_dir
822 root: The root path to pull all packages/files from
823 """
824 # Link in all the files.
825 sym_paths = []
826 for path in paths:
827 new_path = path_rewrite_func(path)
828 dst = output_dir + new_path
829 osutils.SafeMakedirs(os.path.dirname(dst))
830
831 # Is this a symlink which we have to rewrite or wrap?
832 # Delay wrap check until after we have created all paths.
833 src = root + path
834 if os.path.islink(src):
835 tgt = os.readlink(src)
836 if os.path.sep in tgt:
837 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
838
839 # Rewrite absolute links to relative and then generate the symlink
840 # ourselves. All other symlinks can be hardlinked below.
841 if tgt[0] == '/':
842 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
843 os.symlink(tgt, dst)
844 continue
845
846 os.link(src, dst)
847
848 # Now see if any of the symlinks need to be wrapped.
849 for sym, tgt in sym_paths:
850 if tgt in elfs:
851 GeneratePathWrapper(output_dir, sym, tgt)
852
853 # Locate all the dependencies for all the ELFs. Stick them all in the
854 # top level "lib" dir to make the wrapper simpler. This exact path does
855 # not matter since we execute ldso directly, and we tell the ldso the
856 # exact path to search for its libraries.
857 libdir = os.path.join(output_dir, 'lib')
858 osutils.SafeMakedirs(libdir)
859 donelibs = set()
860 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400861 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500862 interp = e['interp']
863 if interp:
864 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400865 interp = os.path.join('/lib', os.path.basename(interp))
866 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
867 libpaths=e['rpath'] + e['runpath'])
Mike Frysinger35247af2012-11-16 18:58:06 -0500868
869 for lib, lib_data in e['libs'].iteritems():
870 if lib in donelibs:
871 continue
872
873 src = path = lib_data['path']
874 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -0700875 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -0500876 continue
877 donelibs.add(lib)
878
879 # Needed libs are the SONAME, but that is usually a symlink, not a
880 # real file. So link in the target rather than the symlink itself.
881 # We have to walk all the possible symlinks (SONAME could point to a
882 # symlink which points to a symlink), and we have to handle absolute
883 # ourselves (since we have a "root" argument).
884 dst = os.path.join(libdir, os.path.basename(path))
885 src = ReadlinkRoot(src, root)
886
887 os.link(root + src, dst)
888
889
890def _EnvdGetVar(envd, var):
891 """Given a Gentoo env.d file, extract a var from it
892
893 Args:
894 envd: The env.d file to load (may be a glob path)
895 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -0500896
Mike Frysinger35247af2012-11-16 18:58:06 -0500897 Returns:
898 The value of |var|
899 """
900 envds = glob.glob(envd)
901 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
902 envd = envds[0]
903 return cros_build_lib.LoadKeyValueFile(envd)[var]
904
905
906def _ProcessBinutilsConfig(target, output_dir):
907 """Do what binutils-config would have done"""
908 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500909
910 # Locate the bin dir holding the gold linker.
911 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
912 target, 'binutils-bin')
913 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -0500914 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500915 if not srcpath:
916 # Maybe this target doesn't support gold.
917 globpath = os.path.join(binutils_bin_path, '*')
918 srcpath = glob.glob(globpath)
919 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
920 % globpath)
921 srcpath = srcpath[0]
922 ld_path = os.path.join(srcpath, 'ld')
923 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
924 ld_path = os.path.join(srcpath, 'ld.bfd')
925 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
926 ld_path = os.path.join(srcpath, 'ld.gold')
927 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
928 % ld_path)
929
930 # Nope, no gold support to be found.
931 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -0700932 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500933 else:
934 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
935 gold_supported = True
Mike Frysinger78b7a812014-11-26 19:45:23 -0500936 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500937
Mike Frysinger78b7a812014-11-26 19:45:23 -0500938 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -0500939 gccpath = os.path.join('/usr', 'libexec', 'gcc')
940 for prog in os.listdir(output_dir + srcpath):
941 # Skip binaries already wrapped.
942 if not prog.endswith('.real'):
943 GeneratePathWrapper(output_dir, binpath + prog,
944 os.path.join(srcpath, prog))
945 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
946 os.path.join(srcpath, prog))
947
David James27ac4ae2012-12-03 23:16:15 -0800948 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500949 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
950 if gold_supported:
951 envd += '-gold'
Mike Frysinger35247af2012-11-16 18:58:06 -0500952 srcpath = _EnvdGetVar(envd, 'LIBPATH')
953 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
954 output_dir + libpath)
955
956
957def _ProcessGccConfig(target, output_dir):
958 """Do what gcc-config would have done"""
959 binpath = '/bin'
960 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
961 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
962 for prog in os.listdir(output_dir + srcpath):
963 # Skip binaries already wrapped.
964 if (not prog.endswith('.real') and
965 not prog.endswith('.elf') and
966 prog.startswith(target)):
967 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
968 os.path.join(srcpath, prog))
969 return srcpath
970
971
Frank Henigman179ec7c2015-02-06 03:01:09 -0500972def _ProcessSysrootWrappers(_target, output_dir, srcpath):
973 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -0500974 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -0500975 for sysroot_wrapper in glob.glob(os.path.join(
976 output_dir + srcpath, 'sysroot_wrapper*')):
977 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
978 for num in xrange(len(contents)):
979 if '@CCACHE_DEFAULT@' in contents[num]:
980 contents[num] = 'use_ccache = False'
981 break
982 # Can't update the wrapper in place since it's a hardlink to a file in /.
983 os.unlink(sysroot_wrapper)
984 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
985 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500986
987
988def _ProcessDistroCleanups(target, output_dir):
989 """Clean up the tree and remove all distro-specific requirements
990
991 Args:
992 target: The toolchain target name
993 output_dir: The output directory to clean up
994 """
995 _ProcessBinutilsConfig(target, output_dir)
996 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -0500997 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Mike Frysinger35247af2012-11-16 18:58:06 -0500998
999 osutils.RmDir(os.path.join(output_dir, 'etc'))
1000
1001
1002def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1003 """Setup a tree from the packages for the specified target
1004
1005 This populates a path with all the files from toolchain packages so that
1006 a tarball can easily be generated from the result.
1007
1008 Args:
1009 target: The target to create a packagable root from
1010 output_dir: The output directory to place all the files
1011 ldpaths: A dict of static ldpath information
1012 root: The root path to pull all packages/files from
1013 """
1014 # Find all the files owned by the packages for this target.
1015 paths, elfs = _GetFilesForTarget(target, root=root)
1016
1017 # Link in all the package's files, any ELF dependencies, and wrap any
1018 # executable ELFs with helper scripts.
1019 def MoveUsrBinToBin(path):
1020 """Move /usr/bin to /bin so people can just use that toplevel dir"""
1021 return path[4:] if path.startswith('/usr/bin/') else path
1022 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1023 path_rewrite_func=MoveUsrBinToBin, root=root)
1024
1025 # The packages, when part of the normal distro, have helper scripts
1026 # that setup paths and such. Since we are making this standalone, we
1027 # need to preprocess all that ourselves.
1028 _ProcessDistroCleanups(target, output_dir)
1029
1030
1031def CreatePackages(targets_wanted, output_dir, root='/'):
1032 """Create redistributable cross-compiler packages for the specified targets
1033
1034 This creates toolchain packages that should be usable in conjunction with
1035 a downloaded sysroot (created elsewhere).
1036
1037 Tarballs (one per target) will be created in $PWD.
1038
1039 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001040 targets_wanted: The targets to package up.
1041 output_dir: The directory to put the packages in.
1042 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001043 """
Ralph Nathan03047282015-03-23 11:09:32 -07001044 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001045 osutils.SafeMakedirs(output_dir)
1046 ldpaths = lddtree.LoadLdpaths(root)
1047 targets = ExpandTargets(targets_wanted)
1048
David James4bc13702013-03-26 08:08:04 -07001049 with osutils.TempDir() as tempdir:
Mike Frysinger35247af2012-11-16 18:58:06 -05001050 # We have to split the root generation from the compression stages. This is
1051 # because we hardlink in all the files (to avoid overhead of reading/writing
1052 # the copies multiple times). But tar gets angry if a file's hardlink count
1053 # changes from when it starts reading a file to when it finishes.
1054 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1055 for target in targets:
1056 output_target_dir = os.path.join(tempdir, target)
1057 queue.put([target, output_target_dir, ldpaths, root])
1058
1059 # Build the tarball.
1060 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1061 for target in targets:
1062 tar_file = os.path.join(output_dir, target + '.tar.xz')
1063 queue.put([tar_file, os.path.join(tempdir, target)])
1064
1065
Brian Harring30675052012-02-29 12:18:22 -08001066def main(argv):
Mike Frysinger0c808452014-11-06 17:30:23 -05001067 parser = commandline.ArgumentParser(description=__doc__)
1068 parser.add_argument('-u', '--nousepkg',
1069 action='store_false', dest='usepkg', default=True,
1070 help='Use prebuilt packages if possible')
1071 parser.add_argument('-d', '--deleteold',
1072 action='store_true', dest='deleteold', default=False,
1073 help='Unmerge deprecated packages')
1074 parser.add_argument('-t', '--targets',
1075 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001076 help="Comma separated list of tuples. Special keywords "
1077 "'host', 'sdk', 'boards', 'bricks' and 'all' are "
1078 "allowed. Defaults to 'sdk'.")
1079 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1080 help='Comma separated list of boards whose toolchains we '
1081 'will always include. Default: none')
1082 parser.add_argument('--include-bricks', default='', metavar='BRICKS',
1083 help='Comma separated list of bricks whose toolchains we '
1084 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001085 parser.add_argument('--hostonly',
1086 dest='hostonly', default=False, action='store_true',
1087 help='Only setup the host toolchain. '
1088 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001089 parser.add_argument('--show-board-cfg', '--show-cfg',
1090 dest='cfg_name', default=None,
1091 help='Board or brick to list toolchains tuples for')
Mike Frysinger0c808452014-11-06 17:30:23 -05001092 parser.add_argument('--create-packages',
1093 action='store_true', default=False,
1094 help='Build redistributable packages')
1095 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1096 help='Output directory')
1097 parser.add_argument('--reconfig', default=False, action='store_true',
1098 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001099 parser.add_argument('--sysroot', type='path',
1100 help='The sysroot in which to install the toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001101
Mike Frysinger0c808452014-11-06 17:30:23 -05001102 options = parser.parse_args(argv)
1103 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001104
Mike Frysinger35247af2012-11-16 18:58:06 -05001105 # Figure out what we're supposed to do and reject conflicting options.
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001106 if options.cfg_name and options.create_packages:
Mike Frysinger35247af2012-11-16 18:58:06 -05001107 parser.error('conflicting options: create-packages & show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001108
Gilad Arnold8195b532015-04-07 10:56:30 +03001109 targets_wanted = set(options.targets.split(','))
1110 boards_wanted = (set(options.include_boards.split(','))
1111 if options.include_boards else set())
1112 bricks_wanted = (set(options.include_bricks.split(','))
1113 if options.include_bricks else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001114
Rahul Chaudhry4b803052015-05-13 15:25:56 -07001115 # pylint: disable=global-statement
1116 global TARGET_GO_ENABLED
1117 if GetStablePackageVersion('sys-devel/crossdev', True) < '20150527':
1118 # Crossdev --ex-pkg flag was added in version 20150527.
1119 # Disable Go toolchain until the chroot gets a newer crossdev.
1120 TARGET_GO_ENABLED = ()
1121 if options.usepkg:
1122 # For bootstrapping, disable Go toolchain if using binary packages.
1123 # Allow the sdk bot to catch up and make binary packages available.
1124 TARGET_GO_ENABLED = ()
1125
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001126 if options.cfg_name:
1127 ShowConfig(options.cfg_name)
Mike Frysinger35247af2012-11-16 18:58:06 -05001128 elif options.create_packages:
1129 cros_build_lib.AssertInsideChroot()
1130 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001131 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001132 else:
1133 cros_build_lib.AssertInsideChroot()
1134 # This has to be always run as root.
1135 if os.geteuid() != 0:
1136 cros_build_lib.Die('this script must be run as root')
1137
1138 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001139 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001140 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001141 options.reconfig, targets_wanted, boards_wanted,
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001142 bricks_wanted, root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001143 Crossdev.Save()
1144
1145 return 0