blob: fbb5cff268cac21404fc529da7b2a739c2f7f639 [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
Mike Frysinger7ccee992012-06-01 21:27:59 -040010import json
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
David Jamese5867812012-10-19 12:02:20 -070016from chromite.buildbot import portage_utilities
Brian Harring503f3ab2012-03-09 21:39:41 -080017from 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 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
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100169
170def GetTuplesForOverlays(overlays):
171 """Returns a set of tuples for a given set of overlays."""
Mike Frysinger7ccee992012-06-01 21:27:59 -0400172 tuples = {}
173 default_settings = {
174 'sdk' : True,
175 'crossdev' : '',
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400176 'default' : False,
Mike Frysinger7ccee992012-06-01 21:27:59 -0400177 }
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100178
179 for overlay in overlays:
180 config = os.path.join(overlay, 'toolchain.conf')
181 if os.path.exists(config):
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400182 first_tuple = None
183 seen_default = False
184
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100185 for line in osutils.ReadFile(config).splitlines():
Mike Frysinger7ccee992012-06-01 21:27:59 -0400186 # Split by hash sign so that comments are ignored.
187 # Then split the line to get the tuple and its options.
188 line = line.split('#', 1)[0].split()
189
190 if len(line) > 0:
191 tuple = line[0]
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400192 if not first_tuple:
193 first_tuple = tuple
Mike Frysinger7ccee992012-06-01 21:27:59 -0400194 if tuple not in tuples:
195 tuples[tuple] = copy.copy(default_settings)
196 if len(line) > 1:
197 tuples[tuple].update(json.loads(' '.join(line[1:])))
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400198 if tuples[tuple]['default']:
199 seen_default = True
200
201 # If the user has not explicitly marked a toolchain as default,
202 # automatically select the first tuple that we saw in the conf.
203 if not seen_default and first_tuple:
204 tuples[first_tuple]['default'] = True
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100205
206 return tuples
207
208
Zdenek Behan508dcce2011-12-05 15:39:32 +0100209# Tree interface functions. They help with retrieving data about the current
210# state of the tree:
211def GetAllTargets():
212 """Get the complete list of targets.
213
214 returns the list of cross targets for the current tree
215 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400216 targets = GetToolchainsForBoard('all')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100217
218 # Remove the host target as that is not a cross-target. Replace with 'host'.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400219 del targets[GetHostTuple()]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100220 return targets
221
222
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100223def GetToolchainsForBoard(board):
224 """Get a list of toolchain tuples for a given board name
225
226 returns the list of toolchain tuples for the given board
227 """
David Jamese5867812012-10-19 12:02:20 -0700228 overlays = portage_utilities.FindOverlays(
229 constants.BOTH_OVERLAYS, None if board in ('all', 'sdk') else board)
Mike Frysingereaebb582012-06-19 13:04:53 -0400230 toolchains = GetTuplesForOverlays(overlays)
231 if board == 'sdk':
232 toolchains = FilterToolchains(toolchains, 'sdk', True)
233 return toolchains
234
235
236def FilterToolchains(targets, key, value):
237 """Filter out targets based on their attributes.
238
239 args:
240 targets - dict of toolchains
241 key - metadata to examine
242 value - expected value for metadata
243
244 returns a dict where all targets whose metadata key does not match value
245 have been deleted.
246 """
247 for target, metadata in targets.items():
248 if metadata[key] != value:
249 del targets[target]
250 return targets
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100251
252
Zdenek Behan508dcce2011-12-05 15:39:32 +0100253def GetInstalledPackageVersions(target, package):
254 """Extracts the list of current versions of a target, package pair.
255
256 args:
257 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
258
259 returns the list of versions of the package currently installed.
260 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100261 versions = []
262 # This is the package name in terms of portage.
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100263 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100264 for pkg in portage.db['/']['vartree'].dbapi.match(atom):
265 version = portage.versions.cpv_getversion(pkg)
266 versions.append(version)
267 return versions
268
269
Zdenek Behan699ddd32012-04-13 07:14:08 +0200270def GetStablePackageVersion(target, package, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100271 """Extracts the current stable version for a given package.
272
273 args:
274 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200275 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100276
277 returns a string containing the latest version.
278 """
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200279 def mass_portageq_splitline(entry):
280 """Splits the output of mass_best_visible into package:version tuple."""
281 # mass_best_visible returns lines of the format "package:cpv"
282 split_string = entry.split(':', 1)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200283 if split_string[1]:
284 split_string[1] = portage.versions.cpv_getversion(split_string[1])
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200285 return split_string
286
287 CACHE_ATTR = '_target_stable_map'
288
Zdenek Behan699ddd32012-04-13 07:14:08 +0200289 pkgtype = "installed" if installed else "ebuild"
290
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200291 val = getattr(VAR_CACHE, CACHE_ATTR, {})
292 if not target in val:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200293 val[target] = {}
294 if not pkgtype in val[target]:
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200295 keyword = GetPortageKeyword(target)
296 extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
297 # Evaluate all packages for a target in one swoop, because it's much faster.
298 pkgs = [GetPortagePackage(target, p) for p in GetTargetPackages(target)]
Zdenek Behan699ddd32012-04-13 07:14:08 +0200299 cmd = ['portageq', 'mass_best_visible', '/', pkgtype] + pkgs
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200300 cpvs = cros_build_lib.RunCommand(cmd,
301 print_cmd=False, redirect_stdout=True,
302 extra_env=extra_env).output.splitlines()
Zdenek Behan699ddd32012-04-13 07:14:08 +0200303 val[target][pkgtype] = dict(map(mass_portageq_splitline, cpvs))
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200304 setattr(VAR_CACHE, CACHE_ATTR, val)
305
Zdenek Behan699ddd32012-04-13 07:14:08 +0200306 return val[target][pkgtype][GetPortagePackage(target, package)]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100307
308
Zdenek Behan699ddd32012-04-13 07:14:08 +0200309def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100310 """Resolves keywords in a given version list for a particular package.
311
312 Resolving means replacing PACKAGE_STABLE with the actual number.
313
314 args:
315 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
316 versions - list of versions to resolve
317
318 returns list of purely numeric versions equivalent to argument
319 """
320 resolved = []
321 for version in versions:
322 if version == PACKAGE_STABLE:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200323 resolved.append(GetStablePackageVersion(target, package, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100324 elif version != PACKAGE_NONE:
325 resolved.append(version)
326 return resolved
327
328
329def GetDesiredPackageVersions(target, package):
330 """Produces the list of desired versions for each target, package pair.
331
332 The first version in the list is implicitly treated as primary, ie.
333 the version that will be initialized by crossdev and selected.
334
335 If the version is PACKAGE_STABLE, it really means the current version which
336 is emerged by using the package atom with no particular version key.
337 Since crossdev unmasks all packages by default, this will actually
338 mean 'unstable' in most cases.
339
340 args:
341 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
342
343 returns a list composed of either a version string, PACKAGE_STABLE
344 """
345 packagemap = GetPackageMap(target)
346
347 versions = []
348 if package in packagemap:
349 versions.append(packagemap[package])
350
351 return versions
352
353
354def TargetIsInitialized(target):
355 """Verifies if the given list of targets has been correctly initialized.
356
357 This determines whether we have to call crossdev while emerging
358 toolchain packages or can do it using emerge. Emerge is naturally
359 preferred, because all packages can be updated in a single pass.
360
361 args:
362 targets - list of individual cross targets which are checked
363
364 returns True if target is completely initialized
365 returns False otherwise
366 """
367 # Check if packages for the given target all have a proper version.
368 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100369 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100370 # Do we even want this package && is it initialized?
371 if not IsPackageDisabled(target, package) and \
372 not GetInstalledPackageVersions(target, package):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100373 return False
374 return True
375 except cros_build_lib.RunCommandError:
376 # Fails - The target has likely never been initialized before.
377 return False
378
379
380def RemovePackageMask(target):
381 """Removes a package.mask file for the given platform.
382
383 The pre-existing package.mask files can mess with the keywords.
384
385 args:
386 target - the target for which to remove the file
387 """
388 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700389 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100390
391
392def CreatePackageMask(target, masks):
393 """[Re]creates a package.mask file for the given platform.
394
395 args:
396 target - the given target on which to operate
397 masks - a map of package : version,
398 where version is the highest permissible version (mask >)
399 """
400 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
401 assert not os.path.exists(maskfile)
Brian Harringaf019fb2012-05-10 15:06:13 -0700402 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100403
404 with open(maskfile, 'w') as f:
405 for pkg, m in masks.items():
406 f.write('>%s-%s\n' % (pkg, m))
407
408
409def CreatePackageKeywords(target):
410 """[Re]create a package.keywords file for the platform.
411
412 This sets all package.keywords files to unmask all stable/testing packages.
413 TODO: Note that this approach should be deprecated and is only done for
414 compatibility reasons. In the future, we'd like to stop using keywords
415 altogether, and keep just stable unmasked.
416
417 args:
418 target - target for which to recreate package.keywords
419 """
420 maskfile = os.path.join('/etc/portage/package.keywords', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700421 osutils.SafeUnlink(maskfile)
422
423 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100424
425 keyword = GetPortageKeyword(target)
426
427 with open(maskfile, 'w') as f:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100428 for pkg in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100429 if IsPackageDisabled(target, pkg):
430 continue
431 f.write('%s %s ~%s\n' %
432 (GetPortagePackage(target, pkg), keyword, keyword))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433
434
435# Main functions performing the actual update steps.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200436def UpdateCrossdevTargets(targets, usepkg, config_only=False):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 """Calls crossdev to initialize a cross target.
438 args:
439 targets - the list of targets to initialize using crossdev
440 usepkg - copies the commandline opts
441 """
Zdenek Behane6fd5f82012-08-16 18:50:31 +0200442 cmdbase = ['crossdev', '--show-fail-log']
443 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
444 # Pick stable by default, and override as necessary.
445 cmdbase.extend(['-P', '--oneshot'])
446 if usepkg:
447 cmdbase.extend(['-P', '--getbinpkg',
448 '-P', '--usepkgonly',
449 '--without-headers'])
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100450
Zdenek Behane6fd5f82012-08-16 18:50:31 +0200451 cmdbase.extend(['--overlays', '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)])
452 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
453
454 for target in targets:
455 cmd = cmdbase + ['-t', target]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100456
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100457 for pkg in GetTargetPackages(target):
458 if pkg == 'gdb':
459 # Gdb does not have selectable versions.
460 cmd.append('--ex-gdb')
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100461 continue
Zdenek Behan508dcce2011-12-05 15:39:32 +0100462 # The first of the desired versions is the "primary" one.
463 version = GetDesiredPackageVersions(target, pkg)[0]
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100464 cmd.extend(['--%s' % pkg, version])
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200465
Mike Frysinger7ccee992012-06-01 21:27:59 -0400466 cmd.extend(targets[target]['crossdev'].split())
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200467 if config_only:
468 # In this case we want to just quietly reinit
469 cmd.append('--init-target')
470 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
471 else:
472 cros_build_lib.RunCommand(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100473
474
475def UpdateTargets(targets, usepkg):
476 """Determines which packages need update/unmerge and defers to portage.
477
478 args:
479 targets - the list of targets to update
480 usepkg - copies the commandline option
481 """
482 # TODO(zbehan): This process is rather complex due to package.* handling.
483 # With some semantic changes over the original setup_board functionality,
484 # it can be considerably cleaned up.
485 mergemap = {}
486
487 # For each target, we do two things. Figure out the list of updates,
488 # and figure out the appropriate keywords/masks. Crossdev will initialize
489 # these, but they need to be regenerated on every update.
490 print 'Determining required toolchain updates...'
491 for target in targets:
492 # Record the highest needed version for each target, for masking purposes.
493 RemovePackageMask(target)
494 CreatePackageKeywords(target)
495 packagemasks = {}
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100496 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100497 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100498 if IsPackageDisabled(target, package):
499 continue
500 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100501 current = GetInstalledPackageVersions(target, package)
502 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200503 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100504 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100505
506 # Pick the highest version for mask.
507 packagemasks[pkg] = portage.versions.best(desired_num)
508
509 CreatePackageMask(target, packagemasks)
510
511 packages = []
512 for pkg in mergemap:
513 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200514 if ver != PACKAGE_NONE:
515 # Be a little more permissive for usepkg, the binaries may not exist.
516 packages.append('%s%s-%s' % ('<=' if usepkg else '=', pkg, ver))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517
518 if not packages:
519 print 'Nothing to update!'
520 return
521
522 print 'Updating packages:'
523 print packages
524
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100525 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100526 if usepkg:
527 cmd.extend(['--getbinpkg', '--usepkgonly'])
528
529 cmd.extend(packages)
530 cros_build_lib.RunCommand(cmd)
531
532
533def CleanTargets(targets):
534 """Unmerges old packages that are assumed unnecessary."""
535 unmergemap = {}
536 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100537 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100538 if IsPackageDisabled(target, package):
539 continue
540 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100541 current = GetInstalledPackageVersions(target, package)
542 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200543 desired_num = VersionListToNumeric(target, package, desired, True)
544 if not set(desired_num).issubset(current):
545 print 'Some packages have been held back, skipping clean!'
546 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100547 unmergemap[pkg] = set(current).difference(desired_num)
548
549 # Cleaning doesn't care about consistency and rebuilding package.* files.
550 packages = []
551 for pkg, vers in unmergemap.iteritems():
552 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
553
554 if packages:
555 print 'Cleaning packages:'
556 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100557 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558 cmd.extend(packages)
559 cros_build_lib.RunCommand(cmd)
560 else:
561 print 'Nothing to clean!'
562
563
564def SelectActiveToolchains(targets, suffixes):
565 """Runs gcc-config and binutils-config to select the desired.
566
567 args:
568 targets - the targets to select
569 """
570 for package in ['gcc', 'binutils']:
571 for target in targets:
572 # Pick the first version in the numbered list as the selected one.
573 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200574 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100575 desired = desired_num[0]
576 # *-config does not play revisions, strip them, keep just PV.
577 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
578
579 if target == 'host':
580 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100581 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100582
583 # And finally, attach target to it.
584 desired = '%s-%s' % (target, desired)
585
586 # Target specific hacks
587 if package in suffixes:
588 if target in suffixes[package]:
589 desired += suffixes[package][target]
590
591 cmd = [ package + '-config', '-c', target ]
592 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200593 redirect_stdout=True).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100594 # Do not gcc-config when the current is live or nothing needs to be done.
595 if current != desired and current != '9999':
596 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100597 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100598
599
Mike Frysinger7ccee992012-06-01 21:27:59 -0400600def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted,
601 boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100602 """Performs all steps to create a synchronized toolchain enviroment.
603
604 args:
605 arguments correspond to the given commandline flags
606 """
Mike Frysinger7ccee992012-06-01 21:27:59 -0400607 targets = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100608 if not hostonly:
609 # For hostonly, we can skip most of the below logic, much of which won't
610 # work on bare systems where this is useful.
611 alltargets = GetAllTargets()
Mike Frysinger7ccee992012-06-01 21:27:59 -0400612 targets_wanted = set(targets_wanted)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100613 if targets_wanted == set(['all']):
Mike Frysinger7ccee992012-06-01 21:27:59 -0400614 targets = alltargets
Mike Frysingereaebb582012-06-19 13:04:53 -0400615 elif targets_wanted == set(['sdk']):
616 # Filter out all the non-sdk toolchains as we don't want to mess
617 # with those in all of our builds.
618 targets = FilterToolchains(alltargets, 'sdk', True)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100619 else:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100620 # Verify user input.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400621 nonexistant = []
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100622 for target in targets_wanted:
623 if target not in alltargets:
624 nonexistant.append(target)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400625 else:
626 targets[target] = alltargets[target]
627 if nonexistant:
628 cros_build_lib.Die('Invalid targets: ' + ','.join(nonexistant))
629
Mike Frysinger7ccee992012-06-01 21:27:59 -0400630 # Now re-add any targets that might be from this board. This is
631 # to allow unofficial boards to declare their own toolchains.
632 for board in boards_wanted:
633 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100634
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100635 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400636 crossdev_targets = {}
637 reconfig_targets = {}
638 for target in targets:
639 if TargetIsInitialized(target):
640 reconfig_targets[target] = targets[target]
641 else:
642 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100643 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200644 print 'The following targets need to be re-initialized:'
645 print crossdev_targets
646 UpdateCrossdevTargets(crossdev_targets, usepkg)
647 # Those that were not initialized may need a config update.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200648 UpdateCrossdevTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100649
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100650 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400651 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100652
653 # Now update all packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100654 UpdateTargets(targets, usepkg)
655 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
656
657 if deleteold:
658 CleanTargets(targets)
659
660
Brian Harring30675052012-02-29 12:18:22 -0800661def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100662 usage = """usage: %prog [options]
663
664 The script installs and updates the toolchains in your chroot.
665 """
666 parser = optparse.OptionParser(usage)
667 parser.add_option('-u', '--nousepkg',
668 action='store_false', dest='usepkg', default=True,
669 help=('Use prebuilt packages if possible.'))
670 parser.add_option('-d', '--deleteold',
671 action='store_true', dest='deleteold', default=False,
672 help=('Unmerge deprecated packages.'))
673 parser.add_option('-t', '--targets',
Mike Frysingereaebb582012-06-19 13:04:53 -0400674 dest='targets', default='sdk',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100675 help=('Comma separated list of tuples. '
Mike Frysingereaebb582012-06-19 13:04:53 -0400676 'Special keyword \'host\' is allowed. Default: sdk.'))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400677 parser.add_option('--include-boards',
678 dest='include_boards', default='',
679 help=('Comma separated list of boards whose toolchains we'
680 ' will always include. Default: none.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100681 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100682 dest='hostonly', default=False, action='store_true',
683 help=('Only setup the host toolchain. '
684 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100685 parser.add_option('--show-board-cfg',
686 dest='board_cfg', default=None,
687 help=('Board to list toolchain tuples for'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100688
Brian Harring30675052012-02-29 12:18:22 -0800689 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100690
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100691 if options.board_cfg:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400692 toolchains = GetToolchainsForBoard(options.board_cfg)
693 # Make sure we display the default toolchain first.
694 tuples = toolchains.keys()
695 for tuple in tuples:
696 if toolchains[tuple]['default']:
697 tuples.remove(tuple)
698 tuples.insert(0, tuple)
699 break
700 print ','.join(tuples)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100701 return 0
702
Mike Frysinger984d0622012-06-01 16:08:44 -0400703 # This has to be always run as root.
704 if not os.getuid() == 0:
705 print "%s: This script must be run as root!" % sys.argv[0]
706 sys.exit(1)
707
Zdenek Behan508dcce2011-12-05 15:39:32 +0100708 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400709 boards = set(options.include_boards.split(',')) if options.include_boards \
710 else set()
711 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets,
712 boards)