blob: cec5b7b79ae927729b209b6af319b8f44fdc463c [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
Zdenek Behan508dcce2011-12-05 15:39:32 +0100170# Tree interface functions. They help with retrieving data about the current
171# state of the tree:
172def GetAllTargets():
173 """Get the complete list of targets.
174
175 returns the list of cross targets for the current tree
176 """
Zdenek Behan331f9822012-04-13 05:02:36 +0200177 cmd = [CROS_OVERLAY_LIST_CMD, '--all_boards']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100178 overlays = cros_build_lib.RunCommand(cmd, print_cmd=False,
179 redirect_stdout=True).output.splitlines()
180 targets = set()
181 for overlay in overlays:
182 config = os.path.join(overlay, 'toolchain.conf')
183 if os.path.exists(config):
Brian Harringaf019fb2012-05-10 15:06:13 -0700184 for line in osutils.ReadFile(config).splitlines():
185 line = line.split('#', 1)[0]
186 targets.update(line.split())
Zdenek Behan508dcce2011-12-05 15:39:32 +0100187
188 # Remove the host target as that is not a cross-target. Replace with 'host'.
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100189 targets.discard(GetHostTuple())
Zdenek Behan508dcce2011-12-05 15:39:32 +0100190 return targets
191
192
193def GetInstalledPackageVersions(target, package):
194 """Extracts the list of current versions of a target, package pair.
195
196 args:
197 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
198
199 returns the list of versions of the package currently installed.
200 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100201 versions = []
202 # This is the package name in terms of portage.
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100203 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100204 for pkg in portage.db['/']['vartree'].dbapi.match(atom):
205 version = portage.versions.cpv_getversion(pkg)
206 versions.append(version)
207 return versions
208
209
Zdenek Behan699ddd32012-04-13 07:14:08 +0200210def GetStablePackageVersion(target, package, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100211 """Extracts the current stable version for a given package.
212
213 args:
214 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200215 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100216
217 returns a string containing the latest version.
218 """
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200219 def mass_portageq_splitline(entry):
220 """Splits the output of mass_best_visible into package:version tuple."""
221 # mass_best_visible returns lines of the format "package:cpv"
222 split_string = entry.split(':', 1)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200223 if split_string[1]:
224 split_string[1] = portage.versions.cpv_getversion(split_string[1])
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200225 return split_string
226
227 CACHE_ATTR = '_target_stable_map'
228
Zdenek Behan699ddd32012-04-13 07:14:08 +0200229 pkgtype = "installed" if installed else "ebuild"
230
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200231 val = getattr(VAR_CACHE, CACHE_ATTR, {})
232 if not target in val:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200233 val[target] = {}
234 if not pkgtype in val[target]:
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200235 keyword = GetPortageKeyword(target)
236 extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
237 # Evaluate all packages for a target in one swoop, because it's much faster.
238 pkgs = [GetPortagePackage(target, p) for p in GetTargetPackages(target)]
Zdenek Behan699ddd32012-04-13 07:14:08 +0200239 cmd = ['portageq', 'mass_best_visible', '/', pkgtype] + pkgs
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200240 cpvs = cros_build_lib.RunCommand(cmd,
241 print_cmd=False, redirect_stdout=True,
242 extra_env=extra_env).output.splitlines()
Zdenek Behan699ddd32012-04-13 07:14:08 +0200243 val[target][pkgtype] = dict(map(mass_portageq_splitline, cpvs))
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200244 setattr(VAR_CACHE, CACHE_ATTR, val)
245
Zdenek Behan699ddd32012-04-13 07:14:08 +0200246 return val[target][pkgtype][GetPortagePackage(target, package)]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100247
248
Zdenek Behan699ddd32012-04-13 07:14:08 +0200249def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100250 """Resolves keywords in a given version list for a particular package.
251
252 Resolving means replacing PACKAGE_STABLE with the actual number.
253
254 args:
255 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
256 versions - list of versions to resolve
257
258 returns list of purely numeric versions equivalent to argument
259 """
260 resolved = []
261 for version in versions:
262 if version == PACKAGE_STABLE:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200263 resolved.append(GetStablePackageVersion(target, package, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100264 elif version != PACKAGE_NONE:
265 resolved.append(version)
266 return resolved
267
268
269def GetDesiredPackageVersions(target, package):
270 """Produces the list of desired versions for each target, package pair.
271
272 The first version in the list is implicitly treated as primary, ie.
273 the version that will be initialized by crossdev and selected.
274
275 If the version is PACKAGE_STABLE, it really means the current version which
276 is emerged by using the package atom with no particular version key.
277 Since crossdev unmasks all packages by default, this will actually
278 mean 'unstable' in most cases.
279
280 args:
281 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
282
283 returns a list composed of either a version string, PACKAGE_STABLE
284 """
285 packagemap = GetPackageMap(target)
286
287 versions = []
288 if package in packagemap:
289 versions.append(packagemap[package])
290
291 return versions
292
293
294def TargetIsInitialized(target):
295 """Verifies if the given list of targets has been correctly initialized.
296
297 This determines whether we have to call crossdev while emerging
298 toolchain packages or can do it using emerge. Emerge is naturally
299 preferred, because all packages can be updated in a single pass.
300
301 args:
302 targets - list of individual cross targets which are checked
303
304 returns True if target is completely initialized
305 returns False otherwise
306 """
307 # Check if packages for the given target all have a proper version.
308 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100309 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100310 # Do we even want this package && is it initialized?
311 if not IsPackageDisabled(target, package) and \
312 not GetInstalledPackageVersions(target, package):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100313 return False
314 return True
315 except cros_build_lib.RunCommandError:
316 # Fails - The target has likely never been initialized before.
317 return False
318
319
320def RemovePackageMask(target):
321 """Removes a package.mask file for the given platform.
322
323 The pre-existing package.mask files can mess with the keywords.
324
325 args:
326 target - the target for which to remove the file
327 """
328 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700329 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100330
331
332def CreatePackageMask(target, masks):
333 """[Re]creates a package.mask file for the given platform.
334
335 args:
336 target - the given target on which to operate
337 masks - a map of package : version,
338 where version is the highest permissible version (mask >)
339 """
340 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
341 assert not os.path.exists(maskfile)
Brian Harringaf019fb2012-05-10 15:06:13 -0700342 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100343
344 with open(maskfile, 'w') as f:
345 for pkg, m in masks.items():
346 f.write('>%s-%s\n' % (pkg, m))
347
348
349def CreatePackageKeywords(target):
350 """[Re]create a package.keywords file for the platform.
351
352 This sets all package.keywords files to unmask all stable/testing packages.
353 TODO: Note that this approach should be deprecated and is only done for
354 compatibility reasons. In the future, we'd like to stop using keywords
355 altogether, and keep just stable unmasked.
356
357 args:
358 target - target for which to recreate package.keywords
359 """
360 maskfile = os.path.join('/etc/portage/package.keywords', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700361 osutils.SafeUnlink(maskfile)
362
363 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100364
365 keyword = GetPortageKeyword(target)
366
367 with open(maskfile, 'w') as f:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100368 for pkg in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100369 if IsPackageDisabled(target, pkg):
370 continue
371 f.write('%s %s ~%s\n' %
372 (GetPortagePackage(target, pkg), keyword, keyword))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100373
374
375# Main functions performing the actual update steps.
376def InitializeCrossdevTargets(targets, usepkg):
377 """Calls crossdev to initialize a cross target.
378 args:
379 targets - the list of targets to initialize using crossdev
380 usepkg - copies the commandline opts
381 """
382 print 'The following targets need to be re-initialized:'
383 print targets
Zdenek Behan508dcce2011-12-05 15:39:32 +0100384
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100385 extra_env = { 'FEATURES' : 'splitdebug' }
Zdenek Behan508dcce2011-12-05 15:39:32 +0100386 for target in targets:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100387 cmd = ['crossdev', '--show-fail-log',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100388 '-t', target]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100389 # Pick stable by default, and override as necessary.
390 cmd.extend(['-P', '--oneshot'])
391 if usepkg:
392 cmd.extend(['-P', '--getbinpkg',
393 '-P', '--usepkgonly',
394 '--without-headers'])
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100395
396 cmd.extend(['--overlays', '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)])
397 cmd.extend(['--ov-output', CROSSDEV_OVERLAY])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100398
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100399 for pkg in GetTargetPackages(target):
400 if pkg == 'gdb':
401 # Gdb does not have selectable versions.
402 cmd.append('--ex-gdb')
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100403 continue
Zdenek Behan508dcce2011-12-05 15:39:32 +0100404 # The first of the desired versions is the "primary" one.
405 version = GetDesiredPackageVersions(target, pkg)[0]
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100406 cmd.extend(['--%s' % pkg, version])
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100407 cros_build_lib.RunCommand(cmd, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100408
409
410def UpdateTargets(targets, usepkg):
411 """Determines which packages need update/unmerge and defers to portage.
412
413 args:
414 targets - the list of targets to update
415 usepkg - copies the commandline option
416 """
417 # TODO(zbehan): This process is rather complex due to package.* handling.
418 # With some semantic changes over the original setup_board functionality,
419 # it can be considerably cleaned up.
420 mergemap = {}
421
422 # For each target, we do two things. Figure out the list of updates,
423 # and figure out the appropriate keywords/masks. Crossdev will initialize
424 # these, but they need to be regenerated on every update.
425 print 'Determining required toolchain updates...'
426 for target in targets:
427 # Record the highest needed version for each target, for masking purposes.
428 RemovePackageMask(target)
429 CreatePackageKeywords(target)
430 packagemasks = {}
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100431 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100432 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100433 if IsPackageDisabled(target, package):
434 continue
435 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100436 current = GetInstalledPackageVersions(target, package)
437 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200438 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100439 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100440
441 # Pick the highest version for mask.
442 packagemasks[pkg] = portage.versions.best(desired_num)
443
444 CreatePackageMask(target, packagemasks)
445
446 packages = []
447 for pkg in mergemap:
448 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200449 if ver != PACKAGE_NONE:
450 # Be a little more permissive for usepkg, the binaries may not exist.
451 packages.append('%s%s-%s' % ('<=' if usepkg else '=', pkg, ver))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100452
453 if not packages:
454 print 'Nothing to update!'
455 return
456
457 print 'Updating packages:'
458 print packages
459
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100460 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100461 if usepkg:
462 cmd.extend(['--getbinpkg', '--usepkgonly'])
463
464 cmd.extend(packages)
465 cros_build_lib.RunCommand(cmd)
466
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)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100476 current = GetInstalledPackageVersions(target, package)
477 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
526 cmd = [ package + '-config', '-c', target ]
527 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200528 redirect_stdout=True).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529 # Do not gcc-config when the current is live or nothing needs to be done.
530 if current != desired and current != '9999':
531 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100532 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100533
534
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100535def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536 """Performs all steps to create a synchronized toolchain enviroment.
537
538 args:
539 arguments correspond to the given commandline flags
540 """
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100541 targets = set()
542 if not hostonly:
543 # For hostonly, we can skip most of the below logic, much of which won't
544 # work on bare systems where this is useful.
545 alltargets = GetAllTargets()
546 nonexistant = []
547 if targets_wanted == set(['all']):
548 targets = set(alltargets)
549 else:
550 targets = set(targets_wanted)
551 # Verify user input.
552 for target in targets_wanted:
553 if target not in alltargets:
554 nonexistant.append(target)
555 if nonexistant:
556 raise Exception("Invalid targets: " + ','.join(nonexistant))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100557
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100558 # First check and initialize all cross targets that need to be.
559 crossdev_targets = \
560 [t for t in targets if not TargetIsInitialized(t)]
561 if crossdev_targets:
562 InitializeCrossdevTargets(crossdev_targets, usepkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100563
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100564 # We want host updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100565 targets.add('host')
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100566
567 # Now update all packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100568 UpdateTargets(targets, usepkg)
569 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
570
571 if deleteold:
572 CleanTargets(targets)
573
574
Brian Harring30675052012-02-29 12:18:22 -0800575def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100576 usage = """usage: %prog [options]
577
578 The script installs and updates the toolchains in your chroot.
579 """
580 parser = optparse.OptionParser(usage)
581 parser.add_option('-u', '--nousepkg',
582 action='store_false', dest='usepkg', default=True,
583 help=('Use prebuilt packages if possible.'))
584 parser.add_option('-d', '--deleteold',
585 action='store_true', dest='deleteold', default=False,
586 help=('Unmerge deprecated packages.'))
587 parser.add_option('-t', '--targets',
588 dest='targets', default='all',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100589 help=('Comma separated list of tuples. '
590 'Special keyword \'host\' is allowed. Default: all.'))
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100591 parser.add_option('', '--hostonly',
592 dest='hostonly', default=False, action='store_true',
593 help=('Only setup the host toolchain. '
594 'Useful for bootstrapping chroot.'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595
Brian Harring30675052012-02-29 12:18:22 -0800596 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100597
Mike Frysingerb9a28fe2012-03-21 11:35:42 -0400598 # This has to be always ran as root.
599 if not os.getuid() == 0:
600 print "%s: This script must be run as root!" % sys.argv[0]
601 sys.exit(1)
602
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603 targets = set(options.targets.split(','))
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100604 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets)