blob: 83f3e3e1e63a23e40cbc92cec6af1831b6b07b2d [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
Zdenek Behan508dcce2011-12-05 15:39:32 +010017
18# Some sanity checks first.
19if not cros_build_lib.IsInsideChroot():
20 print '%s: This needs to be run inside the chroot' % sys.argv[0]
21 sys.exit(1)
22# Only import portage after we've checked that we're inside the chroot.
23# Outside may not have portage, in which case the above may not happen.
24import portage
25
26
Matt Tennantf1e30972012-03-02 16:30:07 -080027EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan331f9822012-04-13 05:02:36 +020028CROS_OVERLAY_LIST_CMD = os.path.join(
29 constants.SOURCE_ROOT, 'src/platform/dev/host/cros_overlay_list')
Zdenek Behan508dcce2011-12-05 15:39:32 +010030PACKAGE_STABLE = '[stable]'
31PACKAGE_NONE = '[none]'
32SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010033
34CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
35STABLE_OVERLAY = '/usr/local/portage/stable'
36CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010037
38
39# TODO: The versions are stored here very much like in setup_board.
40# The goal for future is to differentiate these using a config file.
41# This is done essentially by messing with GetDesiredPackageVersions()
42DEFAULT_VERSION = PACKAGE_STABLE
43DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010044}
45TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010046 'host' : {
47 'binutils' : '2.21.1',
48 'gdb' : PACKAGE_NONE,
49 },
50}
51# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
52CONFIG_TARGET_SUFFIXES = {
53 'binutils' : {
54 'i686-pc-linux-gnu' : '-gold',
55 'x86_64-cros-linux-gnu' : '-gold',
56 },
57}
Zdenek Behan508dcce2011-12-05 15:39:32 +010058# Global per-run cache that will be filled ondemand in by GetPackageMap()
59# function as needed.
60target_version_map = {
61}
62
63
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010064# Global variable cache. It has to be a descendant of 'object', rather than
65# instance thereof, because attributes cannot be set on 'object' instances.
66class VariableCache(object):
67 pass
68VAR_CACHE = VariableCache()
69
70
Zdenek Behan508dcce2011-12-05 15:39:32 +010071def GetPackageMap(target):
72 """Compiles a package map for the given target from the constants.
73
74 Uses a cache in target_version_map, that is dynamically filled in as needed,
75 since here everything is static data and the structuring is for ease of
76 configurability only.
77
78 args:
79 target - the target for which to return a version map
80
81 returns a map between packages and desired versions in internal format
82 (using the PACKAGE_* constants)
83 """
84 if target in target_version_map:
85 return target_version_map[target]
86
87 # Start from copy of the global defaults.
88 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
89
Zdenek Behanf4d18a02012-03-22 15:45:05 +010090 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +010091 # prefer any specific overrides
92 if pkg in TARGET_VERSION_MAP.get(target, {}):
93 result[pkg] = TARGET_VERSION_MAP[target][pkg]
94 else:
95 # finally, if not already set, set a sane default
96 result.setdefault(pkg, DEFAULT_VERSION)
97 target_version_map[target] = result
98 return result
99
100
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100101def GetHostTuple():
102 """Returns compiler tuple for the host system.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100103
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100104 Caches the result, because the command can be fairly expensive, and never
105 changes throughout a single run.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100106 """
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100107 CACHE_ATTR = '_host_tuple'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100108
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100109 val = getattr(VAR_CACHE, CACHE_ATTR, None)
110 if val is None:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100111 val = portage.settings['CHOST']
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100112 setattr(VAR_CACHE, CACHE_ATTR, val)
113 return val
114
115
116def GetCrossdevConf(target):
117 """Returns a map of crossdev provided variables about a tuple."""
118 CACHE_ATTR = '_target_tuple_map'
119
120 val = getattr(VAR_CACHE, CACHE_ATTR, {})
121 if not target in val:
122 # Find out the crossdev tuple.
123 target_tuple = target
124 if target == 'host':
125 target_tuple = GetHostTuple()
126 # Catch output of crossdev.
127 out = cros_build_lib.RunCommand(['crossdev', '--show-target-cfg',
128 '--ex-gdb', target_tuple],
129 print_cmd=False, redirect_stdout=True).output.splitlines()
130 # List of tuples split at the first '=', converted into dict.
131 val[target] = dict([x.split('=', 1) for x in out])
132 setattr(VAR_CACHE, CACHE_ATTR, {})
133 return val[target]
134
135
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100136def GetTargetPackages(target):
137 """Returns a list of packages for a given target."""
138 conf = GetCrossdevConf(target)
139 # Undesired packages are denoted by empty ${pkg}_pn variable.
140 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
141
142
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100143# Portage helper functions:
144def GetPortagePackage(target, package):
145 """Returns a package name for the given target."""
146 conf = GetCrossdevConf(target)
147 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100148 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100149 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100150 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100151 category = conf['category']
152 # Portage package:
153 pn = conf[package + '_pn']
154 # Final package name:
155 assert(category)
156 assert(pn)
157 return '%s/%s' % (category, pn)
158
159
160def GetPortageKeyword(target):
161 """Returns a portage friendly keyword for a given target."""
162 return GetCrossdevConf(target)['arch']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100163
164
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100165def IsPackageDisabled(target, package):
166 """Returns if the given package is not used for the target."""
167 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
168
Zdenek Behan508dcce2011-12-05 15:39:32 +0100169# Tree interface functions. They help with retrieving data about the current
170# state of the tree:
171def GetAllTargets():
172 """Get the complete list of targets.
173
174 returns the list of cross targets for the current tree
175 """
Zdenek Behan331f9822012-04-13 05:02:36 +0200176 cmd = [CROS_OVERLAY_LIST_CMD, '--all_boards']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100177 overlays = cros_build_lib.RunCommand(cmd, print_cmd=False,
178 redirect_stdout=True).output.splitlines()
179 targets = set()
180 for overlay in overlays:
181 config = os.path.join(overlay, 'toolchain.conf')
182 if os.path.exists(config):
183 with open(config) as config_file:
184 lines = config_file.read().splitlines()
185 for line in lines:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100186 line = line.split('#', 1)[0]
187 targets.update(line.split())
Zdenek Behan508dcce2011-12-05 15:39:32 +0100188
189 # Remove the host target as that is not a cross-target. Replace with 'host'.
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100190 targets.discard(GetHostTuple())
Zdenek Behan508dcce2011-12-05 15:39:32 +0100191 return targets
192
193
194def GetInstalledPackageVersions(target, package):
195 """Extracts the list of current versions of a target, package pair.
196
197 args:
198 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
199
200 returns the list of versions of the package currently installed.
201 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100202 versions = []
203 # This is the package name in terms of portage.
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100204 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100205 for pkg in portage.db['/']['vartree'].dbapi.match(atom):
206 version = portage.versions.cpv_getversion(pkg)
207 versions.append(version)
208 return versions
209
210
Zdenek Behan699ddd32012-04-13 07:14:08 +0200211def GetStablePackageVersion(target, package, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100212 """Extracts the current stable version for a given package.
213
214 args:
215 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200216 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100217
218 returns a string containing the latest version.
219 """
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200220 def mass_portageq_splitline(entry):
221 """Splits the output of mass_best_visible into package:version tuple."""
222 # mass_best_visible returns lines of the format "package:cpv"
223 split_string = entry.split(':', 1)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200224 if split_string[1]:
225 split_string[1] = portage.versions.cpv_getversion(split_string[1])
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200226 return split_string
227
228 CACHE_ATTR = '_target_stable_map'
229
Zdenek Behan699ddd32012-04-13 07:14:08 +0200230 pkgtype = "installed" if installed else "ebuild"
231
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200232 val = getattr(VAR_CACHE, CACHE_ATTR, {})
233 if not target in val:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200234 val[target] = {}
235 if not pkgtype in val[target]:
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200236 keyword = GetPortageKeyword(target)
237 extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
238 # Evaluate all packages for a target in one swoop, because it's much faster.
239 pkgs = [GetPortagePackage(target, p) for p in GetTargetPackages(target)]
Zdenek Behan699ddd32012-04-13 07:14:08 +0200240 cmd = ['portageq', 'mass_best_visible', '/', pkgtype] + pkgs
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200241 cpvs = cros_build_lib.RunCommand(cmd,
242 print_cmd=False, redirect_stdout=True,
243 extra_env=extra_env).output.splitlines()
Zdenek Behan699ddd32012-04-13 07:14:08 +0200244 val[target][pkgtype] = dict(map(mass_portageq_splitline, cpvs))
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200245 setattr(VAR_CACHE, CACHE_ATTR, val)
246
Zdenek Behan699ddd32012-04-13 07:14:08 +0200247 return val[target][pkgtype][GetPortagePackage(target, package)]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100248
249
Zdenek Behan699ddd32012-04-13 07:14:08 +0200250def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100251 """Resolves keywords in a given version list for a particular package.
252
253 Resolving means replacing PACKAGE_STABLE with the actual number.
254
255 args:
256 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
257 versions - list of versions to resolve
258
259 returns list of purely numeric versions equivalent to argument
260 """
261 resolved = []
262 for version in versions:
263 if version == PACKAGE_STABLE:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200264 resolved.append(GetStablePackageVersion(target, package, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100265 elif version != PACKAGE_NONE:
266 resolved.append(version)
267 return resolved
268
269
270def GetDesiredPackageVersions(target, package):
271 """Produces the list of desired versions for each target, package pair.
272
273 The first version in the list is implicitly treated as primary, ie.
274 the version that will be initialized by crossdev and selected.
275
276 If the version is PACKAGE_STABLE, it really means the current version which
277 is emerged by using the package atom with no particular version key.
278 Since crossdev unmasks all packages by default, this will actually
279 mean 'unstable' in most cases.
280
281 args:
282 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
283
284 returns a list composed of either a version string, PACKAGE_STABLE
285 """
286 packagemap = GetPackageMap(target)
287
288 versions = []
289 if package in packagemap:
290 versions.append(packagemap[package])
291
292 return versions
293
294
295def TargetIsInitialized(target):
296 """Verifies if the given list of targets has been correctly initialized.
297
298 This determines whether we have to call crossdev while emerging
299 toolchain packages or can do it using emerge. Emerge is naturally
300 preferred, because all packages can be updated in a single pass.
301
302 args:
303 targets - list of individual cross targets which are checked
304
305 returns True if target is completely initialized
306 returns False otherwise
307 """
308 # Check if packages for the given target all have a proper version.
309 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100310 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100311 # Do we even want this package && is it initialized?
312 if not IsPackageDisabled(target, package) and \
313 not GetInstalledPackageVersions(target, package):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100314 return False
315 return True
316 except cros_build_lib.RunCommandError:
317 # Fails - The target has likely never been initialized before.
318 return False
319
320
321def RemovePackageMask(target):
322 """Removes a package.mask file for the given platform.
323
324 The pre-existing package.mask files can mess with the keywords.
325
326 args:
327 target - the target for which to remove the file
328 """
329 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100330 try:
331 os.unlink(maskfile)
332 except EnvironmentError as e:
333 if e.errno != errno.ENOENT:
334 raise
Zdenek Behan508dcce2011-12-05 15:39:32 +0100335
336
337def CreatePackageMask(target, masks):
338 """[Re]creates a package.mask file for the given platform.
339
340 args:
341 target - the given target on which to operate
342 masks - a map of package : version,
343 where version is the highest permissible version (mask >)
344 """
345 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
346 assert not os.path.exists(maskfile)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100347 cros_build_lib.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100348
349 with open(maskfile, 'w') as f:
350 for pkg, m in masks.items():
351 f.write('>%s-%s\n' % (pkg, m))
352
353
354def CreatePackageKeywords(target):
355 """[Re]create a package.keywords file for the platform.
356
357 This sets all package.keywords files to unmask all stable/testing packages.
358 TODO: Note that this approach should be deprecated and is only done for
359 compatibility reasons. In the future, we'd like to stop using keywords
360 altogether, and keep just stable unmasked.
361
362 args:
363 target - target for which to recreate package.keywords
364 """
365 maskfile = os.path.join('/etc/portage/package.keywords', 'cross-' + target)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100366 try:
367 os.unlink(maskfile)
368 except EnvironmentError as e:
369 if e.errno != errno.ENOENT:
370 raise
371 cros_build_lib.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100372
373 keyword = GetPortageKeyword(target)
374
375 with open(maskfile, 'w') as f:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100376 for pkg in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100377 if IsPackageDisabled(target, pkg):
378 continue
379 f.write('%s %s ~%s\n' %
380 (GetPortagePackage(target, pkg), keyword, keyword))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100381
382
383# Main functions performing the actual update steps.
384def InitializeCrossdevTargets(targets, usepkg):
385 """Calls crossdev to initialize a cross target.
386 args:
387 targets - the list of targets to initialize using crossdev
388 usepkg - copies the commandline opts
389 """
390 print 'The following targets need to be re-initialized:'
391 print targets
Zdenek Behan508dcce2011-12-05 15:39:32 +0100392
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100393 extra_env = { 'FEATURES' : 'splitdebug' }
Zdenek Behan508dcce2011-12-05 15:39:32 +0100394 for target in targets:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100395 cmd = ['crossdev', '--show-fail-log',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100396 '-t', target]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100397 # Pick stable by default, and override as necessary.
398 cmd.extend(['-P', '--oneshot'])
399 if usepkg:
400 cmd.extend(['-P', '--getbinpkg',
401 '-P', '--usepkgonly',
402 '--without-headers'])
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100403
404 cmd.extend(['--overlays', '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)])
405 cmd.extend(['--ov-output', CROSSDEV_OVERLAY])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100406
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100407 for pkg in GetTargetPackages(target):
408 if pkg == 'gdb':
409 # Gdb does not have selectable versions.
410 cmd.append('--ex-gdb')
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100411 continue
Zdenek Behan508dcce2011-12-05 15:39:32 +0100412 # The first of the desired versions is the "primary" one.
413 version = GetDesiredPackageVersions(target, pkg)[0]
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100414 cmd.extend(['--%s' % pkg, version])
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100415 cros_build_lib.RunCommand(cmd, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100416
417
418def 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 """
425 # TODO(zbehan): This process is rather complex due to package.* handling.
426 # With some semantic changes over the original setup_board functionality,
427 # it can be considerably cleaned up.
428 mergemap = {}
429
430 # For each target, we do two things. Figure out the list of updates,
431 # and figure out the appropriate keywords/masks. Crossdev will initialize
432 # these, but they need to be regenerated on every update.
433 print 'Determining required toolchain updates...'
434 for target in targets:
435 # Record the highest needed version for each target, for masking purposes.
436 RemovePackageMask(target)
437 CreatePackageKeywords(target)
438 packagemasks = {}
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100439 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100440 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100441 if IsPackageDisabled(target, package):
442 continue
443 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100444 current = GetInstalledPackageVersions(target, package)
445 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200446 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100447 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100448
449 # Pick the highest version for mask.
450 packagemasks[pkg] = portage.versions.best(desired_num)
451
452 CreatePackageMask(target, packagemasks)
453
454 packages = []
455 for pkg in mergemap:
456 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200457 if ver != PACKAGE_NONE:
458 # Be a little more permissive for usepkg, the binaries may not exist.
459 packages.append('%s%s-%s' % ('<=' if usepkg else '=', pkg, ver))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460
461 if not packages:
462 print 'Nothing to update!'
463 return
464
465 print 'Updating packages:'
466 print packages
467
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100468 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100469 if usepkg:
470 cmd.extend(['--getbinpkg', '--usepkgonly'])
471
472 cmd.extend(packages)
473 cros_build_lib.RunCommand(cmd)
474
475
476def CleanTargets(targets):
477 """Unmerges old packages that are assumed unnecessary."""
478 unmergemap = {}
479 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100480 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100481 if IsPackageDisabled(target, package):
482 continue
483 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100484 current = GetInstalledPackageVersions(target, package)
485 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200486 desired_num = VersionListToNumeric(target, package, desired, True)
487 if not set(desired_num).issubset(current):
488 print 'Some packages have been held back, skipping clean!'
489 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100490 unmergemap[pkg] = set(current).difference(desired_num)
491
492 # Cleaning doesn't care about consistency and rebuilding package.* files.
493 packages = []
494 for pkg, vers in unmergemap.iteritems():
495 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
496
497 if packages:
498 print 'Cleaning packages:'
499 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100500 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100501 cmd.extend(packages)
502 cros_build_lib.RunCommand(cmd)
503 else:
504 print 'Nothing to clean!'
505
506
507def SelectActiveToolchains(targets, suffixes):
508 """Runs gcc-config and binutils-config to select the desired.
509
510 args:
511 targets - the targets to select
512 """
513 for package in ['gcc', 'binutils']:
514 for target in targets:
515 # Pick the first version in the numbered list as the selected one.
516 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200517 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100518 desired = desired_num[0]
519 # *-config does not play revisions, strip them, keep just PV.
520 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
521
522 if target == 'host':
523 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100524 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100525
526 # And finally, attach target to it.
527 desired = '%s-%s' % (target, desired)
528
529 # Target specific hacks
530 if package in suffixes:
531 if target in suffixes[package]:
532 desired += suffixes[package][target]
533
534 cmd = [ package + '-config', '-c', target ]
535 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200536 redirect_stdout=True).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100537 # Do not gcc-config when the current is live or nothing needs to be done.
538 if current != desired and current != '9999':
539 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100540 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541
542
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100543def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100544 """Performs all steps to create a synchronized toolchain enviroment.
545
546 args:
547 arguments correspond to the given commandline flags
548 """
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100549 targets = set()
550 if not hostonly:
551 # For hostonly, we can skip most of the below logic, much of which won't
552 # work on bare systems where this is useful.
553 alltargets = GetAllTargets()
554 nonexistant = []
555 if targets_wanted == set(['all']):
556 targets = set(alltargets)
557 else:
558 targets = set(targets_wanted)
559 # Verify user input.
560 for target in targets_wanted:
561 if target not in alltargets:
562 nonexistant.append(target)
563 if nonexistant:
564 raise Exception("Invalid targets: " + ','.join(nonexistant))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100565
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100566 # First check and initialize all cross targets that need to be.
567 crossdev_targets = \
568 [t for t in targets if not TargetIsInitialized(t)]
569 if crossdev_targets:
570 InitializeCrossdevTargets(crossdev_targets, usepkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100571
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100572 # We want host updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100573 targets.add('host')
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100574
575 # Now update all packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100576 UpdateTargets(targets, usepkg)
577 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
578
579 if deleteold:
580 CleanTargets(targets)
581
582
Brian Harring30675052012-02-29 12:18:22 -0800583def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100584 usage = """usage: %prog [options]
585
586 The script installs and updates the toolchains in your chroot.
587 """
588 parser = optparse.OptionParser(usage)
589 parser.add_option('-u', '--nousepkg',
590 action='store_false', dest='usepkg', default=True,
591 help=('Use prebuilt packages if possible.'))
592 parser.add_option('-d', '--deleteold',
593 action='store_true', dest='deleteold', default=False,
594 help=('Unmerge deprecated packages.'))
595 parser.add_option('-t', '--targets',
596 dest='targets', default='all',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100597 help=('Comma separated list of tuples. '
598 'Special keyword \'host\' is allowed. Default: all.'))
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100599 parser.add_option('', '--hostonly',
600 dest='hostonly', default=False, action='store_true',
601 help=('Only setup the host toolchain. '
602 'Useful for bootstrapping chroot.'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603
Brian Harring30675052012-02-29 12:18:22 -0800604 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100605
Mike Frysingerb9a28fe2012-03-21 11:35:42 -0400606 # This has to be always ran as root.
607 if not os.getuid() == 0:
608 print "%s: This script must be run as root!" % sys.argv[0]
609 sys.exit(1)
610
Zdenek Behan508dcce2011-12-05 15:39:32 +0100611 targets = set(options.targets.split(','))
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100612 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets)