blob: c8f96be8f0158abb4be9dd5d2c538d1255a68cff [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])
David James55096062012-11-03 16:31:23 -0700132 setattr(VAR_CACHE, CACHE_ATTR, val)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100133 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!'
David James55096062012-11-03 16:31:23 -0700520 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100521
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)
David James55096062012-11-03 16:31:23 -0700531 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532
533
534def CleanTargets(targets):
535 """Unmerges old packages that are assumed unnecessary."""
536 unmergemap = {}
537 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100538 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100539 if IsPackageDisabled(target, package):
540 continue
541 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100542 current = GetInstalledPackageVersions(target, package)
543 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200544 desired_num = VersionListToNumeric(target, package, desired, True)
545 if not set(desired_num).issubset(current):
546 print 'Some packages have been held back, skipping clean!'
547 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100548 unmergemap[pkg] = set(current).difference(desired_num)
549
550 # Cleaning doesn't care about consistency and rebuilding package.* files.
551 packages = []
552 for pkg, vers in unmergemap.iteritems():
553 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
554
555 if packages:
556 print 'Cleaning packages:'
557 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100558 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100559 cmd.extend(packages)
560 cros_build_lib.RunCommand(cmd)
561 else:
562 print 'Nothing to clean!'
563
564
565def SelectActiveToolchains(targets, suffixes):
566 """Runs gcc-config and binutils-config to select the desired.
567
568 args:
569 targets - the targets to select
570 """
571 for package in ['gcc', 'binutils']:
572 for target in targets:
573 # Pick the first version in the numbered list as the selected one.
574 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200575 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100576 desired = desired_num[0]
577 # *-config does not play revisions, strip them, keep just PV.
578 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
579
580 if target == 'host':
581 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100582 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100583
584 # And finally, attach target to it.
585 desired = '%s-%s' % (target, desired)
586
587 # Target specific hacks
588 if package in suffixes:
589 if target in suffixes[package]:
590 desired += suffixes[package][target]
591
592 cmd = [ package + '-config', '-c', target ]
593 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200594 redirect_stdout=True).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595 # Do not gcc-config when the current is live or nothing needs to be done.
596 if current != desired and current != '9999':
597 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100598 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100599
600
Mike Frysinger7ccee992012-06-01 21:27:59 -0400601def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted,
602 boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100603 """Performs all steps to create a synchronized toolchain enviroment.
604
605 args:
606 arguments correspond to the given commandline flags
607 """
Mike Frysinger7ccee992012-06-01 21:27:59 -0400608 targets = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100609 if not hostonly:
610 # For hostonly, we can skip most of the below logic, much of which won't
611 # work on bare systems where this is useful.
612 alltargets = GetAllTargets()
Mike Frysinger7ccee992012-06-01 21:27:59 -0400613 targets_wanted = set(targets_wanted)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100614 if targets_wanted == set(['all']):
Mike Frysinger7ccee992012-06-01 21:27:59 -0400615 targets = alltargets
Mike Frysingereaebb582012-06-19 13:04:53 -0400616 elif targets_wanted == set(['sdk']):
617 # Filter out all the non-sdk toolchains as we don't want to mess
618 # with those in all of our builds.
619 targets = FilterToolchains(alltargets, 'sdk', True)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100620 else:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100621 # Verify user input.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400622 nonexistant = []
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100623 for target in targets_wanted:
624 if target not in alltargets:
625 nonexistant.append(target)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400626 else:
627 targets[target] = alltargets[target]
628 if nonexistant:
629 cros_build_lib.Die('Invalid targets: ' + ','.join(nonexistant))
630
Mike Frysinger7ccee992012-06-01 21:27:59 -0400631 # Now re-add any targets that might be from this board. This is
632 # to allow unofficial boards to declare their own toolchains.
633 for board in boards_wanted:
634 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100635
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100636 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400637 crossdev_targets = {}
638 reconfig_targets = {}
639 for target in targets:
640 if TargetIsInitialized(target):
641 reconfig_targets[target] = targets[target]
642 else:
643 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100644 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200645 print 'The following targets need to be re-initialized:'
646 print crossdev_targets
647 UpdateCrossdevTargets(crossdev_targets, usepkg)
648 # Those that were not initialized may need a config update.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200649 UpdateCrossdevTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100650
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100651 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400652 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100653
654 # Now update all packages.
David James55096062012-11-03 16:31:23 -0700655 if UpdateTargets(targets, usepkg):
656 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100657
David James55096062012-11-03 16:31:23 -0700658 if deleteold:
659 CleanTargets(targets)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100660
661
Brian Harring30675052012-02-29 12:18:22 -0800662def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100663 usage = """usage: %prog [options]
664
665 The script installs and updates the toolchains in your chroot.
666 """
667 parser = optparse.OptionParser(usage)
668 parser.add_option('-u', '--nousepkg',
669 action='store_false', dest='usepkg', default=True,
670 help=('Use prebuilt packages if possible.'))
671 parser.add_option('-d', '--deleteold',
672 action='store_true', dest='deleteold', default=False,
673 help=('Unmerge deprecated packages.'))
674 parser.add_option('-t', '--targets',
Mike Frysingereaebb582012-06-19 13:04:53 -0400675 dest='targets', default='sdk',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100676 help=('Comma separated list of tuples. '
Mike Frysingereaebb582012-06-19 13:04:53 -0400677 'Special keyword \'host\' is allowed. Default: sdk.'))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400678 parser.add_option('--include-boards',
679 dest='include_boards', default='',
680 help=('Comma separated list of boards whose toolchains we'
681 ' will always include. Default: none.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100682 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100683 dest='hostonly', default=False, action='store_true',
684 help=('Only setup the host toolchain. '
685 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100686 parser.add_option('--show-board-cfg',
687 dest='board_cfg', default=None,
688 help=('Board to list toolchain tuples for'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100689
Brian Harring30675052012-02-29 12:18:22 -0800690 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100691
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100692 if options.board_cfg:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400693 toolchains = GetToolchainsForBoard(options.board_cfg)
694 # Make sure we display the default toolchain first.
695 tuples = toolchains.keys()
696 for tuple in tuples:
697 if toolchains[tuple]['default']:
698 tuples.remove(tuple)
699 tuples.insert(0, tuple)
700 break
701 print ','.join(tuples)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100702 return 0
703
Mike Frysinger984d0622012-06-01 16:08:44 -0400704 # This has to be always run as root.
705 if not os.getuid() == 0:
706 print "%s: This script must be run as root!" % sys.argv[0]
707 sys.exit(1)
708
Zdenek Behan508dcce2011-12-05 15:39:32 +0100709 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400710 boards = set(options.include_boards.split(',')) if options.include_boards \
711 else set()
712 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets,
713 boards)