blob: 7c91b7624913a647a316f34a0c7aba672e85b242 [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
Zdenek Behan7e33b4e2012-03-12 17:00:56 +010010import errno
Zdenek Behan508dcce2011-12-05 15:39:32 +010011import optparse
12import os
13import sys
14
Brian Harring503f3ab2012-03-09 21:39:41 -080015from chromite.buildbot import constants
16from chromite.lib import cros_build_lib
Brian Harringaf019fb2012-05-10 15:06:13 -070017from chromite.lib import osutils
Zdenek Behan508dcce2011-12-05 15:39:32 +010018
19# Some sanity checks first.
20if not cros_build_lib.IsInsideChroot():
21 print '%s: This needs to be run inside the chroot' % sys.argv[0]
22 sys.exit(1)
23# Only import portage after we've checked that we're inside the chroot.
24# Outside may not have portage, in which case the above may not happen.
25import portage
26
27
Matt Tennantf1e30972012-03-02 16:30:07 -080028EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan331f9822012-04-13 05:02:36 +020029CROS_OVERLAY_LIST_CMD = os.path.join(
30 constants.SOURCE_ROOT, 'src/platform/dev/host/cros_overlay_list')
Zdenek Behan508dcce2011-12-05 15:39:32 +010031PACKAGE_STABLE = '[stable]'
32PACKAGE_NONE = '[none]'
33SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010034
35CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
36STABLE_OVERLAY = '/usr/local/portage/stable'
37CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010038
39
40# TODO: The versions are stored here very much like in setup_board.
41# The goal for future is to differentiate these using a config file.
42# This is done essentially by messing with GetDesiredPackageVersions()
43DEFAULT_VERSION = PACKAGE_STABLE
44DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010045}
46TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010047 'host' : {
48 'binutils' : '2.21.1',
49 'gdb' : PACKAGE_NONE,
50 },
51}
52# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
53CONFIG_TARGET_SUFFIXES = {
54 'binutils' : {
55 'i686-pc-linux-gnu' : '-gold',
56 'x86_64-cros-linux-gnu' : '-gold',
57 },
58}
Zdenek Behan508dcce2011-12-05 15:39:32 +010059# Global per-run cache that will be filled ondemand in by GetPackageMap()
60# function as needed.
61target_version_map = {
62}
63
64
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010065# Global variable cache. It has to be a descendant of 'object', rather than
66# instance thereof, because attributes cannot be set on 'object' instances.
67class VariableCache(object):
68 pass
69VAR_CACHE = VariableCache()
70
71
Zdenek Behan508dcce2011-12-05 15:39:32 +010072def GetPackageMap(target):
73 """Compiles a package map for the given target from the constants.
74
75 Uses a cache in target_version_map, that is dynamically filled in as needed,
76 since here everything is static data and the structuring is for ease of
77 configurability only.
78
79 args:
80 target - the target for which to return a version map
81
82 returns a map between packages and desired versions in internal format
83 (using the PACKAGE_* constants)
84 """
85 if target in target_version_map:
86 return target_version_map[target]
87
88 # Start from copy of the global defaults.
89 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
90
Zdenek Behanf4d18a02012-03-22 15:45:05 +010091 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +010092 # prefer any specific overrides
93 if pkg in TARGET_VERSION_MAP.get(target, {}):
94 result[pkg] = TARGET_VERSION_MAP[target][pkg]
95 else:
96 # finally, if not already set, set a sane default
97 result.setdefault(pkg, DEFAULT_VERSION)
98 target_version_map[target] = result
99 return result
100
101
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100102def GetHostTuple():
103 """Returns compiler tuple for the host system.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100104
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100105 Caches the result, because the command can be fairly expensive, and never
106 changes throughout a single run.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100107 """
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100108 CACHE_ATTR = '_host_tuple'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100109
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100110 val = getattr(VAR_CACHE, CACHE_ATTR, None)
111 if val is None:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100112 val = portage.settings['CHOST']
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100113 setattr(VAR_CACHE, CACHE_ATTR, val)
114 return val
115
116
117def GetCrossdevConf(target):
118 """Returns a map of crossdev provided variables about a tuple."""
119 CACHE_ATTR = '_target_tuple_map'
120
121 val = getattr(VAR_CACHE, CACHE_ATTR, {})
122 if not target in val:
123 # Find out the crossdev tuple.
124 target_tuple = target
125 if target == 'host':
126 target_tuple = GetHostTuple()
127 # Catch output of crossdev.
128 out = cros_build_lib.RunCommand(['crossdev', '--show-target-cfg',
129 '--ex-gdb', target_tuple],
130 print_cmd=False, redirect_stdout=True).output.splitlines()
131 # List of tuples split at the first '=', converted into dict.
132 val[target] = dict([x.split('=', 1) for x in out])
133 setattr(VAR_CACHE, CACHE_ATTR, {})
134 return val[target]
135
136
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100137def GetTargetPackages(target):
138 """Returns a list of packages for a given target."""
139 conf = GetCrossdevConf(target)
140 # Undesired packages are denoted by empty ${pkg}_pn variable.
141 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
142
143
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100144# Portage helper functions:
145def GetPortagePackage(target, package):
146 """Returns a package name for the given target."""
147 conf = GetCrossdevConf(target)
148 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100149 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100150 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100151 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100152 category = conf['category']
153 # Portage package:
154 pn = conf[package + '_pn']
155 # Final package name:
156 assert(category)
157 assert(pn)
158 return '%s/%s' % (category, pn)
159
160
161def GetPortageKeyword(target):
162 """Returns a portage friendly keyword for a given target."""
163 return GetCrossdevConf(target)['arch']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100164
165
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100166def IsPackageDisabled(target, package):
167 """Returns if the given package is not used for the target."""
168 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
169
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100170
171def GetTuplesForOverlays(overlays):
172 """Returns a set of tuples for a given set of overlays."""
173 tuples = set()
174
175 for overlay in overlays:
176 config = os.path.join(overlay, 'toolchain.conf')
177 if os.path.exists(config):
178 for line in osutils.ReadFile(config).splitlines():
179 # Split by hash sign so that comments are ignored
180 line = line.split('#', 1)[0]
181 tuples.update(line.split())
182
183 return tuples
184
185
Zdenek Behan508dcce2011-12-05 15:39:32 +0100186# Tree interface functions. They help with retrieving data about the current
187# state of the tree:
188def GetAllTargets():
189 """Get the complete list of targets.
190
191 returns the list of cross targets for the current tree
192 """
Zdenek Behan331f9822012-04-13 05:02:36 +0200193 cmd = [CROS_OVERLAY_LIST_CMD, '--all_boards']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100194 overlays = cros_build_lib.RunCommand(cmd, print_cmd=False,
195 redirect_stdout=True).output.splitlines()
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100196
197 targets = GetTuplesForOverlays(overlays)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100198
199 # Remove the host target as that is not a cross-target. Replace with 'host'.
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100200 targets.discard(GetHostTuple())
Zdenek Behan508dcce2011-12-05 15:39:32 +0100201 return targets
202
203
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100204def GetToolchainsForBoard(board):
205 """Get a list of toolchain tuples for a given board name
206
207 returns the list of toolchain tuples for the given board
208 """
209 cmd = [CROS_OVERLAY_LIST_CMD, '--board=' + board]
210 overlays = cros_build_lib.RunCommand(
211 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
212
213 return GetTuplesForOverlays(overlays)
214
215
Zdenek Behan508dcce2011-12-05 15:39:32 +0100216def GetInstalledPackageVersions(target, package):
217 """Extracts the list of current versions of a target, package pair.
218
219 args:
220 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
221
222 returns the list of versions of the package currently installed.
223 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100224 versions = []
225 # This is the package name in terms of portage.
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100226 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100227 for pkg in portage.db['/']['vartree'].dbapi.match(atom):
228 version = portage.versions.cpv_getversion(pkg)
229 versions.append(version)
230 return versions
231
232
Zdenek Behan699ddd32012-04-13 07:14:08 +0200233def GetStablePackageVersion(target, package, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100234 """Extracts the current stable version for a given package.
235
236 args:
237 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200238 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100239
240 returns a string containing the latest version.
241 """
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200242 def mass_portageq_splitline(entry):
243 """Splits the output of mass_best_visible into package:version tuple."""
244 # mass_best_visible returns lines of the format "package:cpv"
245 split_string = entry.split(':', 1)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200246 if split_string[1]:
247 split_string[1] = portage.versions.cpv_getversion(split_string[1])
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200248 return split_string
249
250 CACHE_ATTR = '_target_stable_map'
251
Zdenek Behan699ddd32012-04-13 07:14:08 +0200252 pkgtype = "installed" if installed else "ebuild"
253
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200254 val = getattr(VAR_CACHE, CACHE_ATTR, {})
255 if not target in val:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200256 val[target] = {}
257 if not pkgtype in val[target]:
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200258 keyword = GetPortageKeyword(target)
259 extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
260 # Evaluate all packages for a target in one swoop, because it's much faster.
261 pkgs = [GetPortagePackage(target, p) for p in GetTargetPackages(target)]
Zdenek Behan699ddd32012-04-13 07:14:08 +0200262 cmd = ['portageq', 'mass_best_visible', '/', pkgtype] + pkgs
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200263 cpvs = cros_build_lib.RunCommand(cmd,
264 print_cmd=False, redirect_stdout=True,
265 extra_env=extra_env).output.splitlines()
Zdenek Behan699ddd32012-04-13 07:14:08 +0200266 val[target][pkgtype] = dict(map(mass_portageq_splitline, cpvs))
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200267 setattr(VAR_CACHE, CACHE_ATTR, val)
268
Zdenek Behan699ddd32012-04-13 07:14:08 +0200269 return val[target][pkgtype][GetPortagePackage(target, package)]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100270
271
Zdenek Behan699ddd32012-04-13 07:14:08 +0200272def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100273 """Resolves keywords in a given version list for a particular package.
274
275 Resolving means replacing PACKAGE_STABLE with the actual number.
276
277 args:
278 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
279 versions - list of versions to resolve
280
281 returns list of purely numeric versions equivalent to argument
282 """
283 resolved = []
284 for version in versions:
285 if version == PACKAGE_STABLE:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200286 resolved.append(GetStablePackageVersion(target, package, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100287 elif version != PACKAGE_NONE:
288 resolved.append(version)
289 return resolved
290
291
292def GetDesiredPackageVersions(target, package):
293 """Produces the list of desired versions for each target, package pair.
294
295 The first version in the list is implicitly treated as primary, ie.
296 the version that will be initialized by crossdev and selected.
297
298 If the version is PACKAGE_STABLE, it really means the current version which
299 is emerged by using the package atom with no particular version key.
300 Since crossdev unmasks all packages by default, this will actually
301 mean 'unstable' in most cases.
302
303 args:
304 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
305
306 returns a list composed of either a version string, PACKAGE_STABLE
307 """
308 packagemap = GetPackageMap(target)
309
310 versions = []
311 if package in packagemap:
312 versions.append(packagemap[package])
313
314 return versions
315
316
317def TargetIsInitialized(target):
318 """Verifies if the given list of targets has been correctly initialized.
319
320 This determines whether we have to call crossdev while emerging
321 toolchain packages or can do it using emerge. Emerge is naturally
322 preferred, because all packages can be updated in a single pass.
323
324 args:
325 targets - list of individual cross targets which are checked
326
327 returns True if target is completely initialized
328 returns False otherwise
329 """
330 # Check if packages for the given target all have a proper version.
331 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100332 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100333 # Do we even want this package && is it initialized?
334 if not IsPackageDisabled(target, package) and \
335 not GetInstalledPackageVersions(target, package):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100336 return False
337 return True
338 except cros_build_lib.RunCommandError:
339 # Fails - The target has likely never been initialized before.
340 return False
341
342
343def RemovePackageMask(target):
344 """Removes a package.mask file for the given platform.
345
346 The pre-existing package.mask files can mess with the keywords.
347
348 args:
349 target - the target for which to remove the file
350 """
351 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700352 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100353
354
355def CreatePackageMask(target, masks):
356 """[Re]creates a package.mask file for the given platform.
357
358 args:
359 target - the given target on which to operate
360 masks - a map of package : version,
361 where version is the highest permissible version (mask >)
362 """
363 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
364 assert not os.path.exists(maskfile)
Brian Harringaf019fb2012-05-10 15:06:13 -0700365 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100366
367 with open(maskfile, 'w') as f:
368 for pkg, m in masks.items():
369 f.write('>%s-%s\n' % (pkg, m))
370
371
372def CreatePackageKeywords(target):
373 """[Re]create a package.keywords file for the platform.
374
375 This sets all package.keywords files to unmask all stable/testing packages.
376 TODO: Note that this approach should be deprecated and is only done for
377 compatibility reasons. In the future, we'd like to stop using keywords
378 altogether, and keep just stable unmasked.
379
380 args:
381 target - target for which to recreate package.keywords
382 """
383 maskfile = os.path.join('/etc/portage/package.keywords', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700384 osutils.SafeUnlink(maskfile)
385
386 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100387
388 keyword = GetPortageKeyword(target)
389
390 with open(maskfile, 'w') as f:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100391 for pkg in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100392 if IsPackageDisabled(target, pkg):
393 continue
394 f.write('%s %s ~%s\n' %
395 (GetPortagePackage(target, pkg), keyword, keyword))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100396
397
398# Main functions performing the actual update steps.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200399def UpdateCrossdevTargets(targets, usepkg, config_only=False):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100400 """Calls crossdev to initialize a cross target.
401 args:
402 targets - the list of targets to initialize using crossdev
403 usepkg - copies the commandline opts
404 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100405 for target in targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200406 cmd = ['crossdev', '--show-fail-log', '-t', target]
407 cmd.extend(['--env', 'FEATURES=splitdebug'])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100408 # Pick stable by default, and override as necessary.
409 cmd.extend(['-P', '--oneshot'])
410 if usepkg:
411 cmd.extend(['-P', '--getbinpkg',
412 '-P', '--usepkgonly',
413 '--without-headers'])
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100414
415 cmd.extend(['--overlays', '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)])
416 cmd.extend(['--ov-output', CROSSDEV_OVERLAY])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100418 for pkg in GetTargetPackages(target):
419 if pkg == 'gdb':
420 # Gdb does not have selectable versions.
421 cmd.append('--ex-gdb')
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100422 continue
Zdenek Behan508dcce2011-12-05 15:39:32 +0100423 # The first of the desired versions is the "primary" one.
424 version = GetDesiredPackageVersions(target, pkg)[0]
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100425 cmd.extend(['--%s' % pkg, version])
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200426
427 if config_only:
428 # In this case we want to just quietly reinit
429 cmd.append('--init-target')
430 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
431 else:
432 cros_build_lib.RunCommand(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433
434
435def UpdateTargets(targets, usepkg):
436 """Determines which packages need update/unmerge and defers to portage.
437
438 args:
439 targets - the list of targets to update
440 usepkg - copies the commandline option
441 """
442 # TODO(zbehan): This process is rather complex due to package.* handling.
443 # With some semantic changes over the original setup_board functionality,
444 # it can be considerably cleaned up.
445 mergemap = {}
446
447 # For each target, we do two things. Figure out the list of updates,
448 # and figure out the appropriate keywords/masks. Crossdev will initialize
449 # these, but they need to be regenerated on every update.
450 print 'Determining required toolchain updates...'
451 for target in targets:
452 # Record the highest needed version for each target, for masking purposes.
453 RemovePackageMask(target)
454 CreatePackageKeywords(target)
455 packagemasks = {}
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100456 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100457 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100458 if IsPackageDisabled(target, package):
459 continue
460 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100461 current = GetInstalledPackageVersions(target, package)
462 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200463 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100464 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100465
466 # Pick the highest version for mask.
467 packagemasks[pkg] = portage.versions.best(desired_num)
468
469 CreatePackageMask(target, packagemasks)
470
471 packages = []
472 for pkg in mergemap:
473 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200474 if ver != PACKAGE_NONE:
475 # Be a little more permissive for usepkg, the binaries may not exist.
476 packages.append('%s%s-%s' % ('<=' if usepkg else '=', pkg, ver))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100477
478 if not packages:
479 print 'Nothing to update!'
480 return
481
482 print 'Updating packages:'
483 print packages
484
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100485 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100486 if usepkg:
487 cmd.extend(['--getbinpkg', '--usepkgonly'])
488
489 cmd.extend(packages)
490 cros_build_lib.RunCommand(cmd)
491
492
493def CleanTargets(targets):
494 """Unmerges old packages that are assumed unnecessary."""
495 unmergemap = {}
496 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100497 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100498 if IsPackageDisabled(target, package):
499 continue
500 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100501 current = GetInstalledPackageVersions(target, package)
502 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200503 desired_num = VersionListToNumeric(target, package, desired, True)
504 if not set(desired_num).issubset(current):
505 print 'Some packages have been held back, skipping clean!'
506 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100507 unmergemap[pkg] = set(current).difference(desired_num)
508
509 # Cleaning doesn't care about consistency and rebuilding package.* files.
510 packages = []
511 for pkg, vers in unmergemap.iteritems():
512 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
513
514 if packages:
515 print 'Cleaning packages:'
516 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100517 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100518 cmd.extend(packages)
519 cros_build_lib.RunCommand(cmd)
520 else:
521 print 'Nothing to clean!'
522
523
524def SelectActiveToolchains(targets, suffixes):
525 """Runs gcc-config and binutils-config to select the desired.
526
527 args:
528 targets - the targets to select
529 """
530 for package in ['gcc', 'binutils']:
531 for target in targets:
532 # Pick the first version in the numbered list as the selected one.
533 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200534 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535 desired = desired_num[0]
536 # *-config does not play revisions, strip them, keep just PV.
537 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
538
539 if target == 'host':
540 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100541 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100542
543 # And finally, attach target to it.
544 desired = '%s-%s' % (target, desired)
545
546 # Target specific hacks
547 if package in suffixes:
548 if target in suffixes[package]:
549 desired += suffixes[package][target]
550
551 cmd = [ package + '-config', '-c', target ]
552 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200553 redirect_stdout=True).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100554 # Do not gcc-config when the current is live or nothing needs to be done.
555 if current != desired and current != '9999':
556 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100557 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558
559
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100560def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561 """Performs all steps to create a synchronized toolchain enviroment.
562
563 args:
564 arguments correspond to the given commandline flags
565 """
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100566 targets = set()
567 if not hostonly:
568 # For hostonly, we can skip most of the below logic, much of which won't
569 # work on bare systems where this is useful.
570 alltargets = GetAllTargets()
571 nonexistant = []
572 if targets_wanted == set(['all']):
573 targets = set(alltargets)
574 else:
575 targets = set(targets_wanted)
576 # Verify user input.
577 for target in targets_wanted:
578 if target not in alltargets:
579 nonexistant.append(target)
580 if nonexistant:
581 raise Exception("Invalid targets: " + ','.join(nonexistant))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100582
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100583 # First check and initialize all cross targets that need to be.
584 crossdev_targets = \
585 [t for t in targets if not TargetIsInitialized(t)]
586 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200587 print 'The following targets need to be re-initialized:'
588 print crossdev_targets
589 UpdateCrossdevTargets(crossdev_targets, usepkg)
590 # Those that were not initialized may need a config update.
591 reconfig_targets = targets.difference(set(crossdev_targets))
592 UpdateCrossdevTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100594 # We want host updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595 targets.add('host')
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100596
597 # Now update all packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100598 UpdateTargets(targets, usepkg)
599 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
600
601 if deleteold:
602 CleanTargets(targets)
603
604
Brian Harring30675052012-02-29 12:18:22 -0800605def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100606 usage = """usage: %prog [options]
607
608 The script installs and updates the toolchains in your chroot.
609 """
610 parser = optparse.OptionParser(usage)
611 parser.add_option('-u', '--nousepkg',
612 action='store_false', dest='usepkg', default=True,
613 help=('Use prebuilt packages if possible.'))
614 parser.add_option('-d', '--deleteold',
615 action='store_true', dest='deleteold', default=False,
616 help=('Unmerge deprecated packages.'))
617 parser.add_option('-t', '--targets',
618 dest='targets', default='all',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100619 help=('Comma separated list of tuples. '
620 'Special keyword \'host\' is allowed. Default: all.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100621 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100622 dest='hostonly', default=False, action='store_true',
623 help=('Only setup the host toolchain. '
624 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100625 parser.add_option('--show-board-cfg',
626 dest='board_cfg', default=None,
627 help=('Board to list toolchain tuples for'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100628
Brian Harring30675052012-02-29 12:18:22 -0800629 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100630
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100631 if options.board_cfg:
632 print ','.join(GetToolchainsForBoard(options.board_cfg))
633 return 0
634
Mike Frysinger984d0622012-06-01 16:08:44 -0400635 # This has to be always run as root.
636 if not os.getuid() == 0:
637 print "%s: This script must be run as root!" % sys.argv[0]
638 sys.exit(1)
639
Zdenek Behan508dcce2011-12-05 15:39:32 +0100640 targets = set(options.targets.split(','))
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100641 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets)