blob: 944d3fd7b69a0498f39c23ce134c3c0610455466 [file] [log] [blame]
Zdenek Behan508dcce2011-12-05 15:39:32 +01001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Mike Frysinger750c5f52014-09-16 16:16:57 -04005"""This script manages the installed toolchains in the chroot."""
Zdenek Behan508dcce2011-12-05 15:39:32 +01006
Mike Frysinger383367e2014-09-16 15:06:17 -04007from __future__ import print_function
8
Zdenek Behan508dcce2011-12-05 15:39:32 +01009import 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
Don Garrett88b8d782014-05-13 17:30:55 -070014from chromite.cbuildbot import constants
Mike Frysinger506e75f2012-12-17 14:21:13 -050015from chromite.lib import commandline
Brian Harring503f3ab2012-03-09 21:39:41 -080016from chromite.lib import cros_build_lib
Brian Harringaf019fb2012-05-10 15:06:13 -070017from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050018from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080019from chromite.lib import toolchain
Mike Frysinger35247af2012-11-16 18:58:06 -050020
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.
Don Garrett25f309a2014-03-19 14:02:12 -070028 # pylint: disable=F0401
Mike Frysinger31596002012-12-03 23:54:24 -050029 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010030
31
Matt Tennantf1e30972012-03-02 16:30:07 -080032EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010033PACKAGE_STABLE = '[stable]'
34PACKAGE_NONE = '[none]'
35SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010036
37CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
38STABLE_OVERLAY = '/usr/local/portage/stable'
39CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010040
41
42# TODO: The versions are stored here very much like in setup_board.
43# The goal for future is to differentiate these using a config file.
44# This is done essentially by messing with GetDesiredPackageVersions()
45DEFAULT_VERSION = PACKAGE_STABLE
46DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010047}
48TARGET_VERSION_MAP = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050049 'host' : {
50 'gdb' : PACKAGE_NONE,
51 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010052}
53# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
54CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050055 'binutils' : {
56 'i686-pc-linux-gnu' : '-gold',
57 'x86_64-cros-linux-gnu' : '-gold',
58 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010059}
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':
David James27ac4ae2012-12-03 23:16:15 -0800100 target_tuple = toolchain.GetHostTuple()
David James66a09c42012-11-05 13:31:38 -0800101 # Catch output of crossdev.
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500102 out = cros_build_lib.RunCommand(
103 ['crossdev', '--show-target-cfg', '--ex-gdb', target_tuple],
104 print_cmd=False, redirect_stdout=True).output.splitlines()
David James66a09c42012-11-05 13:31:38 -0800105 # 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:
Don Garrett25f309a2014-03-19 14:02:12 -0700114 targets: The list of targets to initialize using crossdev.
115 usepkg: Copies the commandline opts.
116 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800117 """
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
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500166 Args:
167 target: The target for which to return a version map
Zdenek Behan508dcce2011-12-05 15:39:32 +0100168
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500169 Returns:
170 A map between packages and desired versions in internal format
171 (using the PACKAGE_* constants)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100172 """
173 if target in target_version_map:
174 return target_version_map[target]
175
176 # Start from copy of the global defaults.
177 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
178
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100179 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100180 # prefer any specific overrides
181 if pkg in TARGET_VERSION_MAP.get(target, {}):
182 result[pkg] = TARGET_VERSION_MAP[target][pkg]
183 else:
184 # finally, if not already set, set a sane default
185 result.setdefault(pkg, DEFAULT_VERSION)
186 target_version_map[target] = result
187 return result
188
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:
Mike Frysinger422faf42014-11-12 23:16:48 -0500209 assert category
210 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100211 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
David James66a09c42012-11-05 13:31:38 -0800219def GetInstalledPackageVersions(atom):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100220 """Extracts the list of current versions of a target, package pair.
221
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500222 Args:
223 atom: The atom to operate on (e.g. sys-devel/gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100224
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500225 Returns:
226 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100227 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100228 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500229 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800230 for pkg in portage.db['/']['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100231 version = portage.versions.cpv_getversion(pkg)
232 versions.append(version)
233 return versions
234
235
David James90239b92012-11-05 15:31:34 -0800236def GetStablePackageVersion(atom, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100237 """Extracts the current stable version for a given package.
238
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500239 Args:
240 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
241 installed: Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100242
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500243 Returns:
244 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100245 """
David James90239b92012-11-05 15:31:34 -0800246 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500247 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800248 cpv = portage.best(portage.db['/'][pkgtype].dbapi.match(atom, use_cache=0))
249 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100250
251
Zdenek Behan699ddd32012-04-13 07:14:08 +0200252def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100253 """Resolves keywords in a given version list for a particular package.
254
255 Resolving means replacing PACKAGE_STABLE with the actual number.
256
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500257 Args:
258 target: The target to operate on (e.g. i686-pc-linux-gnu)
259 package: The target/package to operate on (e.g. gcc)
260 versions: List of versions to resolve
261 installed: Query installed packages
Zdenek Behan508dcce2011-12-05 15:39:32 +0100262
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500263 Returns:
264 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100265 """
266 resolved = []
David James90239b92012-11-05 15:31:34 -0800267 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100268 for version in versions:
269 if version == PACKAGE_STABLE:
David James90239b92012-11-05 15:31:34 -0800270 resolved.append(GetStablePackageVersion(atom, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100271 elif version != PACKAGE_NONE:
272 resolved.append(version)
273 return resolved
274
275
276def GetDesiredPackageVersions(target, package):
277 """Produces the list of desired versions for each target, package pair.
278
279 The first version in the list is implicitly treated as primary, ie.
280 the version that will be initialized by crossdev and selected.
281
282 If the version is PACKAGE_STABLE, it really means the current version which
283 is emerged by using the package atom with no particular version key.
284 Since crossdev unmasks all packages by default, this will actually
285 mean 'unstable' in most cases.
286
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500287 Args:
288 target: The target to operate on (e.g. i686-pc-linux-gnu)
289 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100290
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500291 Returns:
292 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100293 """
294 packagemap = GetPackageMap(target)
295
296 versions = []
297 if package in packagemap:
298 versions.append(packagemap[package])
299
300 return versions
301
302
303def TargetIsInitialized(target):
304 """Verifies if the given list of targets has been correctly initialized.
305
306 This determines whether we have to call crossdev while emerging
307 toolchain packages or can do it using emerge. Emerge is naturally
308 preferred, because all packages can be updated in a single pass.
309
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500310 Args:
311 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100312
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500313 Returns:
314 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100315 """
316 # Check if packages for the given target all have a proper version.
317 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100318 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800319 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100320 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800321 if not IsPackageDisabled(target, package) and not (
322 GetStablePackageVersion(atom, True) and
323 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100324 return False
325 return True
326 except cros_build_lib.RunCommandError:
327 # Fails - The target has likely never been initialized before.
328 return False
329
330
331def RemovePackageMask(target):
332 """Removes a package.mask file for the given platform.
333
334 The pre-existing package.mask files can mess with the keywords.
335
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500336 Args:
337 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100338 """
339 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700340 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341
342
Zdenek Behan508dcce2011-12-05 15:39:32 +0100343# Main functions performing the actual update steps.
Mike Frysingerc880a962013-11-08 13:59:06 -0500344def RebuildLibtool():
345 """Rebuild libtool as needed
346
347 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
348 gcc, libtool will break. We can't use binary packages either as those will
349 most likely be compiled against the previous version of gcc.
350 """
351 needs_update = False
352 with open('/usr/bin/libtool') as f:
353 for line in f:
354 # Look for a line like:
355 # sys_lib_search_path_spec="..."
356 # It'll be a list of paths and gcc will be one of them.
357 if line.startswith('sys_lib_search_path_spec='):
358 line = line.rstrip()
359 for path in line.split('=', 1)[1].strip('"').split():
360 if not os.path.exists(path):
Mike Frysinger383367e2014-09-16 15:06:17 -0400361 print('Rebuilding libtool after gcc upgrade')
362 print(' %s' % line)
363 print(' missing path: %s' % path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500364 needs_update = True
365 break
366
367 if needs_update:
368 break
369
370 if needs_update:
371 cmd = [EMERGE_CMD, '--oneshot', 'sys-devel/libtool']
372 cros_build_lib.RunCommand(cmd)
373
374
Zdenek Behan508dcce2011-12-05 15:39:32 +0100375def UpdateTargets(targets, usepkg):
376 """Determines which packages need update/unmerge and defers to portage.
377
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500378 Args:
379 targets: The list of targets to update
380 usepkg: Copies the commandline option
Zdenek Behan508dcce2011-12-05 15:39:32 +0100381 """
David James90239b92012-11-05 15:31:34 -0800382 # Remove keyword files created by old versions of cros_setup_toolchains.
383 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100384
385 # For each target, we do two things. Figure out the list of updates,
386 # and figure out the appropriate keywords/masks. Crossdev will initialize
387 # these, but they need to be regenerated on every update.
Mike Frysinger383367e2014-09-16 15:06:17 -0400388 print('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800389 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100390 for target in targets:
391 # Record the highest needed version for each target, for masking purposes.
392 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100393 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100394 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100395 if IsPackageDisabled(target, package):
396 continue
397 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800398 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100399 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200400 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100401 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100402
Zdenek Behan508dcce2011-12-05 15:39:32 +0100403 packages = []
404 for pkg in mergemap:
405 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200406 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800407 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100408
409 if not packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400410 print('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800411 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100412
Mike Frysinger383367e2014-09-16 15:06:17 -0400413 print('Updating packages:')
414 print(packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100416 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100417 if usepkg:
418 cmd.extend(['--getbinpkg', '--usepkgonly'])
419
420 cmd.extend(packages)
421 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800422 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100423
424
425def CleanTargets(targets):
426 """Unmerges old packages that are assumed unnecessary."""
427 unmergemap = {}
428 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100429 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100430 if IsPackageDisabled(target, package):
431 continue
432 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800433 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100434 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200435 desired_num = VersionListToNumeric(target, package, desired, True)
436 if not set(desired_num).issubset(current):
Mike Frysinger383367e2014-09-16 15:06:17 -0400437 print('Some packages have been held back, skipping clean!')
Zdenek Behan699ddd32012-04-13 07:14:08 +0200438 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439 unmergemap[pkg] = set(current).difference(desired_num)
440
441 # Cleaning doesn't care about consistency and rebuilding package.* files.
442 packages = []
443 for pkg, vers in unmergemap.iteritems():
444 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
445
446 if packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400447 print('Cleaning packages:')
448 print(packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100449 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100450 cmd.extend(packages)
451 cros_build_lib.RunCommand(cmd)
452 else:
Mike Frysinger383367e2014-09-16 15:06:17 -0400453 print('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100454
455
456def SelectActiveToolchains(targets, suffixes):
457 """Runs gcc-config and binutils-config to select the desired.
458
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500459 Args:
460 targets: The targets to select
461 suffixes: Optional target-specific hacks
Zdenek Behan508dcce2011-12-05 15:39:32 +0100462 """
463 for package in ['gcc', 'binutils']:
464 for target in targets:
465 # Pick the first version in the numbered list as the selected one.
466 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200467 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100468 desired = desired_num[0]
469 # *-config does not play revisions, strip them, keep just PV.
470 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
471
472 if target == 'host':
473 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800474 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100475
476 # And finally, attach target to it.
477 desired = '%s-%s' % (target, desired)
478
479 # Target specific hacks
480 if package in suffixes:
481 if target in suffixes[package]:
482 desired += suffixes[package][target]
483
David James7ec5efc2012-11-06 09:39:49 -0800484 extra_env = {'CHOST': target}
485 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500486 result = cros_build_lib.RunCommand(
487 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
488 current = result.output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100489 # Do not gcc-config when the current is live or nothing needs to be done.
490 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500491 cmd = [package + '-config', desired]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100492 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100493
494
Mike Frysinger35247af2012-11-16 18:58:06 -0500495def ExpandTargets(targets_wanted):
496 """Expand any possible toolchain aliases into full targets
497
498 This will expand 'all' and 'sdk' into the respective toolchain tuples.
499
500 Args:
501 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500502
Mike Frysinger35247af2012-11-16 18:58:06 -0500503 Returns:
504 Full list of tuples with pseudo targets removed.
505 """
David James27ac4ae2012-12-03 23:16:15 -0800506 alltargets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500507 targets_wanted = set(targets_wanted)
508 if targets_wanted == set(['all']):
509 targets = alltargets
510 elif targets_wanted == set(['sdk']):
511 # Filter out all the non-sdk toolchains as we don't want to mess
512 # with those in all of our builds.
David James27ac4ae2012-12-03 23:16:15 -0800513 targets = toolchain.FilterToolchains(alltargets, 'sdk', True)
Mike Frysingerda838af2014-11-05 12:36:48 -0500514 elif targets_wanted == set(['boards']):
515 # Only pull targets from the boards.
516 targets = {}
Mike Frysinger35247af2012-11-16 18:58:06 -0500517 else:
518 # Verify user input.
519 nonexistent = targets_wanted.difference(alltargets)
520 if nonexistent:
521 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
522 targets = dict((t, alltargets[t]) for t in targets_wanted)
523 return targets
524
525
David Jamesf8c672f2012-11-06 13:38:11 -0800526def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
527 targets_wanted, boards_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100528 """Performs all steps to create a synchronized toolchain enviroment.
529
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500530 Args:
531 usepkg: Use prebuilt packages
532 deleteold: Unmerge deprecated packages
533 hostonly: Only setup the host toolchain
534 reconfig: Reload crossdev config and reselect toolchains
535 targets_wanted: All the targets to update
536 boards_wanted: Load targets from these boards
Zdenek Behan508dcce2011-12-05 15:39:32 +0100537 """
David Jamesf8c672f2012-11-06 13:38:11 -0800538 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100539 if not hostonly:
540 # For hostonly, we can skip most of the below logic, much of which won't
541 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500542 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400543
Mike Frysinger7ccee992012-06-01 21:27:59 -0400544 # Now re-add any targets that might be from this board. This is
545 # to allow unofficial boards to declare their own toolchains.
546 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800547 targets.update(toolchain.GetToolchainsForBoard(board))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100548
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100549 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400550 for target in targets:
551 if TargetIsInitialized(target):
552 reconfig_targets[target] = targets[target]
553 else:
554 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100555 if crossdev_targets:
Mike Frysinger383367e2014-09-16 15:06:17 -0400556 print('The following targets need to be re-initialized:')
557 print(crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800558 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200559 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800560 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100562 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400563 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100564
565 # Now update all packages.
David Jamesf8c672f2012-11-06 13:38:11 -0800566 if UpdateTargets(targets, usepkg) or crossdev_targets or reconfig:
567 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
David James7ec5efc2012-11-06 09:39:49 -0800568
569 if deleteold:
570 CleanTargets(targets)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100571
Mike Frysingerc880a962013-11-08 13:59:06 -0500572 # Now that we've cleared out old versions, see if we need to rebuild
573 # anything. Can't do this earlier as it might not be broken.
574 RebuildLibtool()
575
Zdenek Behan508dcce2011-12-05 15:39:32 +0100576
Mike Frysinger35247af2012-11-16 18:58:06 -0500577def ShowBoardConfig(board):
578 """Show the toolchain tuples used by |board|
579
580 Args:
581 board: The board to query.
582 """
David James27ac4ae2012-12-03 23:16:15 -0800583 toolchains = toolchain.GetToolchainsForBoard(board)
Mike Frysinger35247af2012-11-16 18:58:06 -0500584 # Make sure we display the default toolchain first.
Mike Frysinger383367e2014-09-16 15:06:17 -0400585 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800586 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400587 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500588
589
Mike Frysinger35247af2012-11-16 18:58:06 -0500590def GeneratePathWrapper(root, wrappath, path):
591 """Generate a shell script to execute another shell script
592
593 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
594 argv[0] won't be pointing to the correct path, generate a shell script that
595 just executes another program with its full path.
596
597 Args:
598 root: The root tree to generate scripts inside of
599 wrappath: The full path (inside |root|) to create the wrapper
600 path: The target program which this wrapper will execute
601 """
602 replacements = {
603 'path': path,
604 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
605 }
606 wrapper = """#!/bin/sh
607base=$(realpath "$0")
608basedir=${base%%/*}
609exec "${basedir}/%(relroot)s%(path)s" "$@"
610""" % replacements
611 root_wrapper = root + wrappath
612 if os.path.islink(root_wrapper):
613 os.unlink(root_wrapper)
614 else:
615 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
616 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400617 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500618
619
620def FileIsCrosSdkElf(elf):
621 """Determine if |elf| is an ELF that we execute in the cros_sdk
622
623 We don't need this to be perfect, just quick. It makes sure the ELF
624 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
625
626 Args:
627 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500628
Mike Frysinger35247af2012-11-16 18:58:06 -0500629 Returns:
630 True if we think |elf| is a native ELF
631 """
632 with open(elf) as f:
633 data = f.read(20)
634 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
635 return (data[0:4] == '\x7fELF' and
636 data[4] == '\x02' and
637 data[5] == '\x01' and
638 data[18] == '\x3e')
639
640
641def IsPathPackagable(ptype, path):
642 """Should the specified file be included in a toolchain package?
643
644 We only need to handle files as we'll create dirs as we need them.
645
646 Further, trim files that won't be useful:
647 - non-english translations (.mo) since it'd require env vars
648 - debug files since these are for the host compiler itself
649 - info/man pages as they're big, and docs are online, and the
650 native docs should work fine for the most part (`man gcc`)
651
652 Args:
653 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
654 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500655
Mike Frysinger35247af2012-11-16 18:58:06 -0500656 Returns:
657 True if we want to include this path in the package
658 """
659 return not (ptype in ('dir',) or
660 path.startswith('/usr/lib/debug/') or
661 os.path.splitext(path)[1] == '.mo' or
662 ('/man/' in path or '/info/' in path))
663
664
665def ReadlinkRoot(path, root):
666 """Like os.readlink(), but relative to a |root|
667
668 Args:
669 path: The symlink to read
670 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500671
Mike Frysinger35247af2012-11-16 18:58:06 -0500672 Returns:
673 A fully resolved symlink path
674 """
675 while os.path.islink(root + path):
676 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
677 return path
678
679
680def _GetFilesForTarget(target, root='/'):
681 """Locate all the files to package for |target|
682
683 This does not cover ELF dependencies.
684
685 Args:
686 target: The toolchain target name
687 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500688
Mike Frysinger35247af2012-11-16 18:58:06 -0500689 Returns:
690 A tuple of a set of all packable paths, and a set of all paths which
691 are also native ELFs
692 """
693 paths = set()
694 elfs = set()
695
696 # Find all the files owned by the packages for this target.
697 for pkg in GetTargetPackages(target):
698 # Ignore packages that are part of the target sysroot.
699 if pkg in ('kernel', 'libc'):
700 continue
701
702 atom = GetPortagePackage(target, pkg)
703 cat, pn = atom.split('/')
704 ver = GetInstalledPackageVersions(atom)[0]
705 cros_build_lib.Info('packaging %s-%s', atom, ver)
706
707 # pylint: disable=E1101
708 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
709 settings=portage.settings)
710 contents = dblink.getcontents()
711 for obj in contents:
712 ptype = contents[obj][0]
713 if not IsPathPackagable(ptype, obj):
714 continue
715
716 if ptype == 'obj':
717 # For native ELFs, we need to pull in their dependencies too.
718 if FileIsCrosSdkElf(obj):
719 elfs.add(obj)
720 paths.add(obj)
721
722 return paths, elfs
723
724
725def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500726 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500727 """Link in all packable files and their runtime dependencies
728
729 This also wraps up executable ELFs with helper scripts.
730
731 Args:
732 output_dir: The output directory to store files
733 paths: All the files to include
734 elfs: All the files which are ELFs (a subset of |paths|)
735 ldpaths: A dict of static ldpath information
736 path_rewrite_func: User callback to rewrite paths in output_dir
737 root: The root path to pull all packages/files from
738 """
739 # Link in all the files.
740 sym_paths = []
741 for path in paths:
742 new_path = path_rewrite_func(path)
743 dst = output_dir + new_path
744 osutils.SafeMakedirs(os.path.dirname(dst))
745
746 # Is this a symlink which we have to rewrite or wrap?
747 # Delay wrap check until after we have created all paths.
748 src = root + path
749 if os.path.islink(src):
750 tgt = os.readlink(src)
751 if os.path.sep in tgt:
752 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
753
754 # Rewrite absolute links to relative and then generate the symlink
755 # ourselves. All other symlinks can be hardlinked below.
756 if tgt[0] == '/':
757 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
758 os.symlink(tgt, dst)
759 continue
760
761 os.link(src, dst)
762
763 # Now see if any of the symlinks need to be wrapped.
764 for sym, tgt in sym_paths:
765 if tgt in elfs:
766 GeneratePathWrapper(output_dir, sym, tgt)
767
768 # Locate all the dependencies for all the ELFs. Stick them all in the
769 # top level "lib" dir to make the wrapper simpler. This exact path does
770 # not matter since we execute ldso directly, and we tell the ldso the
771 # exact path to search for its libraries.
772 libdir = os.path.join(output_dir, 'lib')
773 osutils.SafeMakedirs(libdir)
774 donelibs = set()
775 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400776 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500777 interp = e['interp']
778 if interp:
779 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400780 interp = os.path.join('/lib', os.path.basename(interp))
781 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
782 libpaths=e['rpath'] + e['runpath'])
Mike Frysinger35247af2012-11-16 18:58:06 -0500783
784 for lib, lib_data in e['libs'].iteritems():
785 if lib in donelibs:
786 continue
787
788 src = path = lib_data['path']
789 if path is None:
790 cros_build_lib.Warning('%s: could not locate %s', elf, lib)
791 continue
792 donelibs.add(lib)
793
794 # Needed libs are the SONAME, but that is usually a symlink, not a
795 # real file. So link in the target rather than the symlink itself.
796 # We have to walk all the possible symlinks (SONAME could point to a
797 # symlink which points to a symlink), and we have to handle absolute
798 # ourselves (since we have a "root" argument).
799 dst = os.path.join(libdir, os.path.basename(path))
800 src = ReadlinkRoot(src, root)
801
802 os.link(root + src, dst)
803
804
805def _EnvdGetVar(envd, var):
806 """Given a Gentoo env.d file, extract a var from it
807
808 Args:
809 envd: The env.d file to load (may be a glob path)
810 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -0500811
Mike Frysinger35247af2012-11-16 18:58:06 -0500812 Returns:
813 The value of |var|
814 """
815 envds = glob.glob(envd)
816 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
817 envd = envds[0]
818 return cros_build_lib.LoadKeyValueFile(envd)[var]
819
820
821def _ProcessBinutilsConfig(target, output_dir):
822 """Do what binutils-config would have done"""
823 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500824
825 # Locate the bin dir holding the gold linker.
826 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
827 target, 'binutils-bin')
828 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -0500829 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500830 if not srcpath:
831 # Maybe this target doesn't support gold.
832 globpath = os.path.join(binutils_bin_path, '*')
833 srcpath = glob.glob(globpath)
834 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
835 % globpath)
836 srcpath = srcpath[0]
837 ld_path = os.path.join(srcpath, 'ld')
838 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
839 ld_path = os.path.join(srcpath, 'ld.bfd')
840 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
841 ld_path = os.path.join(srcpath, 'ld.gold')
842 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
843 % ld_path)
844
845 # Nope, no gold support to be found.
846 gold_supported = False
847 cros_build_lib.Warning('%s: binutils lacks support for the gold linker',
848 target)
849 else:
850 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
851 gold_supported = True
Mike Frysinger78b7a812014-11-26 19:45:23 -0500852 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500853
Mike Frysinger78b7a812014-11-26 19:45:23 -0500854 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -0500855 gccpath = os.path.join('/usr', 'libexec', 'gcc')
856 for prog in os.listdir(output_dir + srcpath):
857 # Skip binaries already wrapped.
858 if not prog.endswith('.real'):
859 GeneratePathWrapper(output_dir, binpath + prog,
860 os.path.join(srcpath, prog))
861 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
862 os.path.join(srcpath, prog))
863
David James27ac4ae2012-12-03 23:16:15 -0800864 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500865 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
866 if gold_supported:
867 envd += '-gold'
Mike Frysinger35247af2012-11-16 18:58:06 -0500868 srcpath = _EnvdGetVar(envd, 'LIBPATH')
869 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
870 output_dir + libpath)
871
872
873def _ProcessGccConfig(target, output_dir):
874 """Do what gcc-config would have done"""
875 binpath = '/bin'
876 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
877 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
878 for prog in os.listdir(output_dir + srcpath):
879 # Skip binaries already wrapped.
880 if (not prog.endswith('.real') and
881 not prog.endswith('.elf') and
882 prog.startswith(target)):
883 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
884 os.path.join(srcpath, prog))
885 return srcpath
886
887
888def _ProcessSysrootWrapper(_target, output_dir, srcpath):
889 """Remove chroot-specific things from our sysroot wrapper"""
890 # Disable ccache since we know it won't work outside of chroot.
891 sysroot_wrapper = glob.glob(os.path.join(
892 output_dir + srcpath, 'sysroot_wrapper*'))[0]
893 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
894 for num in xrange(len(contents)):
895 if '@CCACHE_DEFAULT@' in contents[num]:
896 contents[num] = 'use_ccache = False'
897 break
898 # Can't update the wrapper in place since it's a hardlink to a file in /.
899 os.unlink(sysroot_wrapper)
900 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
Mike Frysinger60ec1012013-10-21 00:11:10 -0400901 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500902
903
904def _ProcessDistroCleanups(target, output_dir):
905 """Clean up the tree and remove all distro-specific requirements
906
907 Args:
908 target: The toolchain target name
909 output_dir: The output directory to clean up
910 """
911 _ProcessBinutilsConfig(target, output_dir)
912 gcc_path = _ProcessGccConfig(target, output_dir)
913 _ProcessSysrootWrapper(target, output_dir, gcc_path)
914
915 osutils.RmDir(os.path.join(output_dir, 'etc'))
916
917
918def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
919 """Setup a tree from the packages for the specified target
920
921 This populates a path with all the files from toolchain packages so that
922 a tarball can easily be generated from the result.
923
924 Args:
925 target: The target to create a packagable root from
926 output_dir: The output directory to place all the files
927 ldpaths: A dict of static ldpath information
928 root: The root path to pull all packages/files from
929 """
930 # Find all the files owned by the packages for this target.
931 paths, elfs = _GetFilesForTarget(target, root=root)
932
933 # Link in all the package's files, any ELF dependencies, and wrap any
934 # executable ELFs with helper scripts.
935 def MoveUsrBinToBin(path):
936 """Move /usr/bin to /bin so people can just use that toplevel dir"""
937 return path[4:] if path.startswith('/usr/bin/') else path
938 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
939 path_rewrite_func=MoveUsrBinToBin, root=root)
940
941 # The packages, when part of the normal distro, have helper scripts
942 # that setup paths and such. Since we are making this standalone, we
943 # need to preprocess all that ourselves.
944 _ProcessDistroCleanups(target, output_dir)
945
946
947def CreatePackages(targets_wanted, output_dir, root='/'):
948 """Create redistributable cross-compiler packages for the specified targets
949
950 This creates toolchain packages that should be usable in conjunction with
951 a downloaded sysroot (created elsewhere).
952
953 Tarballs (one per target) will be created in $PWD.
954
955 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700956 targets_wanted: The targets to package up.
957 output_dir: The directory to put the packages in.
958 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -0500959 """
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500960 cros_build_lib.Info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -0500961 osutils.SafeMakedirs(output_dir)
962 ldpaths = lddtree.LoadLdpaths(root)
963 targets = ExpandTargets(targets_wanted)
964
David James4bc13702013-03-26 08:08:04 -0700965 with osutils.TempDir() as tempdir:
Mike Frysinger35247af2012-11-16 18:58:06 -0500966 # We have to split the root generation from the compression stages. This is
967 # because we hardlink in all the files (to avoid overhead of reading/writing
968 # the copies multiple times). But tar gets angry if a file's hardlink count
969 # changes from when it starts reading a file to when it finishes.
970 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
971 for target in targets:
972 output_target_dir = os.path.join(tempdir, target)
973 queue.put([target, output_target_dir, ldpaths, root])
974
975 # Build the tarball.
976 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
977 for target in targets:
978 tar_file = os.path.join(output_dir, target + '.tar.xz')
979 queue.put([tar_file, os.path.join(tempdir, target)])
980
981
Brian Harring30675052012-02-29 12:18:22 -0800982def main(argv):
Mike Frysinger0c808452014-11-06 17:30:23 -0500983 parser = commandline.ArgumentParser(description=__doc__)
984 parser.add_argument('-u', '--nousepkg',
985 action='store_false', dest='usepkg', default=True,
986 help='Use prebuilt packages if possible')
987 parser.add_argument('-d', '--deleteold',
988 action='store_true', dest='deleteold', default=False,
989 help='Unmerge deprecated packages')
990 parser.add_argument('-t', '--targets',
991 dest='targets', default='sdk',
992 help='Comma separated list of tuples. '
993 'Special keyword \'host\' is allowed. Default: sdk')
994 parser.add_argument('--include-boards',
995 dest='include_boards', default='',
996 help='Comma separated list of boards whose toolchains we'
997 ' will always include. Default: none')
998 parser.add_argument('--hostonly',
999 dest='hostonly', default=False, action='store_true',
1000 help='Only setup the host toolchain. '
1001 'Useful for bootstrapping chroot')
1002 parser.add_argument('--show-board-cfg',
1003 dest='board_cfg', default=None,
1004 help='Board to list toolchain tuples for')
1005 parser.add_argument('--create-packages',
1006 action='store_true', default=False,
1007 help='Build redistributable packages')
1008 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1009 help='Output directory')
1010 parser.add_argument('--reconfig', default=False, action='store_true',
1011 help='Reload crossdev config and reselect toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001012
Mike Frysinger0c808452014-11-06 17:30:23 -05001013 options = parser.parse_args(argv)
1014 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001015
Mike Frysinger35247af2012-11-16 18:58:06 -05001016 # Figure out what we're supposed to do and reject conflicting options.
1017 if options.board_cfg and options.create_packages:
1018 parser.error('conflicting options: create-packages & show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001019
Zdenek Behan508dcce2011-12-05 15:39:32 +01001020 targets = set(options.targets.split(','))
Mike Frysinger0c808452014-11-06 17:30:23 -05001021 boards = (set(options.include_boards.split(',')) if options.include_boards
1022 else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001023
1024 if options.board_cfg:
1025 ShowBoardConfig(options.board_cfg)
1026 elif options.create_packages:
1027 cros_build_lib.AssertInsideChroot()
1028 Crossdev.Load(False)
1029 CreatePackages(targets, options.output_dir)
1030 else:
1031 cros_build_lib.AssertInsideChroot()
1032 # This has to be always run as root.
1033 if os.geteuid() != 0:
1034 cros_build_lib.Die('this script must be run as root')
1035
1036 Crossdev.Load(options.reconfig)
1037 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
1038 options.reconfig, targets, boards)
1039 Crossdev.Save()
1040
1041 return 0