blob: 0d68c4d62b8ac0a03584deac7ae1008293a640e2 [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
Mike Frysinger31596002012-12-03 23:54:24 -050020if cros_build_lib.IsInsideChroot():
21 # Only import portage after we've checked that we're inside the chroot.
22 # Outside may not have portage, in which case the above may not happen.
23 # We'll check in main() if the operation needs portage.
24 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010025
26
Matt Tennantf1e30972012-03-02 16:30:07 -080027EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010028PACKAGE_STABLE = '[stable]'
29PACKAGE_NONE = '[none]'
30SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010031
32CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
33STABLE_OVERLAY = '/usr/local/portage/stable'
34CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010035
36
37# TODO: The versions are stored here very much like in setup_board.
38# The goal for future is to differentiate these using a config file.
39# This is done essentially by messing with GetDesiredPackageVersions()
40DEFAULT_VERSION = PACKAGE_STABLE
41DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010042}
43TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010044 'host' : {
45 'binutils' : '2.21.1',
46 'gdb' : PACKAGE_NONE,
47 },
48}
49# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
50CONFIG_TARGET_SUFFIXES = {
51 'binutils' : {
52 'i686-pc-linux-gnu' : '-gold',
53 'x86_64-cros-linux-gnu' : '-gold',
54 },
55}
Zdenek Behan508dcce2011-12-05 15:39:32 +010056# Global per-run cache that will be filled ondemand in by GetPackageMap()
57# function as needed.
58target_version_map = {
59}
60
61
David James66a09c42012-11-05 13:31:38 -080062class Crossdev(object):
63 """Class for interacting with crossdev and caching its output."""
64
65 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
66 _CACHE = {}
67
68 @classmethod
69 def Load(cls, reconfig):
70 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -080071 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
72 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -080073 if os.path.exists(cls._CACHE_FILE) and not reconfig:
74 with open(cls._CACHE_FILE) as f:
75 data = json.load(f)
David James90239b92012-11-05 15:31:34 -080076 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -080077 cls._CACHE = data
78
79 @classmethod
80 def Save(cls):
81 """Store crossdev cache on disk."""
82 # Save the cache from the successful run.
83 with open(cls._CACHE_FILE, 'w') as f:
84 json.dump(cls._CACHE, f)
85
86 @classmethod
87 def GetConfig(cls, target):
88 """Returns a map of crossdev provided variables about a tuple."""
89 CACHE_ATTR = '_target_tuple_map'
90
91 val = cls._CACHE.setdefault(CACHE_ATTR, {})
92 if not target in val:
93 # Find out the crossdev tuple.
94 target_tuple = target
95 if target == 'host':
96 target_tuple = GetHostTuple()
97 # Catch output of crossdev.
98 out = cros_build_lib.RunCommand(['crossdev', '--show-target-cfg',
99 '--ex-gdb', target_tuple],
100 print_cmd=False, redirect_stdout=True).output.splitlines()
101 # List of tuples split at the first '=', converted into dict.
102 val[target] = dict([x.split('=', 1) for x in out])
103 return val[target]
104
105 @classmethod
106 def UpdateTargets(cls, targets, usepkg, config_only=False):
107 """Calls crossdev to initialize a cross target.
108
109 Args:
110 targets - the list of targets to initialize using crossdev
111 usepkg - copies the commandline opts
112 config_only - Just update
113 """
114 configured_targets = cls._CACHE.setdefault('configured_targets', [])
115
116 cmdbase = ['crossdev', '--show-fail-log']
117 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
118 # Pick stable by default, and override as necessary.
119 cmdbase.extend(['-P', '--oneshot'])
120 if usepkg:
121 cmdbase.extend(['-P', '--getbinpkg',
122 '-P', '--usepkgonly',
123 '--without-headers'])
124
125 overlays = '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)
126 cmdbase.extend(['--overlays', overlays])
127 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
128
129 for target in targets:
130 if config_only and target in configured_targets:
131 continue
132
133 cmd = cmdbase + ['-t', target]
134
135 for pkg in GetTargetPackages(target):
136 if pkg == 'gdb':
137 # Gdb does not have selectable versions.
138 cmd.append('--ex-gdb')
139 continue
140 # The first of the desired versions is the "primary" one.
141 version = GetDesiredPackageVersions(target, pkg)[0]
142 cmd.extend(['--%s' % pkg, version])
143
144 cmd.extend(targets[target]['crossdev'].split())
145 if config_only:
146 # In this case we want to just quietly reinit
147 cmd.append('--init-target')
148 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
149 else:
150 cros_build_lib.RunCommand(cmd)
151
152 configured_targets.append(target)
153
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100154
Zdenek Behan508dcce2011-12-05 15:39:32 +0100155def GetPackageMap(target):
156 """Compiles a package map for the given target from the constants.
157
158 Uses a cache in target_version_map, that is dynamically filled in as needed,
159 since here everything is static data and the structuring is for ease of
160 configurability only.
161
162 args:
163 target - the target for which to return a version map
164
165 returns a map between packages and desired versions in internal format
166 (using the PACKAGE_* constants)
167 """
168 if target in target_version_map:
169 return target_version_map[target]
170
171 # Start from copy of the global defaults.
172 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
173
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100174 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100175 # prefer any specific overrides
176 if pkg in TARGET_VERSION_MAP.get(target, {}):
177 result[pkg] = TARGET_VERSION_MAP[target][pkg]
178 else:
179 # finally, if not already set, set a sane default
180 result.setdefault(pkg, DEFAULT_VERSION)
181 target_version_map[target] = result
182 return result
183
184
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100185def GetHostTuple():
David James90239b92012-11-05 15:31:34 -0800186 """Returns compiler tuple for the host system."""
187 return portage.settings['CHOST']
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100188
189
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100190def GetTargetPackages(target):
191 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800192 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100193 # Undesired packages are denoted by empty ${pkg}_pn variable.
194 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
195
196
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100197# Portage helper functions:
198def GetPortagePackage(target, package):
199 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800200 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100201 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100202 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100203 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100204 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100205 category = conf['category']
206 # Portage package:
207 pn = conf[package + '_pn']
208 # Final package name:
209 assert(category)
210 assert(pn)
211 return '%s/%s' % (category, pn)
212
213
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100214def IsPackageDisabled(target, package):
215 """Returns if the given package is not used for the target."""
216 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
217
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100218
219def GetTuplesForOverlays(overlays):
220 """Returns a set of tuples for a given set of overlays."""
Mike Frysinger7ccee992012-06-01 21:27:59 -0400221 tuples = {}
222 default_settings = {
223 'sdk' : True,
224 'crossdev' : '',
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400225 'default' : False,
Mike Frysinger7ccee992012-06-01 21:27:59 -0400226 }
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100227
228 for overlay in overlays:
229 config = os.path.join(overlay, 'toolchain.conf')
230 if os.path.exists(config):
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400231 first_tuple = None
232 seen_default = False
233
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100234 for line in osutils.ReadFile(config).splitlines():
Mike Frysinger7ccee992012-06-01 21:27:59 -0400235 # Split by hash sign so that comments are ignored.
236 # Then split the line to get the tuple and its options.
237 line = line.split('#', 1)[0].split()
238
239 if len(line) > 0:
240 tuple = line[0]
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400241 if not first_tuple:
242 first_tuple = tuple
Mike Frysinger7ccee992012-06-01 21:27:59 -0400243 if tuple not in tuples:
244 tuples[tuple] = copy.copy(default_settings)
245 if len(line) > 1:
246 tuples[tuple].update(json.loads(' '.join(line[1:])))
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400247 if tuples[tuple]['default']:
248 seen_default = True
249
250 # If the user has not explicitly marked a toolchain as default,
251 # automatically select the first tuple that we saw in the conf.
252 if not seen_default and first_tuple:
253 tuples[first_tuple]['default'] = True
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100254
255 return tuples
256
257
Zdenek Behan508dcce2011-12-05 15:39:32 +0100258# Tree interface functions. They help with retrieving data about the current
259# state of the tree:
260def GetAllTargets():
261 """Get the complete list of targets.
262
263 returns the list of cross targets for the current tree
264 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400265 targets = GetToolchainsForBoard('all')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100266
267 # Remove the host target as that is not a cross-target. Replace with 'host'.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400268 del targets[GetHostTuple()]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100269 return targets
270
271
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100272def GetToolchainsForBoard(board):
273 """Get a list of toolchain tuples for a given board name
274
275 returns the list of toolchain tuples for the given board
276 """
David Jamese5867812012-10-19 12:02:20 -0700277 overlays = portage_utilities.FindOverlays(
278 constants.BOTH_OVERLAYS, None if board in ('all', 'sdk') else board)
Mike Frysingereaebb582012-06-19 13:04:53 -0400279 toolchains = GetTuplesForOverlays(overlays)
280 if board == 'sdk':
281 toolchains = FilterToolchains(toolchains, 'sdk', True)
282 return toolchains
283
284
285def FilterToolchains(targets, key, value):
286 """Filter out targets based on their attributes.
287
288 args:
289 targets - dict of toolchains
290 key - metadata to examine
291 value - expected value for metadata
292
293 returns a dict where all targets whose metadata key does not match value
294 have been deleted.
295 """
296 for target, metadata in targets.items():
297 if metadata[key] != value:
298 del targets[target]
299 return targets
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100300
301
David James66a09c42012-11-05 13:31:38 -0800302def GetInstalledPackageVersions(atom):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100303 """Extracts the list of current versions of a target, package pair.
304
305 args:
David James66a09c42012-11-05 13:31:38 -0800306 atom - the atom to operate on (e.g. sys-devel/gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100307
308 returns the list of versions of the package currently installed.
309 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100310 versions = []
David James90239b92012-11-05 15:31:34 -0800311 for pkg in portage.db['/']['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100312 version = portage.versions.cpv_getversion(pkg)
313 versions.append(version)
314 return versions
315
316
David James90239b92012-11-05 15:31:34 -0800317def GetStablePackageVersion(atom, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100318 """Extracts the current stable version for a given package.
319
320 args:
321 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200322 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100323
324 returns a string containing the latest version.
325 """
David James90239b92012-11-05 15:31:34 -0800326 pkgtype = 'vartree' if installed else 'porttree'
327 cpv = portage.best(portage.db['/'][pkgtype].dbapi.match(atom, use_cache=0))
328 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100329
330
Zdenek Behan699ddd32012-04-13 07:14:08 +0200331def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100332 """Resolves keywords in a given version list for a particular package.
333
334 Resolving means replacing PACKAGE_STABLE with the actual number.
335
336 args:
337 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
338 versions - list of versions to resolve
339
340 returns list of purely numeric versions equivalent to argument
341 """
342 resolved = []
David James90239b92012-11-05 15:31:34 -0800343 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100344 for version in versions:
345 if version == PACKAGE_STABLE:
David James90239b92012-11-05 15:31:34 -0800346 resolved.append(GetStablePackageVersion(atom, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100347 elif version != PACKAGE_NONE:
348 resolved.append(version)
349 return resolved
350
351
352def GetDesiredPackageVersions(target, package):
353 """Produces the list of desired versions for each target, package pair.
354
355 The first version in the list is implicitly treated as primary, ie.
356 the version that will be initialized by crossdev and selected.
357
358 If the version is PACKAGE_STABLE, it really means the current version which
359 is emerged by using the package atom with no particular version key.
360 Since crossdev unmasks all packages by default, this will actually
361 mean 'unstable' in most cases.
362
363 args:
364 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
365
366 returns a list composed of either a version string, PACKAGE_STABLE
367 """
368 packagemap = GetPackageMap(target)
369
370 versions = []
371 if package in packagemap:
372 versions.append(packagemap[package])
373
374 return versions
375
376
377def TargetIsInitialized(target):
378 """Verifies if the given list of targets has been correctly initialized.
379
380 This determines whether we have to call crossdev while emerging
381 toolchain packages or can do it using emerge. Emerge is naturally
382 preferred, because all packages can be updated in a single pass.
383
384 args:
385 targets - list of individual cross targets which are checked
386
387 returns True if target is completely initialized
388 returns False otherwise
389 """
390 # Check if packages for the given target all have a proper version.
391 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100392 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800393 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100394 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800395 if not IsPackageDisabled(target, package) and not (
396 GetStablePackageVersion(atom, True) and
397 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100398 return False
399 return True
400 except cros_build_lib.RunCommandError:
401 # Fails - The target has likely never been initialized before.
402 return False
403
404
405def RemovePackageMask(target):
406 """Removes a package.mask file for the given platform.
407
408 The pre-existing package.mask files can mess with the keywords.
409
410 args:
411 target - the target for which to remove the file
412 """
413 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700414 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415
416
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417# Main functions performing the actual update steps.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100418def UpdateTargets(targets, usepkg):
419 """Determines which packages need update/unmerge and defers to portage.
420
421 args:
422 targets - the list of targets to update
423 usepkg - copies the commandline option
424 """
David James90239b92012-11-05 15:31:34 -0800425 # Remove keyword files created by old versions of cros_setup_toolchains.
426 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100427
428 # For each target, we do two things. Figure out the list of updates,
429 # and figure out the appropriate keywords/masks. Crossdev will initialize
430 # these, but they need to be regenerated on every update.
431 print 'Determining required toolchain updates...'
David James90239b92012-11-05 15:31:34 -0800432 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433 for target in targets:
434 # Record the highest needed version for each target, for masking purposes.
435 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100436 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100438 if IsPackageDisabled(target, package):
439 continue
440 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800441 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200443 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100444 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100445
Zdenek Behan508dcce2011-12-05 15:39:32 +0100446 packages = []
447 for pkg in mergemap:
448 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200449 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800450 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100451
452 if not packages:
453 print 'Nothing to update!'
David Jamesf8c672f2012-11-06 13:38:11 -0800454 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455
456 print 'Updating packages:'
457 print packages
458
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100459 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460 if usepkg:
461 cmd.extend(['--getbinpkg', '--usepkgonly'])
462
463 cmd.extend(packages)
464 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800465 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100466
467
468def CleanTargets(targets):
469 """Unmerges old packages that are assumed unnecessary."""
470 unmergemap = {}
471 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100472 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100473 if IsPackageDisabled(target, package):
474 continue
475 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800476 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100477 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200478 desired_num = VersionListToNumeric(target, package, desired, True)
479 if not set(desired_num).issubset(current):
480 print 'Some packages have been held back, skipping clean!'
481 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100482 unmergemap[pkg] = set(current).difference(desired_num)
483
484 # Cleaning doesn't care about consistency and rebuilding package.* files.
485 packages = []
486 for pkg, vers in unmergemap.iteritems():
487 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
488
489 if packages:
490 print 'Cleaning packages:'
491 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100492 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100493 cmd.extend(packages)
494 cros_build_lib.RunCommand(cmd)
495 else:
496 print 'Nothing to clean!'
497
498
499def SelectActiveToolchains(targets, suffixes):
500 """Runs gcc-config and binutils-config to select the desired.
501
502 args:
503 targets - the targets to select
504 """
505 for package in ['gcc', 'binutils']:
506 for target in targets:
507 # Pick the first version in the numbered list as the selected one.
508 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200509 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510 desired = desired_num[0]
511 # *-config does not play revisions, strip them, keep just PV.
512 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
513
514 if target == 'host':
515 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100516 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517
518 # And finally, attach target to it.
519 desired = '%s-%s' % (target, desired)
520
521 # Target specific hacks
522 if package in suffixes:
523 if target in suffixes[package]:
524 desired += suffixes[package][target]
525
David James7ec5efc2012-11-06 09:39:49 -0800526 extra_env = {'CHOST': target}
527 cmd = ['%s-config' % package, '-c', target]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
David James7ec5efc2012-11-06 09:39:49 -0800529 redirect_stdout=True, extra_env=extra_env).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100530 # Do not gcc-config when the current is live or nothing needs to be done.
531 if current != desired and current != '9999':
532 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100533 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534
535
David Jamesf8c672f2012-11-06 13:38:11 -0800536def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
537 targets_wanted, boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538 """Performs all steps to create a synchronized toolchain enviroment.
539
540 args:
541 arguments correspond to the given commandline flags
542 """
David Jamesf8c672f2012-11-06 13:38:11 -0800543 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100544 if not hostonly:
545 # For hostonly, we can skip most of the below logic, much of which won't
546 # work on bare systems where this is useful.
547 alltargets = GetAllTargets()
Mike Frysinger7ccee992012-06-01 21:27:59 -0400548 targets_wanted = set(targets_wanted)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100549 if targets_wanted == set(['all']):
Mike Frysinger7ccee992012-06-01 21:27:59 -0400550 targets = alltargets
Mike Frysingereaebb582012-06-19 13:04:53 -0400551 elif targets_wanted == set(['sdk']):
552 # Filter out all the non-sdk toolchains as we don't want to mess
553 # with those in all of our builds.
554 targets = FilterToolchains(alltargets, 'sdk', True)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100555 else:
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100556 # Verify user input.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400557 nonexistant = []
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100558 for target in targets_wanted:
559 if target not in alltargets:
560 nonexistant.append(target)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400561 else:
562 targets[target] = alltargets[target]
563 if nonexistant:
564 cros_build_lib.Die('Invalid targets: ' + ','.join(nonexistant))
565
Mike Frysinger7ccee992012-06-01 21:27:59 -0400566 # Now re-add any targets that might be from this board. This is
567 # to allow unofficial boards to declare their own toolchains.
568 for board in boards_wanted:
569 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100570
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100571 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400572 for target in targets:
573 if TargetIsInitialized(target):
574 reconfig_targets[target] = targets[target]
575 else:
576 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100577 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200578 print 'The following targets need to be re-initialized:'
579 print crossdev_targets
David James66a09c42012-11-05 13:31:38 -0800580 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200581 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800582 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100583
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100584 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400585 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100586
587 # Now update all packages.
David Jamesf8c672f2012-11-06 13:38:11 -0800588 if UpdateTargets(targets, usepkg) or crossdev_targets or reconfig:
589 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
David James7ec5efc2012-11-06 09:39:49 -0800590
591 if deleteold:
592 CleanTargets(targets)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593
594
Brian Harring30675052012-02-29 12:18:22 -0800595def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100596 usage = """usage: %prog [options]
597
598 The script installs and updates the toolchains in your chroot.
599 """
600 parser = optparse.OptionParser(usage)
601 parser.add_option('-u', '--nousepkg',
602 action='store_false', dest='usepkg', default=True,
603 help=('Use prebuilt packages if possible.'))
604 parser.add_option('-d', '--deleteold',
605 action='store_true', dest='deleteold', default=False,
606 help=('Unmerge deprecated packages.'))
607 parser.add_option('-t', '--targets',
Mike Frysingereaebb582012-06-19 13:04:53 -0400608 dest='targets', default='sdk',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100609 help=('Comma separated list of tuples. '
Mike Frysingereaebb582012-06-19 13:04:53 -0400610 'Special keyword \'host\' is allowed. Default: sdk.'))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400611 parser.add_option('--include-boards',
612 dest='include_boards', default='',
613 help=('Comma separated list of boards whose toolchains we'
614 ' will always include. Default: none.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100615 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100616 dest='hostonly', default=False, action='store_true',
617 help=('Only setup the host toolchain. '
618 'Useful for bootstrapping chroot.'))
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100619 parser.add_option('--show-board-cfg',
620 dest='board_cfg', default=None,
621 help=('Board to list toolchain tuples for'))
David James66a09c42012-11-05 13:31:38 -0800622 parser.add_option('--reconfig', default=False, action='store_true',
David Jamesf8c672f2012-11-06 13:38:11 -0800623 help=('Reload crossdev config and reselect toolchains.'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100624
Brian Harring30675052012-02-29 12:18:22 -0800625 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100626
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100627 if options.board_cfg:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400628 toolchains = GetToolchainsForBoard(options.board_cfg)
629 # Make sure we display the default toolchain first.
630 tuples = toolchains.keys()
631 for tuple in tuples:
632 if toolchains[tuple]['default']:
633 tuples.remove(tuple)
634 tuples.insert(0, tuple)
635 break
636 print ','.join(tuples)
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100637 return 0
638
Mike Frysinger31596002012-12-03 23:54:24 -0500639 cros_build_lib.AssertInsideChroot()
640
Mike Frysinger984d0622012-06-01 16:08:44 -0400641 # This has to be always run as root.
642 if not os.getuid() == 0:
643 print "%s: This script must be run as root!" % sys.argv[0]
644 sys.exit(1)
645
Zdenek Behan508dcce2011-12-05 15:39:32 +0100646 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -0400647 boards = set(options.include_boards.split(',')) if options.include_boards \
648 else set()
David James66a09c42012-11-05 13:31:38 -0800649 Crossdev.Load(options.reconfig)
David Jamesf8c672f2012-11-06 13:38:11 -0800650 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
651 options.reconfig, targets, boards)
David James66a09c42012-11-05 13:31:38 -0800652 Crossdev.Save()