blob: 9712f8fcb35487fd1f834b0d993592a6a566766e [file] [log] [blame]
Zdenek Behan508dcce2011-12-05 15:39:32 +01001#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This script manages the installed toolchains in the chroot.
7"""
8
9import copy
Mike Frysinger7ccee992012-06-01 21:27:59 -040010import json
Zdenek Behan508dcce2011-12-05 15:39:32 +010011import os
12import sys
13
Brian Harring503f3ab2012-03-09 21:39:41 -080014from chromite.buildbot import constants
David Jamese5867812012-10-19 12:02:20 -070015from chromite.buildbot import portage_utilities
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
Brian Harringaf019fb2012-05-10 15:06:13 -070018from chromite.lib import osutils
Zdenek Behan508dcce2011-12-05 15:39:32 +010019
Mike Frysinger31596002012-12-03 23:54:24 -050020if cros_build_lib.IsInsideChroot():
21 # Only import portage after we've checked that we're inside the chroot.
22 # Outside may not have portage, in which case the above may not happen.
23 # We'll check in main() if the operation needs portage.
24 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010025
26
Matt Tennantf1e30972012-03-02 16:30:07 -080027EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010028PACKAGE_STABLE = '[stable]'
29PACKAGE_NONE = '[none]'
30SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010031
32CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
33STABLE_OVERLAY = '/usr/local/portage/stable'
34CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010035
36
37# TODO: The versions are stored here very much like in setup_board.
38# The goal for future is to differentiate these using a config file.
39# This is done essentially by messing with GetDesiredPackageVersions()
40DEFAULT_VERSION = PACKAGE_STABLE
41DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010042}
43TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010044 'host' : {
45 'binutils' : '2.21.1',
46 'gdb' : PACKAGE_NONE,
47 },
48}
49# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
50CONFIG_TARGET_SUFFIXES = {
51 'binutils' : {
52 'i686-pc-linux-gnu' : '-gold',
53 'x86_64-cros-linux-gnu' : '-gold',
54 },
55}
Zdenek Behan508dcce2011-12-05 15:39:32 +010056# Global per-run cache that will be filled ondemand in by GetPackageMap()
57# function as needed.
58target_version_map = {
59}
60
61
David James66a09c42012-11-05 13:31:38 -080062class Crossdev(object):
63 """Class for interacting with crossdev and caching its output."""
64
65 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
66 _CACHE = {}
67
68 @classmethod
69 def Load(cls, reconfig):
70 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -080071 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
72 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -080073 if os.path.exists(cls._CACHE_FILE) and not reconfig:
74 with open(cls._CACHE_FILE) as f:
75 data = json.load(f)
David James90239b92012-11-05 15:31:34 -080076 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -080077 cls._CACHE = data
78
79 @classmethod
80 def Save(cls):
81 """Store crossdev cache on disk."""
82 # Save the cache from the successful run.
83 with open(cls._CACHE_FILE, 'w') as f:
84 json.dump(cls._CACHE, f)
85
86 @classmethod
87 def GetConfig(cls, target):
88 """Returns a map of crossdev provided variables about a tuple."""
89 CACHE_ATTR = '_target_tuple_map'
90
91 val = cls._CACHE.setdefault(CACHE_ATTR, {})
92 if not target in val:
93 # Find out the crossdev tuple.
94 target_tuple = target
95 if target == 'host':
96 target_tuple = GetHostTuple()
97 # Catch output of crossdev.
98 out = cros_build_lib.RunCommand(['crossdev', '--show-target-cfg',
99 '--ex-gdb', target_tuple],
100 print_cmd=False, redirect_stdout=True).output.splitlines()
101 # List of tuples split at the first '=', converted into dict.
102 val[target] = dict([x.split('=', 1) for x in out])
103 return val[target]
104
105 @classmethod
106 def UpdateTargets(cls, targets, usepkg, config_only=False):
107 """Calls crossdev to initialize a cross target.
108
109 Args:
110 targets - the list of targets to initialize using crossdev
111 usepkg - copies the commandline opts
112 config_only - Just update
113 """
114 configured_targets = cls._CACHE.setdefault('configured_targets', [])
115
116 cmdbase = ['crossdev', '--show-fail-log']
117 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
118 # Pick stable by default, and override as necessary.
119 cmdbase.extend(['-P', '--oneshot'])
120 if usepkg:
121 cmdbase.extend(['-P', '--getbinpkg',
122 '-P', '--usepkgonly',
123 '--without-headers'])
124
125 overlays = '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)
126 cmdbase.extend(['--overlays', overlays])
127 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
128
129 for target in targets:
130 if config_only and target in configured_targets:
131 continue
132
133 cmd = cmdbase + ['-t', target]
134
135 for pkg in GetTargetPackages(target):
136 if pkg == 'gdb':
137 # Gdb does not have selectable versions.
138 cmd.append('--ex-gdb')
139 continue
140 # The first of the desired versions is the "primary" one.
141 version = GetDesiredPackageVersions(target, pkg)[0]
142 cmd.extend(['--%s' % pkg, version])
143
144 cmd.extend(targets[target]['crossdev'].split())
145 if config_only:
146 # In this case we want to just quietly reinit
147 cmd.append('--init-target')
148 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
149 else:
150 cros_build_lib.RunCommand(cmd)
151
152 configured_targets.append(target)
153
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100154
Zdenek Behan508dcce2011-12-05 15:39:32 +0100155def GetPackageMap(target):
156 """Compiles a package map for the given target from the constants.
157
158 Uses a cache in target_version_map, that is dynamically filled in as needed,
159 since here everything is static data and the structuring is for ease of
160 configurability only.
161
162 args:
163 target - the target for which to return a version map
164
165 returns a map between packages and desired versions in internal format
166 (using the PACKAGE_* constants)
167 """
168 if target in target_version_map:
169 return target_version_map[target]
170
171 # Start from copy of the global defaults.
172 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
173
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100174 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100175 # prefer any specific overrides
176 if pkg in TARGET_VERSION_MAP.get(target, {}):
177 result[pkg] = TARGET_VERSION_MAP[target][pkg]
178 else:
179 # finally, if not already set, set a sane default
180 result.setdefault(pkg, DEFAULT_VERSION)
181 target_version_map[target] = result
182 return result
183
184
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100185def GetHostTuple():
David James90239b92012-11-05 15:31:34 -0800186 """Returns compiler tuple for the host system."""
Mike Frysinger506e75f2012-12-17 14:21:13 -0500187 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800188 return portage.settings['CHOST']
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100189
190
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100191def GetTargetPackages(target):
192 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800193 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100194 # Undesired packages are denoted by empty ${pkg}_pn variable.
195 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
196
197
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100198# Portage helper functions:
199def GetPortagePackage(target, package):
200 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800201 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100202 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100203 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100204 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100205 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100206 category = conf['category']
207 # Portage package:
208 pn = conf[package + '_pn']
209 # Final package name:
210 assert(category)
211 assert(pn)
212 return '%s/%s' % (category, pn)
213
214
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100215def IsPackageDisabled(target, package):
216 """Returns if the given package is not used for the target."""
217 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
218
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100219
220def GetTuplesForOverlays(overlays):
221 """Returns a set of tuples for a given set of overlays."""
Mike Frysinger506e75f2012-12-17 14:21:13 -0500222 targets = {}
Mike Frysinger7ccee992012-06-01 21:27:59 -0400223 default_settings = {
224 'sdk' : True,
225 'crossdev' : '',
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400226 'default' : False,
Mike Frysinger7ccee992012-06-01 21:27:59 -0400227 }
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100228
229 for overlay in overlays:
230 config = os.path.join(overlay, 'toolchain.conf')
231 if os.path.exists(config):
Mike Frysinger506e75f2012-12-17 14:21:13 -0500232 first_target = None
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400233 seen_default = False
234
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100235 for line in osutils.ReadFile(config).splitlines():
Mike Frysinger7ccee992012-06-01 21:27:59 -0400236 # Split by hash sign so that comments are ignored.
237 # Then split the line to get the tuple and its options.
238 line = line.split('#', 1)[0].split()
239
240 if len(line) > 0:
Mike Frysinger506e75f2012-12-17 14:21:13 -0500241 target = line[0]
242 if not first_target:
243 first_target = target
244 if target not in targets:
245 targets[target] = copy.copy(default_settings)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400246 if len(line) > 1:
Mike Frysinger506e75f2012-12-17 14:21:13 -0500247 targets[target].update(json.loads(' '.join(line[1:])))
248 if targets[target]['default']:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400249 seen_default = True
250
251 # If the user has not explicitly marked a toolchain as default,
252 # automatically select the first tuple that we saw in the conf.
Mike Frysinger506e75f2012-12-17 14:21:13 -0500253 if not seen_default and first_target:
254 targets[first_target]['default'] = True
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100255
Mike Frysinger506e75f2012-12-17 14:21:13 -0500256 return targets
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100257
258
Zdenek Behan508dcce2011-12-05 15:39:32 +0100259# Tree interface functions. They help with retrieving data about the current
260# state of the tree:
261def GetAllTargets():
262 """Get the complete list of targets.
263
264 returns the list of cross targets for the current tree
265 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400266 targets = GetToolchainsForBoard('all')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100267
268 # Remove the host target as that is not a cross-target. Replace with 'host'.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400269 del targets[GetHostTuple()]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100270 return targets
271
272
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100273def GetToolchainsForBoard(board):
274 """Get a list of toolchain tuples for a given board name
275
276 returns the list of toolchain tuples for the given board
277 """
David Jamese5867812012-10-19 12:02:20 -0700278 overlays = portage_utilities.FindOverlays(
279 constants.BOTH_OVERLAYS, None if board in ('all', 'sdk') else board)
Mike Frysingereaebb582012-06-19 13:04:53 -0400280 toolchains = GetTuplesForOverlays(overlays)
281 if board == 'sdk':
282 toolchains = FilterToolchains(toolchains, 'sdk', True)
283 return toolchains
284
285
286def FilterToolchains(targets, key, value):
287 """Filter out targets based on their attributes.
288
289 args:
290 targets - dict of toolchains
291 key - metadata to examine
292 value - expected value for metadata
293
294 returns a dict where all targets whose metadata key does not match value
295 have been deleted.
296 """
Mike Frysinger506e75f2012-12-17 14:21:13 -0500297 return dict((k, v) for k, v in targets.iteritems() if v[key] == value)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100298
299
David James66a09c42012-11-05 13:31:38 -0800300def GetInstalledPackageVersions(atom):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100301 """Extracts the list of current versions of a target, package pair.
302
303 args:
David James66a09c42012-11-05 13:31:38 -0800304 atom - the atom to operate on (e.g. sys-devel/gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100305
306 returns the list of versions of the package currently installed.
307 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100308 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500309 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800310 for pkg in portage.db['/']['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100311 version = portage.versions.cpv_getversion(pkg)
312 versions.append(version)
313 return versions
314
315
David James90239b92012-11-05 15:31:34 -0800316def GetStablePackageVersion(atom, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100317 """Extracts the current stable version for a given package.
318
319 args:
320 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200321 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100322
323 returns a string containing the latest version.
324 """
David James90239b92012-11-05 15:31:34 -0800325 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500326 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800327 cpv = portage.best(portage.db['/'][pkgtype].dbapi.match(atom, use_cache=0))
328 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100329
330
Zdenek Behan699ddd32012-04-13 07:14:08 +0200331def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100332 """Resolves keywords in a given version list for a particular package.
333
334 Resolving means replacing PACKAGE_STABLE with the actual number.
335
336 args:
337 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
338 versions - list of versions to resolve
339
340 returns list of purely numeric versions equivalent to argument
341 """
342 resolved = []
David James90239b92012-11-05 15:31:34 -0800343 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100344 for version in versions:
345 if version == PACKAGE_STABLE:
David James90239b92012-11-05 15:31:34 -0800346 resolved.append(GetStablePackageVersion(atom, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347 elif version != PACKAGE_NONE:
348 resolved.append(version)
349 return resolved
350
351
352def GetDesiredPackageVersions(target, package):
353 """Produces the list of desired versions for each target, package pair.
354
355 The first version in the list is implicitly treated as primary, ie.
356 the version that will be initialized by crossdev and selected.
357
358 If the version is PACKAGE_STABLE, it really means the current version which
359 is emerged by using the package atom with no particular version key.
360 Since crossdev unmasks all packages by default, this will actually
361 mean 'unstable' in most cases.
362
363 args:
364 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
365
366 returns a list composed of either a version string, PACKAGE_STABLE
367 """
368 packagemap = GetPackageMap(target)
369
370 versions = []
371 if package in packagemap:
372 versions.append(packagemap[package])
373
374 return versions
375
376
377def TargetIsInitialized(target):
378 """Verifies if the given list of targets has been correctly initialized.
379
380 This determines whether we have to call crossdev while emerging
381 toolchain packages or can do it using emerge. Emerge is naturally
382 preferred, because all packages can be updated in a single pass.
383
384 args:
385 targets - list of individual cross targets which are checked
386
387 returns True if target is completely initialized
388 returns False otherwise
389 """
390 # Check if packages for the given target all have a proper version.
391 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100392 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800393 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100394 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800395 if not IsPackageDisabled(target, package) and not (
396 GetStablePackageVersion(atom, True) and
397 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100398 return False
399 return True
400 except cros_build_lib.RunCommandError:
401 # Fails - The target has likely never been initialized before.
402 return False
403
404
405def RemovePackageMask(target):
406 """Removes a package.mask file for the given platform.
407
408 The pre-existing package.mask files can mess with the keywords.
409
410 args:
411 target - the target for which to remove the file
412 """
413 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700414 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415
416
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417# Main functions performing the actual update steps.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100418def UpdateTargets(targets, usepkg):
419 """Determines which packages need update/unmerge and defers to portage.
420
421 args:
422 targets - the list of targets to update
423 usepkg - copies the commandline option
424 """
David James90239b92012-11-05 15:31:34 -0800425 # Remove keyword files created by old versions of cros_setup_toolchains.
426 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100427
428 # For each target, we do two things. Figure out the list of updates,
429 # and figure out the appropriate keywords/masks. Crossdev will initialize
430 # these, but they need to be regenerated on every update.
431 print 'Determining required toolchain updates...'
David James90239b92012-11-05 15:31:34 -0800432 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433 for target in targets:
434 # Record the highest needed version for each target, for masking purposes.
435 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100436 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100438 if IsPackageDisabled(target, package):
439 continue
440 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800441 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200443 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100444 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100445
Zdenek Behan508dcce2011-12-05 15:39:32 +0100446 packages = []
447 for pkg in mergemap:
448 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200449 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800450 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100451
452 if not packages:
453 print 'Nothing to update!'
David Jamesf8c672f2012-11-06 13:38:11 -0800454 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455
456 print 'Updating packages:'
457 print packages
458
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100459 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460 if usepkg:
461 cmd.extend(['--getbinpkg', '--usepkgonly'])
462
463 cmd.extend(packages)
464 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800465 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100466
467
468def CleanTargets(targets):
469 """Unmerges old packages that are assumed unnecessary."""
470 unmergemap = {}
471 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100472 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100473 if IsPackageDisabled(target, package):
474 continue
475 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800476 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100477 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200478 desired_num = VersionListToNumeric(target, package, desired, True)
479 if not set(desired_num).issubset(current):
480 print 'Some packages have been held back, skipping clean!'
481 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100482 unmergemap[pkg] = set(current).difference(desired_num)
483
484 # Cleaning doesn't care about consistency and rebuilding package.* files.
485 packages = []
486 for pkg, vers in unmergemap.iteritems():
487 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
488
489 if packages:
490 print 'Cleaning packages:'
491 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100492 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100493 cmd.extend(packages)
494 cros_build_lib.RunCommand(cmd)
495 else:
496 print 'Nothing to clean!'
497
498
499def SelectActiveToolchains(targets, suffixes):
500 """Runs gcc-config and binutils-config to select the desired.
501
502 args:
503 targets - the targets to select
504 """
505 for package in ['gcc', 'binutils']:
506 for target in targets:
507 # Pick the first version in the numbered list as the selected one.
508 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200509 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510 desired = desired_num[0]
511 # *-config does not play revisions, strip them, keep just PV.
512 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
513
514 if target == 'host':
515 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100516 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517
518 # And finally, attach target to it.
519 desired = '%s-%s' % (target, desired)
520
521 # Target specific hacks
522 if package in suffixes:
523 if target in suffixes[package]:
524 desired += suffixes[package][target]
525
David James7ec5efc2012-11-06 09:39:49 -0800526 extra_env = {'CHOST': target}
527 cmd = ['%s-config' % package, '-c', target]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
David James7ec5efc2012-11-06 09:39:49 -0800529 redirect_stdout=True, extra_env=extra_env).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100530 # Do not gcc-config when the current is live or nothing needs to be done.
531 if current != desired and current != '9999':
532 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100533 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534
535
David Jamesf8c672f2012-11-06 13:38:11 -0800536def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
537 targets_wanted, boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538 """Performs all steps to create a synchronized toolchain enviroment.
539
540 args:
541 arguments correspond to the given commandline flags
542 """
David Jamesf8c672f2012-11-06 13:38:11 -0800543 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100544 if not hostonly:
545 # For hostonly, we can skip most of the below logic, much of which won't
546 # work on bare systems where this is useful.
547 alltargets = GetAllTargets()
Mike Frysinger7ccee992012-06-01 21:27:59 -0400548 targets_wanted = set(targets_wanted)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100549 if targets_wanted == set(['all']):
Mike Frysinger7ccee992012-06-01 21:27:59 -0400550 targets = alltargets
Mike Frysingereaebb582012-06-19 13:04:53 -0400551 elif targets_wanted == set(['sdk']):
552 # Filter out all the non-sdk toolchains as we don't want to mess
553 # with those in all of our builds.
554 targets = FilterToolchains(alltargets, 'sdk', True)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100555 else:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100556 # Verify user input.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400557 nonexistant = []
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100558 for target in targets_wanted:
559 if target not in alltargets:
560 nonexistant.append(target)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400561 else:
562 targets[target] = alltargets[target]
563 if nonexistant:
564 cros_build_lib.Die('Invalid targets: ' + ','.join(nonexistant))
565
Mike Frysinger7ccee992012-06-01 21:27:59 -0400566 # Now re-add any targets that might be from this board. This is
567 # to allow unofficial boards to declare their own toolchains.
568 for board in boards_wanted:
569 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100570
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100571 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400572 for target in targets:
573 if TargetIsInitialized(target):
574 reconfig_targets[target] = targets[target]
575 else:
576 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100577 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200578 print 'The following targets need to be re-initialized:'
579 print crossdev_targets
David James66a09c42012-11-05 13:31:38 -0800580 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200581 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800582 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100583
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100584 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400585 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100586
587 # Now update all packages.
David Jamesf8c672f2012-11-06 13:38:11 -0800588 if UpdateTargets(targets, usepkg) or crossdev_targets or reconfig:
589 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
David James7ec5efc2012-11-06 09:39:49 -0800590
591 if deleteold:
592 CleanTargets(targets)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593
594
Brian Harring30675052012-02-29 12:18:22 -0800595def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100596 usage = """usage: %prog [options]
597
Mike Frysinger506e75f2012-12-17 14:21:13 -0500598 The script installs and updates the toolchains in your chroot."""
599 parser = commandline.OptionParser(usage)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100600 parser.add_option('-u', '--nousepkg',
601 action='store_false', dest='usepkg', default=True,
Mike Frysinger506e75f2012-12-17 14:21:13 -0500602 help='Use prebuilt packages if possible')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603 parser.add_option('-d', '--deleteold',
604 action='store_true', dest='deleteold', default=False,
Mike Frysinger506e75f2012-12-17 14:21:13 -0500605 help='Unmerge deprecated packages')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100606 parser.add_option('-t', '--targets',
Mike Frysingereaebb582012-06-19 13:04:53 -0400607 dest='targets', default='sdk',
Mike Frysinger506e75f2012-12-17 14:21:13 -0500608 help='Comma separated list of tuples. '
609 'Special keyword \'host\' is allowed. Default: sdk')
Mike Frysinger7ccee992012-06-01 21:27:59 -0400610 parser.add_option('--include-boards',
611 dest='include_boards', default='',
Mike Frysinger506e75f2012-12-17 14:21:13 -0500612 help='Comma separated list of boards whose toolchains we'
613 ' will always include. Default: none')
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100614 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100615 dest='hostonly', default=False, action='store_true',
Mike Frysinger506e75f2012-12-17 14:21:13 -0500616 help='Only setup the host toolchain. '
617 'Useful for bootstrapping chroot')
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100618 parser.add_option('--show-board-cfg',
619 dest='board_cfg', default=None,
Mike Frysinger506e75f2012-12-17 14:21:13 -0500620 help='Board to list toolchain tuples for')
David James66a09c42012-11-05 13:31:38 -0800621 parser.add_option('--reconfig', default=False, action='store_true',
Mike Frysinger506e75f2012-12-17 14:21:13 -0500622 help='Reload crossdev config and reselect toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100623
Brian Harring30675052012-02-29 12:18:22 -0800624 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100625
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100626 if options.board_cfg:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400627 toolchains = GetToolchainsForBoard(options.board_cfg)
628 # Make sure we display the default toolchain first.
Mike Frysinger506e75f2012-12-17 14:21:13 -0500629 targets = toolchains.keys()
630 for target in targets:
631 if toolchains[target]['default']:
632 targets.remove(target)
633 targets.insert(0, target)
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400634 break
Mike Frysinger506e75f2012-12-17 14:21:13 -0500635 print ','.join(targets)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100636 return 0
637
Mike Frysinger31596002012-12-03 23:54:24 -0500638 cros_build_lib.AssertInsideChroot()
639
Mike Frysinger984d0622012-06-01 16:08:44 -0400640 # This has to be always run as root.
641 if not os.getuid() == 0:
642 print "%s: This script must be run as root!" % sys.argv[0]
643 sys.exit(1)
644
Zdenek Behan508dcce2011-12-05 15:39:32 +0100645 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400646 boards = set(options.include_boards.split(',')) if options.include_boards \
647 else set()
David James66a09c42012-11-05 13:31:38 -0800648 Crossdev.Load(options.reconfig)
David Jamesf8c672f2012-11-06 13:38:11 -0800649 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
650 options.reconfig, targets, boards)
David James66a09c42012-11-05 13:31:38 -0800651 Crossdev.Save()