blob: c08186aeb60511f66bf84e476136b6fae68bb139 [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
Ralph Nathan03047282015-03-23 11:09:32 -070017from chromite.lib import cros_logging as logging
Brian Harringaf019fb2012-05-10 15:06:13 -070018from chromite.lib import osutils
Mike Frysinger35247af2012-11-16 18:58:06 -050019from chromite.lib import parallel
David James27ac4ae2012-12-03 23:16:15 -080020from chromite.lib import toolchain
Bertrand SIMONNET3c77bc92015-03-20 14:28:54 -070021from chromite.lib import workspace_lib
Mike Frysinger35247af2012-11-16 18:58:06 -050022
23# Needs to be after chromite imports.
24import lddtree
Zdenek Behan508dcce2011-12-05 15:39:32 +010025
Mike Frysinger31596002012-12-03 23:54:24 -050026if cros_build_lib.IsInsideChroot():
27 # Only import portage after we've checked that we're inside the chroot.
28 # Outside may not have portage, in which case the above may not happen.
29 # We'll check in main() if the operation needs portage.
Don Garrett25f309a2014-03-19 14:02:12 -070030 # pylint: disable=F0401
Mike Frysinger31596002012-12-03 23:54:24 -050031 import portage
Zdenek Behan508dcce2011-12-05 15:39:32 +010032
33
Matt Tennantf1e30972012-03-02 16:30:07 -080034EMERGE_CMD = os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')
Zdenek Behan508dcce2011-12-05 15:39:32 +010035PACKAGE_STABLE = '[stable]'
36PACKAGE_NONE = '[none]'
37SRC_ROOT = os.path.realpath(constants.SOURCE_ROOT)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010038
39CHROMIUMOS_OVERLAY = '/usr/local/portage/chromiumos'
40STABLE_OVERLAY = '/usr/local/portage/stable'
41CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010042
43
44# TODO: The versions are stored here very much like in setup_board.
45# The goal for future is to differentiate these using a config file.
46# This is done essentially by messing with GetDesiredPackageVersions()
47DEFAULT_VERSION = PACKAGE_STABLE
48DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010049}
50TARGET_VERSION_MAP = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050051 'host' : {
52 'gdb' : PACKAGE_NONE,
53 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010054}
55# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
56CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050057 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -040058 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -080059 'armv7a-cros-linux-gnueabi': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -050060 'i686-pc-linux-gnu' : '-gold',
61 'x86_64-cros-linux-gnu' : '-gold',
62 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010063}
Zdenek Behan508dcce2011-12-05 15:39:32 +010064# Global per-run cache that will be filled ondemand in by GetPackageMap()
65# function as needed.
66target_version_map = {
67}
68
69
David James66a09c42012-11-05 13:31:38 -080070class Crossdev(object):
71 """Class for interacting with crossdev and caching its output."""
72
73 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
74 _CACHE = {}
75
76 @classmethod
77 def Load(cls, reconfig):
78 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -080079 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
80 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -080081 if os.path.exists(cls._CACHE_FILE) and not reconfig:
82 with open(cls._CACHE_FILE) as f:
83 data = json.load(f)
David James90239b92012-11-05 15:31:34 -080084 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -080085 cls._CACHE = data
86
87 @classmethod
88 def Save(cls):
89 """Store crossdev cache on disk."""
90 # Save the cache from the successful run.
91 with open(cls._CACHE_FILE, 'w') as f:
92 json.dump(cls._CACHE, f)
93
94 @classmethod
95 def GetConfig(cls, target):
96 """Returns a map of crossdev provided variables about a tuple."""
97 CACHE_ATTR = '_target_tuple_map'
98
99 val = cls._CACHE.setdefault(CACHE_ATTR, {})
100 if not target in val:
101 # Find out the crossdev tuple.
102 target_tuple = target
103 if target == 'host':
David James27ac4ae2012-12-03 23:16:15 -0800104 target_tuple = toolchain.GetHostTuple()
David James66a09c42012-11-05 13:31:38 -0800105 # Catch output of crossdev.
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500106 out = cros_build_lib.RunCommand(
107 ['crossdev', '--show-target-cfg', '--ex-gdb', target_tuple],
108 print_cmd=False, redirect_stdout=True).output.splitlines()
David James66a09c42012-11-05 13:31:38 -0800109 # List of tuples split at the first '=', converted into dict.
110 val[target] = dict([x.split('=', 1) for x in out])
111 return val[target]
112
113 @classmethod
114 def UpdateTargets(cls, targets, usepkg, config_only=False):
115 """Calls crossdev to initialize a cross target.
116
117 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700118 targets: The list of targets to initialize using crossdev.
119 usepkg: Copies the commandline opts.
120 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800121 """
122 configured_targets = cls._CACHE.setdefault('configured_targets', [])
123
124 cmdbase = ['crossdev', '--show-fail-log']
125 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
126 # Pick stable by default, and override as necessary.
127 cmdbase.extend(['-P', '--oneshot'])
128 if usepkg:
129 cmdbase.extend(['-P', '--getbinpkg',
130 '-P', '--usepkgonly',
131 '--without-headers'])
132
133 overlays = '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)
134 cmdbase.extend(['--overlays', overlays])
135 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
136
137 for target in targets:
138 if config_only and target in configured_targets:
139 continue
140
141 cmd = cmdbase + ['-t', target]
142
143 for pkg in GetTargetPackages(target):
144 if pkg == 'gdb':
145 # Gdb does not have selectable versions.
146 cmd.append('--ex-gdb')
147 continue
148 # The first of the desired versions is the "primary" one.
149 version = GetDesiredPackageVersions(target, pkg)[0]
150 cmd.extend(['--%s' % pkg, version])
151
152 cmd.extend(targets[target]['crossdev'].split())
153 if config_only:
154 # In this case we want to just quietly reinit
155 cmd.append('--init-target')
156 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
157 else:
158 cros_build_lib.RunCommand(cmd)
159
160 configured_targets.append(target)
161
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100162
Zdenek Behan508dcce2011-12-05 15:39:32 +0100163def GetPackageMap(target):
164 """Compiles a package map for the given target from the constants.
165
166 Uses a cache in target_version_map, that is dynamically filled in as needed,
167 since here everything is static data and the structuring is for ease of
168 configurability only.
169
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500170 Args:
171 target: The target for which to return a version map
Zdenek Behan508dcce2011-12-05 15:39:32 +0100172
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500173 Returns:
174 A map between packages and desired versions in internal format
175 (using the PACKAGE_* constants)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100176 """
177 if target in target_version_map:
178 return target_version_map[target]
179
180 # Start from copy of the global defaults.
181 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
182
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100183 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100184 # prefer any specific overrides
185 if pkg in TARGET_VERSION_MAP.get(target, {}):
186 result[pkg] = TARGET_VERSION_MAP[target][pkg]
187 else:
188 # finally, if not already set, set a sane default
189 result.setdefault(pkg, DEFAULT_VERSION)
190 target_version_map[target] = result
191 return result
192
193
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100194def GetTargetPackages(target):
195 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800196 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100197 # Undesired packages are denoted by empty ${pkg}_pn variable.
198 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
199
200
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100201# Portage helper functions:
202def GetPortagePackage(target, package):
203 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800204 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100205 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100206 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100207 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100208 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100209 category = conf['category']
210 # Portage package:
211 pn = conf[package + '_pn']
212 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500213 assert category
214 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100215 return '%s/%s' % (category, pn)
216
217
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100218def IsPackageDisabled(target, package):
219 """Returns if the given package is not used for the target."""
220 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
221
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100222
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700223def PortageTrees(root):
224 """Return the portage trees for a given root."""
225 if root == '/':
226 return portage.db['/']
227 # The portage logic requires the path always end in a slash.
228 root = root.rstrip('/') + '/'
229 return portage.create_trees(target_root=root, config_root=root)[root]
230
231
232def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100233 """Extracts the list of current versions of a target, package pair.
234
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500235 Args:
236 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700237 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100238
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500239 Returns:
240 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100241 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100242 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500243 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700244 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100245 version = portage.versions.cpv_getversion(pkg)
246 versions.append(version)
247 return versions
248
249
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700250def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100251 """Extracts the current stable version for a given package.
252
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500253 Args:
254 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
255 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700256 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100257
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500258 Returns:
259 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100260 """
David James90239b92012-11-05 15:31:34 -0800261 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500262 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700263 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800264 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100265
266
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700267def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100268 """Resolves keywords in a given version list for a particular package.
269
270 Resolving means replacing PACKAGE_STABLE with the actual number.
271
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500272 Args:
273 target: The target to operate on (e.g. i686-pc-linux-gnu)
274 package: The target/package to operate on (e.g. gcc)
275 versions: List of versions to resolve
276 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700277 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100278
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500279 Returns:
280 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100281 """
282 resolved = []
David James90239b92012-11-05 15:31:34 -0800283 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700284 if not installed:
285 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100286 for version in versions:
287 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700288 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100289 elif version != PACKAGE_NONE:
290 resolved.append(version)
291 return resolved
292
293
294def GetDesiredPackageVersions(target, package):
295 """Produces the list of desired versions for each target, package pair.
296
297 The first version in the list is implicitly treated as primary, ie.
298 the version that will be initialized by crossdev and selected.
299
300 If the version is PACKAGE_STABLE, it really means the current version which
301 is emerged by using the package atom with no particular version key.
302 Since crossdev unmasks all packages by default, this will actually
303 mean 'unstable' in most cases.
304
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500305 Args:
306 target: The target to operate on (e.g. i686-pc-linux-gnu)
307 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100308
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500309 Returns:
310 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100311 """
312 packagemap = GetPackageMap(target)
313
314 versions = []
315 if package in packagemap:
316 versions.append(packagemap[package])
317
318 return versions
319
320
321def TargetIsInitialized(target):
322 """Verifies if the given list of targets has been correctly initialized.
323
324 This determines whether we have to call crossdev while emerging
325 toolchain packages or can do it using emerge. Emerge is naturally
326 preferred, because all packages can be updated in a single pass.
327
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500328 Args:
329 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100330
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500331 Returns:
332 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100333 """
334 # Check if packages for the given target all have a proper version.
335 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100336 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800337 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100338 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800339 if not IsPackageDisabled(target, package) and not (
340 GetStablePackageVersion(atom, True) and
341 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100342 return False
343 return True
344 except cros_build_lib.RunCommandError:
345 # Fails - The target has likely never been initialized before.
346 return False
347
348
349def RemovePackageMask(target):
350 """Removes a package.mask file for the given platform.
351
352 The pre-existing package.mask files can mess with the keywords.
353
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500354 Args:
355 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100356 """
357 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700358 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100359
360
Zdenek Behan508dcce2011-12-05 15:39:32 +0100361# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700362def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500363 """Rebuild libtool as needed
364
365 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
366 gcc, libtool will break. We can't use binary packages either as those will
367 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700368
369 Args:
370 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500371 """
372 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700373 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500374 for line in f:
375 # Look for a line like:
376 # sys_lib_search_path_spec="..."
377 # It'll be a list of paths and gcc will be one of them.
378 if line.startswith('sys_lib_search_path_spec='):
379 line = line.rstrip()
380 for path in line.split('=', 1)[1].strip('"').split():
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700381 if not os.path.exists(os.path.join(root, path.lstrip(os.path.sep))):
Mike Frysinger383367e2014-09-16 15:06:17 -0400382 print('Rebuilding libtool after gcc upgrade')
383 print(' %s' % line)
384 print(' missing path: %s' % path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500385 needs_update = True
386 break
387
388 if needs_update:
389 break
390
391 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700392 cmd = [EMERGE_CMD, '--oneshot']
393 if root != '/':
394 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
395 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500396 cros_build_lib.RunCommand(cmd)
397
398
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700399def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100400 """Determines which packages need update/unmerge and defers to portage.
401
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500402 Args:
403 targets: The list of targets to update
404 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700405 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100406 """
David James90239b92012-11-05 15:31:34 -0800407 # Remove keyword files created by old versions of cros_setup_toolchains.
408 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100409
410 # For each target, we do two things. Figure out the list of updates,
411 # and figure out the appropriate keywords/masks. Crossdev will initialize
412 # these, but they need to be regenerated on every update.
Mike Frysinger383367e2014-09-16 15:06:17 -0400413 print('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800414 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415 for target in targets:
416 # Record the highest needed version for each target, for masking purposes.
417 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100418 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100419 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100420 if IsPackageDisabled(target, package):
421 continue
422 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700423 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100424 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200425 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100426 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100427
Zdenek Behan508dcce2011-12-05 15:39:32 +0100428 packages = []
429 for pkg in mergemap:
430 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200431 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800432 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433
434 if not packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400435 print('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800436 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437
Mike Frysinger383367e2014-09-16 15:06:17 -0400438 print('Updating packages:')
439 print(packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100440
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100441 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442 if usepkg:
443 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700444 if root != '/':
445 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100446
447 cmd.extend(packages)
448 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800449 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100450
451
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700452def CleanTargets(targets, root='/'):
453 """Unmerges old packages that are assumed unnecessary.
454
455 Args:
456 targets: The list of targets to clean up.
457 root: The install root in which we want packages cleaned up.
458 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100459 unmergemap = {}
460 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100461 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100462 if IsPackageDisabled(target, package):
463 continue
464 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700465 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100466 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700467 # NOTE: This refers to installed packages (vartree) rather than the
468 # Portage version (porttree and/or bintree) when determining the current
469 # version. While this isn't the most accurate thing to do, it is probably
470 # a good simple compromise, which should have the desired result of
471 # uninstalling everything but the latest installed version. In
472 # particular, using the bintree (--usebinpkg) requires a non-trivial
473 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200474 desired_num = VersionListToNumeric(target, package, desired, True)
475 if not set(desired_num).issubset(current):
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700476 print('Error detecting stable version for %s, skipping clean!' % pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200477 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100478 unmergemap[pkg] = set(current).difference(desired_num)
479
480 # Cleaning doesn't care about consistency and rebuilding package.* files.
481 packages = []
482 for pkg, vers in unmergemap.iteritems():
483 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
484
485 if packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400486 print('Cleaning packages:')
487 print(packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100488 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700489 if root != '/':
490 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100491 cmd.extend(packages)
492 cros_build_lib.RunCommand(cmd)
493 else:
Mike Frysinger383367e2014-09-16 15:06:17 -0400494 print('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100495
496
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700497def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100498 """Runs gcc-config and binutils-config to select the desired.
499
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500500 Args:
501 targets: The targets to select
502 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700503 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100504 """
505 for package in ['gcc', 'binutils']:
506 for target in targets:
507 # Pick the first version in the numbered list as the selected one.
508 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700509 desired_num = VersionListToNumeric(target, package, desired, True,
510 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100511 desired = desired_num[0]
512 # *-config does not play revisions, strip them, keep just PV.
513 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
514
515 if target == 'host':
516 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800517 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100518
519 # And finally, attach target to it.
520 desired = '%s-%s' % (target, desired)
521
522 # Target specific hacks
523 if package in suffixes:
524 if target in suffixes[package]:
525 desired += suffixes[package][target]
526
David James7ec5efc2012-11-06 09:39:49 -0800527 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700528 if root != '/':
529 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800530 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500531 result = cros_build_lib.RunCommand(
532 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
533 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700534
535 # Do not reconfig when the current is live or nothing needs to be done.
536 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100537 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500538 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700539 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100540
541
Mike Frysinger35247af2012-11-16 18:58:06 -0500542def ExpandTargets(targets_wanted):
543 """Expand any possible toolchain aliases into full targets
544
545 This will expand 'all' and 'sdk' into the respective toolchain tuples.
546
547 Args:
548 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500549
Mike Frysinger35247af2012-11-16 18:58:06 -0500550 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300551 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500552 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500553 targets_wanted = set(targets_wanted)
Gilad Arnold8195b532015-04-07 10:56:30 +0300554 if targets_wanted in (set(['boards']), set(['bricks'])):
555 # Only pull targets from the included boards/bricks.
556 return {}
557
558 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500559 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300560 return all_targets
561 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500562 # Filter out all the non-sdk toolchains as we don't want to mess
563 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300564 return toolchain.FilterToolchains(all_targets, 'sdk', True)
565
566 # Verify user input.
567 nonexistent = targets_wanted.difference(all_targets)
568 if nonexistent:
569 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
570 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500571
572
David Jamesf8c672f2012-11-06 13:38:11 -0800573def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700574 targets_wanted, boards_wanted, bricks_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100575 """Performs all steps to create a synchronized toolchain enviroment.
576
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500577 Args:
578 usepkg: Use prebuilt packages
579 deleteold: Unmerge deprecated packages
580 hostonly: Only setup the host toolchain
581 reconfig: Reload crossdev config and reselect toolchains
582 targets_wanted: All the targets to update
583 boards_wanted: Load targets from these boards
Gilad Arnold8195b532015-04-07 10:56:30 +0300584 bricks_wanted: Load targets from these bricks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700585 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100586 """
David Jamesf8c672f2012-11-06 13:38:11 -0800587 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100588 if not hostonly:
589 # For hostonly, we can skip most of the below logic, much of which won't
590 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500591 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400592
Gilad Arnold8195b532015-04-07 10:56:30 +0300593 # Now re-add any targets that might be from this board/brick. This is to
594 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400595 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800596 targets.update(toolchain.GetToolchainsForBoard(board))
Gilad Arnold8195b532015-04-07 10:56:30 +0300597 for brick in bricks_wanted:
598 targets.update(toolchain.GetToolchainsForBrick(brick))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100599
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100600 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400601 for target in targets:
602 if TargetIsInitialized(target):
603 reconfig_targets[target] = targets[target]
604 else:
605 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100606 if crossdev_targets:
Mike Frysinger383367e2014-09-16 15:06:17 -0400607 print('The following targets need to be re-initialized:')
608 print(crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800609 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200610 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800611 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100612
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100613 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400614 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100615
616 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700617 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
618 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800619
620 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700621 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100622
Mike Frysingerc880a962013-11-08 13:59:06 -0500623 # Now that we've cleared out old versions, see if we need to rebuild
624 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700625 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500626
Zdenek Behan508dcce2011-12-05 15:39:32 +0100627
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700628def ShowConfig(name):
629 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500630
631 Args:
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700632 name: The board name or brick locator to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500633 """
Bertrand SIMONNET3c77bc92015-03-20 14:28:54 -0700634 if workspace_lib.IsLocator(name):
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700635 toolchains = toolchain.GetToolchainsForBrick(name)
636 else:
637 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500638 # Make sure we display the default toolchain first.
Mike Frysinger383367e2014-09-16 15:06:17 -0400639 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800640 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400641 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500642
643
Mike Frysinger35247af2012-11-16 18:58:06 -0500644def GeneratePathWrapper(root, wrappath, path):
645 """Generate a shell script to execute another shell script
646
647 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
648 argv[0] won't be pointing to the correct path, generate a shell script that
649 just executes another program with its full path.
650
651 Args:
652 root: The root tree to generate scripts inside of
653 wrappath: The full path (inside |root|) to create the wrapper
654 path: The target program which this wrapper will execute
655 """
656 replacements = {
657 'path': path,
658 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
659 }
660 wrapper = """#!/bin/sh
661base=$(realpath "$0")
662basedir=${base%%/*}
663exec "${basedir}/%(relroot)s%(path)s" "$@"
664""" % replacements
665 root_wrapper = root + wrappath
666 if os.path.islink(root_wrapper):
667 os.unlink(root_wrapper)
668 else:
669 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
670 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400671 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500672
673
674def FileIsCrosSdkElf(elf):
675 """Determine if |elf| is an ELF that we execute in the cros_sdk
676
677 We don't need this to be perfect, just quick. It makes sure the ELF
678 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
679
680 Args:
681 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500682
Mike Frysinger35247af2012-11-16 18:58:06 -0500683 Returns:
684 True if we think |elf| is a native ELF
685 """
686 with open(elf) as f:
687 data = f.read(20)
688 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
689 return (data[0:4] == '\x7fELF' and
690 data[4] == '\x02' and
691 data[5] == '\x01' and
692 data[18] == '\x3e')
693
694
695def IsPathPackagable(ptype, path):
696 """Should the specified file be included in a toolchain package?
697
698 We only need to handle files as we'll create dirs as we need them.
699
700 Further, trim files that won't be useful:
701 - non-english translations (.mo) since it'd require env vars
702 - debug files since these are for the host compiler itself
703 - info/man pages as they're big, and docs are online, and the
704 native docs should work fine for the most part (`man gcc`)
705
706 Args:
707 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
708 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500709
Mike Frysinger35247af2012-11-16 18:58:06 -0500710 Returns:
711 True if we want to include this path in the package
712 """
713 return not (ptype in ('dir',) or
714 path.startswith('/usr/lib/debug/') or
715 os.path.splitext(path)[1] == '.mo' or
716 ('/man/' in path or '/info/' in path))
717
718
719def ReadlinkRoot(path, root):
720 """Like os.readlink(), but relative to a |root|
721
722 Args:
723 path: The symlink to read
724 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500725
Mike Frysinger35247af2012-11-16 18:58:06 -0500726 Returns:
727 A fully resolved symlink path
728 """
729 while os.path.islink(root + path):
730 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
731 return path
732
733
734def _GetFilesForTarget(target, root='/'):
735 """Locate all the files to package for |target|
736
737 This does not cover ELF dependencies.
738
739 Args:
740 target: The toolchain target name
741 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500742
Mike Frysinger35247af2012-11-16 18:58:06 -0500743 Returns:
744 A tuple of a set of all packable paths, and a set of all paths which
745 are also native ELFs
746 """
747 paths = set()
748 elfs = set()
749
750 # Find all the files owned by the packages for this target.
751 for pkg in GetTargetPackages(target):
752 # Ignore packages that are part of the target sysroot.
753 if pkg in ('kernel', 'libc'):
754 continue
755
756 atom = GetPortagePackage(target, pkg)
757 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700758 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700759 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500760
761 # pylint: disable=E1101
762 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
763 settings=portage.settings)
764 contents = dblink.getcontents()
765 for obj in contents:
766 ptype = contents[obj][0]
767 if not IsPathPackagable(ptype, obj):
768 continue
769
770 if ptype == 'obj':
771 # For native ELFs, we need to pull in their dependencies too.
772 if FileIsCrosSdkElf(obj):
773 elfs.add(obj)
774 paths.add(obj)
775
776 return paths, elfs
777
778
779def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500780 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500781 """Link in all packable files and their runtime dependencies
782
783 This also wraps up executable ELFs with helper scripts.
784
785 Args:
786 output_dir: The output directory to store files
787 paths: All the files to include
788 elfs: All the files which are ELFs (a subset of |paths|)
789 ldpaths: A dict of static ldpath information
790 path_rewrite_func: User callback to rewrite paths in output_dir
791 root: The root path to pull all packages/files from
792 """
793 # Link in all the files.
794 sym_paths = []
795 for path in paths:
796 new_path = path_rewrite_func(path)
797 dst = output_dir + new_path
798 osutils.SafeMakedirs(os.path.dirname(dst))
799
800 # Is this a symlink which we have to rewrite or wrap?
801 # Delay wrap check until after we have created all paths.
802 src = root + path
803 if os.path.islink(src):
804 tgt = os.readlink(src)
805 if os.path.sep in tgt:
806 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
807
808 # Rewrite absolute links to relative and then generate the symlink
809 # ourselves. All other symlinks can be hardlinked below.
810 if tgt[0] == '/':
811 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
812 os.symlink(tgt, dst)
813 continue
814
815 os.link(src, dst)
816
817 # Now see if any of the symlinks need to be wrapped.
818 for sym, tgt in sym_paths:
819 if tgt in elfs:
820 GeneratePathWrapper(output_dir, sym, tgt)
821
822 # Locate all the dependencies for all the ELFs. Stick them all in the
823 # top level "lib" dir to make the wrapper simpler. This exact path does
824 # not matter since we execute ldso directly, and we tell the ldso the
825 # exact path to search for its libraries.
826 libdir = os.path.join(output_dir, 'lib')
827 osutils.SafeMakedirs(libdir)
828 donelibs = set()
829 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400830 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500831 interp = e['interp']
832 if interp:
833 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400834 interp = os.path.join('/lib', os.path.basename(interp))
835 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
836 libpaths=e['rpath'] + e['runpath'])
Mike Frysinger35247af2012-11-16 18:58:06 -0500837
838 for lib, lib_data in e['libs'].iteritems():
839 if lib in donelibs:
840 continue
841
842 src = path = lib_data['path']
843 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -0700844 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -0500845 continue
846 donelibs.add(lib)
847
848 # Needed libs are the SONAME, but that is usually a symlink, not a
849 # real file. So link in the target rather than the symlink itself.
850 # We have to walk all the possible symlinks (SONAME could point to a
851 # symlink which points to a symlink), and we have to handle absolute
852 # ourselves (since we have a "root" argument).
853 dst = os.path.join(libdir, os.path.basename(path))
854 src = ReadlinkRoot(src, root)
855
856 os.link(root + src, dst)
857
858
859def _EnvdGetVar(envd, var):
860 """Given a Gentoo env.d file, extract a var from it
861
862 Args:
863 envd: The env.d file to load (may be a glob path)
864 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -0500865
Mike Frysinger35247af2012-11-16 18:58:06 -0500866 Returns:
867 The value of |var|
868 """
869 envds = glob.glob(envd)
870 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
871 envd = envds[0]
872 return cros_build_lib.LoadKeyValueFile(envd)[var]
873
874
875def _ProcessBinutilsConfig(target, output_dir):
876 """Do what binutils-config would have done"""
877 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500878
879 # Locate the bin dir holding the gold linker.
880 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
881 target, 'binutils-bin')
882 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -0500883 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500884 if not srcpath:
885 # Maybe this target doesn't support gold.
886 globpath = os.path.join(binutils_bin_path, '*')
887 srcpath = glob.glob(globpath)
888 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
889 % globpath)
890 srcpath = srcpath[0]
891 ld_path = os.path.join(srcpath, 'ld')
892 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
893 ld_path = os.path.join(srcpath, 'ld.bfd')
894 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
895 ld_path = os.path.join(srcpath, 'ld.gold')
896 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
897 % ld_path)
898
899 # Nope, no gold support to be found.
900 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -0700901 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500902 else:
903 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
904 gold_supported = True
Mike Frysinger78b7a812014-11-26 19:45:23 -0500905 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500906
Mike Frysinger78b7a812014-11-26 19:45:23 -0500907 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -0500908 gccpath = os.path.join('/usr', 'libexec', 'gcc')
909 for prog in os.listdir(output_dir + srcpath):
910 # Skip binaries already wrapped.
911 if not prog.endswith('.real'):
912 GeneratePathWrapper(output_dir, binpath + prog,
913 os.path.join(srcpath, prog))
914 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
915 os.path.join(srcpath, prog))
916
David James27ac4ae2012-12-03 23:16:15 -0800917 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500918 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
919 if gold_supported:
920 envd += '-gold'
Mike Frysinger35247af2012-11-16 18:58:06 -0500921 srcpath = _EnvdGetVar(envd, 'LIBPATH')
922 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
923 output_dir + libpath)
924
925
926def _ProcessGccConfig(target, output_dir):
927 """Do what gcc-config would have done"""
928 binpath = '/bin'
929 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
930 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
931 for prog in os.listdir(output_dir + srcpath):
932 # Skip binaries already wrapped.
933 if (not prog.endswith('.real') and
934 not prog.endswith('.elf') and
935 prog.startswith(target)):
936 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
937 os.path.join(srcpath, prog))
938 return srcpath
939
940
Frank Henigman179ec7c2015-02-06 03:01:09 -0500941def _ProcessSysrootWrappers(_target, output_dir, srcpath):
942 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -0500943 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -0500944 for sysroot_wrapper in glob.glob(os.path.join(
945 output_dir + srcpath, 'sysroot_wrapper*')):
946 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
947 for num in xrange(len(contents)):
948 if '@CCACHE_DEFAULT@' in contents[num]:
949 contents[num] = 'use_ccache = False'
950 break
951 # Can't update the wrapper in place since it's a hardlink to a file in /.
952 os.unlink(sysroot_wrapper)
953 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
954 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500955
956
957def _ProcessDistroCleanups(target, output_dir):
958 """Clean up the tree and remove all distro-specific requirements
959
960 Args:
961 target: The toolchain target name
962 output_dir: The output directory to clean up
963 """
964 _ProcessBinutilsConfig(target, output_dir)
965 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -0500966 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Mike Frysinger35247af2012-11-16 18:58:06 -0500967
968 osutils.RmDir(os.path.join(output_dir, 'etc'))
969
970
971def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
972 """Setup a tree from the packages for the specified target
973
974 This populates a path with all the files from toolchain packages so that
975 a tarball can easily be generated from the result.
976
977 Args:
978 target: The target to create a packagable root from
979 output_dir: The output directory to place all the files
980 ldpaths: A dict of static ldpath information
981 root: The root path to pull all packages/files from
982 """
983 # Find all the files owned by the packages for this target.
984 paths, elfs = _GetFilesForTarget(target, root=root)
985
986 # Link in all the package's files, any ELF dependencies, and wrap any
987 # executable ELFs with helper scripts.
988 def MoveUsrBinToBin(path):
989 """Move /usr/bin to /bin so people can just use that toplevel dir"""
990 return path[4:] if path.startswith('/usr/bin/') else path
991 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
992 path_rewrite_func=MoveUsrBinToBin, root=root)
993
994 # The packages, when part of the normal distro, have helper scripts
995 # that setup paths and such. Since we are making this standalone, we
996 # need to preprocess all that ourselves.
997 _ProcessDistroCleanups(target, output_dir)
998
999
1000def CreatePackages(targets_wanted, output_dir, root='/'):
1001 """Create redistributable cross-compiler packages for the specified targets
1002
1003 This creates toolchain packages that should be usable in conjunction with
1004 a downloaded sysroot (created elsewhere).
1005
1006 Tarballs (one per target) will be created in $PWD.
1007
1008 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001009 targets_wanted: The targets to package up.
1010 output_dir: The directory to put the packages in.
1011 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001012 """
Ralph Nathan03047282015-03-23 11:09:32 -07001013 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001014 osutils.SafeMakedirs(output_dir)
1015 ldpaths = lddtree.LoadLdpaths(root)
1016 targets = ExpandTargets(targets_wanted)
1017
David James4bc13702013-03-26 08:08:04 -07001018 with osutils.TempDir() as tempdir:
Mike Frysinger35247af2012-11-16 18:58:06 -05001019 # We have to split the root generation from the compression stages. This is
1020 # because we hardlink in all the files (to avoid overhead of reading/writing
1021 # the copies multiple times). But tar gets angry if a file's hardlink count
1022 # changes from when it starts reading a file to when it finishes.
1023 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1024 for target in targets:
1025 output_target_dir = os.path.join(tempdir, target)
1026 queue.put([target, output_target_dir, ldpaths, root])
1027
1028 # Build the tarball.
1029 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1030 for target in targets:
1031 tar_file = os.path.join(output_dir, target + '.tar.xz')
1032 queue.put([tar_file, os.path.join(tempdir, target)])
1033
1034
Brian Harring30675052012-02-29 12:18:22 -08001035def main(argv):
Mike Frysinger0c808452014-11-06 17:30:23 -05001036 parser = commandline.ArgumentParser(description=__doc__)
1037 parser.add_argument('-u', '--nousepkg',
1038 action='store_false', dest='usepkg', default=True,
1039 help='Use prebuilt packages if possible')
1040 parser.add_argument('-d', '--deleteold',
1041 action='store_true', dest='deleteold', default=False,
1042 help='Unmerge deprecated packages')
1043 parser.add_argument('-t', '--targets',
1044 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001045 help="Comma separated list of tuples. Special keywords "
1046 "'host', 'sdk', 'boards', 'bricks' and 'all' are "
1047 "allowed. Defaults to 'sdk'.")
1048 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1049 help='Comma separated list of boards whose toolchains we '
1050 'will always include. Default: none')
1051 parser.add_argument('--include-bricks', default='', metavar='BRICKS',
1052 help='Comma separated list of bricks whose toolchains we '
1053 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001054 parser.add_argument('--hostonly',
1055 dest='hostonly', default=False, action='store_true',
1056 help='Only setup the host toolchain. '
1057 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001058 parser.add_argument('--show-board-cfg', '--show-cfg',
1059 dest='cfg_name', default=None,
1060 help='Board or brick to list toolchains tuples for')
Mike Frysinger0c808452014-11-06 17:30:23 -05001061 parser.add_argument('--create-packages',
1062 action='store_true', default=False,
1063 help='Build redistributable packages')
1064 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1065 help='Output directory')
1066 parser.add_argument('--reconfig', default=False, action='store_true',
1067 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001068 parser.add_argument('--sysroot', type='path',
1069 help='The sysroot in which to install the toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001070
Mike Frysinger0c808452014-11-06 17:30:23 -05001071 options = parser.parse_args(argv)
1072 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001073
Mike Frysinger35247af2012-11-16 18:58:06 -05001074 # Figure out what we're supposed to do and reject conflicting options.
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001075 if options.cfg_name and options.create_packages:
Mike Frysinger35247af2012-11-16 18:58:06 -05001076 parser.error('conflicting options: create-packages & show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001077
Gilad Arnold8195b532015-04-07 10:56:30 +03001078 targets_wanted = set(options.targets.split(','))
1079 boards_wanted = (set(options.include_boards.split(','))
1080 if options.include_boards else set())
1081 bricks_wanted = (set(options.include_bricks.split(','))
1082 if options.include_bricks else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001083
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001084 if options.cfg_name:
1085 ShowConfig(options.cfg_name)
Mike Frysinger35247af2012-11-16 18:58:06 -05001086 elif options.create_packages:
1087 cros_build_lib.AssertInsideChroot()
1088 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001089 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001090 else:
1091 cros_build_lib.AssertInsideChroot()
1092 # This has to be always run as root.
1093 if os.geteuid() != 0:
1094 cros_build_lib.Die('this script must be run as root')
1095
1096 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001097 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001098 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001099 options.reconfig, targets_wanted, boards_wanted,
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001100 bricks_wanted, root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001101 Crossdev.Save()
1102
1103 return 0