blob: fa4fd488adeecaf4ff6ea1e23bbe175163020999 [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'
Christopher Wileyb22c0712015-06-02 10:37:03 -070040ECLASS_OVERLAY = '/usr/local/portage/eclass-overlay'
Zdenek Behan4eb6fd22012-03-12 17:00:56 +010041STABLE_OVERLAY = '/usr/local/portage/stable'
42CROSSDEV_OVERLAY = '/usr/local/portage/crossdev'
Zdenek Behan508dcce2011-12-05 15:39:32 +010043
44
45# TODO: The versions are stored here very much like in setup_board.
46# The goal for future is to differentiate these using a config file.
47# This is done essentially by messing with GetDesiredPackageVersions()
48DEFAULT_VERSION = PACKAGE_STABLE
49DEFAULT_TARGET_VERSION_MAP = {
Zdenek Behan508dcce2011-12-05 15:39:32 +010050}
51TARGET_VERSION_MAP = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050052 'host' : {
53 'gdb' : PACKAGE_NONE,
Rahul Chaudhry4b803052015-05-13 15:25:56 -070054 'ex_go' : PACKAGE_NONE,
Mike Frysingerd6e2df02014-11-26 02:55:04 -050055 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010056}
Rahul Chaudhry4b803052015-05-13 15:25:56 -070057
58# Enable the Go compiler for these targets.
59TARGET_GO_ENABLED = (
60 'x86_64-cros-linux-gnu',
61 'i686-pc-linux-gnu',
62 'armv7a-cros-linux-gnueabi',
63)
64CROSSDEV_GO_ARGS = ['--ex-pkg', 'dev-lang/go']
65
Zdenek Behan508dcce2011-12-05 15:39:32 +010066# Overrides for {gcc,binutils}-config, pick a package with particular suffix.
67CONFIG_TARGET_SUFFIXES = {
Mike Frysingerd6e2df02014-11-26 02:55:04 -050068 'binutils' : {
Mike Frysinger8a83c622015-05-28 00:35:05 -040069 'armv6j-cros-linux-gnueabi': '-gold',
Han Shen43b84422015-02-19 11:38:13 -080070 'armv7a-cros-linux-gnueabi': '-gold',
Mike Frysingerd6e2df02014-11-26 02:55:04 -050071 'i686-pc-linux-gnu' : '-gold',
72 'x86_64-cros-linux-gnu' : '-gold',
73 },
Zdenek Behan508dcce2011-12-05 15:39:32 +010074}
Zdenek Behan508dcce2011-12-05 15:39:32 +010075# Global per-run cache that will be filled ondemand in by GetPackageMap()
76# function as needed.
77target_version_map = {
78}
79
80
David James66a09c42012-11-05 13:31:38 -080081class Crossdev(object):
82 """Class for interacting with crossdev and caching its output."""
83
84 _CACHE_FILE = os.path.join(CROSSDEV_OVERLAY, '.configured.json')
85 _CACHE = {}
86
87 @classmethod
88 def Load(cls, reconfig):
89 """Load crossdev cache from disk."""
David James90239b92012-11-05 15:31:34 -080090 crossdev_version = GetStablePackageVersion('sys-devel/crossdev', True)
91 cls._CACHE = {'crossdev_version': crossdev_version}
David James66a09c42012-11-05 13:31:38 -080092 if os.path.exists(cls._CACHE_FILE) and not reconfig:
93 with open(cls._CACHE_FILE) as f:
94 data = json.load(f)
David James90239b92012-11-05 15:31:34 -080095 if crossdev_version == data.get('crossdev_version'):
David James66a09c42012-11-05 13:31:38 -080096 cls._CACHE = data
97
98 @classmethod
99 def Save(cls):
100 """Store crossdev cache on disk."""
101 # Save the cache from the successful run.
102 with open(cls._CACHE_FILE, 'w') as f:
103 json.dump(cls._CACHE, f)
104
105 @classmethod
106 def GetConfig(cls, target):
107 """Returns a map of crossdev provided variables about a tuple."""
108 CACHE_ATTR = '_target_tuple_map'
109
110 val = cls._CACHE.setdefault(CACHE_ATTR, {})
111 if not target in val:
112 # Find out the crossdev tuple.
113 target_tuple = target
114 if target == 'host':
David James27ac4ae2012-12-03 23:16:15 -0800115 target_tuple = toolchain.GetHostTuple()
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700116 # Build the crossdev command.
117 cmd = ['crossdev', '--show-target-cfg', '--ex-gdb']
118 if target in TARGET_GO_ENABLED:
119 cmd.extend(CROSSDEV_GO_ARGS)
120 cmd.extend(['-t', target_tuple])
David James66a09c42012-11-05 13:31:38 -0800121 # Catch output of crossdev.
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700122 out = cros_build_lib.RunCommand(cmd, print_cmd=False,
123 redirect_stdout=True).output.splitlines()
David James66a09c42012-11-05 13:31:38 -0800124 # List of tuples split at the first '=', converted into dict.
125 val[target] = dict([x.split('=', 1) for x in out])
126 return val[target]
127
128 @classmethod
129 def UpdateTargets(cls, targets, usepkg, config_only=False):
130 """Calls crossdev to initialize a cross target.
131
132 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700133 targets: The list of targets to initialize using crossdev.
134 usepkg: Copies the commandline opts.
135 config_only: Just update.
David James66a09c42012-11-05 13:31:38 -0800136 """
137 configured_targets = cls._CACHE.setdefault('configured_targets', [])
138
139 cmdbase = ['crossdev', '--show-fail-log']
140 cmdbase.extend(['--env', 'FEATURES=splitdebug'])
141 # Pick stable by default, and override as necessary.
142 cmdbase.extend(['-P', '--oneshot'])
143 if usepkg:
144 cmdbase.extend(['-P', '--getbinpkg',
145 '-P', '--usepkgonly',
146 '--without-headers'])
147
Christopher Wileyb22c0712015-06-02 10:37:03 -0700148 overlays = ' '.join((CHROMIUMOS_OVERLAY, ECLASS_OVERLAY, STABLE_OVERLAY))
David James66a09c42012-11-05 13:31:38 -0800149 cmdbase.extend(['--overlays', overlays])
150 cmdbase.extend(['--ov-output', CROSSDEV_OVERLAY])
151
152 for target in targets:
153 if config_only and target in configured_targets:
154 continue
155
156 cmd = cmdbase + ['-t', target]
157
158 for pkg in GetTargetPackages(target):
159 if pkg == 'gdb':
160 # Gdb does not have selectable versions.
161 cmd.append('--ex-gdb')
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700162 elif pkg == 'ex_go':
163 # Go does not have selectable versions.
164 cmd.extend(CROSSDEV_GO_ARGS)
165 else:
166 # The first of the desired versions is the "primary" one.
167 version = GetDesiredPackageVersions(target, pkg)[0]
168 cmd.extend(['--%s' % pkg, version])
David James66a09c42012-11-05 13:31:38 -0800169
170 cmd.extend(targets[target]['crossdev'].split())
171 if config_only:
172 # In this case we want to just quietly reinit
173 cmd.append('--init-target')
174 cros_build_lib.RunCommand(cmd, print_cmd=False, redirect_stdout=True)
175 else:
176 cros_build_lib.RunCommand(cmd)
177
178 configured_targets.append(target)
179
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100180
Zdenek Behan508dcce2011-12-05 15:39:32 +0100181def GetPackageMap(target):
182 """Compiles a package map for the given target from the constants.
183
184 Uses a cache in target_version_map, that is dynamically filled in as needed,
185 since here everything is static data and the structuring is for ease of
186 configurability only.
187
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500188 Args:
189 target: The target for which to return a version map
Zdenek Behan508dcce2011-12-05 15:39:32 +0100190
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500191 Returns:
192 A map between packages and desired versions in internal format
193 (using the PACKAGE_* constants)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100194 """
195 if target in target_version_map:
196 return target_version_map[target]
197
198 # Start from copy of the global defaults.
199 result = copy.copy(DEFAULT_TARGET_VERSION_MAP)
200
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100201 for pkg in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100202 # prefer any specific overrides
203 if pkg in TARGET_VERSION_MAP.get(target, {}):
204 result[pkg] = TARGET_VERSION_MAP[target][pkg]
205 else:
206 # finally, if not already set, set a sane default
207 result.setdefault(pkg, DEFAULT_VERSION)
208 target_version_map[target] = result
209 return result
210
211
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100212def GetTargetPackages(target):
213 """Returns a list of packages for a given target."""
David James66a09c42012-11-05 13:31:38 -0800214 conf = Crossdev.GetConfig(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100215 # Undesired packages are denoted by empty ${pkg}_pn variable.
216 return [x for x in conf['crosspkgs'].strip("'").split() if conf[x+'_pn']]
217
218
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100219# Portage helper functions:
220def GetPortagePackage(target, package):
221 """Returns a package name for the given target."""
David James66a09c42012-11-05 13:31:38 -0800222 conf = Crossdev.GetConfig(target)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100223 # Portage category:
Zdenek Behan508dcce2011-12-05 15:39:32 +0100224 if target == 'host':
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100225 category = conf[package + '_category']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100226 else:
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100227 category = conf['category']
228 # Portage package:
229 pn = conf[package + '_pn']
230 # Final package name:
Mike Frysinger422faf42014-11-12 23:16:48 -0500231 assert category
232 assert pn
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100233 return '%s/%s' % (category, pn)
234
235
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100236def IsPackageDisabled(target, package):
237 """Returns if the given package is not used for the target."""
238 return GetDesiredPackageVersions(target, package) == [PACKAGE_NONE]
239
Liam McLoughlinf54a0782012-05-17 23:36:52 +0100240
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700241def PortageTrees(root):
242 """Return the portage trees for a given root."""
243 if root == '/':
244 return portage.db['/']
245 # The portage logic requires the path always end in a slash.
246 root = root.rstrip('/') + '/'
247 return portage.create_trees(target_root=root, config_root=root)[root]
248
249
250def GetInstalledPackageVersions(atom, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100251 """Extracts the list of current versions of a target, package pair.
252
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500253 Args:
254 atom: The atom to operate on (e.g. sys-devel/gcc)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700255 root: The root to check for installed packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100256
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500257 Returns:
258 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100259 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100260 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500261 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700262 for pkg in PortageTrees(root)['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100263 version = portage.versions.cpv_getversion(pkg)
264 versions.append(version)
265 return versions
266
267
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700268def GetStablePackageVersion(atom, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100269 """Extracts the current stable version for a given package.
270
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500271 Args:
272 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
273 installed: Whether we want installed packages or ebuilds
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700274 root: The root to use when querying packages.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100275
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500276 Returns:
277 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100278 """
David James90239b92012-11-05 15:31:34 -0800279 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500280 # pylint: disable=E1101
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700281 cpv = portage.best(PortageTrees(root)[pkgtype].dbapi.match(atom, use_cache=0))
David James90239b92012-11-05 15:31:34 -0800282 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100283
284
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700285def VersionListToNumeric(target, package, versions, installed, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100286 """Resolves keywords in a given version list for a particular package.
287
288 Resolving means replacing PACKAGE_STABLE with the actual number.
289
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500290 Args:
291 target: The target to operate on (e.g. i686-pc-linux-gnu)
292 package: The target/package to operate on (e.g. gcc)
293 versions: List of versions to resolve
294 installed: Query installed packages
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700295 root: The install root to use; ignored if |installed| is False.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100296
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500297 Returns:
298 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100299 """
300 resolved = []
David James90239b92012-11-05 15:31:34 -0800301 atom = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700302 if not installed:
303 root = '/'
Zdenek Behan508dcce2011-12-05 15:39:32 +0100304 for version in versions:
305 if version == PACKAGE_STABLE:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700306 resolved.append(GetStablePackageVersion(atom, installed, root=root))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100307 elif version != PACKAGE_NONE:
308 resolved.append(version)
309 return resolved
310
311
312def GetDesiredPackageVersions(target, package):
313 """Produces the list of desired versions for each target, package pair.
314
315 The first version in the list is implicitly treated as primary, ie.
316 the version that will be initialized by crossdev and selected.
317
318 If the version is PACKAGE_STABLE, it really means the current version which
319 is emerged by using the package atom with no particular version key.
320 Since crossdev unmasks all packages by default, this will actually
321 mean 'unstable' in most cases.
322
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500323 Args:
324 target: The target to operate on (e.g. i686-pc-linux-gnu)
325 package: The target/package to operate on (e.g. gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100326
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500327 Returns:
328 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100329 """
330 packagemap = GetPackageMap(target)
331
332 versions = []
333 if package in packagemap:
334 versions.append(packagemap[package])
335
336 return versions
337
338
339def TargetIsInitialized(target):
340 """Verifies if the given list of targets has been correctly initialized.
341
342 This determines whether we have to call crossdev while emerging
343 toolchain packages or can do it using emerge. Emerge is naturally
344 preferred, because all packages can be updated in a single pass.
345
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500346 Args:
347 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100348
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500349 Returns:
350 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100351 """
352 # Check if packages for the given target all have a proper version.
353 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100354 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800355 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100356 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800357 if not IsPackageDisabled(target, package) and not (
358 GetStablePackageVersion(atom, True) and
359 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100360 return False
361 return True
362 except cros_build_lib.RunCommandError:
363 # Fails - The target has likely never been initialized before.
364 return False
365
366
367def RemovePackageMask(target):
368 """Removes a package.mask file for the given platform.
369
370 The pre-existing package.mask files can mess with the keywords.
371
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500372 Args:
373 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100374 """
375 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700376 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100377
378
Zdenek Behan508dcce2011-12-05 15:39:32 +0100379# Main functions performing the actual update steps.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700380def RebuildLibtool(root='/'):
Mike Frysingerc880a962013-11-08 13:59:06 -0500381 """Rebuild libtool as needed
382
383 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
384 gcc, libtool will break. We can't use binary packages either as those will
385 most likely be compiled against the previous version of gcc.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700386
387 Args:
388 root: The install root where we want libtool rebuilt.
Mike Frysingerc880a962013-11-08 13:59:06 -0500389 """
390 needs_update = False
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700391 with open(os.path.join(root, 'usr/bin/libtool')) as f:
Mike Frysingerc880a962013-11-08 13:59:06 -0500392 for line in f:
393 # Look for a line like:
394 # sys_lib_search_path_spec="..."
395 # It'll be a list of paths and gcc will be one of them.
396 if line.startswith('sys_lib_search_path_spec='):
397 line = line.rstrip()
398 for path in line.split('=', 1)[1].strip('"').split():
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700399 if not os.path.exists(os.path.join(root, path.lstrip(os.path.sep))):
Mike Frysinger383367e2014-09-16 15:06:17 -0400400 print('Rebuilding libtool after gcc upgrade')
401 print(' %s' % line)
402 print(' missing path: %s' % path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500403 needs_update = True
404 break
405
406 if needs_update:
407 break
408
409 if needs_update:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700410 cmd = [EMERGE_CMD, '--oneshot']
411 if root != '/':
412 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
413 cmd.append('sys-devel/libtool')
Mike Frysingerc880a962013-11-08 13:59:06 -0500414 cros_build_lib.RunCommand(cmd)
415
416
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700417def UpdateTargets(targets, usepkg, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100418 """Determines which packages need update/unmerge and defers to portage.
419
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500420 Args:
421 targets: The list of targets to update
422 usepkg: Copies the commandline option
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700423 root: The install root in which we want packages updated.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100424 """
David James90239b92012-11-05 15:31:34 -0800425 # Remove keyword files created by old versions of cros_setup_toolchains.
426 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100427
428 # For each target, we do two things. Figure out the list of updates,
429 # and figure out the appropriate keywords/masks. Crossdev will initialize
430 # these, but they need to be regenerated on every update.
Mike Frysinger383367e2014-09-16 15:06:17 -0400431 print('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800432 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100433 for target in targets:
434 # Record the highest needed version for each target, for masking purposes.
435 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100436 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100438 if IsPackageDisabled(target, package):
439 continue
440 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700441 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100442 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200443 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100444 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100445
Zdenek Behan508dcce2011-12-05 15:39:32 +0100446 packages = []
447 for pkg in mergemap:
448 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200449 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800450 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100451
452 if not packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400453 print('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800454 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100455
Mike Frysinger383367e2014-09-16 15:06:17 -0400456 print('Updating packages:')
457 print(packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100458
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100459 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460 if usepkg:
461 cmd.extend(['--getbinpkg', '--usepkgonly'])
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700462 if root != '/':
463 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100464
465 cmd.extend(packages)
466 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800467 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100468
469
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700470def CleanTargets(targets, root='/'):
471 """Unmerges old packages that are assumed unnecessary.
472
473 Args:
474 targets: The list of targets to clean up.
475 root: The install root in which we want packages cleaned up.
476 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100477 unmergemap = {}
478 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100479 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100480 if IsPackageDisabled(target, package):
481 continue
482 pkg = GetPortagePackage(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700483 current = GetInstalledPackageVersions(pkg, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100484 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700485 # NOTE: This refers to installed packages (vartree) rather than the
486 # Portage version (porttree and/or bintree) when determining the current
487 # version. While this isn't the most accurate thing to do, it is probably
488 # a good simple compromise, which should have the desired result of
489 # uninstalling everything but the latest installed version. In
490 # particular, using the bintree (--usebinpkg) requires a non-trivial
491 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200492 desired_num = VersionListToNumeric(target, package, desired, True)
493 if not set(desired_num).issubset(current):
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700494 print('Error detecting stable version for %s, skipping clean!' % pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200495 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100496 unmergemap[pkg] = set(current).difference(desired_num)
497
498 # Cleaning doesn't care about consistency and rebuilding package.* files.
499 packages = []
500 for pkg, vers in unmergemap.iteritems():
501 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
502
503 if packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400504 print('Cleaning packages:')
505 print(packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100506 cmd = [EMERGE_CMD, '--unmerge']
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700507 if root != '/':
508 cmd.extend(['--sysroot=%s' % root, '--root=%s' % root])
Zdenek Behan508dcce2011-12-05 15:39:32 +0100509 cmd.extend(packages)
510 cros_build_lib.RunCommand(cmd)
511 else:
Mike Frysinger383367e2014-09-16 15:06:17 -0400512 print('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100513
514
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700515def SelectActiveToolchains(targets, suffixes, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100516 """Runs gcc-config and binutils-config to select the desired.
517
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500518 Args:
519 targets: The targets to select
520 suffixes: Optional target-specific hacks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700521 root: The root where we want to select toolchain versions.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100522 """
523 for package in ['gcc', 'binutils']:
524 for target in targets:
525 # Pick the first version in the numbered list as the selected one.
526 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700527 desired_num = VersionListToNumeric(target, package, desired, True,
528 root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100529 desired = desired_num[0]
530 # *-config does not play revisions, strip them, keep just PV.
531 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
532
533 if target == 'host':
534 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800535 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100536
537 # And finally, attach target to it.
538 desired = '%s-%s' % (target, desired)
539
540 # Target specific hacks
541 if package in suffixes:
542 if target in suffixes[package]:
543 desired += suffixes[package][target]
544
David James7ec5efc2012-11-06 09:39:49 -0800545 extra_env = {'CHOST': target}
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700546 if root != '/':
547 extra_env['ROOT'] = root
David James7ec5efc2012-11-06 09:39:49 -0800548 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500549 result = cros_build_lib.RunCommand(
550 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
551 current = result.output.splitlines()[0]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700552
553 # Do not reconfig when the current is live or nothing needs to be done.
554 extra_env = {'ROOT': root} if root != '/' else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100555 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500556 cmd = [package + '-config', desired]
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700557 cros_build_lib.RunCommand(cmd, print_cmd=False, extra_env=extra_env)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100558
559
Mike Frysinger35247af2012-11-16 18:58:06 -0500560def ExpandTargets(targets_wanted):
561 """Expand any possible toolchain aliases into full targets
562
563 This will expand 'all' and 'sdk' into the respective toolchain tuples.
564
565 Args:
566 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500567
Mike Frysinger35247af2012-11-16 18:58:06 -0500568 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300569 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500570 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500571 targets_wanted = set(targets_wanted)
Gilad Arnold8195b532015-04-07 10:56:30 +0300572 if targets_wanted in (set(['boards']), set(['bricks'])):
573 # Only pull targets from the included boards/bricks.
574 return {}
575
576 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500577 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300578 return all_targets
579 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500580 # Filter out all the non-sdk toolchains as we don't want to mess
581 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300582 return toolchain.FilterToolchains(all_targets, 'sdk', True)
583
584 # Verify user input.
585 nonexistent = targets_wanted.difference(all_targets)
586 if nonexistent:
587 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
588 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500589
590
David Jamesf8c672f2012-11-06 13:38:11 -0800591def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700592 targets_wanted, boards_wanted, bricks_wanted, root='/'):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100593 """Performs all steps to create a synchronized toolchain enviroment.
594
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500595 Args:
596 usepkg: Use prebuilt packages
597 deleteold: Unmerge deprecated packages
598 hostonly: Only setup the host toolchain
599 reconfig: Reload crossdev config and reselect toolchains
600 targets_wanted: All the targets to update
601 boards_wanted: Load targets from these boards
Gilad Arnold8195b532015-04-07 10:56:30 +0300602 bricks_wanted: Load targets from these bricks
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700603 root: The root in which to install the toolchains.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100604 """
David Jamesf8c672f2012-11-06 13:38:11 -0800605 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100606 if not hostonly:
607 # For hostonly, we can skip most of the below logic, much of which won't
608 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500609 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400610
Gilad Arnold8195b532015-04-07 10:56:30 +0300611 # Now re-add any targets that might be from this board/brick. This is to
612 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400613 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800614 targets.update(toolchain.GetToolchainsForBoard(board))
Gilad Arnold8195b532015-04-07 10:56:30 +0300615 for brick in bricks_wanted:
616 targets.update(toolchain.GetToolchainsForBrick(brick))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100617
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100618 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400619 for target in targets:
620 if TargetIsInitialized(target):
621 reconfig_targets[target] = targets[target]
622 else:
623 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100624 if crossdev_targets:
Mike Frysinger383367e2014-09-16 15:06:17 -0400625 print('The following targets need to be re-initialized:')
626 print(crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800627 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200628 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800629 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100630
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100631 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400632 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100633
634 # Now update all packages.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700635 if UpdateTargets(targets, usepkg, root=root) or crossdev_targets or reconfig:
636 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES, root=root)
David James7ec5efc2012-11-06 09:39:49 -0800637
638 if deleteold:
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700639 CleanTargets(targets, root=root)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100640
Mike Frysingerc880a962013-11-08 13:59:06 -0500641 # Now that we've cleared out old versions, see if we need to rebuild
642 # anything. Can't do this earlier as it might not be broken.
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700643 RebuildLibtool(root=root)
Mike Frysingerc880a962013-11-08 13:59:06 -0500644
Zdenek Behan508dcce2011-12-05 15:39:32 +0100645
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700646def ShowConfig(name):
647 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500648
649 Args:
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700650 name: The board name or brick locator to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500651 """
Bertrand SIMONNET3c77bc92015-03-20 14:28:54 -0700652 if workspace_lib.IsLocator(name):
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700653 toolchains = toolchain.GetToolchainsForBrick(name)
654 else:
655 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500656 # Make sure we display the default toolchain first.
Mike Frysinger383367e2014-09-16 15:06:17 -0400657 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800658 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400659 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500660
661
Mike Frysinger35247af2012-11-16 18:58:06 -0500662def GeneratePathWrapper(root, wrappath, path):
663 """Generate a shell script to execute another shell script
664
665 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
666 argv[0] won't be pointing to the correct path, generate a shell script that
667 just executes another program with its full path.
668
669 Args:
670 root: The root tree to generate scripts inside of
671 wrappath: The full path (inside |root|) to create the wrapper
672 path: The target program which this wrapper will execute
673 """
674 replacements = {
675 'path': path,
676 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
677 }
678 wrapper = """#!/bin/sh
679base=$(realpath "$0")
680basedir=${base%%/*}
681exec "${basedir}/%(relroot)s%(path)s" "$@"
682""" % replacements
683 root_wrapper = root + wrappath
684 if os.path.islink(root_wrapper):
685 os.unlink(root_wrapper)
686 else:
687 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
688 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400689 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500690
691
692def FileIsCrosSdkElf(elf):
693 """Determine if |elf| is an ELF that we execute in the cros_sdk
694
695 We don't need this to be perfect, just quick. It makes sure the ELF
696 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
697
698 Args:
699 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500700
Mike Frysinger35247af2012-11-16 18:58:06 -0500701 Returns:
702 True if we think |elf| is a native ELF
703 """
704 with open(elf) as f:
705 data = f.read(20)
706 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
707 return (data[0:4] == '\x7fELF' and
708 data[4] == '\x02' and
709 data[5] == '\x01' and
710 data[18] == '\x3e')
711
712
713def IsPathPackagable(ptype, path):
714 """Should the specified file be included in a toolchain package?
715
716 We only need to handle files as we'll create dirs as we need them.
717
718 Further, trim files that won't be useful:
719 - non-english translations (.mo) since it'd require env vars
720 - debug files since these are for the host compiler itself
721 - info/man pages as they're big, and docs are online, and the
722 native docs should work fine for the most part (`man gcc`)
723
724 Args:
725 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
726 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500727
Mike Frysinger35247af2012-11-16 18:58:06 -0500728 Returns:
729 True if we want to include this path in the package
730 """
731 return not (ptype in ('dir',) or
732 path.startswith('/usr/lib/debug/') or
733 os.path.splitext(path)[1] == '.mo' or
734 ('/man/' in path or '/info/' in path))
735
736
737def ReadlinkRoot(path, root):
738 """Like os.readlink(), but relative to a |root|
739
740 Args:
741 path: The symlink to read
742 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500743
Mike Frysinger35247af2012-11-16 18:58:06 -0500744 Returns:
745 A fully resolved symlink path
746 """
747 while os.path.islink(root + path):
748 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
749 return path
750
751
752def _GetFilesForTarget(target, root='/'):
753 """Locate all the files to package for |target|
754
755 This does not cover ELF dependencies.
756
757 Args:
758 target: The toolchain target name
759 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500760
Mike Frysinger35247af2012-11-16 18:58:06 -0500761 Returns:
762 A tuple of a set of all packable paths, and a set of all paths which
763 are also native ELFs
764 """
765 paths = set()
766 elfs = set()
767
768 # Find all the files owned by the packages for this target.
769 for pkg in GetTargetPackages(target):
770 # Ignore packages that are part of the target sysroot.
771 if pkg in ('kernel', 'libc'):
772 continue
773
Rahul Chaudhry4b803052015-05-13 15:25:56 -0700774 # Skip Go compiler from redistributable packages.
775 # The "go" executable has GOROOT=/usr/lib/go/${CTARGET} hardcoded
776 # into it. Due to this, the toolchain cannot be unpacked anywhere
777 # else and be readily useful. To enable packaging Go, we need to:
778 # -) Tweak the wrappers/environment to override GOROOT
779 # automatically based on the unpack location.
780 # -) Make sure the ELF dependency checking and wrapping logic
781 # below skips the Go toolchain executables and libraries.
782 # -) Make sure the packaging process maintains the relative
783 # timestamps of precompiled standard library packages.
784 # (see dev-lang/go ebuild for details).
785 if pkg == 'ex_go':
786 continue
787
Mike Frysinger35247af2012-11-16 18:58:06 -0500788 atom = GetPortagePackage(target, pkg)
789 cat, pn = atom.split('/')
Gilad Arnold2dab78c2015-05-21 14:43:33 -0700790 ver = GetInstalledPackageVersions(atom, root=root)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700791 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500792
793 # pylint: disable=E1101
794 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
795 settings=portage.settings)
796 contents = dblink.getcontents()
797 for obj in contents:
798 ptype = contents[obj][0]
799 if not IsPathPackagable(ptype, obj):
800 continue
801
802 if ptype == 'obj':
803 # For native ELFs, we need to pull in their dependencies too.
804 if FileIsCrosSdkElf(obj):
805 elfs.add(obj)
806 paths.add(obj)
807
808 return paths, elfs
809
810
811def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500812 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500813 """Link in all packable files and their runtime dependencies
814
815 This also wraps up executable ELFs with helper scripts.
816
817 Args:
818 output_dir: The output directory to store files
819 paths: All the files to include
820 elfs: All the files which are ELFs (a subset of |paths|)
821 ldpaths: A dict of static ldpath information
822 path_rewrite_func: User callback to rewrite paths in output_dir
823 root: The root path to pull all packages/files from
824 """
825 # Link in all the files.
826 sym_paths = []
827 for path in paths:
828 new_path = path_rewrite_func(path)
829 dst = output_dir + new_path
830 osutils.SafeMakedirs(os.path.dirname(dst))
831
832 # Is this a symlink which we have to rewrite or wrap?
833 # Delay wrap check until after we have created all paths.
834 src = root + path
835 if os.path.islink(src):
836 tgt = os.readlink(src)
837 if os.path.sep in tgt:
838 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
839
840 # Rewrite absolute links to relative and then generate the symlink
841 # ourselves. All other symlinks can be hardlinked below.
842 if tgt[0] == '/':
843 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
844 os.symlink(tgt, dst)
845 continue
846
847 os.link(src, dst)
848
849 # Now see if any of the symlinks need to be wrapped.
850 for sym, tgt in sym_paths:
851 if tgt in elfs:
852 GeneratePathWrapper(output_dir, sym, tgt)
853
854 # Locate all the dependencies for all the ELFs. Stick them all in the
855 # top level "lib" dir to make the wrapper simpler. This exact path does
856 # not matter since we execute ldso directly, and we tell the ldso the
857 # exact path to search for its libraries.
858 libdir = os.path.join(output_dir, 'lib')
859 osutils.SafeMakedirs(libdir)
860 donelibs = set()
861 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400862 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500863 interp = e['interp']
864 if interp:
865 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400866 interp = os.path.join('/lib', os.path.basename(interp))
867 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
868 libpaths=e['rpath'] + e['runpath'])
Mike Frysinger35247af2012-11-16 18:58:06 -0500869
870 for lib, lib_data in e['libs'].iteritems():
871 if lib in donelibs:
872 continue
873
874 src = path = lib_data['path']
875 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -0700876 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -0500877 continue
878 donelibs.add(lib)
879
880 # Needed libs are the SONAME, but that is usually a symlink, not a
881 # real file. So link in the target rather than the symlink itself.
882 # We have to walk all the possible symlinks (SONAME could point to a
883 # symlink which points to a symlink), and we have to handle absolute
884 # ourselves (since we have a "root" argument).
885 dst = os.path.join(libdir, os.path.basename(path))
886 src = ReadlinkRoot(src, root)
887
888 os.link(root + src, dst)
889
890
891def _EnvdGetVar(envd, var):
892 """Given a Gentoo env.d file, extract a var from it
893
894 Args:
895 envd: The env.d file to load (may be a glob path)
896 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -0500897
Mike Frysinger35247af2012-11-16 18:58:06 -0500898 Returns:
899 The value of |var|
900 """
901 envds = glob.glob(envd)
902 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
903 envd = envds[0]
904 return cros_build_lib.LoadKeyValueFile(envd)[var]
905
906
907def _ProcessBinutilsConfig(target, output_dir):
908 """Do what binutils-config would have done"""
909 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500910
911 # Locate the bin dir holding the gold linker.
912 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
913 target, 'binutils-bin')
914 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -0500915 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500916 if not srcpath:
917 # Maybe this target doesn't support gold.
918 globpath = os.path.join(binutils_bin_path, '*')
919 srcpath = glob.glob(globpath)
920 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
921 % globpath)
922 srcpath = srcpath[0]
923 ld_path = os.path.join(srcpath, 'ld')
924 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
925 ld_path = os.path.join(srcpath, 'ld.bfd')
926 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
927 ld_path = os.path.join(srcpath, 'ld.gold')
928 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
929 % ld_path)
930
931 # Nope, no gold support to be found.
932 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -0700933 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500934 else:
935 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
936 gold_supported = True
Mike Frysinger78b7a812014-11-26 19:45:23 -0500937 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500938
Mike Frysinger78b7a812014-11-26 19:45:23 -0500939 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -0500940 gccpath = os.path.join('/usr', 'libexec', 'gcc')
941 for prog in os.listdir(output_dir + srcpath):
942 # Skip binaries already wrapped.
943 if not prog.endswith('.real'):
944 GeneratePathWrapper(output_dir, binpath + prog,
945 os.path.join(srcpath, prog))
946 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
947 os.path.join(srcpath, prog))
948
David James27ac4ae2012-12-03 23:16:15 -0800949 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500950 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
951 if gold_supported:
952 envd += '-gold'
Mike Frysinger35247af2012-11-16 18:58:06 -0500953 srcpath = _EnvdGetVar(envd, 'LIBPATH')
954 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
955 output_dir + libpath)
956
957
958def _ProcessGccConfig(target, output_dir):
959 """Do what gcc-config would have done"""
960 binpath = '/bin'
961 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
962 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
963 for prog in os.listdir(output_dir + srcpath):
964 # Skip binaries already wrapped.
965 if (not prog.endswith('.real') and
966 not prog.endswith('.elf') and
967 prog.startswith(target)):
968 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
969 os.path.join(srcpath, prog))
970 return srcpath
971
972
Frank Henigman179ec7c2015-02-06 03:01:09 -0500973def _ProcessSysrootWrappers(_target, output_dir, srcpath):
974 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -0500975 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -0500976 for sysroot_wrapper in glob.glob(os.path.join(
977 output_dir + srcpath, 'sysroot_wrapper*')):
978 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
979 for num in xrange(len(contents)):
980 if '@CCACHE_DEFAULT@' in contents[num]:
981 contents[num] = 'use_ccache = False'
982 break
983 # Can't update the wrapper in place since it's a hardlink to a file in /.
984 os.unlink(sysroot_wrapper)
985 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
986 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500987
988
989def _ProcessDistroCleanups(target, output_dir):
990 """Clean up the tree and remove all distro-specific requirements
991
992 Args:
993 target: The toolchain target name
994 output_dir: The output directory to clean up
995 """
996 _ProcessBinutilsConfig(target, output_dir)
997 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -0500998 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Mike Frysinger35247af2012-11-16 18:58:06 -0500999
1000 osutils.RmDir(os.path.join(output_dir, 'etc'))
1001
1002
1003def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
1004 """Setup a tree from the packages for the specified target
1005
1006 This populates a path with all the files from toolchain packages so that
1007 a tarball can easily be generated from the result.
1008
1009 Args:
1010 target: The target to create a packagable root from
1011 output_dir: The output directory to place all the files
1012 ldpaths: A dict of static ldpath information
1013 root: The root path to pull all packages/files from
1014 """
1015 # Find all the files owned by the packages for this target.
1016 paths, elfs = _GetFilesForTarget(target, root=root)
1017
1018 # Link in all the package's files, any ELF dependencies, and wrap any
1019 # executable ELFs with helper scripts.
1020 def MoveUsrBinToBin(path):
1021 """Move /usr/bin to /bin so people can just use that toplevel dir"""
1022 return path[4:] if path.startswith('/usr/bin/') else path
1023 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
1024 path_rewrite_func=MoveUsrBinToBin, root=root)
1025
1026 # The packages, when part of the normal distro, have helper scripts
1027 # that setup paths and such. Since we are making this standalone, we
1028 # need to preprocess all that ourselves.
1029 _ProcessDistroCleanups(target, output_dir)
1030
1031
1032def CreatePackages(targets_wanted, output_dir, root='/'):
1033 """Create redistributable cross-compiler packages for the specified targets
1034
1035 This creates toolchain packages that should be usable in conjunction with
1036 a downloaded sysroot (created elsewhere).
1037
1038 Tarballs (one per target) will be created in $PWD.
1039
1040 Args:
Don Garrett25f309a2014-03-19 14:02:12 -07001041 targets_wanted: The targets to package up.
1042 output_dir: The directory to put the packages in.
1043 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -05001044 """
Ralph Nathan03047282015-03-23 11:09:32 -07001045 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001046 osutils.SafeMakedirs(output_dir)
1047 ldpaths = lddtree.LoadLdpaths(root)
1048 targets = ExpandTargets(targets_wanted)
1049
David James4bc13702013-03-26 08:08:04 -07001050 with osutils.TempDir() as tempdir:
Mike Frysinger35247af2012-11-16 18:58:06 -05001051 # We have to split the root generation from the compression stages. This is
1052 # because we hardlink in all the files (to avoid overhead of reading/writing
1053 # the copies multiple times). But tar gets angry if a file's hardlink count
1054 # changes from when it starts reading a file to when it finishes.
1055 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
1056 for target in targets:
1057 output_target_dir = os.path.join(tempdir, target)
1058 queue.put([target, output_target_dir, ldpaths, root])
1059
1060 # Build the tarball.
1061 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
1062 for target in targets:
1063 tar_file = os.path.join(output_dir, target + '.tar.xz')
1064 queue.put([tar_file, os.path.join(tempdir, target)])
1065
1066
Brian Harring30675052012-02-29 12:18:22 -08001067def main(argv):
Mike Frysinger0c808452014-11-06 17:30:23 -05001068 parser = commandline.ArgumentParser(description=__doc__)
1069 parser.add_argument('-u', '--nousepkg',
1070 action='store_false', dest='usepkg', default=True,
1071 help='Use prebuilt packages if possible')
1072 parser.add_argument('-d', '--deleteold',
1073 action='store_true', dest='deleteold', default=False,
1074 help='Unmerge deprecated packages')
1075 parser.add_argument('-t', '--targets',
1076 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001077 help="Comma separated list of tuples. Special keywords "
1078 "'host', 'sdk', 'boards', 'bricks' and 'all' are "
1079 "allowed. Defaults to 'sdk'.")
1080 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1081 help='Comma separated list of boards whose toolchains we '
1082 'will always include. Default: none')
1083 parser.add_argument('--include-bricks', default='', metavar='BRICKS',
1084 help='Comma separated list of bricks whose toolchains we '
1085 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001086 parser.add_argument('--hostonly',
1087 dest='hostonly', default=False, action='store_true',
1088 help='Only setup the host toolchain. '
1089 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001090 parser.add_argument('--show-board-cfg', '--show-cfg',
1091 dest='cfg_name', default=None,
1092 help='Board or brick to list toolchains tuples for')
Mike Frysinger0c808452014-11-06 17:30:23 -05001093 parser.add_argument('--create-packages',
1094 action='store_true', default=False,
1095 help='Build redistributable packages')
1096 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1097 help='Output directory')
1098 parser.add_argument('--reconfig', default=False, action='store_true',
1099 help='Reload crossdev config and reselect toolchains')
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001100 parser.add_argument('--sysroot', type='path',
1101 help='The sysroot in which to install the toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001102
Mike Frysinger0c808452014-11-06 17:30:23 -05001103 options = parser.parse_args(argv)
1104 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001105
Mike Frysinger35247af2012-11-16 18:58:06 -05001106 # Figure out what we're supposed to do and reject conflicting options.
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001107 if options.cfg_name and options.create_packages:
Mike Frysinger35247af2012-11-16 18:58:06 -05001108 parser.error('conflicting options: create-packages & show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001109
Gilad Arnold8195b532015-04-07 10:56:30 +03001110 targets_wanted = set(options.targets.split(','))
1111 boards_wanted = (set(options.include_boards.split(','))
1112 if options.include_boards else set())
1113 bricks_wanted = (set(options.include_bricks.split(','))
1114 if options.include_bricks else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001115
Rahul Chaudhry4b803052015-05-13 15:25:56 -07001116 # pylint: disable=global-statement
1117 global TARGET_GO_ENABLED
1118 if GetStablePackageVersion('sys-devel/crossdev', True) < '20150527':
1119 # Crossdev --ex-pkg flag was added in version 20150527.
1120 # Disable Go toolchain until the chroot gets a newer crossdev.
1121 TARGET_GO_ENABLED = ()
Rahul Chaudhry4b803052015-05-13 15:25:56 -07001122
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001123 if options.cfg_name:
1124 ShowConfig(options.cfg_name)
Mike Frysinger35247af2012-11-16 18:58:06 -05001125 elif options.create_packages:
1126 cros_build_lib.AssertInsideChroot()
1127 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001128 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001129 else:
1130 cros_build_lib.AssertInsideChroot()
1131 # This has to be always run as root.
1132 if os.geteuid() != 0:
1133 cros_build_lib.Die('this script must be run as root')
1134
1135 Crossdev.Load(options.reconfig)
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001136 root = options.sysroot or '/'
Mike Frysinger35247af2012-11-16 18:58:06 -05001137 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001138 options.reconfig, targets_wanted, boards_wanted,
Gilad Arnold2dab78c2015-05-21 14:43:33 -07001139 bricks_wanted, root=root)
Mike Frysinger35247af2012-11-16 18:58:06 -05001140 Crossdev.Save()
1141
1142 return 0