blob: f1c4babfc260cd503b509e8ed73bd33a987eebff [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
10import optparse
11import os
12import sys
13
Brian Harringb938c782012-02-29 15:14:38 -080014import chromite.lib.cros_build_lib as cros_build_lib
15import chromite.buildbot.constants as constants
Zdenek Behan508dcce2011-12-05 15:39:32 +010016
17# Some sanity checks first.
18if not cros_build_lib.IsInsideChroot():
19 print '%s: This needs to be run inside the chroot' % sys.argv[0]
20 sys.exit(1)
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.
23import portage
24
25
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010026EMERGE_CMD = os.path.join(os.path.dirname(__file__),
27 '../bin', '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# NOTE: gdb is only built using --ex-gdb through crossdev.
32CROSSDEV_PACKAGES = ['gcc', 'libc', 'binutils', 'kernel']
33TOOLCHAIN_PACKAGES = CROSSDEV_PACKAGES + ['gdb']
34
35
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 'binutils' : '2.21-r4',
47}
48TARGET_VERSION_MAP = {
49 'arm-none-eabi' : {
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010050 'kernel' : PACKAGE_NONE,
Zdenek Behan508dcce2011-12-05 15:39:32 +010051 'gdb' : PACKAGE_NONE,
52 },
53 'host' : {
54 'binutils' : '2.21.1',
55 'gdb' : PACKAGE_NONE,
56 },
57}
58# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
59CONFIG_TARGET_SUFFIXES = {
60 'binutils' : {
61 'i686-pc-linux-gnu' : '-gold',
62 'x86_64-cros-linux-gnu' : '-gold',
63 },
64}
Zdenek Behan508dcce2011-12-05 15:39:32 +010065# Global per-run cache that will be filled ondemand in by GetPackageMap()
66# function as needed.
67target_version_map = {
68}
69
70
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010071# Global variable cache. It has to be a descendant of 'object', rather than
72# instance thereof, because attributes cannot be set on 'object' instances.
73class VariableCache(object):
74 pass
75VAR_CACHE = VariableCache()
76
77
Zdenek Behan508dcce2011-12-05 15:39:32 +010078def GetPackageMap(target):
79 """Compiles a package map for the given target from the constants.
80
81 Uses a cache in target_version_map, that is dynamically filled in as needed,
82 since here everything is static data and the structuring is for ease of
83 configurability only.
84
85 args:
86 target - the target for which to return a version map
87
88 returns a map between packages and desired versions in internal format
89 (using the PACKAGE_* constants)
90 """
91 if target in target_version_map:
92 return target_version_map[target]
93
94 # Start from copy of the global defaults.
95 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
96
97 for pkg in TOOLCHAIN_PACKAGES:
98 # prefer any specific overrides
99 if pkg in TARGET_VERSION_MAP.get(target, {}):
100 result[pkg] = TARGET_VERSION_MAP[target][pkg]
101 else:
102 # finally, if not already set, set a sane default
103 result.setdefault(pkg, DEFAULT_VERSION)
104 target_version_map[target] = result
105 return result
106
107
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100108def GetHostTuple():
109 """Returns compiler tuple for the host system.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100110
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100111 Caches the result, because the command can be fairly expensive, and never
112 changes throughout a single run.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100113 """
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100114 CACHE_ATTR = '_host_tuple'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100115
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100116 val = getattr(VAR_CACHE, CACHE_ATTR, None)
117 if val is None:
118 val = cros_build_lib.RunCommand(['portageq', 'envvar', 'CHOST'],
119 print_cmd=False, redirect_stdout=True).output
120 setattr(VAR_CACHE, CACHE_ATTR, val)
121 return val
122
123
124def GetCrossdevConf(target):
125 """Returns a map of crossdev provided variables about a tuple."""
126 CACHE_ATTR = '_target_tuple_map'
127
128 val = getattr(VAR_CACHE, CACHE_ATTR, {})
129 if not target in val:
130 # Find out the crossdev tuple.
131 target_tuple = target
132 if target == 'host':
133 target_tuple = GetHostTuple()
134 # Catch output of crossdev.
135 out = cros_build_lib.RunCommand(['crossdev', '--show-target-cfg',
136 '--ex-gdb', target_tuple],
137 print_cmd=False, redirect_stdout=True).output.splitlines()
138 # List of tuples split at the first '=', converted into dict.
139 val[target] = dict([x.split('=', 1) for x in out])
140 setattr(VAR_CACHE, CACHE_ATTR, {})
141 return val[target]
142
143
144# Portage helper functions:
145def GetPortagePackage(target, package):
146 """Returns a package name for the given target."""
147 conf = GetCrossdevConf(target)
148 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100149 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100150 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100151 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100152 category = conf['category']
153 # Portage package:
154 pn = conf[package + '_pn']
155 # Final package name:
156 assert(category)
157 assert(pn)
158 return '%s/%s' % (category, pn)
159
160
161def GetPortageKeyword(target):
162 """Returns a portage friendly keyword for a given target."""
163 return GetCrossdevConf(target)['arch']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100164
165
166def GetHostTarget():
167 """Returns a string for the host target tuple."""
168 return portage.settings['CHOST']
169
170
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100171def IsPackageDisabled(target, package):
172 """Returns if the given package is not used for the target."""
173 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
174
Zdenek Behan508dcce2011-12-05 15:39:32 +0100175# Tree interface functions. They help with retrieving data about the current
176# state of the tree:
177def GetAllTargets():
178 """Get the complete list of targets.
179
180 returns the list of cross targets for the current tree
181 """
182 cmd = ['cros_overlay_list', '--all_boards']
183 overlays = cros_build_lib.RunCommand(cmd, print_cmd=False,
184 redirect_stdout=True).output.splitlines()
185 targets = set()
186 for overlay in overlays:
187 config = os.path.join(overlay, 'toolchain.conf')
188 if os.path.exists(config):
189 with open(config) as config_file:
190 lines = config_file.read().splitlines()
191 for line in lines:
192 # Remove empty lines and commented lines.
193 if line and not line.startswith('#'):
194 targets.add(line)
195
196 # Remove the host target as that is not a cross-target. Replace with 'host'.
197 targets.remove(GetHostTarget())
198 targets.add('host')
199 return targets
200
201
202def GetInstalledPackageVersions(target, package):
203 """Extracts the list of current versions of a target, package pair.
204
205 args:
206 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
207
208 returns the list of versions of the package currently installed.
209 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100210 versions = []
211 # This is the package name in terms of portage.
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100212 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100213 for pkg in portage.db['/']['vartree'].dbapi.match(atom):
214 version = portage.versions.cpv_getversion(pkg)
215 versions.append(version)
216 return versions
217
218
Zdenek Behan508dcce2011-12-05 15:39:32 +0100219def GetStablePackageVersion(target, package):
220 """Extracts the current stable version for a given package.
221
222 args:
223 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
224
225 returns a string containing the latest version.
226 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100227 keyword = GetPortageKeyword(target)
228 extra_env = {'ACCEPT_KEYWORDS' : '-* ' + keyword}
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100229 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100230 cpv = cros_build_lib.RunCommand(['portageq', 'best_visible', '/', atom],
231 print_cmd=False, redirect_stdout=True,
232 extra_env=extra_env).output.splitlines()[0]
233 return portage.versions.cpv_getversion(cpv)
234
235
236def VersionListToNumeric(target, package, versions):
237 """Resolves keywords in a given version list for a particular package.
238
239 Resolving means replacing PACKAGE_STABLE with the actual number.
240
241 args:
242 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
243 versions - list of versions to resolve
244
245 returns list of purely numeric versions equivalent to argument
246 """
247 resolved = []
248 for version in versions:
249 if version == PACKAGE_STABLE:
250 resolved.append(GetStablePackageVersion(target, package))
251 elif version != PACKAGE_NONE:
252 resolved.append(version)
253 return resolved
254
255
256def GetDesiredPackageVersions(target, package):
257 """Produces the list of desired versions for each target, package pair.
258
259 The first version in the list is implicitly treated as primary, ie.
260 the version that will be initialized by crossdev and selected.
261
262 If the version is PACKAGE_STABLE, it really means the current version which
263 is emerged by using the package atom with no particular version key.
264 Since crossdev unmasks all packages by default, this will actually
265 mean 'unstable' in most cases.
266
267 args:
268 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
269
270 returns a list composed of either a version string, PACKAGE_STABLE
271 """
272 packagemap = GetPackageMap(target)
273
274 versions = []
275 if package in packagemap:
276 versions.append(packagemap[package])
277
278 return versions
279
280
281def TargetIsInitialized(target):
282 """Verifies if the given list of targets has been correctly initialized.
283
284 This determines whether we have to call crossdev while emerging
285 toolchain packages or can do it using emerge. Emerge is naturally
286 preferred, because all packages can be updated in a single pass.
287
288 args:
289 targets - list of individual cross targets which are checked
290
291 returns True if target is completely initialized
292 returns False otherwise
293 """
294 # Check if packages for the given target all have a proper version.
295 try:
296 for package in TOOLCHAIN_PACKAGES:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100297 # Do we even want this package && is it initialized?
298 if not IsPackageDisabled(target, package) and \
299 not GetInstalledPackageVersions(target, package):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100300 return False
301 return True
302 except cros_build_lib.RunCommandError:
303 # Fails - The target has likely never been initialized before.
304 return False
305
306
307def RemovePackageMask(target):
308 """Removes a package.mask file for the given platform.
309
310 The pre-existing package.mask files can mess with the keywords.
311
312 args:
313 target - the target for which to remove the file
314 """
315 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
316 # Note: We have to use sudo here, in case the file is created with
317 # root ownership. However, sudo in chroot is always passwordless.
318 cros_build_lib.SudoRunCommand(['rm', '-f', maskfile],
319 redirect_stdout=True, print_cmd=False)
320
321
322def CreatePackageMask(target, masks):
323 """[Re]creates a package.mask file for the given platform.
324
325 args:
326 target - the given target on which to operate
327 masks - a map of package : version,
328 where version is the highest permissible version (mask >)
329 """
330 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
331 assert not os.path.exists(maskfile)
332 # package.mask/ isn't writable, so we need to create and
333 # chown the file before we use it.
334 cros_build_lib.SudoRunCommand(['touch', maskfile],
335 redirect_stdout=True, print_cmd=False)
336 cros_build_lib.SudoRunCommand(['chown', str(os.getuid()), maskfile],
337 redirect_stdout=True, print_cmd=False)
338
339 with open(maskfile, 'w') as f:
340 for pkg, m in masks.items():
341 f.write('>%s-%s\n' % (pkg, m))
342
343
344def CreatePackageKeywords(target):
345 """[Re]create a package.keywords file for the platform.
346
347 This sets all package.keywords files to unmask all stable/testing packages.
348 TODO: Note that this approach should be deprecated and is only done for
349 compatibility reasons. In the future, we'd like to stop using keywords
350 altogether, and keep just stable unmasked.
351
352 args:
353 target - target for which to recreate package.keywords
354 """
355 maskfile = os.path.join('/etc/portage/package.keywords', 'cross-' + target)
356 cros_build_lib.SudoRunCommand(['rm', '-f', maskfile],
357 redirect_stdout=True, print_cmd=False)
358 cros_build_lib.SudoRunCommand(['touch', maskfile],
359 redirect_stdout=True, print_cmd=False)
360 cros_build_lib.SudoRunCommand(['chown', '%d' % os.getuid(), maskfile],
361 redirect_stdout=True, print_cmd=False)
362
363 keyword = GetPortageKeyword(target)
364
365 with open(maskfile, 'w') as f:
366 for pkg in TOOLCHAIN_PACKAGES:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100367 if IsPackageDisabled(target, pkg):
368 continue
369 f.write('%s %s ~%s\n' %
370 (GetPortagePackage(target, pkg), keyword, keyword))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100371
372
373# Main functions performing the actual update steps.
374def InitializeCrossdevTargets(targets, usepkg):
375 """Calls crossdev to initialize a cross target.
376 args:
377 targets - the list of targets to initialize using crossdev
378 usepkg - copies the commandline opts
379 """
380 print 'The following targets need to be re-initialized:'
381 print targets
Zdenek Behan508dcce2011-12-05 15:39:32 +0100382
383 for target in targets:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100384 cmd = ['sudo', 'FEATURES=splitdebug', 'crossdev', '--show-fail-log',
385 '-t', target]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100386 # Pick stable by default, and override as necessary.
387 cmd.extend(['-P', '--oneshot'])
388 if usepkg:
389 cmd.extend(['-P', '--getbinpkg',
390 '-P', '--usepkgonly',
391 '--without-headers'])
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100392
393 cmd.extend(['--overlays', '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)])
394 cmd.extend(['--ov-output', CROSSDEV_OVERLAY])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100395
396 # HACK(zbehan): arm-none-eabi uses newlib which doesn't like headers-only.
397 if target == 'arm-none-eabi':
398 cmd.append('--without-headers')
399
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100400 if not IsPackageDisabled(target, 'gdb'):
401 cmd.append('--ex-gdb')
402
403 for pkg in CROSSDEV_PACKAGES:
404 if IsPackageDisabled(target, pkg):
405 continue
Zdenek Behan508dcce2011-12-05 15:39:32 +0100406 # The first of the desired versions is the "primary" one.
407 version = GetDesiredPackageVersions(target, pkg)[0]
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100408 cmd.extend(['--%s' % pkg, version])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100409 cros_build_lib.RunCommand(cmd)
410
411
412def UpdateTargets(targets, usepkg):
413 """Determines which packages need update/unmerge and defers to portage.
414
415 args:
416 targets - the list of targets to update
417 usepkg - copies the commandline option
418 """
419 # TODO(zbehan): This process is rather complex due to package.* handling.
420 # With some semantic changes over the original setup_board functionality,
421 # it can be considerably cleaned up.
422 mergemap = {}
423
424 # For each target, we do two things. Figure out the list of updates,
425 # and figure out the appropriate keywords/masks. Crossdev will initialize
426 # these, but they need to be regenerated on every update.
427 print 'Determining required toolchain updates...'
428 for target in targets:
429 # Record the highest needed version for each target, for masking purposes.
430 RemovePackageMask(target)
431 CreatePackageKeywords(target)
432 packagemasks = {}
433 for package in TOOLCHAIN_PACKAGES:
434 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100435 if IsPackageDisabled(target, package):
436 continue
437 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100438 current = GetInstalledPackageVersions(target, package)
439 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100440 desired_num = VersionListToNumeric(target, package, desired)
441 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442
443 # Pick the highest version for mask.
444 packagemasks[pkg] = portage.versions.best(desired_num)
445
446 CreatePackageMask(target, packagemasks)
447
448 packages = []
449 for pkg in mergemap:
450 for ver in mergemap[pkg]:
451 if ver == PACKAGE_STABLE:
452 packages.append(pkg)
453 elif ver != PACKAGE_NONE:
454 packages.append('=%s-%s' % (pkg, ver))
455
456 if not packages:
457 print 'Nothing to update!'
458 return
459
460 print 'Updating packages:'
461 print packages
462
Zdenek Behan508dcce2011-12-05 15:39:32 +0100463 cmd = ['sudo', '-E', 'FEATURES=splitdebug', EMERGE_CMD,
464 '--oneshot', '--update']
465 if usepkg:
466 cmd.extend(['--getbinpkg', '--usepkgonly'])
467
468 cmd.extend(packages)
469 cros_build_lib.RunCommand(cmd)
470
471
472def CleanTargets(targets):
473 """Unmerges old packages that are assumed unnecessary."""
474 unmergemap = {}
475 for target in targets:
476 for package in TOOLCHAIN_PACKAGES:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100477 if IsPackageDisabled(target, package):
478 continue
479 pkg = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100480 current = GetInstalledPackageVersions(target, package)
481 desired = GetDesiredPackageVersions(target, package)
482 desired_num = VersionListToNumeric(target, package, desired)
483 assert set(desired).issubset(set(desired))
484 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
494 cmd = ['sudo', '-E', EMERGE_CMD, '--unmerge']
495 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)
511 desired_num = VersionListToNumeric(target, package, desired)
512 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).
518 target = GetHostTarget()
519
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,
530 redirect_stdout=True).output.splitlines()[0]
531 # 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 ]
534 cros_build_lib.SudoRunCommand(cmd, print_cmd=False)
535
536
537def UpdateToolchains(usepkg, deleteold, targets):
538 """Performs all steps to create a synchronized toolchain enviroment.
539
540 args:
541 arguments correspond to the given commandline flags
542 """
543 alltargets = GetAllTargets()
544 nonexistant = []
545 if targets == set(['all']):
546 targets = alltargets
547 else:
548 # Verify user input.
549 for target in targets:
550 if target not in alltargets:
551 nonexistant.append(target)
552 if nonexistant:
553 raise Exception("Invalid targets: " + ','.join(nonexistant))
554
555 # First check and initialize all cross targets that need to be.
556 crossdev_targets = \
557 [t for t in targets if not TargetIsInitialized(t) and not 'host' == t]
558 if crossdev_targets:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100559 InitializeCrossdevTargets(crossdev_targets, usepkg)
560
561 # Now update all packages, including host.
562 targets.add('host')
563 UpdateTargets(targets, usepkg)
564 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
565
566 if deleteold:
567 CleanTargets(targets)
568
569
Brian Harring30675052012-02-29 12:18:22 -0800570def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100571 usage = """usage: %prog [options]
572
573 The script installs and updates the toolchains in your chroot.
574 """
575 parser = optparse.OptionParser(usage)
576 parser.add_option('-u', '--nousepkg',
577 action='store_false', dest='usepkg', default=True,
578 help=('Use prebuilt packages if possible.'))
579 parser.add_option('-d', '--deleteold',
580 action='store_true', dest='deleteold', default=False,
581 help=('Unmerge deprecated packages.'))
582 parser.add_option('-t', '--targets',
583 dest='targets', default='all',
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100584 help=('Comma separated list of tuples. '
585 'Special keyword \'host\' is allowed. Default: all.'))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100586
Brian Harring30675052012-02-29 12:18:22 -0800587 (options, _remaining_arguments) = parser.parse_args(argv)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100588
589 targets = set(options.targets.split(','))
590 UpdateToolchains(options.usepkg, options.deleteold, targets)