blob: 1686ac83e15e980b175d9c52740fecbfd3e554d7 [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 Frysinger35247af2012-11-16 18:58:06 -050010import glob
Mike Frysinger7ccee992012-06-01 21:27:59 -040011import json
Zdenek Behan508dcce2011-12-05 15:39:32 +010012import os
Zdenek Behan508dcce2011-12-05 15:39:32 +010013
Brian Harring503f3ab2012-03-09 21:39:41 -080014from chromite.buildbot import constants
David Jamese5867812012-10-19 12:02:20 -070015from chromite.buildbot import portage_utilities
Mike Frysinger506e75f2012-12-17 14:21:13 -050016from chromite.lib import commandline
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
Mike Frysinger35247af2012-11-16 18:58:06 -050019from chromite.lib import parallel
20
21# Needs to be after chromite imports.
22import lddtree
Zdenek Behan508dcce2011-12-05 15:39:32 +010023
Mike Frysinger31596002012-12-03 23:54:24 -050024if cros_build_lib.IsInsideChroot():
25 # Only import portage after we've checked that we're inside the chroot.
26 # Outside may not have portage, in which case the above may not happen.
27 # We'll check in main() if the operation needs portage.
28 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010029
30
Matt Tennantf1e30972012-03-02 16:30:07 -080031EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010032PACKAGE_STABLE = '[stable]'
33PACKAGE_NONE = '[none]'
34SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010035
36CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
37STABLE_OVERLAY = '/usr/local/portage/stable'
38CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010039
40
41# TODO: The versions are stored here very much like in setup_board.
42# The goal for future is to differentiate these using a config file.
43# This is done essentially by messing with GetDesiredPackageVersions()
44DEFAULT_VERSION = PACKAGE_STABLE
45DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010046}
47TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010048 'host' : {
49 'binutils' : '2.21.1',
50 'gdb' : PACKAGE_NONE,
51 },
52}
53# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
54CONFIG_TARGET_SUFFIXES = {
55 'binutils' : {
56 'i686-pc-linux-gnu' : '-gold',
57 'x86_64-cros-linux-gnu' : '-gold',
58 },
59}
Zdenek Behan508dcce2011-12-05 15:39:32 +010060# Global per-run cache that will be filled ondemand in by GetPackageMap()
61# function as needed.
62target_version_map = {
63}
64
65
David James66a09c42012-11-05 13:31:38 -080066class Crossdev(object):
67 """Class for interacting with crossdev and caching its output."""
68
69 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
70 _CACHE = {}
71
72 @classmethod
73 def Load(cls, reconfig):
74 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -080075 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
76 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -080077 if os.path.exists(cls._CACHE_FILE) and not reconfig:
78 with open(cls._CACHE_FILE) as f:
79 data = json.load(f)
David James90239b92012-11-05 15:31:34 -080080 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -080081 cls._CACHE = data
82
83 @classmethod
84 def Save(cls):
85 """Store crossdev cache on disk."""
86 # Save the cache from the successful run.
87 with open(cls._CACHE_FILE, 'w') as f:
88 json.dump(cls._CACHE, f)
89
90 @classmethod
91 def GetConfig(cls, target):
92 """Returns a map of crossdev provided variables about a tuple."""
93 CACHE_ATTR = '_target_tuple_map'
94
95 val = cls._CACHE.setdefault(CACHE_ATTR, {})
96 if not target in val:
97 # Find out the crossdev tuple.
98 target_tuple = target
99 if target == 'host':
100 target_tuple = GetHostTuple()
101 # Catch output of crossdev.
102 out = cros_build_lib.RunCommand(['crossdev', '--show-target-cfg',
103 '--ex-gdb', target_tuple],
104 print_cmd=False, redirect_stdout=True).output.splitlines()
105 # List of tuples split at the first '=', converted into dict.
106 val[target] = dict([x.split('=', 1) for x in out])
107 return val[target]
108
109 @classmethod
110 def UpdateTargets(cls, targets, usepkg, config_only=False):
111 """Calls crossdev to initialize a cross target.
112
113 Args:
114 targets - the list of targets to initialize using crossdev
115 usepkg - copies the commandline opts
116 config_only - Just update
117 """
118 configured_targets = cls._CACHE.setdefault('configured_targets', [])
119
120 cmdbase = ['crossdev', '--show-fail-log']
121 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
122 # Pick stable by default, and override as necessary.
123 cmdbase.extend(['-P', '--oneshot'])
124 if usepkg:
125 cmdbase.extend(['-P', '--getbinpkg',
126 '-P', '--usepkgonly',
127 '--without-headers'])
128
129 overlays = '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)
130 cmdbase.extend(['--overlays', overlays])
131 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
132
133 for target in targets:
134 if config_only and target in configured_targets:
135 continue
136
137 cmd = cmdbase + ['-t', target]
138
139 for pkg in GetTargetPackages(target):
140 if pkg == 'gdb':
141 # Gdb does not have selectable versions.
142 cmd.append('--ex-gdb')
143 continue
144 # The first of the desired versions is the "primary" one.
145 version = GetDesiredPackageVersions(target, pkg)[0]
146 cmd.extend(['--%s' % pkg, version])
147
148 cmd.extend(targets[target]['crossdev'].split())
149 if config_only:
150 # In this case we want to just quietly reinit
151 cmd.append('--init-target')
152 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
153 else:
154 cros_build_lib.RunCommand(cmd)
155
156 configured_targets.append(target)
157
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100158
Zdenek Behan508dcce2011-12-05 15:39:32 +0100159def GetPackageMap(target):
160 """Compiles a package map for the given target from the constants.
161
162 Uses a cache in target_version_map, that is dynamically filled in as needed,
163 since here everything is static data and the structuring is for ease of
164 configurability only.
165
166 args:
167 target - the target for which to return a version map
168
169 returns a map between packages and desired versions in internal format
170 (using the PACKAGE_* constants)
171 """
172 if target in target_version_map:
173 return target_version_map[target]
174
175 # Start from copy of the global defaults.
176 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
177
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100178 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100179 # prefer any specific overrides
180 if pkg in TARGET_VERSION_MAP.get(target, {}):
181 result[pkg] = TARGET_VERSION_MAP[target][pkg]
182 else:
183 # finally, if not already set, set a sane default
184 result.setdefault(pkg, DEFAULT_VERSION)
185 target_version_map[target] = result
186 return result
187
188
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100189def GetHostTuple():
David James90239b92012-11-05 15:31:34 -0800190 """Returns compiler tuple for the host system."""
Mike Frysinger506e75f2012-12-17 14:21:13 -0500191 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800192 return portage.settings['CHOST']
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100193
194
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100195def GetTargetPackages(target):
196 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800197 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100198 # Undesired packages are denoted by empty ${pkg}_pn variable.
199 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
200
201
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100202# Portage helper functions:
203def GetPortagePackage(target, package):
204 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800205 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100206 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100207 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100208 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100209 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100210 category = conf['category']
211 # Portage package:
212 pn = conf[package + '_pn']
213 # Final package name:
214 assert(category)
215 assert(pn)
216 return '%s/%s' % (category, pn)
217
218
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100219def IsPackageDisabled(target, package):
220 """Returns if the given package is not used for the target."""
221 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
222
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100223
224def GetTuplesForOverlays(overlays):
225 """Returns a set of tuples for a given set of overlays."""
Mike Frysinger506e75f2012-12-17 14:21:13 -0500226 targets = {}
Mike Frysinger7ccee992012-06-01 21:27:59 -0400227 default_settings = {
228 'sdk' : True,
229 'crossdev' : '',
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400230 'default' : False,
Mike Frysinger7ccee992012-06-01 21:27:59 -0400231 }
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100232
233 for overlay in overlays:
234 config = os.path.join(overlay, 'toolchain.conf')
235 if os.path.exists(config):
Mike Frysinger506e75f2012-12-17 14:21:13 -0500236 first_target = None
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400237 seen_default = False
238
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100239 for line in osutils.ReadFile(config).splitlines():
Mike Frysinger7ccee992012-06-01 21:27:59 -0400240 # Split by hash sign so that comments are ignored.
241 # Then split the line to get the tuple and its options.
242 line = line.split('#', 1)[0].split()
243
244 if len(line) > 0:
Mike Frysinger506e75f2012-12-17 14:21:13 -0500245 target = line[0]
246 if not first_target:
247 first_target = target
248 if target not in targets:
249 targets[target] = copy.copy(default_settings)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400250 if len(line) > 1:
Mike Frysinger506e75f2012-12-17 14:21:13 -0500251 targets[target].update(json.loads(' '.join(line[1:])))
252 if targets[target]['default']:
Mike Frysinger47ad3ec2012-06-04 17:41:13 -0400253 seen_default = True
254
255 # If the user has not explicitly marked a toolchain as default,
256 # automatically select the first tuple that we saw in the conf.
Mike Frysinger506e75f2012-12-17 14:21:13 -0500257 if not seen_default and first_target:
258 targets[first_target]['default'] = True
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100259
Mike Frysinger506e75f2012-12-17 14:21:13 -0500260 return targets
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100261
262
Zdenek Behan508dcce2011-12-05 15:39:32 +0100263# Tree interface functions. They help with retrieving data about the current
264# state of the tree:
265def GetAllTargets():
266 """Get the complete list of targets.
267
268 returns the list of cross targets for the current tree
269 """
Mike Frysingereeecfda2012-06-01 16:34:50 -0400270 targets = GetToolchainsForBoard('all')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100271
272 # Remove the host target as that is not a cross-target. Replace with 'host'.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400273 del targets[GetHostTuple()]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100274 return targets
275
276
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100277def GetToolchainsForBoard(board):
278 """Get a list of toolchain tuples for a given board name
279
280 returns the list of toolchain tuples for the given board
281 """
David Jamese5867812012-10-19 12:02:20 -0700282 overlays = portage_utilities.FindOverlays(
283 constants.BOTH_OVERLAYS, None if board in ('all', 'sdk') else board)
Mike Frysingereaebb582012-06-19 13:04:53 -0400284 toolchains = GetTuplesForOverlays(overlays)
285 if board == 'sdk':
286 toolchains = FilterToolchains(toolchains, 'sdk', True)
287 return toolchains
288
289
290def FilterToolchains(targets, key, value):
291 """Filter out targets based on their attributes.
292
293 args:
294 targets - dict of toolchains
295 key - metadata to examine
296 value - expected value for metadata
297
298 returns a dict where all targets whose metadata key does not match value
299 have been deleted.
300 """
Mike Frysinger506e75f2012-12-17 14:21:13 -0500301 return dict((k, v) for k, v in targets.iteritems() if v[key] == value)
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 = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500313 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800314 for pkg in portage.db['/']['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100315 version = portage.versions.cpv_getversion(pkg)
316 versions.append(version)
317 return versions
318
319
David James90239b92012-11-05 15:31:34 -0800320def GetStablePackageVersion(atom, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100321 """Extracts the current stable version for a given package.
322
323 args:
324 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
Zdenek Behan699ddd32012-04-13 07:14:08 +0200325 installed - Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100326
327 returns a string containing the latest version.
328 """
David James90239b92012-11-05 15:31:34 -0800329 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500330 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800331 cpv = portage.best(portage.db['/'][pkgtype].dbapi.match(atom, use_cache=0))
332 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100333
334
Zdenek Behan699ddd32012-04-13 07:14:08 +0200335def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100336 """Resolves keywords in a given version list for a particular package.
337
338 Resolving means replacing PACKAGE_STABLE with the actual number.
339
340 args:
341 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
342 versions - list of versions to resolve
343
344 returns list of purely numeric versions equivalent to argument
345 """
346 resolved = []
David James90239b92012-11-05 15:31:34 -0800347 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100348 for version in versions:
349 if version == PACKAGE_STABLE:
David James90239b92012-11-05 15:31:34 -0800350 resolved.append(GetStablePackageVersion(atom, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100351 elif version != PACKAGE_NONE:
352 resolved.append(version)
353 return resolved
354
355
356def GetDesiredPackageVersions(target, package):
357 """Produces the list of desired versions for each target, package pair.
358
359 The first version in the list is implicitly treated as primary, ie.
360 the version that will be initialized by crossdev and selected.
361
362 If the version is PACKAGE_STABLE, it really means the current version which
363 is emerged by using the package atom with no particular version key.
364 Since crossdev unmasks all packages by default, this will actually
365 mean 'unstable' in most cases.
366
367 args:
368 target, package - the target/package to operate on eg. i686-pc-linux-gnu,gcc
369
370 returns a list composed of either a version string, PACKAGE_STABLE
371 """
372 packagemap = GetPackageMap(target)
373
374 versions = []
375 if package in packagemap:
376 versions.append(packagemap[package])
377
378 return versions
379
380
381def TargetIsInitialized(target):
382 """Verifies if the given list of targets has been correctly initialized.
383
384 This determines whether we have to call crossdev while emerging
385 toolchain packages or can do it using emerge. Emerge is naturally
386 preferred, because all packages can be updated in a single pass.
387
388 args:
389 targets - list of individual cross targets which are checked
390
391 returns True if target is completely initialized
392 returns False otherwise
393 """
394 # Check if packages for the given target all have a proper version.
395 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100396 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800397 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100398 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800399 if not IsPackageDisabled(target, package) and not (
400 GetStablePackageVersion(atom, True) and
401 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100402 return False
403 return True
404 except cros_build_lib.RunCommandError:
405 # Fails - The target has likely never been initialized before.
406 return False
407
408
409def RemovePackageMask(target):
410 """Removes a package.mask file for the given platform.
411
412 The pre-existing package.mask files can mess with the keywords.
413
414 args:
415 target - the target for which to remove the file
416 """
417 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700418 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419
420
Zdenek Behan508dcce2011-12-05 15:39:32 +0100421# Main functions performing the actual update steps.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100422def UpdateTargets(targets, usepkg):
423 """Determines which packages need update/unmerge and defers to portage.
424
425 args:
426 targets - the list of targets to update
427 usepkg - copies the commandline option
428 """
David James90239b92012-11-05 15:31:34 -0800429 # Remove keyword files created by old versions of cros_setup_toolchains.
430 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100431
432 # For each target, we do two things. Figure out the list of updates,
433 # and figure out the appropriate keywords/masks. Crossdev will initialize
434 # these, but they need to be regenerated on every update.
435 print 'Determining required toolchain updates...'
David James90239b92012-11-05 15:31:34 -0800436 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 for target in targets:
438 # Record the highest needed version for each target, for masking purposes.
439 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100440 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100441 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100442 if IsPackageDisabled(target, package):
443 continue
444 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800445 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100446 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200447 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100448 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100449
Zdenek Behan508dcce2011-12-05 15:39:32 +0100450 packages = []
451 for pkg in mergemap:
452 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200453 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800454 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455
456 if not packages:
457 print 'Nothing to update!'
David Jamesf8c672f2012-11-06 13:38:11 -0800458 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100459
460 print 'Updating packages:'
461 print packages
462
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100463 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100464 if usepkg:
465 cmd.extend(['--getbinpkg', '--usepkgonly'])
466
467 cmd.extend(packages)
468 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800469 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100470
471
472def CleanTargets(targets):
473 """Unmerges old packages that are assumed unnecessary."""
474 unmergemap = {}
475 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100476 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100477 if IsPackageDisabled(target, package):
478 continue
479 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800480 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100481 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200482 desired_num = VersionListToNumeric(target, package, desired, True)
483 if not set(desired_num).issubset(current):
484 print 'Some packages have been held back, skipping clean!'
485 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100486 unmergemap[pkg] = set(current).difference(desired_num)
487
488 # Cleaning doesn't care about consistency and rebuilding package.* files.
489 packages = []
490 for pkg, vers in unmergemap.iteritems():
491 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
492
493 if packages:
494 print 'Cleaning packages:'
495 print packages
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100496 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100497 cmd.extend(packages)
498 cros_build_lib.RunCommand(cmd)
499 else:
500 print 'Nothing to clean!'
501
502
503def SelectActiveToolchains(targets, suffixes):
504 """Runs gcc-config and binutils-config to select the desired.
505
506 args:
507 targets - the targets to select
508 """
509 for package in ['gcc', 'binutils']:
510 for target in targets:
511 # Pick the first version in the numbered list as the selected one.
512 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200513 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100514 desired = desired_num[0]
515 # *-config does not play revisions, strip them, keep just PV.
516 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
517
518 if target == 'host':
519 # *-config is the only tool treating host identically (by tuple).
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100520 target = GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100521
522 # And finally, attach target to it.
523 desired = '%s-%s' % (target, desired)
524
525 # Target specific hacks
526 if package in suffixes:
527 if target in suffixes[package]:
528 desired += suffixes[package][target]
529
David James7ec5efc2012-11-06 09:39:49 -0800530 extra_env = {'CHOST': target}
531 cmd = ['%s-config' % package, '-c', target]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100532 current = cros_build_lib.RunCommand(cmd, print_cmd=False,
David James7ec5efc2012-11-06 09:39:49 -0800533 redirect_stdout=True, extra_env=extra_env).output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100534 # Do not gcc-config when the current is live or nothing needs to be done.
535 if current != desired and current != '9999':
536 cmd = [ package + '-config', desired ]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100537 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538
539
Mike Frysinger35247af2012-11-16 18:58:06 -0500540def ExpandTargets(targets_wanted):
541 """Expand any possible toolchain aliases into full targets
542
543 This will expand 'all' and 'sdk' into the respective toolchain tuples.
544
545 Args:
546 targets_wanted: The targets specified by the user.
547 Returns:
548 Full list of tuples with pseudo targets removed.
549 """
550 alltargets = GetAllTargets()
551 targets_wanted = set(targets_wanted)
552 if targets_wanted == set(['all']):
553 targets = alltargets
554 elif targets_wanted == set(['sdk']):
555 # Filter out all the non-sdk toolchains as we don't want to mess
556 # with those in all of our builds.
557 targets = FilterToolchains(alltargets, 'sdk', True)
558 else:
559 # Verify user input.
560 nonexistent = targets_wanted.difference(alltargets)
561 if nonexistent:
562 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
563 targets = dict((t, alltargets[t]) for t in targets_wanted)
564 return targets
565
566
David Jamesf8c672f2012-11-06 13:38:11 -0800567def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
568 targets_wanted, boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100569 """Performs all steps to create a synchronized toolchain enviroment.
570
571 args:
572 arguments correspond to the given commandline flags
573 """
David Jamesf8c672f2012-11-06 13:38:11 -0800574 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100575 if not hostonly:
576 # For hostonly, we can skip most of the below logic, much of which won't
577 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500578 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400579
Mike Frysinger7ccee992012-06-01 21:27:59 -0400580 # Now re-add any targets that might be from this board. This is
581 # to allow unofficial boards to declare their own toolchains.
582 for board in boards_wanted:
583 targets.update(GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100584
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100585 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400586 for target in targets:
587 if TargetIsInitialized(target):
588 reconfig_targets[target] = targets[target]
589 else:
590 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100591 if crossdev_targets:
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200592 print 'The following targets need to be re-initialized:'
593 print crossdev_targets
David James66a09c42012-11-05 13:31:38 -0800594 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200595 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800596 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100597
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100598 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400599 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100600
601 # Now update all packages.
David Jamesf8c672f2012-11-06 13:38:11 -0800602 if UpdateTargets(targets, usepkg) or crossdev_targets or reconfig:
603 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
David James7ec5efc2012-11-06 09:39:49 -0800604
605 if deleteold:
606 CleanTargets(targets)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100607
608
Mike Frysinger35247af2012-11-16 18:58:06 -0500609def ShowBoardConfig(board):
610 """Show the toolchain tuples used by |board|
611
612 Args:
613 board: The board to query.
614 """
615 toolchains = GetToolchainsForBoard(board)
616 # Make sure we display the default toolchain first.
617 print ','.join(FilterToolchains(toolchains, 'default', True).keys() +
618 FilterToolchains(toolchains, 'default', False).keys())
619
620
621def GenerateLdsoWrapper(root, path, interp, libpaths=()):
622 """Generate a shell script wrapper which uses local ldso to run the ELF
623
624 Since we cannot rely on the host glibc (or other libraries), we need to
625 execute the local packaged ldso directly and tell it where to find our
626 copies of libraries.
627
628 Args:
629 root: The root tree to generate scripts inside of
630 path: The full path (inside |root|) to the program to wrap
631 interp: The ldso interpreter that we need to execute
632 libpaths: Extra lib paths to search for libraries
633 """
634 basedir = os.path.dirname(path)
635 libpaths = ['/lib'] + list(libpaths)
636 replacements = {
637 'interp': os.path.join(os.path.relpath('/lib', basedir),
638 os.path.basename(interp)),
639 'libpaths': ':'.join(['${basedir}/' + os.path.relpath(p, basedir)
640 for p in libpaths]),
641 }
642 wrapper = """#!/bin/sh
643base=$(realpath "$0")
644basedir=${base%%/*}
645exec \
646 "${basedir}/%(interp)s" \
647 --library-path "%(libpaths)s" \
648 --inhibit-rpath '' \
649 "${base}.elf" \
650 "$@"
651""" % replacements
652 wrappath = root + path
653 os.rename(wrappath, wrappath + '.elf')
654 osutils.WriteFile(wrappath, wrapper)
655 os.chmod(wrappath, 0755)
656
657
658def GeneratePathWrapper(root, wrappath, path):
659 """Generate a shell script to execute another shell script
660
661 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
662 argv[0] won't be pointing to the correct path, generate a shell script that
663 just executes another program with its full path.
664
665 Args:
666 root: The root tree to generate scripts inside of
667 wrappath: The full path (inside |root|) to create the wrapper
668 path: The target program which this wrapper will execute
669 """
670 replacements = {
671 'path': path,
672 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
673 }
674 wrapper = """#!/bin/sh
675base=$(realpath "$0")
676basedir=${base%%/*}
677exec "${basedir}/%(relroot)s%(path)s" "$@"
678""" % replacements
679 root_wrapper = root + wrappath
680 if os.path.islink(root_wrapper):
681 os.unlink(root_wrapper)
682 else:
683 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
684 osutils.WriteFile(root_wrapper, wrapper)
685 os.chmod(root_wrapper, 0755)
686
687
688def FileIsCrosSdkElf(elf):
689 """Determine if |elf| is an ELF that we execute in the cros_sdk
690
691 We don't need this to be perfect, just quick. It makes sure the ELF
692 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
693
694 Args:
695 elf: The file to check
696 Returns:
697 True if we think |elf| is a native ELF
698 """
699 with open(elf) as f:
700 data = f.read(20)
701 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
702 return (data[0:4] == '\x7fELF' and
703 data[4] == '\x02' and
704 data[5] == '\x01' and
705 data[18] == '\x3e')
706
707
708def IsPathPackagable(ptype, path):
709 """Should the specified file be included in a toolchain package?
710
711 We only need to handle files as we'll create dirs as we need them.
712
713 Further, trim files that won't be useful:
714 - non-english translations (.mo) since it'd require env vars
715 - debug files since these are for the host compiler itself
716 - info/man pages as they're big, and docs are online, and the
717 native docs should work fine for the most part (`man gcc`)
718
719 Args:
720 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
721 path: The full path to inspect
722 Returns:
723 True if we want to include this path in the package
724 """
725 return not (ptype in ('dir',) or
726 path.startswith('/usr/lib/debug/') or
727 os.path.splitext(path)[1] == '.mo' or
728 ('/man/' in path or '/info/' in path))
729
730
731def ReadlinkRoot(path, root):
732 """Like os.readlink(), but relative to a |root|
733
734 Args:
735 path: The symlink to read
736 root: The path to use for resolving absolute symlinks
737 Returns:
738 A fully resolved symlink path
739 """
740 while os.path.islink(root + path):
741 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
742 return path
743
744
745def _GetFilesForTarget(target, root='/'):
746 """Locate all the files to package for |target|
747
748 This does not cover ELF dependencies.
749
750 Args:
751 target: The toolchain target name
752 root: The root path to pull all packages from
753 Returns:
754 A tuple of a set of all packable paths, and a set of all paths which
755 are also native ELFs
756 """
757 paths = set()
758 elfs = set()
759
760 # Find all the files owned by the packages for this target.
761 for pkg in GetTargetPackages(target):
762 # Ignore packages that are part of the target sysroot.
763 if pkg in ('kernel', 'libc'):
764 continue
765
766 atom = GetPortagePackage(target, pkg)
767 cat, pn = atom.split('/')
768 ver = GetInstalledPackageVersions(atom)[0]
769 cros_build_lib.Info('packaging %s-%s', atom, ver)
770
771 # pylint: disable=E1101
772 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
773 settings=portage.settings)
774 contents = dblink.getcontents()
775 for obj in contents:
776 ptype = contents[obj][0]
777 if not IsPathPackagable(ptype, obj):
778 continue
779
780 if ptype == 'obj':
781 # For native ELFs, we need to pull in their dependencies too.
782 if FileIsCrosSdkElf(obj):
783 elfs.add(obj)
784 paths.add(obj)
785
786 return paths, elfs
787
788
789def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
790 path_rewrite_func=lambda x:x, root='/'):
791 """Link in all packable files and their runtime dependencies
792
793 This also wraps up executable ELFs with helper scripts.
794
795 Args:
796 output_dir: The output directory to store files
797 paths: All the files to include
798 elfs: All the files which are ELFs (a subset of |paths|)
799 ldpaths: A dict of static ldpath information
800 path_rewrite_func: User callback to rewrite paths in output_dir
801 root: The root path to pull all packages/files from
802 """
803 # Link in all the files.
804 sym_paths = []
805 for path in paths:
806 new_path = path_rewrite_func(path)
807 dst = output_dir + new_path
808 osutils.SafeMakedirs(os.path.dirname(dst))
809
810 # Is this a symlink which we have to rewrite or wrap?
811 # Delay wrap check until after we have created all paths.
812 src = root + path
813 if os.path.islink(src):
814 tgt = os.readlink(src)
815 if os.path.sep in tgt:
816 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
817
818 # Rewrite absolute links to relative and then generate the symlink
819 # ourselves. All other symlinks can be hardlinked below.
820 if tgt[0] == '/':
821 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
822 os.symlink(tgt, dst)
823 continue
824
825 os.link(src, dst)
826
827 # Now see if any of the symlinks need to be wrapped.
828 for sym, tgt in sym_paths:
829 if tgt in elfs:
830 GeneratePathWrapper(output_dir, sym, tgt)
831
832 # Locate all the dependencies for all the ELFs. Stick them all in the
833 # top level "lib" dir to make the wrapper simpler. This exact path does
834 # not matter since we execute ldso directly, and we tell the ldso the
835 # exact path to search for its libraries.
836 libdir = os.path.join(output_dir, 'lib')
837 osutils.SafeMakedirs(libdir)
838 donelibs = set()
839 for elf in elfs:
840 e = lddtree.ParseELF(elf, root, ldpaths)
841 interp = e['interp']
842 if interp:
843 # Generate a wrapper if it is executable.
844 GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
845 libpaths=e['rpath'] + e['runpath'])
846
847 for lib, lib_data in e['libs'].iteritems():
848 if lib in donelibs:
849 continue
850
851 src = path = lib_data['path']
852 if path is None:
853 cros_build_lib.Warning('%s: could not locate %s', elf, lib)
854 continue
855 donelibs.add(lib)
856
857 # Needed libs are the SONAME, but that is usually a symlink, not a
858 # real file. So link in the target rather than the symlink itself.
859 # We have to walk all the possible symlinks (SONAME could point to a
860 # symlink which points to a symlink), and we have to handle absolute
861 # ourselves (since we have a "root" argument).
862 dst = os.path.join(libdir, os.path.basename(path))
863 src = ReadlinkRoot(src, root)
864
865 os.link(root + src, dst)
866
867
868def _EnvdGetVar(envd, var):
869 """Given a Gentoo env.d file, extract a var from it
870
871 Args:
872 envd: The env.d file to load (may be a glob path)
873 var: The var to extract
874 Returns:
875 The value of |var|
876 """
877 envds = glob.glob(envd)
878 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
879 envd = envds[0]
880 return cros_build_lib.LoadKeyValueFile(envd)[var]
881
882
883def _ProcessBinutilsConfig(target, output_dir):
884 """Do what binutils-config would have done"""
885 binpath = os.path.join('/bin', target + '-')
886 globpath = os.path.join(output_dir, 'usr', GetHostTuple(), target,
887 'binutils-bin', '*-gold')
888 srcpath = glob.glob(globpath)
889 assert len(srcpath) == 1, '%s: did not match 1 path' % globpath
890 srcpath = srcpath[0][len(output_dir):]
891 gccpath = os.path.join('/usr', 'libexec', 'gcc')
892 for prog in os.listdir(output_dir + srcpath):
893 # Skip binaries already wrapped.
894 if not prog.endswith('.real'):
895 GeneratePathWrapper(output_dir, binpath + prog,
896 os.path.join(srcpath, prog))
897 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
898 os.path.join(srcpath, prog))
899
900 libpath = os.path.join('/usr', GetHostTuple(), target, 'lib')
901 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*-gold')
902 srcpath = _EnvdGetVar(envd, 'LIBPATH')
903 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
904 output_dir + libpath)
905
906
907def _ProcessGccConfig(target, output_dir):
908 """Do what gcc-config would have done"""
909 binpath = '/bin'
910 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
911 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
912 for prog in os.listdir(output_dir + srcpath):
913 # Skip binaries already wrapped.
914 if (not prog.endswith('.real') and
915 not prog.endswith('.elf') and
916 prog.startswith(target)):
917 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
918 os.path.join(srcpath, prog))
919 return srcpath
920
921
922def _ProcessSysrootWrapper(_target, output_dir, srcpath):
923 """Remove chroot-specific things from our sysroot wrapper"""
924 # Disable ccache since we know it won't work outside of chroot.
925 sysroot_wrapper = glob.glob(os.path.join(
926 output_dir + srcpath, 'sysroot_wrapper*'))[0]
927 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
928 for num in xrange(len(contents)):
929 if '@CCACHE_DEFAULT@' in contents[num]:
930 contents[num] = 'use_ccache = False'
931 break
932 # Can't update the wrapper in place since it's a hardlink to a file in /.
933 os.unlink(sysroot_wrapper)
934 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
935 os.chmod(sysroot_wrapper, 0755)
936
937
938def _ProcessDistroCleanups(target, output_dir):
939 """Clean up the tree and remove all distro-specific requirements
940
941 Args:
942 target: The toolchain target name
943 output_dir: The output directory to clean up
944 """
945 _ProcessBinutilsConfig(target, output_dir)
946 gcc_path = _ProcessGccConfig(target, output_dir)
947 _ProcessSysrootWrapper(target, output_dir, gcc_path)
948
949 osutils.RmDir(os.path.join(output_dir, 'etc'))
950
951
952def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
953 """Setup a tree from the packages for the specified target
954
955 This populates a path with all the files from toolchain packages so that
956 a tarball can easily be generated from the result.
957
958 Args:
959 target: The target to create a packagable root from
960 output_dir: The output directory to place all the files
961 ldpaths: A dict of static ldpath information
962 root: The root path to pull all packages/files from
963 """
964 # Find all the files owned by the packages for this target.
965 paths, elfs = _GetFilesForTarget(target, root=root)
966
967 # Link in all the package's files, any ELF dependencies, and wrap any
968 # executable ELFs with helper scripts.
969 def MoveUsrBinToBin(path):
970 """Move /usr/bin to /bin so people can just use that toplevel dir"""
971 return path[4:] if path.startswith('/usr/bin/') else path
972 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
973 path_rewrite_func=MoveUsrBinToBin, root=root)
974
975 # The packages, when part of the normal distro, have helper scripts
976 # that setup paths and such. Since we are making this standalone, we
977 # need to preprocess all that ourselves.
978 _ProcessDistroCleanups(target, output_dir)
979
980
981def CreatePackages(targets_wanted, output_dir, root='/'):
982 """Create redistributable cross-compiler packages for the specified targets
983
984 This creates toolchain packages that should be usable in conjunction with
985 a downloaded sysroot (created elsewhere).
986
987 Tarballs (one per target) will be created in $PWD.
988
989 Args:
990 targets_wanted: The targets to package up
991 root: The root path to pull all packages/files from
992 """
993 osutils.SafeMakedirs(output_dir)
994 ldpaths = lddtree.LoadLdpaths(root)
995 targets = ExpandTargets(targets_wanted)
996
997 with osutils.TempDirContextManager() as tempdir:
998 # We have to split the root generation from the compression stages. This is
999 # because we hardlink in all the files (to avoid overhead of reading/writing
1000 # the copies multiple times). But tar gets angry if a file's hardlink count
1001 # changes from when it starts reading a file to when it finishes.
1002 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1003 for target in targets:
1004 output_target_dir = os.path.join(tempdir, target)
1005 queue.put([target, output_target_dir, ldpaths, root])
1006
1007 # Build the tarball.
1008 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1009 for target in targets:
1010 tar_file = os.path.join(output_dir, target + '.tar.xz')
1011 queue.put([tar_file, os.path.join(tempdir, target)])
1012
1013
Brian Harring30675052012-02-29 12:18:22 -08001014def main(argv):
Zdenek Behan508dcce2011-12-05 15:39:32 +01001015 usage = """usage: %prog [options]
1016
Mike Frysinger506e75f2012-12-17 14:21:13 -05001017 The script installs and updates the toolchains in your chroot."""
1018 parser = commandline.OptionParser(usage)
Zdenek Behan508dcce2011-12-05 15:39:32 +01001019 parser.add_option('-u', '--nousepkg',
1020 action='store_false', dest='usepkg', default=True,
Mike Frysinger506e75f2012-12-17 14:21:13 -05001021 help='Use prebuilt packages if possible')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001022 parser.add_option('-d', '--deleteold',
1023 action='store_true', dest='deleteold', default=False,
Mike Frysinger506e75f2012-12-17 14:21:13 -05001024 help='Unmerge deprecated packages')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001025 parser.add_option('-t', '--targets',
Mike Frysingereaebb582012-06-19 13:04:53 -04001026 dest='targets', default='sdk',
Mike Frysinger506e75f2012-12-17 14:21:13 -05001027 help='Comma separated list of tuples. '
1028 'Special keyword \'host\' is allowed. Default: sdk')
Mike Frysinger7ccee992012-06-01 21:27:59 -04001029 parser.add_option('--include-boards',
1030 dest='include_boards', default='',
Mike Frysinger506e75f2012-12-17 14:21:13 -05001031 help='Comma separated list of boards whose toolchains we'
1032 ' will always include. Default: none')
Liam McLoughlinf54a0782012-05-17 23:36:52 +01001033 parser.add_option('--hostonly',
Zdenek Behan7e33b4e2012-03-12 17:00:56 +01001034 dest='hostonly', default=False, action='store_true',
Mike Frysinger506e75f2012-12-17 14:21:13 -05001035 help='Only setup the host toolchain. '
1036 'Useful for bootstrapping chroot')
Liam McLoughlinf54a0782012-05-17 23:36:52 +01001037 parser.add_option('--show-board-cfg',
1038 dest='board_cfg', default=None,
Mike Frysinger506e75f2012-12-17 14:21:13 -05001039 help='Board to list toolchain tuples for')
Mike Frysinger35247af2012-11-16 18:58:06 -05001040 parser.add_option('--create-packages',
1041 action='store_true', default=False,
1042 help='Build redistributable packages')
1043 parser.add_option('--output-dir', default=os.getcwd(), type='path',
1044 help='Output directory')
David James66a09c42012-11-05 13:31:38 -08001045 parser.add_option('--reconfig', default=False, action='store_true',
Mike Frysinger506e75f2012-12-17 14:21:13 -05001046 help='Reload crossdev config and reselect toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001047
Mike Frysinger35247af2012-11-16 18:58:06 -05001048 (options, remaining_arguments) = parser.parse_args(argv)
1049 if len(remaining_arguments):
1050 parser.error('script does not take arguments: %s' % remaining_arguments)
Zdenek Behan508dcce2011-12-05 15:39:32 +01001051
Mike Frysinger35247af2012-11-16 18:58:06 -05001052 # Figure out what we're supposed to do and reject conflicting options.
1053 if options.board_cfg and options.create_packages:
1054 parser.error('conflicting options: create-packages & show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001055
Zdenek Behan508dcce2011-12-05 15:39:32 +01001056 targets = set(options.targets.split(','))
Mike Frysinger7ccee992012-06-01 21:27:59 -04001057 boards = set(options.include_boards.split(',')) if options.include_boards \
1058 else set()
Mike Frysinger35247af2012-11-16 18:58:06 -05001059
1060 if options.board_cfg:
1061 ShowBoardConfig(options.board_cfg)
1062 elif options.create_packages:
1063 cros_build_lib.AssertInsideChroot()
1064 Crossdev.Load(False)
1065 CreatePackages(targets, options.output_dir)
1066 else:
1067 cros_build_lib.AssertInsideChroot()
1068 # This has to be always run as root.
1069 if os.geteuid() != 0:
1070 cros_build_lib.Die('this script must be run as root')
1071
1072 Crossdev.Load(options.reconfig)
1073 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
1074 options.reconfig, targets, boards)
1075 Crossdev.Save()
1076
1077 return 0