blob: a84b5a340c9ec3100eb4f225a1dadaaec8e011d5 [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
Mike Frysinger7ccee992012-06-01 21:27:59 -040011import json
Zdenek Behan508dcce2011-12-05 15:39:32 +010012import optparse
13import os
14import sys
15
Brian Harring503f3ab2012-03-09 21:39:41 -080016from chromite.buildbot import constants
17from 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
20# Some sanity checks first.
21if not cros_build_lib.IsInsideChroot():
22 print '%s: This needs to be run inside the chroot' % sys.argv[0]
23 sys.exit(1)
24# Only import portage after we've checked that we're inside the chroot.
25# Outside may not have portage, in which case the above may not happen.
26import portage
27
28
Matt Tennantf1e30972012-03-02 16:30:07 -080029EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan331f9822012-04-13 05:02:36 +020030CROS_OVERLAY_LIST_CMD = os.path.join(
31 constants.SOURCE_ROOT, 'src/platform/dev/host/cros_overlay_list')
Zdenek Behan508dcce2011-12-05 15:39:32 +010032PACKAGE_STABLE = '[stable]'
33PACKAGE_NONE = '[none]'
34SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010035
36CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
37STABLE_OVERLAY = '/usr/local/portage/stable'
38CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010039
40
41# TODO: The versions are stored here very much like in setup_board.
42# The goal for future is to differentiate these using a config file.
43# This is done essentially by messing with GetDesiredPackageVersions()
44DEFAULT_VERSION = PACKAGE_STABLE
45DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010046}
47TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010048 'host' : {
49 'binutils' : '2.21.1',
50 'gdb' : PACKAGE_NONE,
51 },
52}
53# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
54CONFIG_TARGET_SUFFIXES = {
55 'binutils' : {
56 'i686-pc-linux-gnu' : '-gold',
57 'x86_64-cros-linux-gnu' : '-gold',
58 },
59}
Zdenek Behan508dcce2011-12-05 15:39:32 +010060# Global per-run cache that will be filled ondemand in by GetPackageMap()
61# function as needed.
62target_version_map = {
63}
64
65
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010066# Global variable cache. It has to be a descendant of 'object', rather than
67# instance thereof, because attributes cannot be set on 'object' instances.
68class VariableCache(object):
69 pass
70VAR_CACHE = VariableCache()
71
72
Zdenek Behan508dcce2011-12-05 15:39:32 +010073def GetPackageMap(target):
74 """Compiles a package map for the given target from the constants.
75
76 Uses a cache in target_version_map, that is dynamically filled in as needed,
77 since here everything is static data and the structuring is for ease of
78 configurability only.
79
80 args:
81 target - the target for which to return a version map
82
83 returns a map between packages and desired versions in internal format
84 (using the PACKAGE_* constants)
85 """
86 if target in target_version_map:
87 return target_version_map[target]
88
89 # Start from copy of the global defaults.
90 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
91
Zdenek Behanf4d18a02012-03-22 15:45:05 +010092 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +010093 # prefer any specific overrides
94 if pkg in TARGET_VERSION_MAP.get(target, {}):
95 result[pkg] = TARGET_VERSION_MAP[target][pkg]
96 else:
97 # finally, if not already set, set a sane default
98 result.setdefault(pkg, DEFAULT_VERSION)
99 target_version_map[target] = result
100 return result
101
102
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100103def GetHostTuple():
104 """Returns compiler tuple for the host system.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100105
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100106 Caches the result, because the command can be fairly expensive, and never
107 changes throughout a single run.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100108 """
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100109 CACHE_ATTR = '_host_tuple'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100110
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100111 val = getattr(VAR_CACHE, CACHE_ATTR, None)
112 if val is None:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100113 val = portage.settings['CHOST']
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100114 setattr(VAR_CACHE, CACHE_ATTR, val)
115 return val
116
117
118def GetCrossdevConf(target):
119 """Returns a map of crossdev provided variables about a tuple."""
120 CACHE_ATTR = '_target_tuple_map'
121
122 val = getattr(VAR_CACHE, CACHE_ATTR, {})
123 if not target in val:
124 # Find out the crossdev tuple.
125 target_tuple = target
126 if target == 'host':
127 target_tuple = GetHostTuple()
128 # Catch output of crossdev.
129 out = cros_build_lib.RunCommand(['crossdev', '--show-target-cfg',
130 '--ex-gdb', target_tuple],
131 print_cmd=False, redirect_stdout=True).output.splitlines()
132 # List of tuples split at the first '=', converted into dict.
133 val[target] = dict([x.split('=', 1) for x in out])
134 setattr(VAR_CACHE, CACHE_ATTR, {})
135 return val[target]
136
137
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100138def GetTargetPackages(target):
139 """Returns a list of packages for a given target."""
140 conf = GetCrossdevConf(target)
141 # Undesired packages are denoted by empty ${pkg}_pn variable.
142 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
143
144
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100145# Portage helper functions:
146def GetPortagePackage(target, package):
147 """Returns a package name for the given target."""
148 conf = GetCrossdevConf(target)
149 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100150 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100151 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100152 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100153 category = conf['category']
154 # Portage package:
155 pn = conf[package + '_pn']
156 # Final package name:
157 assert(category)
158 assert(pn)
159 return '%s/%s' % (category, pn)
160
161
162def GetPortageKeyword(target):
163 """Returns a portage friendly keyword for a given target."""
164 return GetCrossdevConf(target)['arch']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100165
166
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100167def IsPackageDisabled(target, package):
168 """Returns if the given package is not used for the target."""
169 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
170
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100171
172def GetTuplesForOverlays(overlays):
173 """Returns a set of tuples for a given set of overlays."""
Mike Frysinger7ccee992012-06-01 21:27:59 -0400174 tuples = {}
175 default_settings = {
176 'sdk' : True,
177 'crossdev' : '',
178 }
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100179
180 for overlay in overlays:
181 config = os.path.join(overlay, 'toolchain.conf')
182 if os.path.exists(config):
183 for line in osutils.ReadFile(config).splitlines():
Mike Frysinger7ccee992012-06-01 21:27:59 -0400184 # Split by hash sign so that comments are ignored.
185 # Then split the line to get the tuple and its options.
186 line = line.split('#', 1)[0].split()
187
188 if len(line) > 0:
189 tuple = line[0]
190 if tuple not in tuples:
191 tuples[tuple] = copy.copy(default_settings)
192 if len(line) > 1:
193 tuples[tuple].update(json.loads(' '.join(line[1:])))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100194
195 return tuples
196
197
Zdenek Behan508dcce2011-12-05 15:39:32 +0100198# Tree interface functions. They help with retrieving data about the current
199# state of the tree:
200def GetAllTargets():
201 """Get the complete list of targets.
202
203 returns the list of cross targets for the current tree
204 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400205 targets = GetToolchainsForBoard('all')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100206
207 # Remove the host target as that is not a cross-target. Replace with 'host'.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400208 del targets[GetHostTuple()]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100209 return targets
210
211
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100212def GetToolchainsForBoard(board):
213 """Get a list of toolchain tuples for a given board name
214
215 returns the list of toolchain tuples for the given board
216 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400217 cmd = [CROS_OVERLAY_LIST_CMD]
218 if board == 'all':
219 cmd.append('--all_boards')
220 else:
221 cmd.append('--board=' + board)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100222 overlays = cros_build_lib.RunCommand(
223 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
224
225 return GetTuplesForOverlays(overlays)
226
227
Zdenek Behan508dcce2011-12-05 15:39:32 +0100228def GetInstalledPackageVersions(target, package):
229 """Extracts the list of current versions of a target, package pair.
230
231 args:
232 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
233
234 returns the list of versions of the package currently installed.
235 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100236 versions = []
237 # This is the package name in terms of portage.
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100238 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100239 for pkg in portage.db['/']['vartree'].dbapi.match(atom):
240 version = portage.versions.cpv_getversion(pkg)
241 versions.append(version)
242 return versions
243
244
Zdenek Behan699ddd32012-04-13 07:14:08 +0200245def GetStablePackageVersion(target, package, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100246 """Extracts the current stable version for a given package.
247
248 args:
249 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200250 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100251
252 returns a string containing the latest version.
253 """
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200254 def mass_portageq_splitline(entry):
255 """Splits the output of mass_best_visible into package:version tuple."""
256 # mass_best_visible returns lines of the format "package:cpv"
257 split_string = entry.split(':', 1)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200258 if split_string[1]:
259 split_string[1] = portage.versions.cpv_getversion(split_string[1])
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200260 return split_string
261
262 CACHE_ATTR = '_target_stable_map'
263
Zdenek Behan699ddd32012-04-13 07:14:08 +0200264 pkgtype = "installed" if installed else "ebuild"
265
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200266 val = getattr(VAR_CACHE, CACHE_ATTR, {})
267 if not target in val:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200268 val[target] = {}
269 if not pkgtype in val[target]:
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200270 keyword = GetPortageKeyword(target)
271 extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
272 # Evaluate all packages for a target in one swoop, because it's much faster.
273 pkgs = [GetPortagePackage(target, p) for p in GetTargetPackages(target)]
Zdenek Behan699ddd32012-04-13 07:14:08 +0200274 cmd = ['portageq', 'mass_best_visible', '/', pkgtype] + pkgs
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200275 cpvs = cros_build_lib.RunCommand(cmd,
276 print_cmd=False, redirect_stdout=True,
277 extra_env=extra_env).output.splitlines()
Zdenek Behan699ddd32012-04-13 07:14:08 +0200278 val[target][pkgtype] = dict(map(mass_portageq_splitline, cpvs))
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200279 setattr(VAR_CACHE, CACHE_ATTR, val)
280
Zdenek Behan699ddd32012-04-13 07:14:08 +0200281 return val[target][pkgtype][GetPortagePackage(target, package)]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100282
283
Zdenek Behan699ddd32012-04-13 07:14:08 +0200284def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100285 """Resolves keywords in a given version list for a particular package.
286
287 Resolving means replacing PACKAGE_STABLE with the actual number.
288
289 args:
290 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
291 versions - list of versions to resolve
292
293 returns list of purely numeric versions equivalent to argument
294 """
295 resolved = []
296 for version in versions:
297 if version == PACKAGE_STABLE:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200298 resolved.append(GetStablePackageVersion(target, package, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100299 elif version != PACKAGE_NONE:
300 resolved.append(version)
301 return resolved
302
303
304def GetDesiredPackageVersions(target, package):
305 """Produces the list of desired versions for each target, package pair.
306
307 The first version in the list is implicitly treated as primary, ie.
308 the version that will be initialized by crossdev and selected.
309
310 If the version is PACKAGE_STABLE, it really means the current version which
311 is emerged by using the package atom with no particular version key.
312 Since crossdev unmasks all packages by default, this will actually
313 mean 'unstable' in most cases.
314
315 args:
316 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
317
318 returns a list composed of either a version string, PACKAGE_STABLE
319 """
320 packagemap = GetPackageMap(target)
321
322 versions = []
323 if package in packagemap:
324 versions.append(packagemap[package])
325
326 return versions
327
328
329def TargetIsInitialized(target):
330 """Verifies if the given list of targets has been correctly initialized.
331
332 This determines whether we have to call crossdev while emerging
333 toolchain packages or can do it using emerge. Emerge is naturally
334 preferred, because all packages can be updated in a single pass.
335
336 args:
337 targets - list of individual cross targets which are checked
338
339 returns True if target is completely initialized
340 returns False otherwise
341 """
342 # Check if packages for the given target all have a proper version.
343 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100344 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100345 # Do we even want this package && is it initialized?
346 if not IsPackageDisabled(target, package) and \
347 not GetInstalledPackageVersions(target, package):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100348 return False
349 return True
350 except cros_build_lib.RunCommandError:
351 # Fails - The target has likely never been initialized before.
352 return False
353
354
355def RemovePackageMask(target):
356 """Removes a package.mask file for the given platform.
357
358 The pre-existing package.mask files can mess with the keywords.
359
360 args:
361 target - the target for which to remove the file
362 """
363 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700364 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100365
366
367def CreatePackageMask(target, masks):
368 """[Re]creates a package.mask file for the given platform.
369
370 args:
371 target - the given target on which to operate
372 masks - a map of package : version,
373 where version is the highest permissible version (mask >)
374 """
375 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
376 assert not os.path.exists(maskfile)
Brian Harringaf019fb2012-05-10 15:06:13 -0700377 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378
379 with open(maskfile, 'w') as f:
380 for pkg, m in masks.items():
381 f.write('>%s-%s\n' % (pkg, m))
382
383
384def CreatePackageKeywords(target):
385 """[Re]create a package.keywords file for the platform.
386
387 This sets all package.keywords files to unmask all stable/testing packages.
388 TODO: Note that this approach should be deprecated and is only done for
389 compatibility reasons. In the future, we'd like to stop using keywords
390 altogether, and keep just stable unmasked.
391
392 args:
393 target - target for which to recreate package.keywords
394 """
395 maskfile = os.path.join('/etc/portage/package.keywords', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700396 osutils.SafeUnlink(maskfile)
397
398 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100399
400 keyword = GetPortageKeyword(target)
401
402 with open(maskfile, 'w') as f:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100403 for pkg in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100404 if IsPackageDisabled(target, pkg):
405 continue
406 f.write('%s %s ~%s\n' %
407 (GetPortagePackage(target, pkg), keyword, keyword))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100408
409
410# Main functions performing the actual update steps.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200411def UpdateCrossdevTargets(targets, usepkg, config_only=False):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100412 """Calls crossdev to initialize a cross target.
413 args:
414 targets - the list of targets to initialize using crossdev
415 usepkg - copies the commandline opts
416 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417 for target in targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200418 cmd = ['crossdev', '--show-fail-log', '-t', target]
419 cmd.extend(['--env', 'FEATURES=splitdebug'])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100420 # Pick stable by default, and override as necessary.
421 cmd.extend(['-P', '--oneshot'])
422 if usepkg:
423 cmd.extend(['-P', '--getbinpkg',
424 '-P', '--usepkgonly',
425 '--without-headers'])
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100426
427 cmd.extend(['--overlays', '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)])
428 cmd.extend(['--ov-output', CROSSDEV_OVERLAY])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100429
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100430 for pkg in GetTargetPackages(target):
431 if pkg == 'gdb':
432 # Gdb does not have selectable versions.
433 cmd.append('--ex-gdb')
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100434 continue
Zdenek Behan508dcce2011-12-05 15:39:32 +0100435 # The first of the desired versions is the "primary" one.
436 version = GetDesiredPackageVersions(target, pkg)[0]
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100437 cmd.extend(['--%s' % pkg, version])
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200438
Mike Frysinger7ccee992012-06-01 21:27:59 -0400439 cmd.extend(targets[target]['crossdev'].split())
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200440 if config_only:
441 # In this case we want to just quietly reinit
442 cmd.append('--init-target')
443 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
444 else:
445 cros_build_lib.RunCommand(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100446
447
448def UpdateTargets(targets, usepkg):
449 """Determines which packages need update/unmerge and defers to portage.
450
451 args:
452 targets - the list of targets to update
453 usepkg - copies the commandline option
454 """
455 # TODO(zbehan): This process is rather complex due to package.* handling.
456 # With some semantic changes over the original setup_board functionality,
457 # it can be considerably cleaned up.
458 mergemap = {}
459
460 # For each target, we do two things. Figure out the list of updates,
461 # and figure out the appropriate keywords/masks. Crossdev will initialize
462 # these, but they need to be regenerated on every update.
463 print 'Determining required toolchain updates...'
464 for target in targets:
465 # Record the highest needed version for each target, for masking purposes.
466 RemovePackageMask(target)
467 CreatePackageKeywords(target)
468 packagemasks = {}
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100469 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100470 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100471 if IsPackageDisabled(target, package):
472 continue
473 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100474 current = GetInstalledPackageVersions(target, package)
475 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200476 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100477 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100478
479 # Pick the highest version for mask.
480 packagemasks[pkg] = portage.versions.best(desired_num)
481
482 CreatePackageMask(target, packagemasks)
483
484 packages = []
485 for pkg in mergemap:
486 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200487 if ver != PACKAGE_NONE:
488 # Be a little more permissive for usepkg, the binaries may not exist.
489 packages.append('%s%s-%s' % ('<=' if usepkg else '=', pkg, ver))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100490
491 if not packages:
492 print 'Nothing to update!'
493 return
494
495 print 'Updating packages:'
496 print packages
497
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100498 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100499 if usepkg:
500 cmd.extend(['--getbinpkg', '--usepkgonly'])
501
502 cmd.extend(packages)
503 cros_build_lib.RunCommand(cmd)
504
505
506def CleanTargets(targets):
507 """Unmerges old packages that are assumed unnecessary."""
508 unmergemap = {}
509 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100510 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100511 if IsPackageDisabled(target, package):
512 continue
513 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100514 current = GetInstalledPackageVersions(target, package)
515 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200516 desired_num = VersionListToNumeric(target, package, desired, True)
517 if not set(desired_num).issubset(current):
518 print 'Some packages have been held back, skipping clean!'
519 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100520 unmergemap[pkg] = set(current).difference(desired_num)
521
522 # Cleaning doesn't care about consistency and rebuilding package.* files.
523 packages = []
524 for pkg, vers in unmergemap.iteritems():
525 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
526
527 if packages:
528 print 'Cleaning packages:'
529 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100530 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100531 cmd.extend(packages)
532 cros_build_lib.RunCommand(cmd)
533 else:
534 print 'Nothing to clean!'
535
536
537def SelectActiveToolchains(targets, suffixes):
538 """Runs gcc-config and binutils-config to select the desired.
539
540 args:
541 targets - the targets to select
542 """
543 for package in ['gcc', 'binutils']:
544 for target in targets:
545 # Pick the first version in the numbered list as the selected one.
546 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200547 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100548 desired = desired_num[0]
549 # *-config does not play revisions, strip them, keep just PV.
550 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
551
552 if target == 'host':
553 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100554 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100555
556 # And finally, attach target to it.
557 desired = '%s-%s' % (target, desired)
558
559 # Target specific hacks
560 if package in suffixes:
561 if target in suffixes[package]:
562 desired += suffixes[package][target]
563
564 cmd = [ package + '-config', '-c', target ]
565 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200566 redirect_stdout=True).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100567 # Do not gcc-config when the current is live or nothing needs to be done.
568 if current != desired and current != '9999':
569 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100570 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100571
572
Mike Frysinger7ccee992012-06-01 21:27:59 -0400573def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted,
574 boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100575 """Performs all steps to create a synchronized toolchain enviroment.
576
577 args:
578 arguments correspond to the given commandline flags
579 """
Mike Frysinger7ccee992012-06-01 21:27:59 -0400580 targets = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100581 if not hostonly:
582 # For hostonly, we can skip most of the below logic, much of which won't
583 # work on bare systems where this is useful.
584 alltargets = GetAllTargets()
Mike Frysinger7ccee992012-06-01 21:27:59 -0400585 targets_wanted = set(targets_wanted)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100586 if targets_wanted == set(['all']):
Mike Frysinger7ccee992012-06-01 21:27:59 -0400587 targets = alltargets
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100588 else:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100589 # Verify user input.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400590 nonexistant = []
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100591 for target in targets_wanted:
592 if target not in alltargets:
593 nonexistant.append(target)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400594 else:
595 targets[target] = alltargets[target]
596 if nonexistant:
597 cros_build_lib.Die('Invalid targets: ' + ','.join(nonexistant))
598
599 # Filter out all the non-sdk toolchains as we don't want to mess
600 # with those in all of our builds.
601 for target in targets.keys():
602 if not targets[target]['sdk']:
603 del targets[target]
604 # Now re-add any targets that might be from this board. This is
605 # to allow unofficial boards to declare their own toolchains.
606 for board in boards_wanted:
607 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100608
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100609 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400610 crossdev_targets = {}
611 reconfig_targets = {}
612 for target in targets:
613 if TargetIsInitialized(target):
614 reconfig_targets[target] = targets[target]
615 else:
616 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100617 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200618 print 'The following targets need to be re-initialized:'
619 print crossdev_targets
620 UpdateCrossdevTargets(crossdev_targets, usepkg)
621 # Those that were not initialized may need a config update.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200622 UpdateCrossdevTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100623
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100624 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400625 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100626
627 # Now update all packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100628 UpdateTargets(targets, usepkg)
629 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
630
631 if deleteold:
632 CleanTargets(targets)
633
634
Brian Harring30675052012-02-29 12:18:22 -0800635def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100636 usage = """usage: %prog [options]
637
638 The script installs and updates the toolchains in your chroot.
639 """
640 parser = optparse.OptionParser(usage)
641 parser.add_option('-u', '--nousepkg',
642 action='store_false', dest='usepkg', default=True,
643 help=('Use prebuilt packages if possible.'))
644 parser.add_option('-d', '--deleteold',
645 action='store_true', dest='deleteold', default=False,
646 help=('Unmerge deprecated packages.'))
647 parser.add_option('-t', '--targets',
648 dest='targets', default='all',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100649 help=('Comma separated list of tuples. '
650 'Special keyword \'host\' is allowed. Default: all.'))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400651 parser.add_option('--include-boards',
652 dest='include_boards', default='',
653 help=('Comma separated list of boards whose toolchains we'
654 ' will always include. Default: none.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100655 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100656 dest='hostonly', default=False, action='store_true',
657 help=('Only setup the host toolchain. '
658 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100659 parser.add_option('--show-board-cfg',
660 dest='board_cfg', default=None,
661 help=('Board to list toolchain tuples for'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100662
Brian Harring30675052012-02-29 12:18:22 -0800663 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100664
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100665 if options.board_cfg:
666 print ','.join(GetToolchainsForBoard(options.board_cfg))
667 return 0
668
Mike Frysinger984d0622012-06-01 16:08:44 -0400669 # This has to be always run as root.
670 if not os.getuid() == 0:
671 print "%s: This script must be run as root!" % sys.argv[0]
672 sys.exit(1)
673
Zdenek Behan508dcce2011-12-05 15:39:32 +0100674 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400675 boards = set(options.include_boards.split(',')) if options.include_boards \
676 else set()
677 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets,
678 boards)