blob: 86a882b6c4b6c96bcb315be0b723366f0b7f3cdd [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
David James66a09c42012-11-05 13:31:38 -080064class Crossdev(object):
65 """Class for interacting with crossdev and caching its output."""
66
67 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
68 _CACHE = {}
69
70 @classmethod
71 def Load(cls, reconfig):
72 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -080073 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
74 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -080075 if os.path.exists(cls._CACHE_FILE) and not reconfig:
76 with open(cls._CACHE_FILE) as f:
77 data = json.load(f)
David James90239b92012-11-05 15:31:34 -080078 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -080079 cls._CACHE = data
80
81 @classmethod
82 def Save(cls):
83 """Store crossdev cache on disk."""
84 # Save the cache from the successful run.
85 with open(cls._CACHE_FILE, 'w') as f:
86 json.dump(cls._CACHE, f)
87
88 @classmethod
89 def GetConfig(cls, target):
90 """Returns a map of crossdev provided variables about a tuple."""
91 CACHE_ATTR = '_target_tuple_map'
92
93 val = cls._CACHE.setdefault(CACHE_ATTR, {})
94 if not target in val:
95 # Find out the crossdev tuple.
96 target_tuple = target
97 if target == 'host':
98 target_tuple = GetHostTuple()
99 # Catch output of crossdev.
100 out = cros_build_lib.RunCommand(['crossdev', '--show-target-cfg',
101 '--ex-gdb', target_tuple],
102 print_cmd=False, redirect_stdout=True).output.splitlines()
103 # List of tuples split at the first '=', converted into dict.
104 val[target] = dict([x.split('=', 1) for x in out])
105 return val[target]
106
107 @classmethod
108 def UpdateTargets(cls, targets, usepkg, config_only=False):
109 """Calls crossdev to initialize a cross target.
110
111 Args:
112 targets - the list of targets to initialize using crossdev
113 usepkg - copies the commandline opts
114 config_only - Just update
115 """
116 configured_targets = cls._CACHE.setdefault('configured_targets', [])
117
118 cmdbase = ['crossdev', '--show-fail-log']
119 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
120 # Pick stable by default, and override as necessary.
121 cmdbase.extend(['-P', '--oneshot'])
122 if usepkg:
123 cmdbase.extend(['-P', '--getbinpkg',
124 '-P', '--usepkgonly',
125 '--without-headers'])
126
127 overlays = '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)
128 cmdbase.extend(['--overlays', overlays])
129 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
130
131 for target in targets:
132 if config_only and target in configured_targets:
133 continue
134
135 cmd = cmdbase + ['-t', target]
136
137 for pkg in GetTargetPackages(target):
138 if pkg == 'gdb':
139 # Gdb does not have selectable versions.
140 cmd.append('--ex-gdb')
141 continue
142 # The first of the desired versions is the "primary" one.
143 version = GetDesiredPackageVersions(target, pkg)[0]
144 cmd.extend(['--%s' % pkg, version])
145
146 cmd.extend(targets[target]['crossdev'].split())
147 if config_only:
148 # In this case we want to just quietly reinit
149 cmd.append('--init-target')
150 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
151 else:
152 cros_build_lib.RunCommand(cmd)
153
154 configured_targets.append(target)
155
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100156
Zdenek Behan508dcce2011-12-05 15:39:32 +0100157def GetPackageMap(target):
158 """Compiles a package map for the given target from the constants.
159
160 Uses a cache in target_version_map, that is dynamically filled in as needed,
161 since here everything is static data and the structuring is for ease of
162 configurability only.
163
164 args:
165 target - the target for which to return a version map
166
167 returns a map between packages and desired versions in internal format
168 (using the PACKAGE_* constants)
169 """
170 if target in target_version_map:
171 return target_version_map[target]
172
173 # Start from copy of the global defaults.
174 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
175
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100176 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100177 # prefer any specific overrides
178 if pkg in TARGET_VERSION_MAP.get(target, {}):
179 result[pkg] = TARGET_VERSION_MAP[target][pkg]
180 else:
181 # finally, if not already set, set a sane default
182 result.setdefault(pkg, DEFAULT_VERSION)
183 target_version_map[target] = result
184 return result
185
186
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100187def GetHostTuple():
David James90239b92012-11-05 15:31:34 -0800188 """Returns compiler tuple for the host system."""
189 return portage.settings['CHOST']
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100190
191
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100192def GetTargetPackages(target):
193 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800194 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100195 # Undesired packages are denoted by empty ${pkg}_pn variable.
196 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
197
198
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100199# Portage helper functions:
200def GetPortagePackage(target, package):
201 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800202 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100203 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100204 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100205 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100206 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100207 category = conf['category']
208 # Portage package:
209 pn = conf[package + '_pn']
210 # Final package name:
211 assert(category)
212 assert(pn)
213 return '%s/%s' % (category, pn)
214
215
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100216def IsPackageDisabled(target, package):
217 """Returns if the given package is not used for the target."""
218 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
219
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100220
221def GetTuplesForOverlays(overlays):
222 """Returns a set of tuples for a given set of overlays."""
Mike Frysinger7ccee992012-06-01 21:27:59 -0400223 tuples = {}
224 default_settings = {
225 'sdk' : True,
226 'crossdev' : '',
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400227 'default' : False,
Mike Frysinger7ccee992012-06-01 21:27:59 -0400228 }
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100229
230 for overlay in overlays:
231 config = os.path.join(overlay, 'toolchain.conf')
232 if os.path.exists(config):
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400233 first_tuple = None
234 seen_default = False
235
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100236 for line in osutils.ReadFile(config).splitlines():
Mike Frysinger7ccee992012-06-01 21:27:59 -0400237 # Split by hash sign so that comments are ignored.
238 # Then split the line to get the tuple and its options.
239 line = line.split('#', 1)[0].split()
240
241 if len(line) > 0:
242 tuple = line[0]
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400243 if not first_tuple:
244 first_tuple = tuple
Mike Frysinger7ccee992012-06-01 21:27:59 -0400245 if tuple not in tuples:
246 tuples[tuple] = copy.copy(default_settings)
247 if len(line) > 1:
248 tuples[tuple].update(json.loads(' '.join(line[1:])))
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400249 if tuples[tuple]['default']:
250 seen_default = True
251
252 # If the user has not explicitly marked a toolchain as default,
253 # automatically select the first tuple that we saw in the conf.
254 if not seen_default and first_tuple:
255 tuples[first_tuple]['default'] = True
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100256
257 return tuples
258
259
Zdenek Behan508dcce2011-12-05 15:39:32 +0100260# Tree interface functions. They help with retrieving data about the current
261# state of the tree:
262def GetAllTargets():
263 """Get the complete list of targets.
264
265 returns the list of cross targets for the current tree
266 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400267 targets = GetToolchainsForBoard('all')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100268
269 # Remove the host target as that is not a cross-target. Replace with 'host'.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400270 del targets[GetHostTuple()]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100271 return targets
272
273
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100274def GetToolchainsForBoard(board):
275 """Get a list of toolchain tuples for a given board name
276
277 returns the list of toolchain tuples for the given board
278 """
David Jamese5867812012-10-19 12:02:20 -0700279 overlays = portage_utilities.FindOverlays(
280 constants.BOTH_OVERLAYS, None if board in ('all', 'sdk') else board)
Mike Frysingereaebb582012-06-19 13:04:53 -0400281 toolchains = GetTuplesForOverlays(overlays)
282 if board == 'sdk':
283 toolchains = FilterToolchains(toolchains, 'sdk', True)
284 return toolchains
285
286
287def FilterToolchains(targets, key, value):
288 """Filter out targets based on their attributes.
289
290 args:
291 targets - dict of toolchains
292 key - metadata to examine
293 value - expected value for metadata
294
295 returns a dict where all targets whose metadata key does not match value
296 have been deleted.
297 """
298 for target, metadata in targets.items():
299 if metadata[key] != value:
300 del targets[target]
301 return targets
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100302
303
David James66a09c42012-11-05 13:31:38 -0800304def GetInstalledPackageVersions(atom):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100305 """Extracts the list of current versions of a target, package pair.
306
307 args:
David James66a09c42012-11-05 13:31:38 -0800308 atom - the atom to operate on (e.g. sys-devel/gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100309
310 returns the list of versions of the package currently installed.
311 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100312 versions = []
David James90239b92012-11-05 15:31:34 -0800313 for pkg in portage.db['/']['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100314 version = portage.versions.cpv_getversion(pkg)
315 versions.append(version)
316 return versions
317
318
David James90239b92012-11-05 15:31:34 -0800319def GetStablePackageVersion(atom, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100320 """Extracts the current stable version for a given package.
321
322 args:
323 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200324 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100325
326 returns a string containing the latest version.
327 """
David James90239b92012-11-05 15:31:34 -0800328 pkgtype = 'vartree' if installed else 'porttree'
329 cpv = portage.best(portage.db['/'][pkgtype].dbapi.match(atom, use_cache=0))
330 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100331
332
Zdenek Behan699ddd32012-04-13 07:14:08 +0200333def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100334 """Resolves keywords in a given version list for a particular package.
335
336 Resolving means replacing PACKAGE_STABLE with the actual number.
337
338 args:
339 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
340 versions - list of versions to resolve
341
342 returns list of purely numeric versions equivalent to argument
343 """
344 resolved = []
David James90239b92012-11-05 15:31:34 -0800345 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100346 for version in versions:
347 if version == PACKAGE_STABLE:
David James90239b92012-11-05 15:31:34 -0800348 resolved.append(GetStablePackageVersion(atom, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100349 elif version != PACKAGE_NONE:
350 resolved.append(version)
351 return resolved
352
353
354def GetDesiredPackageVersions(target, package):
355 """Produces the list of desired versions for each target, package pair.
356
357 The first version in the list is implicitly treated as primary, ie.
358 the version that will be initialized by crossdev and selected.
359
360 If the version is PACKAGE_STABLE, it really means the current version which
361 is emerged by using the package atom with no particular version key.
362 Since crossdev unmasks all packages by default, this will actually
363 mean 'unstable' in most cases.
364
365 args:
366 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
367
368 returns a list composed of either a version string, PACKAGE_STABLE
369 """
370 packagemap = GetPackageMap(target)
371
372 versions = []
373 if package in packagemap:
374 versions.append(packagemap[package])
375
376 return versions
377
378
379def TargetIsInitialized(target):
380 """Verifies if the given list of targets has been correctly initialized.
381
382 This determines whether we have to call crossdev while emerging
383 toolchain packages or can do it using emerge. Emerge is naturally
384 preferred, because all packages can be updated in a single pass.
385
386 args:
387 targets - list of individual cross targets which are checked
388
389 returns True if target is completely initialized
390 returns False otherwise
391 """
392 # Check if packages for the given target all have a proper version.
393 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100394 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800395 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100396 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800397 if not IsPackageDisabled(target, package) and not (
398 GetStablePackageVersion(atom, True) and
399 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100400 return False
401 return True
402 except cros_build_lib.RunCommandError:
403 # Fails - The target has likely never been initialized before.
404 return False
405
406
407def RemovePackageMask(target):
408 """Removes a package.mask file for the given platform.
409
410 The pre-existing package.mask files can mess with the keywords.
411
412 args:
413 target - the target for which to remove the file
414 """
415 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700416 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417
418
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419# Main functions performing the actual update steps.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100420def UpdateTargets(targets, usepkg):
421 """Determines which packages need update/unmerge and defers to portage.
422
423 args:
424 targets - the list of targets to update
425 usepkg - copies the commandline option
426 """
David James90239b92012-11-05 15:31:34 -0800427 # Remove keyword files created by old versions of cros_setup_toolchains.
428 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100429
430 # For each target, we do two things. Figure out the list of updates,
431 # and figure out the appropriate keywords/masks. Crossdev will initialize
432 # these, but they need to be regenerated on every update.
433 print 'Determining required toolchain updates...'
David James90239b92012-11-05 15:31:34 -0800434 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100435 for target in targets:
436 # Record the highest needed version for each target, for masking purposes.
437 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100438 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100440 if IsPackageDisabled(target, package):
441 continue
442 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800443 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100444 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200445 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100446 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100447
Zdenek Behan508dcce2011-12-05 15:39:32 +0100448 packages = []
449 for pkg in mergemap:
450 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200451 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800452 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100453
454 if not packages:
455 print 'Nothing to update!'
David James7ec5efc2012-11-06 09:39:49 -0800456 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100457
458 print 'Updating packages:'
459 print packages
460
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100461 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100462 if usepkg:
463 cmd.extend(['--getbinpkg', '--usepkgonly'])
464
465 cmd.extend(packages)
466 cros_build_lib.RunCommand(cmd)
467
468
469def CleanTargets(targets):
470 """Unmerges old packages that are assumed unnecessary."""
471 unmergemap = {}
472 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100473 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100474 if IsPackageDisabled(target, package):
475 continue
476 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800477 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100478 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200479 desired_num = VersionListToNumeric(target, package, desired, True)
480 if not set(desired_num).issubset(current):
481 print 'Some packages have been held back, skipping clean!'
482 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100483 unmergemap[pkg] = set(current).difference(desired_num)
484
485 # Cleaning doesn't care about consistency and rebuilding package.* files.
486 packages = []
487 for pkg, vers in unmergemap.iteritems():
488 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
489
490 if packages:
491 print 'Cleaning packages:'
492 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100493 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100494 cmd.extend(packages)
495 cros_build_lib.RunCommand(cmd)
496 else:
497 print 'Nothing to clean!'
498
499
500def SelectActiveToolchains(targets, suffixes):
501 """Runs gcc-config and binutils-config to select the desired.
502
503 args:
504 targets - the targets to select
505 """
506 for package in ['gcc', 'binutils']:
507 for target in targets:
508 # Pick the first version in the numbered list as the selected one.
509 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200510 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100511 desired = desired_num[0]
512 # *-config does not play revisions, strip them, keep just PV.
513 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
514
515 if target == 'host':
516 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100517 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100518
519 # And finally, attach target to it.
520 desired = '%s-%s' % (target, desired)
521
522 # Target specific hacks
523 if package in suffixes:
524 if target in suffixes[package]:
525 desired += suffixes[package][target]
526
David James7ec5efc2012-11-06 09:39:49 -0800527 extra_env = {'CHOST': target}
528 cmd = ['%s-config' % package, '-c', target]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
David James7ec5efc2012-11-06 09:39:49 -0800530 redirect_stdout=True, extra_env=extra_env).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100531 # Do not gcc-config when the current is live or nothing needs to be done.
532 if current != desired and current != '9999':
533 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100534 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100535
536
Mike Frysinger7ccee992012-06-01 21:27:59 -0400537def UpdateToolchains(usepkg, deleteold, hostonly, targets_wanted,
538 boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539 """Performs all steps to create a synchronized toolchain enviroment.
540
541 args:
542 arguments correspond to the given commandline flags
543 """
Mike Frysinger7ccee992012-06-01 21:27:59 -0400544 targets = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100545 if not hostonly:
546 # For hostonly, we can skip most of the below logic, much of which won't
547 # work on bare systems where this is useful.
548 alltargets = GetAllTargets()
Mike Frysinger7ccee992012-06-01 21:27:59 -0400549 targets_wanted = set(targets_wanted)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100550 if targets_wanted == set(['all']):
Mike Frysinger7ccee992012-06-01 21:27:59 -0400551 targets = alltargets
Mike Frysingereaebb582012-06-19 13:04:53 -0400552 elif targets_wanted == set(['sdk']):
553 # Filter out all the non-sdk toolchains as we don't want to mess
554 # with those in all of our builds.
555 targets = FilterToolchains(alltargets, 'sdk', True)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100556 else:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100557 # Verify user input.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400558 nonexistant = []
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100559 for target in targets_wanted:
560 if target not in alltargets:
561 nonexistant.append(target)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400562 else:
563 targets[target] = alltargets[target]
564 if nonexistant:
565 cros_build_lib.Die('Invalid targets: ' + ','.join(nonexistant))
566
Mike Frysinger7ccee992012-06-01 21:27:59 -0400567 # Now re-add any targets that might be from this board. This is
568 # to allow unofficial boards to declare their own toolchains.
569 for board in boards_wanted:
570 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100571
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100572 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400573 crossdev_targets = {}
574 reconfig_targets = {}
575 for target in targets:
576 if TargetIsInitialized(target):
577 reconfig_targets[target] = targets[target]
578 else:
579 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100580 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200581 print 'The following targets need to be re-initialized:'
582 print crossdev_targets
David James66a09c42012-11-05 13:31:38 -0800583 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200584 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800585 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100586
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100587 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400588 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100589
590 # Now update all packages.
David James7ec5efc2012-11-06 09:39:49 -0800591 UpdateTargets(targets, usepkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100592
David James7ec5efc2012-11-06 09:39:49 -0800593 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
594
595 if deleteold:
596 CleanTargets(targets)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100597
598
Brian Harring30675052012-02-29 12:18:22 -0800599def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100600 usage = """usage: %prog [options]
601
602 The script installs and updates the toolchains in your chroot.
603 """
604 parser = optparse.OptionParser(usage)
605 parser.add_option('-u', '--nousepkg',
606 action='store_false', dest='usepkg', default=True,
607 help=('Use prebuilt packages if possible.'))
608 parser.add_option('-d', '--deleteold',
609 action='store_true', dest='deleteold', default=False,
610 help=('Unmerge deprecated packages.'))
611 parser.add_option('-t', '--targets',
Mike Frysingereaebb582012-06-19 13:04:53 -0400612 dest='targets', default='sdk',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100613 help=('Comma separated list of tuples. '
Mike Frysingereaebb582012-06-19 13:04:53 -0400614 'Special keyword \'host\' is allowed. Default: sdk.'))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400615 parser.add_option('--include-boards',
616 dest='include_boards', default='',
617 help=('Comma separated list of boards whose toolchains we'
618 ' will always include. Default: none.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100619 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100620 dest='hostonly', default=False, action='store_true',
621 help=('Only setup the host toolchain. '
622 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100623 parser.add_option('--show-board-cfg',
624 dest='board_cfg', default=None,
625 help=('Board to list toolchain tuples for'))
David James66a09c42012-11-05 13:31:38 -0800626 parser.add_option('--reconfig', default=False, action='store_true',
627 help=('Reload crossdev config'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100628
Brian Harring30675052012-02-29 12:18:22 -0800629 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100630
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100631 if options.board_cfg:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400632 toolchains = GetToolchainsForBoard(options.board_cfg)
633 # Make sure we display the default toolchain first.
634 tuples = toolchains.keys()
635 for tuple in tuples:
636 if toolchains[tuple]['default']:
637 tuples.remove(tuple)
638 tuples.insert(0, tuple)
639 break
640 print ','.join(tuples)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100641 return 0
642
Mike Frysinger984d0622012-06-01 16:08:44 -0400643 # This has to be always run as root.
644 if not os.getuid() == 0:
645 print "%s: This script must be run as root!" % sys.argv[0]
646 sys.exit(1)
647
Zdenek Behan508dcce2011-12-05 15:39:32 +0100648 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400649 boards = set(options.include_boards.split(',')) if options.include_boards \
650 else set()
David James66a09c42012-11-05 13:31:38 -0800651 Crossdev.Load(options.reconfig)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400652 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets,
653 boards)
David James66a09c42012-11-05 13:31:38 -0800654 Crossdev.Save()