blob: 14c421ef7d93531d3853ba547c80f19134714cd4 [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' : '',
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400178 'default' : False,
Mike Frysinger7ccee992012-06-01 21:27:59 -0400179 }
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100180
181 for overlay in overlays:
182 config = os.path.join(overlay, 'toolchain.conf')
183 if os.path.exists(config):
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400184 first_tuple = None
185 seen_default = False
186
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100187 for line in osutils.ReadFile(config).splitlines():
Mike Frysinger7ccee992012-06-01 21:27:59 -0400188 # Split by hash sign so that comments are ignored.
189 # Then split the line to get the tuple and its options.
190 line = line.split('#', 1)[0].split()
191
192 if len(line) > 0:
193 tuple = line[0]
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400194 if not first_tuple:
195 first_tuple = tuple
Mike Frysinger7ccee992012-06-01 21:27:59 -0400196 if tuple not in tuples:
197 tuples[tuple] = copy.copy(default_settings)
198 if len(line) > 1:
199 tuples[tuple].update(json.loads(' '.join(line[1:])))
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400200 if tuples[tuple]['default']:
201 seen_default = True
202
203 # If the user has not explicitly marked a toolchain as default,
204 # automatically select the first tuple that we saw in the conf.
205 if not seen_default and first_tuple:
206 tuples[first_tuple]['default'] = True
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100207
208 return tuples
209
210
Zdenek Behan508dcce2011-12-05 15:39:32 +0100211# Tree interface functions. They help with retrieving data about the current
212# state of the tree:
213def GetAllTargets():
214 """Get the complete list of targets.
215
216 returns the list of cross targets for the current tree
217 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400218 targets = GetToolchainsForBoard('all')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100219
220 # Remove the host target as that is not a cross-target. Replace with 'host'.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400221 del targets[GetHostTuple()]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100222 return targets
223
224
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100225def GetToolchainsForBoard(board):
226 """Get a list of toolchain tuples for a given board name
227
228 returns the list of toolchain tuples for the given board
229 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400230 cmd = [CROS_OVERLAY_LIST_CMD]
231 if board == 'all':
232 cmd.append('--all_boards')
233 else:
234 cmd.append('--board=' + board)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100235 overlays = cros_build_lib.RunCommand(
236 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
237
238 return GetTuplesForOverlays(overlays)
239
240
Zdenek Behan508dcce2011-12-05 15:39:32 +0100241def GetInstalledPackageVersions(target, package):
242 """Extracts the list of current versions of a target, package pair.
243
244 args:
245 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
246
247 returns the list of versions of the package currently installed.
248 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100249 versions = []
250 # This is the package name in terms of portage.
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100251 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100252 for pkg in portage.db['/']['vartree'].dbapi.match(atom):
253 version = portage.versions.cpv_getversion(pkg)
254 versions.append(version)
255 return versions
256
257
Zdenek Behan699ddd32012-04-13 07:14:08 +0200258def GetStablePackageVersion(target, package, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100259 """Extracts the current stable version for a given package.
260
261 args:
262 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200263 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100264
265 returns a string containing the latest version.
266 """
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200267 def mass_portageq_splitline(entry):
268 """Splits the output of mass_best_visible into package:version tuple."""
269 # mass_best_visible returns lines of the format "package:cpv"
270 split_string = entry.split(':', 1)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200271 if split_string[1]:
272 split_string[1] = portage.versions.cpv_getversion(split_string[1])
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200273 return split_string
274
275 CACHE_ATTR = '_target_stable_map'
276
Zdenek Behan699ddd32012-04-13 07:14:08 +0200277 pkgtype = "installed" if installed else "ebuild"
278
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200279 val = getattr(VAR_CACHE, CACHE_ATTR, {})
280 if not target in val:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200281 val[target] = {}
282 if not pkgtype in val[target]:
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200283 keyword = GetPortageKeyword(target)
284 extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
285 # Evaluate all packages for a target in one swoop, because it's much faster.
286 pkgs = [GetPortagePackage(target, p) for p in GetTargetPackages(target)]
Zdenek Behan699ddd32012-04-13 07:14:08 +0200287 cmd = ['portageq', 'mass_best_visible', '/', pkgtype] + pkgs
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200288 cpvs = cros_build_lib.RunCommand(cmd,
289 print_cmd=False, redirect_stdout=True,
290 extra_env=extra_env).output.splitlines()
Zdenek Behan699ddd32012-04-13 07:14:08 +0200291 val[target][pkgtype] = dict(map(mass_portageq_splitline, cpvs))
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200292 setattr(VAR_CACHE, CACHE_ATTR, val)
293
Zdenek Behan699ddd32012-04-13 07:14:08 +0200294 return val[target][pkgtype][GetPortagePackage(target, package)]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100295
296
Zdenek Behan699ddd32012-04-13 07:14:08 +0200297def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100298 """Resolves keywords in a given version list for a particular package.
299
300 Resolving means replacing PACKAGE_STABLE with the actual number.
301
302 args:
303 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
304 versions - list of versions to resolve
305
306 returns list of purely numeric versions equivalent to argument
307 """
308 resolved = []
309 for version in versions:
310 if version == PACKAGE_STABLE:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200311 resolved.append(GetStablePackageVersion(target, package, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100312 elif version != PACKAGE_NONE:
313 resolved.append(version)
314 return resolved
315
316
317def GetDesiredPackageVersions(target, package):
318 """Produces the list of desired versions for each target, package pair.
319
320 The first version in the list is implicitly treated as primary, ie.
321 the version that will be initialized by crossdev and selected.
322
323 If the version is PACKAGE_STABLE, it really means the current version which
324 is emerged by using the package atom with no particular version key.
325 Since crossdev unmasks all packages by default, this will actually
326 mean 'unstable' in most cases.
327
328 args:
329 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
330
331 returns a list composed of either a version string, PACKAGE_STABLE
332 """
333 packagemap = GetPackageMap(target)
334
335 versions = []
336 if package in packagemap:
337 versions.append(packagemap[package])
338
339 return versions
340
341
342def TargetIsInitialized(target):
343 """Verifies if the given list of targets has been correctly initialized.
344
345 This determines whether we have to call crossdev while emerging
346 toolchain packages or can do it using emerge. Emerge is naturally
347 preferred, because all packages can be updated in a single pass.
348
349 args:
350 targets - list of individual cross targets which are checked
351
352 returns True if target is completely initialized
353 returns False otherwise
354 """
355 # Check if packages for the given target all have a proper version.
356 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100357 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100358 # Do we even want this package && is it initialized?
359 if not IsPackageDisabled(target, package) and \
360 not GetInstalledPackageVersions(target, package):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100361 return False
362 return True
363 except cros_build_lib.RunCommandError:
364 # Fails - The target has likely never been initialized before.
365 return False
366
367
368def RemovePackageMask(target):
369 """Removes a package.mask file for the given platform.
370
371 The pre-existing package.mask files can mess with the keywords.
372
373 args:
374 target - the target for which to remove the file
375 """
376 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700377 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378
379
380def CreatePackageMask(target, masks):
381 """[Re]creates a package.mask file for the given platform.
382
383 args:
384 target - the given target on which to operate
385 masks - a map of package : version,
386 where version is the highest permissible version (mask >)
387 """
388 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
389 assert not os.path.exists(maskfile)
Brian Harringaf019fb2012-05-10 15:06:13 -0700390 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100391
392 with open(maskfile, 'w') as f:
393 for pkg, m in masks.items():
394 f.write('>%s-%s\n' % (pkg, m))
395
396
397def CreatePackageKeywords(target):
398 """[Re]create a package.keywords file for the platform.
399
400 This sets all package.keywords files to unmask all stable/testing packages.
401 TODO: Note that this approach should be deprecated and is only done for
402 compatibility reasons. In the future, we'd like to stop using keywords
403 altogether, and keep just stable unmasked.
404
405 args:
406 target - target for which to recreate package.keywords
407 """
408 maskfile = os.path.join('/etc/portage/package.keywords', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700409 osutils.SafeUnlink(maskfile)
410
411 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100412
413 keyword = GetPortageKeyword(target)
414
415 with open(maskfile, 'w') as f:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100416 for pkg in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100417 if IsPackageDisabled(target, pkg):
418 continue
419 f.write('%s %s ~%s\n' %
420 (GetPortagePackage(target, pkg), keyword, keyword))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100421
422
423# Main functions performing the actual update steps.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200424def UpdateCrossdevTargets(targets, usepkg, config_only=False):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100425 """Calls crossdev to initialize a cross target.
426 args:
427 targets - the list of targets to initialize using crossdev
428 usepkg - copies the commandline opts
429 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100430 for target in targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200431 cmd = ['crossdev', '--show-fail-log', '-t', target]
432 cmd.extend(['--env', 'FEATURES=splitdebug'])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433 # Pick stable by default, and override as necessary.
434 cmd.extend(['-P', '--oneshot'])
435 if usepkg:
436 cmd.extend(['-P', '--getbinpkg',
437 '-P', '--usepkgonly',
438 '--without-headers'])
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100439
440 cmd.extend(['--overlays', '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)])
441 cmd.extend(['--ov-output', CROSSDEV_OVERLAY])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100443 for pkg in GetTargetPackages(target):
444 if pkg == 'gdb':
445 # Gdb does not have selectable versions.
446 cmd.append('--ex-gdb')
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100447 continue
Zdenek Behan508dcce2011-12-05 15:39:32 +0100448 # The first of the desired versions is the "primary" one.
449 version = GetDesiredPackageVersions(target, pkg)[0]
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100450 cmd.extend(['--%s' % pkg, version])
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200451
Mike Frysinger7ccee992012-06-01 21:27:59 -0400452 cmd.extend(targets[target]['crossdev'].split())
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200453 if config_only:
454 # In this case we want to just quietly reinit
455 cmd.append('--init-target')
456 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
457 else:
458 cros_build_lib.RunCommand(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100459
460
461def UpdateTargets(targets, usepkg):
462 """Determines which packages need update/unmerge and defers to portage.
463
464 args:
465 targets - the list of targets to update
466 usepkg - copies the commandline option
467 """
468 # TODO(zbehan): This process is rather complex due to package.* handling.
469 # With some semantic changes over the original setup_board functionality,
470 # it can be considerably cleaned up.
471 mergemap = {}
472
473 # For each target, we do two things. Figure out the list of updates,
474 # and figure out the appropriate keywords/masks. Crossdev will initialize
475 # these, but they need to be regenerated on every update.
476 print 'Determining required toolchain updates...'
477 for target in targets:
478 # Record the highest needed version for each target, for masking purposes.
479 RemovePackageMask(target)
480 CreatePackageKeywords(target)
481 packagemasks = {}
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100482 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100483 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100484 if IsPackageDisabled(target, package):
485 continue
486 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100487 current = GetInstalledPackageVersions(target, package)
488 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200489 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100490 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100491
492 # Pick the highest version for mask.
493 packagemasks[pkg] = portage.versions.best(desired_num)
494
495 CreatePackageMask(target, packagemasks)
496
497 packages = []
498 for pkg in mergemap:
499 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200500 if ver != PACKAGE_NONE:
501 # Be a little more permissive for usepkg, the binaries may not exist.
502 packages.append('%s%s-%s' % ('<=' if usepkg else '=', pkg, ver))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100503
504 if not packages:
505 print 'Nothing to update!'
506 return
507
508 print 'Updating packages:'
509 print packages
510
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100511 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100512 if usepkg:
513 cmd.extend(['--getbinpkg', '--usepkgonly'])
514
515 cmd.extend(packages)
516 cros_build_lib.RunCommand(cmd)
517
518
519def CleanTargets(targets):
520 """Unmerges old packages that are assumed unnecessary."""
521 unmergemap = {}
522 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100523 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100524 if IsPackageDisabled(target, package):
525 continue
526 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100527 current = GetInstalledPackageVersions(target, package)
528 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200529 desired_num = VersionListToNumeric(target, package, desired, True)
530 if not set(desired_num).issubset(current):
531 print 'Some packages have been held back, skipping clean!'
532 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100533 unmergemap[pkg] = set(current).difference(desired_num)
534
535 # Cleaning doesn't care about consistency and rebuilding package.* files.
536 packages = []
537 for pkg, vers in unmergemap.iteritems():
538 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
539
540 if packages:
541 print 'Cleaning packages:'
542 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100543 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100544 cmd.extend(packages)
545 cros_build_lib.RunCommand(cmd)
546 else:
547 print 'Nothing to clean!'
548
549
550def SelectActiveToolchains(targets, suffixes):
551 """Runs gcc-config and binutils-config to select the desired.
552
553 args:
554 targets - the targets to select
555 """
556 for package in ['gcc', 'binutils']:
557 for target in targets:
558 # Pick the first version in the numbered list as the selected one.
559 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200560 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561 desired = desired_num[0]
562 # *-config does not play revisions, strip them, keep just PV.
563 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
564
565 if target == 'host':
566 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100567 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100568
569 # And finally, attach target to it.
570 desired = '%s-%s' % (target, desired)
571
572 # Target specific hacks
573 if package in suffixes:
574 if target in suffixes[package]:
575 desired += suffixes[package][target]
576
577 cmd = [ package + '-config', '-c', target ]
578 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200579 redirect_stdout=True).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100580 # Do not gcc-config when the current is live or nothing needs to be done.
581 if current != desired and current != '9999':
582 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100583 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100584
585
Mike Frysinger7ccee992012-06-01 21:27:59 -0400586def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted,
587 boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100588 """Performs all steps to create a synchronized toolchain enviroment.
589
590 args:
591 arguments correspond to the given commandline flags
592 """
Mike Frysinger7ccee992012-06-01 21:27:59 -0400593 targets = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100594 if not hostonly:
595 # For hostonly, we can skip most of the below logic, much of which won't
596 # work on bare systems where this is useful.
597 alltargets = GetAllTargets()
Mike Frysinger7ccee992012-06-01 21:27:59 -0400598 targets_wanted = set(targets_wanted)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100599 if targets_wanted == set(['all']):
Mike Frysinger7ccee992012-06-01 21:27:59 -0400600 targets = alltargets
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100601 else:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100602 # Verify user input.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400603 nonexistant = []
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100604 for target in targets_wanted:
605 if target not in alltargets:
606 nonexistant.append(target)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400607 else:
608 targets[target] = alltargets[target]
609 if nonexistant:
610 cros_build_lib.Die('Invalid targets: ' + ','.join(nonexistant))
611
612 # Filter out all the non-sdk toolchains as we don't want to mess
613 # with those in all of our builds.
614 for target in targets.keys():
615 if not targets[target]['sdk']:
616 del targets[target]
617 # Now re-add any targets that might be from this board. This is
618 # to allow unofficial boards to declare their own toolchains.
619 for board in boards_wanted:
620 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100621
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100622 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400623 crossdev_targets = {}
624 reconfig_targets = {}
625 for target in targets:
626 if TargetIsInitialized(target):
627 reconfig_targets[target] = targets[target]
628 else:
629 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100630 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200631 print 'The following targets need to be re-initialized:'
632 print crossdev_targets
633 UpdateCrossdevTargets(crossdev_targets, usepkg)
634 # Those that were not initialized may need a config update.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200635 UpdateCrossdevTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100636
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100637 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400638 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100639
640 # Now update all packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100641 UpdateTargets(targets, usepkg)
642 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
643
644 if deleteold:
645 CleanTargets(targets)
646
647
Brian Harring30675052012-02-29 12:18:22 -0800648def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100649 usage = """usage: %prog [options]
650
651 The script installs and updates the toolchains in your chroot.
652 """
653 parser = optparse.OptionParser(usage)
654 parser.add_option('-u', '--nousepkg',
655 action='store_false', dest='usepkg', default=True,
656 help=('Use prebuilt packages if possible.'))
657 parser.add_option('-d', '--deleteold',
658 action='store_true', dest='deleteold', default=False,
659 help=('Unmerge deprecated packages.'))
660 parser.add_option('-t', '--targets',
661 dest='targets', default='all',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100662 help=('Comma separated list of tuples. '
663 'Special keyword \'host\' is allowed. Default: all.'))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400664 parser.add_option('--include-boards',
665 dest='include_boards', default='',
666 help=('Comma separated list of boards whose toolchains we'
667 ' will always include. Default: none.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100668 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100669 dest='hostonly', default=False, action='store_true',
670 help=('Only setup the host toolchain. '
671 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100672 parser.add_option('--show-board-cfg',
673 dest='board_cfg', default=None,
674 help=('Board to list toolchain tuples for'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100675
Brian Harring30675052012-02-29 12:18:22 -0800676 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100677
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100678 if options.board_cfg:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400679 toolchains = GetToolchainsForBoard(options.board_cfg)
680 # Make sure we display the default toolchain first.
681 tuples = toolchains.keys()
682 for tuple in tuples:
683 if toolchains[tuple]['default']:
684 tuples.remove(tuple)
685 tuples.insert(0, tuple)
686 break
687 print ','.join(tuples)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100688 return 0
689
Mike Frysinger984d0622012-06-01 16:08:44 -0400690 # This has to be always run as root.
691 if not os.getuid() == 0:
692 print "%s: This script must be run as root!" % sys.argv[0]
693 sys.exit(1)
694
Zdenek Behan508dcce2011-12-05 15:39:32 +0100695 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400696 boards = set(options.include_boards.split(',')) if options.include_boards \
697 else set()
698 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets,
699 boards)