blob: 7c1de2657386ac373a09a9f8d0c24e77e8e65fc2 [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 James55096062012-11-03 16:31:23 -0700456 return False
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)
David James55096062012-11-03 16:31:23 -0700467 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100468
469
470def CleanTargets(targets):
471 """Unmerges old packages that are assumed unnecessary."""
472 unmergemap = {}
473 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100474 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100475 if IsPackageDisabled(target, package):
476 continue
477 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800478 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100479 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200480 desired_num = VersionListToNumeric(target, package, desired, True)
481 if not set(desired_num).issubset(current):
482 print 'Some packages have been held back, skipping clean!'
483 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100484 unmergemap[pkg] = set(current).difference(desired_num)
485
486 # Cleaning doesn't care about consistency and rebuilding package.* files.
487 packages = []
488 for pkg, vers in unmergemap.iteritems():
489 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
490
491 if packages:
492 print 'Cleaning packages:'
493 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100494 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100495 cmd.extend(packages)
496 cros_build_lib.RunCommand(cmd)
497 else:
498 print 'Nothing to clean!'
499
500
501def SelectActiveToolchains(targets, suffixes):
502 """Runs gcc-config and binutils-config to select the desired.
503
504 args:
505 targets - the targets to select
506 """
507 for package in ['gcc', 'binutils']:
508 for target in targets:
509 # Pick the first version in the numbered list as the selected one.
510 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200511 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100512 desired = desired_num[0]
513 # *-config does not play revisions, strip them, keep just PV.
514 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
515
516 if target == 'host':
517 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100518 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100519
520 # And finally, attach target to it.
521 desired = '%s-%s' % (target, desired)
522
523 # Target specific hacks
524 if package in suffixes:
525 if target in suffixes[package]:
526 desired += suffixes[package][target]
527
528 cmd = [ package + '-config', '-c', target ]
529 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
Zdenek Behan699ddd32012-04-13 07:14:08 +0200530 redirect_stdout=True).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 James55096062012-11-03 16:31:23 -0700591 if UpdateTargets(targets, usepkg):
592 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593
David James55096062012-11-03 16:31:23 -0700594 if deleteold:
595 CleanTargets(targets)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100596
597
Brian Harring30675052012-02-29 12:18:22 -0800598def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100599 usage = """usage: %prog [options]
600
601 The script installs and updates the toolchains in your chroot.
602 """
603 parser = optparse.OptionParser(usage)
604 parser.add_option('-u', '--nousepkg',
605 action='store_false', dest='usepkg', default=True,
606 help=('Use prebuilt packages if possible.'))
607 parser.add_option('-d', '--deleteold',
608 action='store_true', dest='deleteold', default=False,
609 help=('Unmerge deprecated packages.'))
610 parser.add_option('-t', '--targets',
Mike Frysingereaebb582012-06-19 13:04:53 -0400611 dest='targets', default='sdk',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100612 help=('Comma separated list of tuples. '
Mike Frysingereaebb582012-06-19 13:04:53 -0400613 'Special keyword \'host\' is allowed. Default: sdk.'))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400614 parser.add_option('--include-boards',
615 dest='include_boards', default='',
616 help=('Comma separated list of boards whose toolchains we'
617 ' will always include. Default: none.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100618 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100619 dest='hostonly', default=False, action='store_true',
620 help=('Only setup the host toolchain. '
621 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100622 parser.add_option('--show-board-cfg',
623 dest='board_cfg', default=None,
624 help=('Board to list toolchain tuples for'))
David James66a09c42012-11-05 13:31:38 -0800625 parser.add_option('--reconfig', default=False, action='store_true',
626 help=('Reload crossdev config'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100627
Brian Harring30675052012-02-29 12:18:22 -0800628 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100629
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100630 if options.board_cfg:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400631 toolchains = GetToolchainsForBoard(options.board_cfg)
632 # Make sure we display the default toolchain first.
633 tuples = toolchains.keys()
634 for tuple in tuples:
635 if toolchains[tuple]['default']:
636 tuples.remove(tuple)
637 tuples.insert(0, tuple)
638 break
639 print ','.join(tuples)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100640 return 0
641
Mike Frysinger984d0622012-06-01 16:08:44 -0400642 # This has to be always run as root.
643 if not os.getuid() == 0:
644 print "%s: This script must be run as root!" % sys.argv[0]
645 sys.exit(1)
646
Zdenek Behan508dcce2011-12-05 15:39:32 +0100647 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400648 boards = set(options.include_boards.split(',')) if options.include_boards \
649 else set()
David James66a09c42012-11-05 13:31:38 -0800650 Crossdev.Load(options.reconfig)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400651 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly, targets,
652 boards)
David James66a09c42012-11-05 13:31:38 -0800653 Crossdev.Save()