blob: cc31b5fed579efc7ae7b0f35362a25852d4687ff [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:
Mike Frysinger652a6f92012-06-19 03:37:04 -0400234 # TODO(vapier):
235 # Some tools will give us the base board name ('tegra2') while others will
236 # give us the board+variant ('tegra2_kaen'). The cros_overlay_list does
237 # not like getting variant names, so strip that off. Not entirely clear
238 # if this is what we want to do, or if the cros_overlay_list should give
239 # us back the overlays (including variants). I'm inclined to go with the
240 # latter, but that requires more testing to prevent fallout.
241 cmd.append('--board=' + board.split('_')[0])
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100242 overlays = cros_build_lib.RunCommand(
243 cmd, print_cmd=False, redirect_stdout=True).output.splitlines()
244
245 return GetTuplesForOverlays(overlays)
246
247
Zdenek Behan508dcce2011-12-05 15:39:32 +0100248def GetInstalledPackageVersions(target, package):
249 """Extracts the list of current versions of a target, package pair.
250
251 args:
252 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
253
254 returns the list of versions of the package currently installed.
255 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100256 versions = []
257 # This is the package name in terms of portage.
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100258 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100259 for pkg in portage.db['/']['vartree'].dbapi.match(atom):
260 version = portage.versions.cpv_getversion(pkg)
261 versions.append(version)
262 return versions
263
264
Zdenek Behan699ddd32012-04-13 07:14:08 +0200265def GetStablePackageVersion(target, package, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100266 """Extracts the current stable version for a given package.
267
268 args:
269 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200270 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100271
272 returns a string containing the latest version.
273 """
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200274 def mass_portageq_splitline(entry):
275 """Splits the output of mass_best_visible into package:version tuple."""
276 # mass_best_visible returns lines of the format "package:cpv"
277 split_string = entry.split(':', 1)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200278 if split_string[1]:
279 split_string[1] = portage.versions.cpv_getversion(split_string[1])
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200280 return split_string
281
282 CACHE_ATTR = '_target_stable_map'
283
Zdenek Behan699ddd32012-04-13 07:14:08 +0200284 pkgtype = "installed" if installed else "ebuild"
285
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200286 val = getattr(VAR_CACHE, CACHE_ATTR, {})
287 if not target in val:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200288 val[target] = {}
289 if not pkgtype in val[target]:
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200290 keyword = GetPortageKeyword(target)
291 extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
292 # Evaluate all packages for a target in one swoop, because it's much faster.
293 pkgs = [GetPortagePackage(target, p) for p in GetTargetPackages(target)]
Zdenek Behan699ddd32012-04-13 07:14:08 +0200294 cmd = ['portageq', 'mass_best_visible', '/', pkgtype] + pkgs
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200295 cpvs = cros_build_lib.RunCommand(cmd,
296 print_cmd=False, redirect_stdout=True,
297 extra_env=extra_env).output.splitlines()
Zdenek Behan699ddd32012-04-13 07:14:08 +0200298 val[target][pkgtype] = dict(map(mass_portageq_splitline, cpvs))
Zdenek Behan20d6ea22012-04-05 05:15:39 +0200299 setattr(VAR_CACHE, CACHE_ATTR, val)
300
Zdenek Behan699ddd32012-04-13 07:14:08 +0200301 return val[target][pkgtype][GetPortagePackage(target, package)]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100302
303
Zdenek Behan699ddd32012-04-13 07:14:08 +0200304def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100305 """Resolves keywords in a given version list for a particular package.
306
307 Resolving means replacing PACKAGE_STABLE with the actual number.
308
309 args:
310 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
311 versions - list of versions to resolve
312
313 returns list of purely numeric versions equivalent to argument
314 """
315 resolved = []
316 for version in versions:
317 if version == PACKAGE_STABLE:
Zdenek Behan699ddd32012-04-13 07:14:08 +0200318 resolved.append(GetStablePackageVersion(target, package, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100319 elif version != PACKAGE_NONE:
320 resolved.append(version)
321 return resolved
322
323
324def GetDesiredPackageVersions(target, package):
325 """Produces the list of desired versions for each target, package pair.
326
327 The first version in the list is implicitly treated as primary, ie.
328 the version that will be initialized by crossdev and selected.
329
330 If the version is PACKAGE_STABLE, it really means the current version which
331 is emerged by using the package atom with no particular version key.
332 Since crossdev unmasks all packages by default, this will actually
333 mean 'unstable' in most cases.
334
335 args:
336 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
337
338 returns a list composed of either a version string, PACKAGE_STABLE
339 """
340 packagemap = GetPackageMap(target)
341
342 versions = []
343 if package in packagemap:
344 versions.append(packagemap[package])
345
346 return versions
347
348
349def TargetIsInitialized(target):
350 """Verifies if the given list of targets has been correctly initialized.
351
352 This determines whether we have to call crossdev while emerging
353 toolchain packages or can do it using emerge. Emerge is naturally
354 preferred, because all packages can be updated in a single pass.
355
356 args:
357 targets - list of individual cross targets which are checked
358
359 returns True if target is completely initialized
360 returns False otherwise
361 """
362 # Check if packages for the given target all have a proper version.
363 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100364 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100365 # Do we even want this package && is it initialized?
366 if not IsPackageDisabled(target, package) and \
367 not GetInstalledPackageVersions(target, package):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100368 return False
369 return True
370 except cros_build_lib.RunCommandError:
371 # Fails - The target has likely never been initialized before.
372 return False
373
374
375def RemovePackageMask(target):
376 """Removes a package.mask file for the given platform.
377
378 The pre-existing package.mask files can mess with the keywords.
379
380 args:
381 target - the target for which to remove the file
382 """
383 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700384 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100385
386
387def CreatePackageMask(target, masks):
388 """[Re]creates a package.mask file for the given platform.
389
390 args:
391 target - the given target on which to operate
392 masks - a map of package : version,
393 where version is the highest permissible version (mask >)
394 """
395 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
396 assert not os.path.exists(maskfile)
Brian Harringaf019fb2012-05-10 15:06:13 -0700397 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100398
399 with open(maskfile, 'w') as f:
400 for pkg, m in masks.items():
401 f.write('>%s-%s\n' % (pkg, m))
402
403
404def CreatePackageKeywords(target):
405 """[Re]create a package.keywords file for the platform.
406
407 This sets all package.keywords files to unmask all stable/testing packages.
408 TODO: Note that this approach should be deprecated and is only done for
409 compatibility reasons. In the future, we'd like to stop using keywords
410 altogether, and keep just stable unmasked.
411
412 args:
413 target - target for which to recreate package.keywords
414 """
415 maskfile = os.path.join('/etc/portage/package.keywords', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700416 osutils.SafeUnlink(maskfile)
417
418 osutils.SafeMakedirs(os.path.dirname(maskfile))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419
420 keyword = GetPortageKeyword(target)
421
422 with open(maskfile, 'w') as f:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100423 for pkg in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100424 if IsPackageDisabled(target, pkg):
425 continue
426 f.write('%s %s ~%s\n' %
427 (GetPortagePackage(target, pkg), keyword, keyword))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100428
429
430# Main functions performing the actual update steps.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200431def UpdateCrossdevTargets(targets, usepkg, config_only=False):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100432 """Calls crossdev to initialize a cross target.
433 args:
434 targets - the list of targets to initialize using crossdev
435 usepkg - copies the commandline opts
436 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 for target in targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200438 cmd = ['crossdev', '--show-fail-log', '-t', target]
439 cmd.extend(['--env', 'FEATURES=splitdebug'])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100440 # Pick stable by default, and override as necessary.
441 cmd.extend(['-P', '--oneshot'])
442 if usepkg:
443 cmd.extend(['-P', '--getbinpkg',
444 '-P', '--usepkgonly',
445 '--without-headers'])
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100446
447 cmd.extend(['--overlays', '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)])
448 cmd.extend(['--ov-output', CROSSDEV_OVERLAY])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100449
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100450 for pkg in GetTargetPackages(target):
451 if pkg == 'gdb':
452 # Gdb does not have selectable versions.
453 cmd.append('--ex-gdb')
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100454 continue
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455 # The first of the desired versions is the "primary" one.
456 version = GetDesiredPackageVersions(target, pkg)[0]
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100457 cmd.extend(['--%s' % pkg, version])
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200458
Mike Frysinger7ccee992012-06-01 21:27:59 -0400459 cmd.extend(targets[target]['crossdev'].split())
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200460 if config_only:
461 # In this case we want to just quietly reinit
462 cmd.append('--init-target')
463 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
464 else:
465 cros_build_lib.RunCommand(cmd)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100466
467
468def UpdateTargets(targets, usepkg):
469 """Determines which packages need update/unmerge and defers to portage.
470
471 args:
472 targets - the list of targets to update
473 usepkg - copies the commandline option
474 """
475 # TODO(zbehan): This process is rather complex due to package.* handling.
476 # With some semantic changes over the original setup_board functionality,
477 # it can be considerably cleaned up.
478 mergemap = {}
479
480 # For each target, we do two things. Figure out the list of updates,
481 # and figure out the appropriate keywords/masks. Crossdev will initialize
482 # these, but they need to be regenerated on every update.
483 print 'Determining required toolchain updates...'
484 for target in targets:
485 # Record the highest needed version for each target, for masking purposes.
486 RemovePackageMask(target)
487 CreatePackageKeywords(target)
488 packagemasks = {}
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100489 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100490 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100491 if IsPackageDisabled(target, package):
492 continue
493 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100494 current = GetInstalledPackageVersions(target, package)
495 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200496 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100497 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100498
499 # Pick the highest version for mask.
500 packagemasks[pkg] = portage.versions.best(desired_num)
501
502 CreatePackageMask(target, packagemasks)
503
504 packages = []
505 for pkg in mergemap:
506 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200507 if ver != PACKAGE_NONE:
508 # Be a little more permissive for usepkg, the binaries may not exist.
509 packages.append('%s%s-%s' % ('<=' if usepkg else '=', pkg, ver))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510
511 if not packages:
512 print 'Nothing to update!'
513 return
514
515 print 'Updating packages:'
516 print packages
517
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100518 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100519 if usepkg:
520 cmd.extend(['--getbinpkg', '--usepkgonly'])
521
522 cmd.extend(packages)
523 cros_build_lib.RunCommand(cmd)
524
525
526def CleanTargets(targets):
527 """Unmerges old packages that are assumed unnecessary."""
528 unmergemap = {}
529 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100530 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100531 if IsPackageDisabled(target, package):
532 continue
533 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534 current = GetInstalledPackageVersions(target, package)
535 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200536 desired_num = VersionListToNumeric(target, package, desired, True)
537 if not set(desired_num).issubset(current):
538 print 'Some packages have been held back, skipping clean!'
539 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540 unmergemap[pkg] = set(current).difference(desired_num)
541
542 # Cleaning doesn't care about consistency and rebuilding package.* files.
543 packages = []
544 for pkg, vers in unmergemap.iteritems():
545 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
546
547 if packages:
548 print 'Cleaning packages:'
549 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100550 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100551 cmd.extend(packages)
552 cros_build_lib.RunCommand(cmd)
553 else:
554 print 'Nothing to clean!'
555
556
557def SelectActiveToolchains(targets, suffixes):
558 """Runs gcc-config and binutils-config to select the desired.
559
560 args:
561 targets - the targets to select
562 """
563 for package in ['gcc', 'binutils']:
564 for target in targets:
565 # Pick the first version in the numbered list as the selected one.
566 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200567 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100568 desired = desired_num[0]
569 # *-config does not play revisions, strip them, keep just PV.
570 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
571
572 if target == 'host':
573 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100574 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100575
576 # And finally, attach target to it.
577 desired = '%s-%s' % (target, desired)
578
579 # Target specific hacks
580 if package in suffixes:
581 if target in suffixes[package]:
582 desired += suffixes[package][target]
583
584 cmd = [ package + '-config', '-c', target ]
585 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200586 redirect_stdout=True).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100587 # Do not gcc-config when the current is live or nothing needs to be done.
588 if current != desired and current != '9999':
589 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100590 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100591
592
Mike Frysinger7ccee992012-06-01 21:27:59 -0400593def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted,
594 boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100595 """Performs all steps to create a synchronized toolchain enviroment.
596
597 args:
598 arguments correspond to the given commandline flags
599 """
Mike Frysinger7ccee992012-06-01 21:27:59 -0400600 targets = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100601 if not hostonly:
602 # For hostonly, we can skip most of the below logic, much of which won't
603 # work on bare systems where this is useful.
604 alltargets = GetAllTargets()
Mike Frysinger7ccee992012-06-01 21:27:59 -0400605 targets_wanted = set(targets_wanted)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100606 if targets_wanted == set(['all']):
Mike Frysinger7ccee992012-06-01 21:27:59 -0400607 targets = alltargets
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100608 else:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100609 # Verify user input.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400610 nonexistant = []
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100611 for target in targets_wanted:
612 if target not in alltargets:
613 nonexistant.append(target)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400614 else:
615 targets[target] = alltargets[target]
616 if nonexistant:
617 cros_build_lib.Die('Invalid targets: ' + ','.join(nonexistant))
618
619 # Filter out all the non-sdk toolchains as we don't want to mess
620 # with those in all of our builds.
621 for target in targets.keys():
622 if not targets[target]['sdk']:
623 del targets[target]
624 # Now re-add any targets that might be from this board. This is
625 # to allow unofficial boards to declare their own toolchains.
626 for board in boards_wanted:
627 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100628
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100629 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400630 crossdev_targets = {}
631 reconfig_targets = {}
632 for target in targets:
633 if TargetIsInitialized(target):
634 reconfig_targets[target] = targets[target]
635 else:
636 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100637 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200638 print 'The following targets need to be re-initialized:'
639 print crossdev_targets
640 UpdateCrossdevTargets(crossdev_targets, usepkg)
641 # Those that were not initialized may need a config update.
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200642 UpdateCrossdevTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100643
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100644 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400645 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100646
647 # Now update all packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100648 UpdateTargets(targets, usepkg)
649 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
650
651 if deleteold:
652 CleanTargets(targets)
653
654
Brian Harring30675052012-02-29 12:18:22 -0800655def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100656 usage = """usage: %prog [options]
657
658 The script installs and updates the toolchains in your chroot.
659 """
660 parser = optparse.OptionParser(usage)
661 parser.add_option('-u', '--nousepkg',
662 action='store_false', dest='usepkg', default=True,
663 help=('Use prebuilt packages if possible.'))
664 parser.add_option('-d', '--deleteold',
665 action='store_true', dest='deleteold', default=False,
666 help=('Unmerge deprecated packages.'))
667 parser.add_option('-t', '--targets',
668 dest='targets', default='all',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100669 help=('Comma separated list of tuples. '
670 'Special keyword \'host\' is allowed. Default: all.'))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400671 parser.add_option('--include-boards',
672 dest='include_boards', default='',
673 help=('Comma separated list of boards whose toolchains we'
674 ' will always include. Default: none.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100675 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100676 dest='hostonly', default=False, action='store_true',
677 help=('Only setup the host toolchain. '
678 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100679 parser.add_option('--show-board-cfg',
680 dest='board_cfg', default=None,
681 help=('Board to list toolchain tuples for'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100682
Brian Harring30675052012-02-29 12:18:22 -0800683 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100684
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100685 if options.board_cfg:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400686 toolchains = GetToolchainsForBoard(options.board_cfg)
687 # Make sure we display the default toolchain first.
688 tuples = toolchains.keys()
689 for tuple in tuples:
690 if toolchains[tuple]['default']:
691 tuples.remove(tuple)
692 tuples.insert(0, tuple)
693 break
694 print ','.join(tuples)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100695 return 0
696
Mike Frysinger984d0622012-06-01 16:08:44 -0400697 # This has to be always run as root.
698 if not os.getuid() == 0:
699 print "%s: This script must be run as root!" % sys.argv[0]
700 sys.exit(1)
701
Zdenek Behan508dcce2011-12-05 15:39:32 +0100702 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400703 boards = set(options.include_boards.split(',')) if options.include_boards \
704 else set()
705 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets,
706 boards)