blob: 61203bb59726dfcc922807580a4c670dae6164b7 [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' : {
Han Shen43b84422015-02-19 11:38:13 -080058 'armv7a-cros-linux-gnueabi': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -050059 'i686-pc-linux-gnu' : '-gold',
60 'x86_64-cros-linux-gnu' : '-gold',
61 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010062}
Zdenek Behan508dcce2011-12-05 15:39:32 +010063# Global per-run cache that will be filled ondemand in by GetPackageMap()
64# function as needed.
65target_version_map = {
66}
67
68
David James66a09c42012-11-05 13:31:38 -080069class Crossdev(object):
70 """Class for interacting with crossdev and caching its output."""
71
72 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
73 _CACHE = {}
74
75 @classmethod
76 def Load(cls, reconfig):
77 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -080078 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
79 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -080080 if os.path.exists(cls._CACHE_FILE) and not reconfig:
81 with open(cls._CACHE_FILE) as f:
82 data = json.load(f)
David James90239b92012-11-05 15:31:34 -080083 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -080084 cls._CACHE = data
85
86 @classmethod
87 def Save(cls):
88 """Store crossdev cache on disk."""
89 # Save the cache from the successful run.
90 with open(cls._CACHE_FILE, 'w') as f:
91 json.dump(cls._CACHE, f)
92
93 @classmethod
94 def GetConfig(cls, target):
95 """Returns a map of crossdev provided variables about a tuple."""
96 CACHE_ATTR = '_target_tuple_map'
97
98 val = cls._CACHE.setdefault(CACHE_ATTR, {})
99 if not target in val:
100 # Find out the crossdev tuple.
101 target_tuple = target
102 if target == 'host':
David James27ac4ae2012-12-03 23:16:15 -0800103 target_tuple = toolchain.GetHostTuple()
David James66a09c42012-11-05 13:31:38 -0800104 # Catch output of crossdev.
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500105 out = cros_build_lib.RunCommand(
106 ['crossdev', '--show-target-cfg', '--ex-gdb', target_tuple],
107 print_cmd=False, redirect_stdout=True).output.splitlines()
David James66a09c42012-11-05 13:31:38 -0800108 # List of tuples split at the first '=', converted into dict.
109 val[target] = dict([x.split('=', 1) for x in out])
110 return val[target]
111
112 @classmethod
113 def UpdateTargets(cls, targets, usepkg, config_only=False):
114 """Calls crossdev to initialize a cross target.
115
116 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700117 targets: The list of targets to initialize using crossdev.
118 usepkg: Copies the commandline opts.
119 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800120 """
121 configured_targets = cls._CACHE.setdefault('configured_targets', [])
122
123 cmdbase = ['crossdev', '--show-fail-log']
124 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
125 # Pick stable by default, and override as necessary.
126 cmdbase.extend(['-P', '--oneshot'])
127 if usepkg:
128 cmdbase.extend(['-P', '--getbinpkg',
129 '-P', '--usepkgonly',
130 '--without-headers'])
131
132 overlays = '%s %s' % (CHROMIUMOS_OVERLAY, STABLE_OVERLAY)
133 cmdbase.extend(['--overlays', overlays])
134 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
135
136 for target in targets:
137 if config_only and target in configured_targets:
138 continue
139
140 cmd = cmdbase + ['-t', target]
141
142 for pkg in GetTargetPackages(target):
143 if pkg == 'gdb':
144 # Gdb does not have selectable versions.
145 cmd.append('--ex-gdb')
146 continue
147 # The first of the desired versions is the "primary" one.
148 version = GetDesiredPackageVersions(target, pkg)[0]
149 cmd.extend(['--%s' % pkg, version])
150
151 cmd.extend(targets[target]['crossdev'].split())
152 if config_only:
153 # In this case we want to just quietly reinit
154 cmd.append('--init-target')
155 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
156 else:
157 cros_build_lib.RunCommand(cmd)
158
159 configured_targets.append(target)
160
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100161
Zdenek Behan508dcce2011-12-05 15:39:32 +0100162def GetPackageMap(target):
163 """Compiles a package map for the given target from the constants.
164
165 Uses a cache in target_version_map, that is dynamically filled in as needed,
166 since here everything is static data and the structuring is for ease of
167 configurability only.
168
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500169 Args:
170 target: The target for which to return a version map
Zdenek Behan508dcce2011-12-05 15:39:32 +0100171
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500172 Returns:
173 A map between packages and desired versions in internal format
174 (using the PACKAGE_* constants)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100175 """
176 if target in target_version_map:
177 return target_version_map[target]
178
179 # Start from copy of the global defaults.
180 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
181
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100182 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100183 # prefer any specific overrides
184 if pkg in TARGET_VERSION_MAP.get(target, {}):
185 result[pkg] = TARGET_VERSION_MAP[target][pkg]
186 else:
187 # finally, if not already set, set a sane default
188 result.setdefault(pkg, DEFAULT_VERSION)
189 target_version_map[target] = result
190 return result
191
192
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100193def GetTargetPackages(target):
194 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800195 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100196 # Undesired packages are denoted by empty ${pkg}_pn variable.
197 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
198
199
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100200# Portage helper functions:
201def GetPortagePackage(target, package):
202 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800203 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100204 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100205 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100206 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100207 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100208 category = conf['category']
209 # Portage package:
210 pn = conf[package + '_pn']
211 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500212 assert category
213 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100214 return '%s/%s' % (category, pn)
215
216
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100217def IsPackageDisabled(target, package):
218 """Returns if the given package is not used for the target."""
219 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
220
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100221
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700222def PortageTrees(root):
223 """Return the portage trees for a given root."""
224 if root == '/':
225 return portage.db['/']
226 # The portage logic requires the path always end in a slash.
227 root = root.rstrip('/') + '/'
228 return portage.create_trees(target_root=root, config_root=root)[root]
229
230
231def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100232 """Extracts the list of current versions of a target, package pair.
233
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500234 Args:
235 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700236 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100237
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500238 Returns:
239 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100240 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100241 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500242 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700243 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100244 version = portage.versions.cpv_getversion(pkg)
245 versions.append(version)
246 return versions
247
248
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700249def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100250 """Extracts the current stable version for a given package.
251
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500252 Args:
253 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
254 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700255 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100256
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500257 Returns:
258 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100259 """
David James90239b92012-11-05 15:31:34 -0800260 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500261 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700262 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800263 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100264
265
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700266def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100267 """Resolves keywords in a given version list for a particular package.
268
269 Resolving means replacing PACKAGE_STABLE with the actual number.
270
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500271 Args:
272 target: The target to operate on (e.g. i686-pc-linux-gnu)
273 package: The target/package to operate on (e.g. gcc)
274 versions: List of versions to resolve
275 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700276 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100277
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500278 Returns:
279 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100280 """
281 resolved = []
David James90239b92012-11-05 15:31:34 -0800282 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700283 if not installed:
284 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100285 for version in versions:
286 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700287 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100288 elif version != PACKAGE_NONE:
289 resolved.append(version)
290 return resolved
291
292
293def GetDesiredPackageVersions(target, package):
294 """Produces the list of desired versions for each target, package pair.
295
296 The first version in the list is implicitly treated as primary, ie.
297 the version that will be initialized by crossdev and selected.
298
299 If the version is PACKAGE_STABLE, it really means the current version which
300 is emerged by using the package atom with no particular version key.
301 Since crossdev unmasks all packages by default, this will actually
302 mean 'unstable' in most cases.
303
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500304 Args:
305 target: The target to operate on (e.g. i686-pc-linux-gnu)
306 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100307
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500308 Returns:
309 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100310 """
311 packagemap = GetPackageMap(target)
312
313 versions = []
314 if package in packagemap:
315 versions.append(packagemap[package])
316
317 return versions
318
319
320def TargetIsInitialized(target):
321 """Verifies if the given list of targets has been correctly initialized.
322
323 This determines whether we have to call crossdev while emerging
324 toolchain packages or can do it using emerge. Emerge is naturally
325 preferred, because all packages can be updated in a single pass.
326
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500327 Args:
328 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100329
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500330 Returns:
331 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100332 """
333 # Check if packages for the given target all have a proper version.
334 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100335 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800336 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100337 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800338 if not IsPackageDisabled(target, package) and not (
339 GetStablePackageVersion(atom, True) and
340 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341 return False
342 return True
343 except cros_build_lib.RunCommandError:
344 # Fails - The target has likely never been initialized before.
345 return False
346
347
348def RemovePackageMask(target):
349 """Removes a package.mask file for the given platform.
350
351 The pre-existing package.mask files can mess with the keywords.
352
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500353 Args:
354 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100355 """
356 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700357 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100358
359
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700361def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500362 """Rebuild libtool as needed
363
364 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
365 gcc, libtool will break. We can't use binary packages either as those will
366 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700367
368 Args:
369 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500370 """
371 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700372 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500373 for line in f:
374 # Look for a line like:
375 # sys_lib_search_path_spec="..."
376 # It'll be a list of paths and gcc will be one of them.
377 if line.startswith('sys_lib_search_path_spec='):
378 line = line.rstrip()
379 for path in line.split('=', 1)[1].strip('"').split():
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700380 if not os.path.exists(os.path.join(root, path.lstrip(os.path.sep))):
Mike Frysinger383367e2014-09-16 15:06:17 -0400381 print('Rebuilding libtool after gcc upgrade')
382 print(' %s' % line)
383 print(' missing path: %s' % path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500384 needs_update = True
385 break
386
387 if needs_update:
388 break
389
390 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700391 cmd = [EMERGE_CMD, '--oneshot']
392 if root != '/':
393 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
394 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500395 cros_build_lib.RunCommand(cmd)
396
397
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700398def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100399 """Determines which packages need update/unmerge and defers to portage.
400
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500401 Args:
402 targets: The list of targets to update
403 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700404 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100405 """
David James90239b92012-11-05 15:31:34 -0800406 # Remove keyword files created by old versions of cros_setup_toolchains.
407 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100408
409 # For each target, we do two things. Figure out the list of updates,
410 # and figure out the appropriate keywords/masks. Crossdev will initialize
411 # these, but they need to be regenerated on every update.
Mike Frysinger383367e2014-09-16 15:06:17 -0400412 print('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800413 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100414 for target in targets:
415 # Record the highest needed version for each target, for masking purposes.
416 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100417 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100418 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100419 if IsPackageDisabled(target, package):
420 continue
421 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700422 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100423 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200424 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100425 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100426
Zdenek Behan508dcce2011-12-05 15:39:32 +0100427 packages = []
428 for pkg in mergemap:
429 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200430 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800431 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100432
433 if not packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400434 print('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800435 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100436
Mike Frysinger383367e2014-09-16 15:06:17 -0400437 print('Updating packages:')
438 print(packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100439
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100440 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100441 if usepkg:
442 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700443 if root != '/':
444 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100445
446 cmd.extend(packages)
447 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800448 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100449
450
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700451def CleanTargets(targets, root='/'):
452 """Unmerges old packages that are assumed unnecessary.
453
454 Args:
455 targets: The list of targets to clean up.
456 root: The install root in which we want packages cleaned up.
457 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100458 unmergemap = {}
459 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100460 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100461 if IsPackageDisabled(target, package):
462 continue
463 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700464 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100465 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700466 # NOTE: This refers to installed packages (vartree) rather than the
467 # Portage version (porttree and/or bintree) when determining the current
468 # version. While this isn't the most accurate thing to do, it is probably
469 # a good simple compromise, which should have the desired result of
470 # uninstalling everything but the latest installed version. In
471 # particular, using the bintree (--usebinpkg) requires a non-trivial
472 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200473 desired_num = VersionListToNumeric(target, package, desired, True)
474 if not set(desired_num).issubset(current):
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700475 print('Error detecting stable version for %s, skipping clean!' % pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200476 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100477 unmergemap[pkg] = set(current).difference(desired_num)
478
479 # Cleaning doesn't care about consistency and rebuilding package.* files.
480 packages = []
481 for pkg, vers in unmergemap.iteritems():
482 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
483
484 if packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400485 print('Cleaning packages:')
486 print(packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100487 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700488 if root != '/':
489 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100490 cmd.extend(packages)
491 cros_build_lib.RunCommand(cmd)
492 else:
Mike Frysinger383367e2014-09-16 15:06:17 -0400493 print('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100494
495
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700496def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100497 """Runs gcc-config and binutils-config to select the desired.
498
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500499 Args:
500 targets: The targets to select
501 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700502 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100503 """
504 for package in ['gcc', 'binutils']:
505 for target in targets:
506 # Pick the first version in the numbered list as the selected one.
507 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700508 desired_num = VersionListToNumeric(target, package, desired, True,
509 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100510 desired = desired_num[0]
511 # *-config does not play revisions, strip them, keep just PV.
512 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
513
514 if target == 'host':
515 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800516 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100517
518 # And finally, attach target to it.
519 desired = '%s-%s' % (target, desired)
520
521 # Target specific hacks
522 if package in suffixes:
523 if target in suffixes[package]:
524 desired += suffixes[package][target]
525
David James7ec5efc2012-11-06 09:39:49 -0800526 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700527 if root != '/':
528 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800529 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500530 result = cros_build_lib.RunCommand(
531 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
532 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700533
534 # Do not reconfig when the current is live or nothing needs to be done.
535 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500537 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700538 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100539
540
Mike Frysinger35247af2012-11-16 18:58:06 -0500541def ExpandTargets(targets_wanted):
542 """Expand any possible toolchain aliases into full targets
543
544 This will expand 'all' and 'sdk' into the respective toolchain tuples.
545
546 Args:
547 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500548
Mike Frysinger35247af2012-11-16 18:58:06 -0500549 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300550 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500551 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500552 targets_wanted = set(targets_wanted)
Gilad Arnold8195b532015-04-07 10:56:30 +0300553 if targets_wanted in (set(['boards']), set(['bricks'])):
554 # Only pull targets from the included boards/bricks.
555 return {}
556
557 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500558 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300559 return all_targets
560 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500561 # Filter out all the non-sdk toolchains as we don't want to mess
562 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300563 return toolchain.FilterToolchains(all_targets, 'sdk', True)
564
565 # Verify user input.
566 nonexistent = targets_wanted.difference(all_targets)
567 if nonexistent:
568 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
569 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500570
571
David Jamesf8c672f2012-11-06 13:38:11 -0800572def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700573 targets_wanted, boards_wanted, bricks_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100574 """Performs all steps to create a synchronized toolchain enviroment.
575
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500576 Args:
577 usepkg: Use prebuilt packages
578 deleteold: Unmerge deprecated packages
579 hostonly: Only setup the host toolchain
580 reconfig: Reload crossdev config and reselect toolchains
581 targets_wanted: All the targets to update
582 boards_wanted: Load targets from these boards
Gilad Arnold8195b532015-04-07 10:56:30 +0300583 bricks_wanted: Load targets from these bricks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700584 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100585 """
David Jamesf8c672f2012-11-06 13:38:11 -0800586 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100587 if not hostonly:
588 # For hostonly, we can skip most of the below logic, much of which won't
589 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500590 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400591
Gilad Arnold8195b532015-04-07 10:56:30 +0300592 # Now re-add any targets that might be from this board/brick. This is to
593 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400594 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800595 targets.update(toolchain.GetToolchainsForBoard(board))
Gilad Arnold8195b532015-04-07 10:56:30 +0300596 for brick in bricks_wanted:
597 targets.update(toolchain.GetToolchainsForBrick(brick))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100598
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100599 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400600 for target in targets:
601 if TargetIsInitialized(target):
602 reconfig_targets[target] = targets[target]
603 else:
604 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100605 if crossdev_targets:
Mike Frysinger383367e2014-09-16 15:06:17 -0400606 print('The following targets need to be re-initialized:')
607 print(crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800608 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200609 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800610 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100611
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100612 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400613 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100614
615 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700616 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
617 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800618
619 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700620 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100621
Mike Frysingerc880a962013-11-08 13:59:06 -0500622 # Now that we've cleared out old versions, see if we need to rebuild
623 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700624 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500625
Zdenek Behan508dcce2011-12-05 15:39:32 +0100626
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700627def ShowConfig(name):
628 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500629
630 Args:
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700631 name: The board name or brick locator to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500632 """
Bertrand SIMONNET3c77bc92015-03-20 14:28:54 -0700633 if workspace_lib.IsLocator(name):
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700634 toolchains = toolchain.GetToolchainsForBrick(name)
635 else:
636 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500637 # Make sure we display the default toolchain first.
Mike Frysinger383367e2014-09-16 15:06:17 -0400638 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800639 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400640 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500641
642
Mike Frysinger35247af2012-11-16 18:58:06 -0500643def GeneratePathWrapper(root, wrappath, path):
644 """Generate a shell script to execute another shell script
645
646 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
647 argv[0] won't be pointing to the correct path, generate a shell script that
648 just executes another program with its full path.
649
650 Args:
651 root: The root tree to generate scripts inside of
652 wrappath: The full path (inside |root|) to create the wrapper
653 path: The target program which this wrapper will execute
654 """
655 replacements = {
656 'path': path,
657 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
658 }
659 wrapper = """#!/bin/sh
660base=$(realpath "$0")
661basedir=${base%%/*}
662exec "${basedir}/%(relroot)s%(path)s" "$@"
663""" % replacements
664 root_wrapper = root + wrappath
665 if os.path.islink(root_wrapper):
666 os.unlink(root_wrapper)
667 else:
668 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
669 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400670 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500671
672
673def FileIsCrosSdkElf(elf):
674 """Determine if |elf| is an ELF that we execute in the cros_sdk
675
676 We don't need this to be perfect, just quick. It makes sure the ELF
677 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
678
679 Args:
680 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500681
Mike Frysinger35247af2012-11-16 18:58:06 -0500682 Returns:
683 True if we think |elf| is a native ELF
684 """
685 with open(elf) as f:
686 data = f.read(20)
687 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
688 return (data[0:4] == '\x7fELF' and
689 data[4] == '\x02' and
690 data[5] == '\x01' and
691 data[18] == '\x3e')
692
693
694def IsPathPackagable(ptype, path):
695 """Should the specified file be included in a toolchain package?
696
697 We only need to handle files as we'll create dirs as we need them.
698
699 Further, trim files that won't be useful:
700 - non-english translations (.mo) since it'd require env vars
701 - debug files since these are for the host compiler itself
702 - info/man pages as they're big, and docs are online, and the
703 native docs should work fine for the most part (`man gcc`)
704
705 Args:
706 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
707 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500708
Mike Frysinger35247af2012-11-16 18:58:06 -0500709 Returns:
710 True if we want to include this path in the package
711 """
712 return not (ptype in ('dir',) or
713 path.startswith('/usr/lib/debug/') or
714 os.path.splitext(path)[1] == '.mo' or
715 ('/man/' in path or '/info/' in path))
716
717
718def ReadlinkRoot(path, root):
719 """Like os.readlink(), but relative to a |root|
720
721 Args:
722 path: The symlink to read
723 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500724
Mike Frysinger35247af2012-11-16 18:58:06 -0500725 Returns:
726 A fully resolved symlink path
727 """
728 while os.path.islink(root + path):
729 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
730 return path
731
732
733def _GetFilesForTarget(target, root='/'):
734 """Locate all the files to package for |target|
735
736 This does not cover ELF dependencies.
737
738 Args:
739 target: The toolchain target name
740 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500741
Mike Frysinger35247af2012-11-16 18:58:06 -0500742 Returns:
743 A tuple of a set of all packable paths, and a set of all paths which
744 are also native ELFs
745 """
746 paths = set()
747 elfs = set()
748
749 # Find all the files owned by the packages for this target.
750 for pkg in GetTargetPackages(target):
751 # Ignore packages that are part of the target sysroot.
752 if pkg in ('kernel', 'libc'):
753 continue
754
755 atom = GetPortagePackage(target, pkg)
756 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700757 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700758 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500759
760 # pylint: disable=E1101
761 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
762 settings=portage.settings)
763 contents = dblink.getcontents()
764 for obj in contents:
765 ptype = contents[obj][0]
766 if not IsPathPackagable(ptype, obj):
767 continue
768
769 if ptype == 'obj':
770 # For native ELFs, we need to pull in their dependencies too.
771 if FileIsCrosSdkElf(obj):
772 elfs.add(obj)
773 paths.add(obj)
774
775 return paths, elfs
776
777
778def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500779 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500780 """Link in all packable files and their runtime dependencies
781
782 This also wraps up executable ELFs with helper scripts.
783
784 Args:
785 output_dir: The output directory to store files
786 paths: All the files to include
787 elfs: All the files which are ELFs (a subset of |paths|)
788 ldpaths: A dict of static ldpath information
789 path_rewrite_func: User callback to rewrite paths in output_dir
790 root: The root path to pull all packages/files from
791 """
792 # Link in all the files.
793 sym_paths = []
794 for path in paths:
795 new_path = path_rewrite_func(path)
796 dst = output_dir + new_path
797 osutils.SafeMakedirs(os.path.dirname(dst))
798
799 # Is this a symlink which we have to rewrite or wrap?
800 # Delay wrap check until after we have created all paths.
801 src = root + path
802 if os.path.islink(src):
803 tgt = os.readlink(src)
804 if os.path.sep in tgt:
805 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
806
807 # Rewrite absolute links to relative and then generate the symlink
808 # ourselves. All other symlinks can be hardlinked below.
809 if tgt[0] == '/':
810 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
811 os.symlink(tgt, dst)
812 continue
813
814 os.link(src, dst)
815
816 # Now see if any of the symlinks need to be wrapped.
817 for sym, tgt in sym_paths:
818 if tgt in elfs:
819 GeneratePathWrapper(output_dir, sym, tgt)
820
821 # Locate all the dependencies for all the ELFs. Stick them all in the
822 # top level "lib" dir to make the wrapper simpler. This exact path does
823 # not matter since we execute ldso directly, and we tell the ldso the
824 # exact path to search for its libraries.
825 libdir = os.path.join(output_dir, 'lib')
826 osutils.SafeMakedirs(libdir)
827 donelibs = set()
828 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400829 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500830 interp = e['interp']
831 if interp:
832 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400833 interp = os.path.join('/lib', os.path.basename(interp))
834 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
835 libpaths=e['rpath'] + e['runpath'])
Mike Frysinger35247af2012-11-16 18:58:06 -0500836
837 for lib, lib_data in e['libs'].iteritems():
838 if lib in donelibs:
839 continue
840
841 src = path = lib_data['path']
842 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -0700843 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -0500844 continue
845 donelibs.add(lib)
846
847 # Needed libs are the SONAME, but that is usually a symlink, not a
848 # real file. So link in the target rather than the symlink itself.
849 # We have to walk all the possible symlinks (SONAME could point to a
850 # symlink which points to a symlink), and we have to handle absolute
851 # ourselves (since we have a "root" argument).
852 dst = os.path.join(libdir, os.path.basename(path))
853 src = ReadlinkRoot(src, root)
854
855 os.link(root + src, dst)
856
857
858def _EnvdGetVar(envd, var):
859 """Given a Gentoo env.d file, extract a var from it
860
861 Args:
862 envd: The env.d file to load (may be a glob path)
863 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -0500864
Mike Frysinger35247af2012-11-16 18:58:06 -0500865 Returns:
866 The value of |var|
867 """
868 envds = glob.glob(envd)
869 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
870 envd = envds[0]
871 return cros_build_lib.LoadKeyValueFile(envd)[var]
872
873
874def _ProcessBinutilsConfig(target, output_dir):
875 """Do what binutils-config would have done"""
876 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500877
878 # Locate the bin dir holding the gold linker.
879 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
880 target, 'binutils-bin')
881 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -0500882 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500883 if not srcpath:
884 # Maybe this target doesn't support gold.
885 globpath = os.path.join(binutils_bin_path, '*')
886 srcpath = glob.glob(globpath)
887 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
888 % globpath)
889 srcpath = srcpath[0]
890 ld_path = os.path.join(srcpath, 'ld')
891 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
892 ld_path = os.path.join(srcpath, 'ld.bfd')
893 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
894 ld_path = os.path.join(srcpath, 'ld.gold')
895 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
896 % ld_path)
897
898 # Nope, no gold support to be found.
899 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -0700900 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500901 else:
902 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
903 gold_supported = True
Mike Frysinger78b7a812014-11-26 19:45:23 -0500904 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500905
Mike Frysinger78b7a812014-11-26 19:45:23 -0500906 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -0500907 gccpath = os.path.join('/usr', 'libexec', 'gcc')
908 for prog in os.listdir(output_dir + srcpath):
909 # Skip binaries already wrapped.
910 if not prog.endswith('.real'):
911 GeneratePathWrapper(output_dir, binpath + prog,
912 os.path.join(srcpath, prog))
913 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
914 os.path.join(srcpath, prog))
915
David James27ac4ae2012-12-03 23:16:15 -0800916 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500917 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
918 if gold_supported:
919 envd += '-gold'
Mike Frysinger35247af2012-11-16 18:58:06 -0500920 srcpath = _EnvdGetVar(envd, 'LIBPATH')
921 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
922 output_dir + libpath)
923
924
925def _ProcessGccConfig(target, output_dir):
926 """Do what gcc-config would have done"""
927 binpath = '/bin'
928 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
929 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
930 for prog in os.listdir(output_dir + srcpath):
931 # Skip binaries already wrapped.
932 if (not prog.endswith('.real') and
933 not prog.endswith('.elf') and
934 prog.startswith(target)):
935 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
936 os.path.join(srcpath, prog))
937 return srcpath
938
939
Frank Henigman179ec7c2015-02-06 03:01:09 -0500940def _ProcessSysrootWrappers(_target, output_dir, srcpath):
941 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -0500942 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -0500943 for sysroot_wrapper in glob.glob(os.path.join(
944 output_dir + srcpath, 'sysroot_wrapper*')):
945 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
946 for num in xrange(len(contents)):
947 if '@CCACHE_DEFAULT@' in contents[num]:
948 contents[num] = 'use_ccache = False'
949 break
950 # Can't update the wrapper in place since it's a hardlink to a file in /.
951 os.unlink(sysroot_wrapper)
952 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
953 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500954
955
956def _ProcessDistroCleanups(target, output_dir):
957 """Clean up the tree and remove all distro-specific requirements
958
959 Args:
960 target: The toolchain target name
961 output_dir: The output directory to clean up
962 """
963 _ProcessBinutilsConfig(target, output_dir)
964 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -0500965 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Mike Frysinger35247af2012-11-16 18:58:06 -0500966
967 osutils.RmDir(os.path.join(output_dir, 'etc'))
968
969
970def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
971 """Setup a tree from the packages for the specified target
972
973 This populates a path with all the files from toolchain packages so that
974 a tarball can easily be generated from the result.
975
976 Args:
977 target: The target to create a packagable root from
978 output_dir: The output directory to place all the files
979 ldpaths: A dict of static ldpath information
980 root: The root path to pull all packages/files from
981 """
982 # Find all the files owned by the packages for this target.
983 paths, elfs = _GetFilesForTarget(target, root=root)
984
985 # Link in all the package's files, any ELF dependencies, and wrap any
986 # executable ELFs with helper scripts.
987 def MoveUsrBinToBin(path):
988 """Move /usr/bin to /bin so people can just use that toplevel dir"""
989 return path[4:] if path.startswith('/usr/bin/') else path
990 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
991 path_rewrite_func=MoveUsrBinToBin, root=root)
992
993 # The packages, when part of the normal distro, have helper scripts
994 # that setup paths and such. Since we are making this standalone, we
995 # need to preprocess all that ourselves.
996 _ProcessDistroCleanups(target, output_dir)
997
998
999def CreatePackages(targets_wanted, output_dir, root='/'):
1000 """Create redistributable cross-compiler packages for the specified targets
1001
1002 This creates toolchain packages that should be usable in conjunction with
1003 a downloaded sysroot (created elsewhere).
1004
1005 Tarballs (one per target) will be created in $PWD.
1006
1007 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001008 targets_wanted: The targets to package up.
1009 output_dir: The directory to put the packages in.
1010 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001011 """
Ralph Nathan03047282015-03-23 11:09:32 -07001012 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001013 osutils.SafeMakedirs(output_dir)
1014 ldpaths = lddtree.LoadLdpaths(root)
1015 targets = ExpandTargets(targets_wanted)
1016
David James4bc13702013-03-26 08:08:04 -07001017 with osutils.TempDir() as tempdir:
Mike Frysinger35247af2012-11-16 18:58:06 -05001018 # We have to split the root generation from the compression stages. This is
1019 # because we hardlink in all the files (to avoid overhead of reading/writing
1020 # the copies multiple times). But tar gets angry if a file's hardlink count
1021 # changes from when it starts reading a file to when it finishes.
1022 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1023 for target in targets:
1024 output_target_dir = os.path.join(tempdir, target)
1025 queue.put([target, output_target_dir, ldpaths, root])
1026
1027 # Build the tarball.
1028 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1029 for target in targets:
1030 tar_file = os.path.join(output_dir, target + '.tar.xz')
1031 queue.put([tar_file, os.path.join(tempdir, target)])
1032
1033
Brian Harring30675052012-02-29 12:18:22 -08001034def main(argv):
Mike Frysinger0c808452014-11-06 17:30:23 -05001035 parser = commandline.ArgumentParser(description=__doc__)
1036 parser.add_argument('-u', '--nousepkg',
1037 action='store_false', dest='usepkg', default=True,
1038 help='Use prebuilt packages if possible')
1039 parser.add_argument('-d', '--deleteold',
1040 action='store_true', dest='deleteold', default=False,
1041 help='Unmerge deprecated packages')
1042 parser.add_argument('-t', '--targets',
1043 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001044 help="Comma separated list of tuples. Special keywords "
1045 "'host', 'sdk', 'boards', 'bricks' and 'all' are "
1046 "allowed. Defaults to 'sdk'.")
1047 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1048 help='Comma separated list of boards whose toolchains we '
1049 'will always include. Default: none')
1050 parser.add_argument('--include-bricks', default='', metavar='BRICKS',
1051 help='Comma separated list of bricks whose toolchains we '
1052 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001053 parser.add_argument('--hostonly',
1054 dest='hostonly', default=False, action='store_true',
1055 help='Only setup the host toolchain. '
1056 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001057 parser.add_argument('--show-board-cfg', '--show-cfg',
1058 dest='cfg_name', default=None,
1059 help='Board or brick to list toolchains tuples for')
Mike Frysinger0c808452014-11-06 17:30:23 -05001060 parser.add_argument('--create-packages',
1061 action='store_true', default=False,
1062 help='Build redistributable packages')
1063 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1064 help='Output directory')
1065 parser.add_argument('--reconfig', default=False, action='store_true',
1066 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001067 parser.add_argument('--sysroot', type='path',
1068 help='The sysroot in which to install the toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001069
Mike Frysinger0c808452014-11-06 17:30:23 -05001070 options = parser.parse_args(argv)
1071 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001072
Mike Frysinger35247af2012-11-16 18:58:06 -05001073 # Figure out what we're supposed to do and reject conflicting options.
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001074 if options.cfg_name and options.create_packages:
Mike Frysinger35247af2012-11-16 18:58:06 -05001075 parser.error('conflicting options: create-packages & show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001076
Gilad Arnold8195b532015-04-07 10:56:30 +03001077 targets_wanted = set(options.targets.split(','))
1078 boards_wanted = (set(options.include_boards.split(','))
1079 if options.include_boards else set())
1080 bricks_wanted = (set(options.include_bricks.split(','))
1081 if options.include_bricks else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001082
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001083 if options.cfg_name:
1084 ShowConfig(options.cfg_name)
Mike Frysinger35247af2012-11-16 18:58:06 -05001085 elif options.create_packages:
1086 cros_build_lib.AssertInsideChroot()
1087 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001088 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001089 else:
1090 cros_build_lib.AssertInsideChroot()
1091 # This has to be always run as root.
1092 if os.geteuid() != 0:
1093 cros_build_lib.Die('this script must be run as root')
1094
1095 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001096 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001097 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001098 options.reconfig, targets_wanted, boards_wanted,
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001099 bricks_wanted, root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001100 Crossdev.Save()
1101
1102 return 0