blob: 25a2e8bb5808f6af88f7b4fbd3509ae2e9ec31dc [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
David James66a09c42012-11-05 13:31:38 -0800222def GetInstalledPackageVersions(atom):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100223 """Extracts the list of current versions of a target, package pair.
224
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500225 Args:
226 atom: The atom to operate on (e.g. sys-devel/gcc)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100227
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500228 Returns:
229 The list of versions of the package currently installed.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100230 """
Zdenek Behan508dcce2011-12-05 15:39:32 +0100231 versions = []
Mike Frysinger506e75f2012-12-17 14:21:13 -0500232 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800233 for pkg in portage.db['/']['vartree'].dbapi.match(atom, use_cache=0):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100234 version = portage.versions.cpv_getversion(pkg)
235 versions.append(version)
236 return versions
237
238
David James90239b92012-11-05 15:31:34 -0800239def GetStablePackageVersion(atom, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100240 """Extracts the current stable version for a given package.
241
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500242 Args:
243 atom: The target/package to operate on eg. i686-pc-linux-gnu,gcc
244 installed: Whether we want installed packages or ebuilds
Zdenek Behan508dcce2011-12-05 15:39:32 +0100245
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500246 Returns:
247 A string containing the latest version.
Zdenek Behan508dcce2011-12-05 15:39:32 +0100248 """
David James90239b92012-11-05 15:31:34 -0800249 pkgtype = 'vartree' if installed else 'porttree'
Mike Frysinger506e75f2012-12-17 14:21:13 -0500250 # pylint: disable=E1101
David James90239b92012-11-05 15:31:34 -0800251 cpv = portage.best(portage.db['/'][pkgtype].dbapi.match(atom, use_cache=0))
252 return portage.versions.cpv_getversion(cpv) if cpv else None
Zdenek Behan508dcce2011-12-05 15:39:32 +0100253
254
Zdenek Behan699ddd32012-04-13 07:14:08 +0200255def VersionListToNumeric(target, package, versions, installed):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100256 """Resolves keywords in a given version list for a particular package.
257
258 Resolving means replacing PACKAGE_STABLE with the actual number.
259
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500260 Args:
261 target: The target to operate on (e.g. i686-pc-linux-gnu)
262 package: The target/package to operate on (e.g. gcc)
263 versions: List of versions to resolve
264 installed: Query installed packages
Zdenek Behan508dcce2011-12-05 15:39:32 +0100265
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500266 Returns:
267 List of purely numeric versions equivalent to argument
Zdenek Behan508dcce2011-12-05 15:39:32 +0100268 """
269 resolved = []
David James90239b92012-11-05 15:31:34 -0800270 atom = GetPortagePackage(target, package)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100271 for version in versions:
272 if version == PACKAGE_STABLE:
David James90239b92012-11-05 15:31:34 -0800273 resolved.append(GetStablePackageVersion(atom, installed))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100274 elif version != PACKAGE_NONE:
275 resolved.append(version)
276 return resolved
277
278
279def GetDesiredPackageVersions(target, package):
280 """Produces the list of desired versions for each target, package pair.
281
282 The first version in the list is implicitly treated as primary, ie.
283 the version that will be initialized by crossdev and selected.
284
285 If the version is PACKAGE_STABLE, it really means the current version which
286 is emerged by using the package atom with no particular version key.
287 Since crossdev unmasks all packages by default, this will actually
288 mean 'unstable' in most cases.
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)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100293
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500294 Returns:
295 A list composed of either a version string, PACKAGE_STABLE
Zdenek Behan508dcce2011-12-05 15:39:32 +0100296 """
297 packagemap = GetPackageMap(target)
298
299 versions = []
300 if package in packagemap:
301 versions.append(packagemap[package])
302
303 return versions
304
305
306def TargetIsInitialized(target):
307 """Verifies if the given list of targets has been correctly initialized.
308
309 This determines whether we have to call crossdev while emerging
310 toolchain packages or can do it using emerge. Emerge is naturally
311 preferred, because all packages can be updated in a single pass.
312
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500313 Args:
314 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100315
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500316 Returns:
317 True if |target| is completely initialized, else False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100318 """
319 # Check if packages for the given target all have a proper version.
320 try:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100321 for package in GetTargetPackages(target):
David James66a09c42012-11-05 13:31:38 -0800322 atom = GetPortagePackage(target, package)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100323 # Do we even want this package && is it initialized?
David James90239b92012-11-05 15:31:34 -0800324 if not IsPackageDisabled(target, package) and not (
325 GetStablePackageVersion(atom, True) and
326 GetStablePackageVersion(atom, False)):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100327 return False
328 return True
329 except cros_build_lib.RunCommandError:
330 # Fails - The target has likely never been initialized before.
331 return False
332
333
334def RemovePackageMask(target):
335 """Removes a package.mask file for the given platform.
336
337 The pre-existing package.mask files can mess with the keywords.
338
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500339 Args:
340 target: The target to operate on (e.g. i686-pc-linux-gnu)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100341 """
342 maskfile = os.path.join('/etc/portage/package.mask', 'cross-' + target)
Brian Harringaf019fb2012-05-10 15:06:13 -0700343 osutils.SafeUnlink(maskfile)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100344
345
Zdenek Behan508dcce2011-12-05 15:39:32 +0100346# Main functions performing the actual update steps.
Mike Frysingerc880a962013-11-08 13:59:06 -0500347def RebuildLibtool():
348 """Rebuild libtool as needed
349
350 Libtool hardcodes full paths to internal gcc files, so whenever we upgrade
351 gcc, libtool will break. We can't use binary packages either as those will
352 most likely be compiled against the previous version of gcc.
353 """
354 needs_update = False
355 with open('/usr/bin/libtool') as f:
356 for line in f:
357 # Look for a line like:
358 # sys_lib_search_path_spec="..."
359 # It'll be a list of paths and gcc will be one of them.
360 if line.startswith('sys_lib_search_path_spec='):
361 line = line.rstrip()
362 for path in line.split('=', 1)[1].strip('"').split():
363 if not os.path.exists(path):
Mike Frysinger383367e2014-09-16 15:06:17 -0400364 print('Rebuilding libtool after gcc upgrade')
365 print(' %s' % line)
366 print(' missing path: %s' % path)
Mike Frysingerc880a962013-11-08 13:59:06 -0500367 needs_update = True
368 break
369
370 if needs_update:
371 break
372
373 if needs_update:
374 cmd = [EMERGE_CMD, '--oneshot', 'sys-devel/libtool']
375 cros_build_lib.RunCommand(cmd)
376
377
Zdenek Behan508dcce2011-12-05 15:39:32 +0100378def UpdateTargets(targets, usepkg):
379 """Determines which packages need update/unmerge and defers to portage.
380
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500381 Args:
382 targets: The list of targets to update
383 usepkg: Copies the commandline option
Zdenek Behan508dcce2011-12-05 15:39:32 +0100384 """
David James90239b92012-11-05 15:31:34 -0800385 # Remove keyword files created by old versions of cros_setup_toolchains.
386 osutils.SafeUnlink('/etc/portage/package.keywords/cross-host')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100387
388 # For each target, we do two things. Figure out the list of updates,
389 # and figure out the appropriate keywords/masks. Crossdev will initialize
390 # these, but they need to be regenerated on every update.
Mike Frysinger383367e2014-09-16 15:06:17 -0400391 print('Determining required toolchain updates...')
David James90239b92012-11-05 15:31:34 -0800392 mergemap = {}
Zdenek Behan508dcce2011-12-05 15:39:32 +0100393 for target in targets:
394 # Record the highest needed version for each target, for masking purposes.
395 RemovePackageMask(target)
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100396 for package in GetTargetPackages(target):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100397 # Portage name for the package
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100398 if IsPackageDisabled(target, package):
399 continue
400 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800401 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100402 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200403 desired_num = VersionListToNumeric(target, package, desired, False)
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100404 mergemap[pkg] = set(desired_num).difference(current)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100405
Zdenek Behan508dcce2011-12-05 15:39:32 +0100406 packages = []
407 for pkg in mergemap:
408 for ver in mergemap[pkg]:
Zdenek Behan677b6d82012-04-11 05:31:47 +0200409 if ver != PACKAGE_NONE:
David James90239b92012-11-05 15:31:34 -0800410 packages.append(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100411
412 if not packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400413 print('Nothing to update!')
David Jamesf8c672f2012-11-06 13:38:11 -0800414 return False
Zdenek Behan508dcce2011-12-05 15:39:32 +0100415
Mike Frysinger383367e2014-09-16 15:06:17 -0400416 print('Updating packages:')
417 print(packages)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100418
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100419 cmd = [EMERGE_CMD, '--oneshot', '--update']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100420 if usepkg:
421 cmd.extend(['--getbinpkg', '--usepkgonly'])
422
423 cmd.extend(packages)
424 cros_build_lib.RunCommand(cmd)
David Jamesf8c672f2012-11-06 13:38:11 -0800425 return True
Zdenek Behan508dcce2011-12-05 15:39:32 +0100426
427
428def CleanTargets(targets):
429 """Unmerges old packages that are assumed unnecessary."""
430 unmergemap = {}
431 for target in targets:
Zdenek Behanf4d18a02012-03-22 15:45:05 +0100432 for package in GetTargetPackages(target):
Zdenek Behan4eb6fd22012-03-12 17:00:56 +0100433 if IsPackageDisabled(target, package):
434 continue
435 pkg = GetPortagePackage(target, package)
David James66a09c42012-11-05 13:31:38 -0800436 current = GetInstalledPackageVersions(pkg)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100437 desired = GetDesiredPackageVersions(target, package)
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700438 # NOTE: This refers to installed packages (vartree) rather than the
439 # Portage version (porttree and/or bintree) when determining the current
440 # version. While this isn't the most accurate thing to do, it is probably
441 # a good simple compromise, which should have the desired result of
442 # uninstalling everything but the latest installed version. In
443 # particular, using the bintree (--usebinpkg) requires a non-trivial
444 # binhost sync and is probably more complex than useful.
Zdenek Behan699ddd32012-04-13 07:14:08 +0200445 desired_num = VersionListToNumeric(target, package, desired, True)
446 if not set(desired_num).issubset(current):
Gilad Arnold2fcb0aa2015-05-21 14:51:41 -0700447 print('Error detecting stable version for %s, skipping clean!' % pkg)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200448 return
Zdenek Behan508dcce2011-12-05 15:39:32 +0100449 unmergemap[pkg] = set(current).difference(desired_num)
450
451 # Cleaning doesn't care about consistency and rebuilding package.* files.
452 packages = []
453 for pkg, vers in unmergemap.iteritems():
454 packages.extend('=%s-%s' % (pkg, ver) for ver in vers if ver != '9999')
455
456 if packages:
Mike Frysinger383367e2014-09-16 15:06:17 -0400457 print('Cleaning packages:')
458 print(packages)
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100459 cmd = [EMERGE_CMD, '--unmerge']
Zdenek Behan508dcce2011-12-05 15:39:32 +0100460 cmd.extend(packages)
461 cros_build_lib.RunCommand(cmd)
462 else:
Mike Frysinger383367e2014-09-16 15:06:17 -0400463 print('Nothing to clean!')
Zdenek Behan508dcce2011-12-05 15:39:32 +0100464
465
466def SelectActiveToolchains(targets, suffixes):
467 """Runs gcc-config and binutils-config to select the desired.
468
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500469 Args:
470 targets: The targets to select
471 suffixes: Optional target-specific hacks
Zdenek Behan508dcce2011-12-05 15:39:32 +0100472 """
473 for package in ['gcc', 'binutils']:
474 for target in targets:
475 # Pick the first version in the numbered list as the selected one.
476 desired = GetDesiredPackageVersions(target, package)
Zdenek Behan699ddd32012-04-13 07:14:08 +0200477 desired_num = VersionListToNumeric(target, package, desired, True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100478 desired = desired_num[0]
479 # *-config does not play revisions, strip them, keep just PV.
480 desired = portage.versions.pkgsplit('%s-%s' % (package, desired))[1]
481
482 if target == 'host':
483 # *-config is the only tool treating host identically (by tuple).
David James27ac4ae2012-12-03 23:16:15 -0800484 target = toolchain.GetHostTuple()
Zdenek Behan508dcce2011-12-05 15:39:32 +0100485
486 # And finally, attach target to it.
487 desired = '%s-%s' % (target, desired)
488
489 # Target specific hacks
490 if package in suffixes:
491 if target in suffixes[package]:
492 desired += suffixes[package][target]
493
David James7ec5efc2012-11-06 09:39:49 -0800494 extra_env = {'CHOST': target}
495 cmd = ['%s-config' % package, '-c', target]
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500496 result = cros_build_lib.RunCommand(
497 cmd, print_cmd=False, redirect_stdout=True, extra_env=extra_env)
498 current = result.output.splitlines()[0]
Zdenek Behan508dcce2011-12-05 15:39:32 +0100499 # Do not gcc-config when the current is live or nothing needs to be done.
500 if current != desired and current != '9999':
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500501 cmd = [package + '-config', desired]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100502 cros_build_lib.RunCommand(cmd, print_cmd=False)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100503
504
Mike Frysinger35247af2012-11-16 18:58:06 -0500505def ExpandTargets(targets_wanted):
506 """Expand any possible toolchain aliases into full targets
507
508 This will expand 'all' and 'sdk' into the respective toolchain tuples.
509
510 Args:
511 targets_wanted: The targets specified by the user.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500512
Mike Frysinger35247af2012-11-16 18:58:06 -0500513 Returns:
Gilad Arnold8195b532015-04-07 10:56:30 +0300514 Dictionary of concrete targets and their toolchain tuples.
Mike Frysinger35247af2012-11-16 18:58:06 -0500515 """
Mike Frysinger35247af2012-11-16 18:58:06 -0500516 targets_wanted = set(targets_wanted)
Gilad Arnold8195b532015-04-07 10:56:30 +0300517 if targets_wanted in (set(['boards']), set(['bricks'])):
518 # Only pull targets from the included boards/bricks.
519 return {}
520
521 all_targets = toolchain.GetAllTargets()
Mike Frysinger35247af2012-11-16 18:58:06 -0500522 if targets_wanted == set(['all']):
Gilad Arnold8195b532015-04-07 10:56:30 +0300523 return all_targets
524 if targets_wanted == set(['sdk']):
Mike Frysinger35247af2012-11-16 18:58:06 -0500525 # Filter out all the non-sdk toolchains as we don't want to mess
526 # with those in all of our builds.
Gilad Arnold8195b532015-04-07 10:56:30 +0300527 return toolchain.FilterToolchains(all_targets, 'sdk', True)
528
529 # Verify user input.
530 nonexistent = targets_wanted.difference(all_targets)
531 if nonexistent:
532 raise ValueError('Invalid targets: %s', ','.join(nonexistent))
533 return {t: all_targets[t] for t in targets_wanted}
Mike Frysinger35247af2012-11-16 18:58:06 -0500534
535
David Jamesf8c672f2012-11-06 13:38:11 -0800536def UpdateToolchains(usepkg, deleteold, hostonly, reconfig,
Gilad Arnold8195b532015-04-07 10:56:30 +0300537 targets_wanted, boards_wanted, bricks_wanted):
Zdenek Behan508dcce2011-12-05 15:39:32 +0100538 """Performs all steps to create a synchronized toolchain enviroment.
539
Mike Frysingerdc649ae2014-11-26 19:29:24 -0500540 Args:
541 usepkg: Use prebuilt packages
542 deleteold: Unmerge deprecated packages
543 hostonly: Only setup the host toolchain
544 reconfig: Reload crossdev config and reselect toolchains
545 targets_wanted: All the targets to update
546 boards_wanted: Load targets from these boards
Gilad Arnold8195b532015-04-07 10:56:30 +0300547 bricks_wanted: Load targets from these bricks
Zdenek Behan508dcce2011-12-05 15:39:32 +0100548 """
David Jamesf8c672f2012-11-06 13:38:11 -0800549 targets, crossdev_targets, reconfig_targets = {}, {}, {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100550 if not hostonly:
551 # For hostonly, we can skip most of the below logic, much of which won't
552 # work on bare systems where this is useful.
Mike Frysinger35247af2012-11-16 18:58:06 -0500553 targets = ExpandTargets(targets_wanted)
Mike Frysinger7ccee992012-06-01 21:27:59 -0400554
Gilad Arnold8195b532015-04-07 10:56:30 +0300555 # Now re-add any targets that might be from this board/brick. This is to
556 # allow unofficial boards to declare their own toolchains.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400557 for board in boards_wanted:
David James27ac4ae2012-12-03 23:16:15 -0800558 targets.update(toolchain.GetToolchainsForBoard(board))
Gilad Arnold8195b532015-04-07 10:56:30 +0300559 for brick in bricks_wanted:
560 targets.update(toolchain.GetToolchainsForBrick(brick))
Zdenek Behan508dcce2011-12-05 15:39:32 +0100561
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100562 # First check and initialize all cross targets that need to be.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400563 for target in targets:
564 if TargetIsInitialized(target):
565 reconfig_targets[target] = targets[target]
566 else:
567 crossdev_targets[target] = targets[target]
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100568 if crossdev_targets:
Mike Frysinger383367e2014-09-16 15:06:17 -0400569 print('The following targets need to be re-initialized:')
570 print(crossdev_targets)
David James66a09c42012-11-05 13:31:38 -0800571 Crossdev.UpdateTargets(crossdev_targets, usepkg)
Zdenek Behan8be29ba2012-05-29 23:10:34 +0200572 # Those that were not initialized may need a config update.
David James66a09c42012-11-05 13:31:38 -0800573 Crossdev.UpdateTargets(reconfig_targets, usepkg, config_only=True)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100574
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100575 # We want host updated.
Mike Frysinger7ccee992012-06-01 21:27:59 -0400576 targets['host'] = {}
Zdenek Behan7e33b4e2012-03-12 17:00:56 +0100577
578 # Now update all packages.
David Jamesf8c672f2012-11-06 13:38:11 -0800579 if UpdateTargets(targets, usepkg) or crossdev_targets or reconfig:
580 SelectActiveToolchains(targets, CONFIG_TARGET_SUFFIXES)
David James7ec5efc2012-11-06 09:39:49 -0800581
582 if deleteold:
583 CleanTargets(targets)
Zdenek Behan508dcce2011-12-05 15:39:32 +0100584
Mike Frysingerc880a962013-11-08 13:59:06 -0500585 # Now that we've cleared out old versions, see if we need to rebuild
586 # anything. Can't do this earlier as it might not be broken.
587 RebuildLibtool()
588
Zdenek Behan508dcce2011-12-05 15:39:32 +0100589
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700590def ShowConfig(name):
591 """Show the toolchain tuples used by |name|
Mike Frysinger35247af2012-11-16 18:58:06 -0500592
593 Args:
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700594 name: The board name or brick locator to query.
Mike Frysinger35247af2012-11-16 18:58:06 -0500595 """
Bertrand SIMONNET3c77bc92015-03-20 14:28:54 -0700596 if workspace_lib.IsLocator(name):
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -0700597 toolchains = toolchain.GetToolchainsForBrick(name)
598 else:
599 toolchains = toolchain.GetToolchainsForBoard(name)
Mike Frysinger35247af2012-11-16 18:58:06 -0500600 # Make sure we display the default toolchain first.
Mike Frysinger383367e2014-09-16 15:06:17 -0400601 print(','.join(
David James27ac4ae2012-12-03 23:16:15 -0800602 toolchain.FilterToolchains(toolchains, 'default', True).keys() +
Mike Frysinger383367e2014-09-16 15:06:17 -0400603 toolchain.FilterToolchains(toolchains, 'default', False).keys()))
Mike Frysinger35247af2012-11-16 18:58:06 -0500604
605
Mike Frysinger35247af2012-11-16 18:58:06 -0500606def GeneratePathWrapper(root, wrappath, path):
607 """Generate a shell script to execute another shell script
608
609 Since we can't symlink a wrapped ELF (see GenerateLdsoWrapper) because the
610 argv[0] won't be pointing to the correct path, generate a shell script that
611 just executes another program with its full path.
612
613 Args:
614 root: The root tree to generate scripts inside of
615 wrappath: The full path (inside |root|) to create the wrapper
616 path: The target program which this wrapper will execute
617 """
618 replacements = {
619 'path': path,
620 'relroot': os.path.relpath('/', os.path.dirname(wrappath)),
621 }
622 wrapper = """#!/bin/sh
623base=$(realpath "$0")
624basedir=${base%%/*}
625exec "${basedir}/%(relroot)s%(path)s" "$@"
626""" % replacements
627 root_wrapper = root + wrappath
628 if os.path.islink(root_wrapper):
629 os.unlink(root_wrapper)
630 else:
631 osutils.SafeMakedirs(os.path.dirname(root_wrapper))
632 osutils.WriteFile(root_wrapper, wrapper)
Mike Frysinger60ec1012013-10-21 00:11:10 -0400633 os.chmod(root_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500634
635
636def FileIsCrosSdkElf(elf):
637 """Determine if |elf| is an ELF that we execute in the cros_sdk
638
639 We don't need this to be perfect, just quick. It makes sure the ELF
640 is a 64bit LSB x86_64 ELF. That is the native type of cros_sdk.
641
642 Args:
643 elf: The file to check
Mike Frysinger1a736a82013-12-12 01:50:59 -0500644
Mike Frysinger35247af2012-11-16 18:58:06 -0500645 Returns:
646 True if we think |elf| is a native ELF
647 """
648 with open(elf) as f:
649 data = f.read(20)
650 # Check the magic number, EI_CLASS, EI_DATA, and e_machine.
651 return (data[0:4] == '\x7fELF' and
652 data[4] == '\x02' and
653 data[5] == '\x01' and
654 data[18] == '\x3e')
655
656
657def IsPathPackagable(ptype, path):
658 """Should the specified file be included in a toolchain package?
659
660 We only need to handle files as we'll create dirs as we need them.
661
662 Further, trim files that won't be useful:
663 - non-english translations (.mo) since it'd require env vars
664 - debug files since these are for the host compiler itself
665 - info/man pages as they're big, and docs are online, and the
666 native docs should work fine for the most part (`man gcc`)
667
668 Args:
669 ptype: A string describing the path type (i.e. 'file' or 'dir' or 'sym')
670 path: The full path to inspect
Mike Frysinger1a736a82013-12-12 01:50:59 -0500671
Mike Frysinger35247af2012-11-16 18:58:06 -0500672 Returns:
673 True if we want to include this path in the package
674 """
675 return not (ptype in ('dir',) or
676 path.startswith('/usr/lib/debug/') or
677 os.path.splitext(path)[1] == '.mo' or
678 ('/man/' in path or '/info/' in path))
679
680
681def ReadlinkRoot(path, root):
682 """Like os.readlink(), but relative to a |root|
683
684 Args:
685 path: The symlink to read
686 root: The path to use for resolving absolute symlinks
Mike Frysinger1a736a82013-12-12 01:50:59 -0500687
Mike Frysinger35247af2012-11-16 18:58:06 -0500688 Returns:
689 A fully resolved symlink path
690 """
691 while os.path.islink(root + path):
692 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
693 return path
694
695
696def _GetFilesForTarget(target, root='/'):
697 """Locate all the files to package for |target|
698
699 This does not cover ELF dependencies.
700
701 Args:
702 target: The toolchain target name
703 root: The root path to pull all packages from
Mike Frysinger1a736a82013-12-12 01:50:59 -0500704
Mike Frysinger35247af2012-11-16 18:58:06 -0500705 Returns:
706 A tuple of a set of all packable paths, and a set of all paths which
707 are also native ELFs
708 """
709 paths = set()
710 elfs = set()
711
712 # Find all the files owned by the packages for this target.
713 for pkg in GetTargetPackages(target):
714 # Ignore packages that are part of the target sysroot.
715 if pkg in ('kernel', 'libc'):
716 continue
717
718 atom = GetPortagePackage(target, pkg)
719 cat, pn = atom.split('/')
720 ver = GetInstalledPackageVersions(atom)[0]
Ralph Nathan03047282015-03-23 11:09:32 -0700721 logging.info('packaging %s-%s', atom, ver)
Mike Frysinger35247af2012-11-16 18:58:06 -0500722
723 # pylint: disable=E1101
724 dblink = portage.dblink(cat, pn + '-' + ver, myroot=root,
725 settings=portage.settings)
726 contents = dblink.getcontents()
727 for obj in contents:
728 ptype = contents[obj][0]
729 if not IsPathPackagable(ptype, obj):
730 continue
731
732 if ptype == 'obj':
733 # For native ELFs, we need to pull in their dependencies too.
734 if FileIsCrosSdkElf(obj):
735 elfs.add(obj)
736 paths.add(obj)
737
738 return paths, elfs
739
740
741def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500742 path_rewrite_func=lambda x: x, root='/'):
Mike Frysinger35247af2012-11-16 18:58:06 -0500743 """Link in all packable files and their runtime dependencies
744
745 This also wraps up executable ELFs with helper scripts.
746
747 Args:
748 output_dir: The output directory to store files
749 paths: All the files to include
750 elfs: All the files which are ELFs (a subset of |paths|)
751 ldpaths: A dict of static ldpath information
752 path_rewrite_func: User callback to rewrite paths in output_dir
753 root: The root path to pull all packages/files from
754 """
755 # Link in all the files.
756 sym_paths = []
757 for path in paths:
758 new_path = path_rewrite_func(path)
759 dst = output_dir + new_path
760 osutils.SafeMakedirs(os.path.dirname(dst))
761
762 # Is this a symlink which we have to rewrite or wrap?
763 # Delay wrap check until after we have created all paths.
764 src = root + path
765 if os.path.islink(src):
766 tgt = os.readlink(src)
767 if os.path.sep in tgt:
768 sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root))))
769
770 # Rewrite absolute links to relative and then generate the symlink
771 # ourselves. All other symlinks can be hardlinked below.
772 if tgt[0] == '/':
773 tgt = os.path.relpath(tgt, os.path.dirname(new_path))
774 os.symlink(tgt, dst)
775 continue
776
777 os.link(src, dst)
778
779 # Now see if any of the symlinks need to be wrapped.
780 for sym, tgt in sym_paths:
781 if tgt in elfs:
782 GeneratePathWrapper(output_dir, sym, tgt)
783
784 # Locate all the dependencies for all the ELFs. Stick them all in the
785 # top level "lib" dir to make the wrapper simpler. This exact path does
786 # not matter since we execute ldso directly, and we tell the ldso the
787 # exact path to search for its libraries.
788 libdir = os.path.join(output_dir, 'lib')
789 osutils.SafeMakedirs(libdir)
790 donelibs = set()
791 for elf in elfs:
Mike Frysingerea7688e2014-07-31 22:40:33 -0400792 e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths)
Mike Frysinger35247af2012-11-16 18:58:06 -0500793 interp = e['interp']
794 if interp:
795 # Generate a wrapper if it is executable.
Mike Frysingerc2ec0902013-03-26 01:28:45 -0400796 interp = os.path.join('/lib', os.path.basename(interp))
797 lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
798 libpaths=e['rpath'] + e['runpath'])
Mike Frysinger35247af2012-11-16 18:58:06 -0500799
800 for lib, lib_data in e['libs'].iteritems():
801 if lib in donelibs:
802 continue
803
804 src = path = lib_data['path']
805 if path is None:
Ralph Nathan446aee92015-03-23 14:44:56 -0700806 logging.warning('%s: could not locate %s', elf, lib)
Mike Frysinger35247af2012-11-16 18:58:06 -0500807 continue
808 donelibs.add(lib)
809
810 # Needed libs are the SONAME, but that is usually a symlink, not a
811 # real file. So link in the target rather than the symlink itself.
812 # We have to walk all the possible symlinks (SONAME could point to a
813 # symlink which points to a symlink), and we have to handle absolute
814 # ourselves (since we have a "root" argument).
815 dst = os.path.join(libdir, os.path.basename(path))
816 src = ReadlinkRoot(src, root)
817
818 os.link(root + src, dst)
819
820
821def _EnvdGetVar(envd, var):
822 """Given a Gentoo env.d file, extract a var from it
823
824 Args:
825 envd: The env.d file to load (may be a glob path)
826 var: The var to extract
Mike Frysinger1a736a82013-12-12 01:50:59 -0500827
Mike Frysinger35247af2012-11-16 18:58:06 -0500828 Returns:
829 The value of |var|
830 """
831 envds = glob.glob(envd)
832 assert len(envds) == 1, '%s: should have exactly 1 env.d file' % envd
833 envd = envds[0]
834 return cros_build_lib.LoadKeyValueFile(envd)[var]
835
836
837def _ProcessBinutilsConfig(target, output_dir):
838 """Do what binutils-config would have done"""
839 binpath = os.path.join('/bin', target + '-')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500840
841 # Locate the bin dir holding the gold linker.
842 binutils_bin_path = os.path.join(output_dir, 'usr', toolchain.GetHostTuple(),
843 target, 'binutils-bin')
844 globpath = os.path.join(binutils_bin_path, '*-gold')
Mike Frysinger35247af2012-11-16 18:58:06 -0500845 srcpath = glob.glob(globpath)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500846 if not srcpath:
847 # Maybe this target doesn't support gold.
848 globpath = os.path.join(binutils_bin_path, '*')
849 srcpath = glob.glob(globpath)
850 assert len(srcpath) == 1, ('%s: matched more than one path (but not *-gold)'
851 % globpath)
852 srcpath = srcpath[0]
853 ld_path = os.path.join(srcpath, 'ld')
854 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
855 ld_path = os.path.join(srcpath, 'ld.bfd')
856 assert os.path.exists(ld_path), '%s: linker is missing!' % ld_path
857 ld_path = os.path.join(srcpath, 'ld.gold')
858 assert not os.path.exists(ld_path), ('%s: exists, but gold dir does not!'
859 % ld_path)
860
861 # Nope, no gold support to be found.
862 gold_supported = False
Ralph Nathan446aee92015-03-23 14:44:56 -0700863 logging.warning('%s: binutils lacks support for the gold linker', target)
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500864 else:
865 assert len(srcpath) == 1, '%s: did not match exactly 1 path' % globpath
866 gold_supported = True
Mike Frysinger78b7a812014-11-26 19:45:23 -0500867 srcpath = srcpath[0]
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500868
Mike Frysinger78b7a812014-11-26 19:45:23 -0500869 srcpath = srcpath[len(output_dir):]
Mike Frysinger35247af2012-11-16 18:58:06 -0500870 gccpath = os.path.join('/usr', 'libexec', 'gcc')
871 for prog in os.listdir(output_dir + srcpath):
872 # Skip binaries already wrapped.
873 if not prog.endswith('.real'):
874 GeneratePathWrapper(output_dir, binpath + prog,
875 os.path.join(srcpath, prog))
876 GeneratePathWrapper(output_dir, os.path.join(gccpath, prog),
877 os.path.join(srcpath, prog))
878
David James27ac4ae2012-12-03 23:16:15 -0800879 libpath = os.path.join('/usr', toolchain.GetHostTuple(), target, 'lib')
Mike Frysingerd4d40fd2014-11-06 17:30:57 -0500880 envd = os.path.join(output_dir, 'etc', 'env.d', 'binutils', '*')
881 if gold_supported:
882 envd += '-gold'
Mike Frysinger35247af2012-11-16 18:58:06 -0500883 srcpath = _EnvdGetVar(envd, 'LIBPATH')
884 os.symlink(os.path.relpath(srcpath, os.path.dirname(libpath)),
885 output_dir + libpath)
886
887
888def _ProcessGccConfig(target, output_dir):
889 """Do what gcc-config would have done"""
890 binpath = '/bin'
891 envd = os.path.join(output_dir, 'etc', 'env.d', 'gcc', '*')
892 srcpath = _EnvdGetVar(envd, 'GCC_PATH')
893 for prog in os.listdir(output_dir + srcpath):
894 # Skip binaries already wrapped.
895 if (not prog.endswith('.real') and
896 not prog.endswith('.elf') and
897 prog.startswith(target)):
898 GeneratePathWrapper(output_dir, os.path.join(binpath, prog),
899 os.path.join(srcpath, prog))
900 return srcpath
901
902
Frank Henigman179ec7c2015-02-06 03:01:09 -0500903def _ProcessSysrootWrappers(_target, output_dir, srcpath):
904 """Remove chroot-specific things from our sysroot wrappers"""
Mike Frysinger35247af2012-11-16 18:58:06 -0500905 # Disable ccache since we know it won't work outside of chroot.
Frank Henigman179ec7c2015-02-06 03:01:09 -0500906 for sysroot_wrapper in glob.glob(os.path.join(
907 output_dir + srcpath, 'sysroot_wrapper*')):
908 contents = osutils.ReadFile(sysroot_wrapper).splitlines()
909 for num in xrange(len(contents)):
910 if '@CCACHE_DEFAULT@' in contents[num]:
911 contents[num] = 'use_ccache = False'
912 break
913 # Can't update the wrapper in place since it's a hardlink to a file in /.
914 os.unlink(sysroot_wrapper)
915 osutils.WriteFile(sysroot_wrapper, '\n'.join(contents))
916 os.chmod(sysroot_wrapper, 0o755)
Mike Frysinger35247af2012-11-16 18:58:06 -0500917
918
919def _ProcessDistroCleanups(target, output_dir):
920 """Clean up the tree and remove all distro-specific requirements
921
922 Args:
923 target: The toolchain target name
924 output_dir: The output directory to clean up
925 """
926 _ProcessBinutilsConfig(target, output_dir)
927 gcc_path = _ProcessGccConfig(target, output_dir)
Frank Henigman179ec7c2015-02-06 03:01:09 -0500928 _ProcessSysrootWrappers(target, output_dir, gcc_path)
Mike Frysinger35247af2012-11-16 18:58:06 -0500929
930 osutils.RmDir(os.path.join(output_dir, 'etc'))
931
932
933def CreatePackagableRoot(target, output_dir, ldpaths, root='/'):
934 """Setup a tree from the packages for the specified target
935
936 This populates a path with all the files from toolchain packages so that
937 a tarball can easily be generated from the result.
938
939 Args:
940 target: The target to create a packagable root from
941 output_dir: The output directory to place all the files
942 ldpaths: A dict of static ldpath information
943 root: The root path to pull all packages/files from
944 """
945 # Find all the files owned by the packages for this target.
946 paths, elfs = _GetFilesForTarget(target, root=root)
947
948 # Link in all the package's files, any ELF dependencies, and wrap any
949 # executable ELFs with helper scripts.
950 def MoveUsrBinToBin(path):
951 """Move /usr/bin to /bin so people can just use that toplevel dir"""
952 return path[4:] if path.startswith('/usr/bin/') else path
953 _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths,
954 path_rewrite_func=MoveUsrBinToBin, root=root)
955
956 # The packages, when part of the normal distro, have helper scripts
957 # that setup paths and such. Since we are making this standalone, we
958 # need to preprocess all that ourselves.
959 _ProcessDistroCleanups(target, output_dir)
960
961
962def CreatePackages(targets_wanted, output_dir, root='/'):
963 """Create redistributable cross-compiler packages for the specified targets
964
965 This creates toolchain packages that should be usable in conjunction with
966 a downloaded sysroot (created elsewhere).
967
968 Tarballs (one per target) will be created in $PWD.
969
970 Args:
Don Garrett25f309a2014-03-19 14:02:12 -0700971 targets_wanted: The targets to package up.
972 output_dir: The directory to put the packages in.
973 root: The root path to pull all packages/files from.
Mike Frysinger35247af2012-11-16 18:58:06 -0500974 """
Ralph Nathan03047282015-03-23 11:09:32 -0700975 logging.info('Writing tarballs to %s', output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -0500976 osutils.SafeMakedirs(output_dir)
977 ldpaths = lddtree.LoadLdpaths(root)
978 targets = ExpandTargets(targets_wanted)
979
David James4bc13702013-03-26 08:08:04 -0700980 with osutils.TempDir() as tempdir:
Mike Frysinger35247af2012-11-16 18:58:06 -0500981 # We have to split the root generation from the compression stages. This is
982 # because we hardlink in all the files (to avoid overhead of reading/writing
983 # the copies multiple times). But tar gets angry if a file's hardlink count
984 # changes from when it starts reading a file to when it finishes.
985 with parallel.BackgroundTaskRunner(CreatePackagableRoot) as queue:
986 for target in targets:
987 output_target_dir = os.path.join(tempdir, target)
988 queue.put([target, output_target_dir, ldpaths, root])
989
990 # Build the tarball.
991 with parallel.BackgroundTaskRunner(cros_build_lib.CreateTarball) as queue:
992 for target in targets:
993 tar_file = os.path.join(output_dir, target + '.tar.xz')
994 queue.put([tar_file, os.path.join(tempdir, target)])
995
996
Brian Harring30675052012-02-29 12:18:22 -0800997def main(argv):
Mike Frysinger0c808452014-11-06 17:30:23 -0500998 parser = commandline.ArgumentParser(description=__doc__)
999 parser.add_argument('-u', '--nousepkg',
1000 action='store_false', dest='usepkg', default=True,
1001 help='Use prebuilt packages if possible')
1002 parser.add_argument('-d', '--deleteold',
1003 action='store_true', dest='deleteold', default=False,
1004 help='Unmerge deprecated packages')
1005 parser.add_argument('-t', '--targets',
1006 dest='targets', default='sdk',
Gilad Arnold8195b532015-04-07 10:56:30 +03001007 help="Comma separated list of tuples. Special keywords "
1008 "'host', 'sdk', 'boards', 'bricks' and 'all' are "
1009 "allowed. Defaults to 'sdk'.")
1010 parser.add_argument('--include-boards', default='', metavar='BOARDS',
1011 help='Comma separated list of boards whose toolchains we '
1012 'will always include. Default: none')
1013 parser.add_argument('--include-bricks', default='', metavar='BRICKS',
1014 help='Comma separated list of bricks whose toolchains we '
1015 'will always include. Default: none')
Mike Frysinger0c808452014-11-06 17:30:23 -05001016 parser.add_argument('--hostonly',
1017 dest='hostonly', default=False, action='store_true',
1018 help='Only setup the host toolchain. '
1019 'Useful for bootstrapping chroot')
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001020 parser.add_argument('--show-board-cfg', '--show-cfg',
1021 dest='cfg_name', default=None,
1022 help='Board or brick to list toolchains tuples for')
Mike Frysinger0c808452014-11-06 17:30:23 -05001023 parser.add_argument('--create-packages',
1024 action='store_true', default=False,
1025 help='Build redistributable packages')
1026 parser.add_argument('--output-dir', default=os.getcwd(), type='path',
1027 help='Output directory')
1028 parser.add_argument('--reconfig', default=False, action='store_true',
1029 help='Reload crossdev config and reselect toolchains')
Zdenek Behan508dcce2011-12-05 15:39:32 +01001030
Mike Frysinger0c808452014-11-06 17:30:23 -05001031 options = parser.parse_args(argv)
1032 options.Freeze()
Zdenek Behan508dcce2011-12-05 15:39:32 +01001033
Mike Frysinger35247af2012-11-16 18:58:06 -05001034 # Figure out what we're supposed to do and reject conflicting options.
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001035 if options.cfg_name and options.create_packages:
Mike Frysinger35247af2012-11-16 18:58:06 -05001036 parser.error('conflicting options: create-packages & show-board-cfg')
Mike Frysinger984d0622012-06-01 16:08:44 -04001037
Gilad Arnold8195b532015-04-07 10:56:30 +03001038 targets_wanted = set(options.targets.split(','))
1039 boards_wanted = (set(options.include_boards.split(','))
1040 if options.include_boards else set())
1041 bricks_wanted = (set(options.include_bricks.split(','))
1042 if options.include_bricks else set())
Mike Frysinger35247af2012-11-16 18:58:06 -05001043
Bertrand SIMONNETcae9d5f2015-03-09 15:58:01 -07001044 if options.cfg_name:
1045 ShowConfig(options.cfg_name)
Mike Frysinger35247af2012-11-16 18:58:06 -05001046 elif options.create_packages:
1047 cros_build_lib.AssertInsideChroot()
1048 Crossdev.Load(False)
Gilad Arnold8195b532015-04-07 10:56:30 +03001049 CreatePackages(targets_wanted, options.output_dir)
Mike Frysinger35247af2012-11-16 18:58:06 -05001050 else:
1051 cros_build_lib.AssertInsideChroot()
1052 # This has to be always run as root.
1053 if os.geteuid() != 0:
1054 cros_build_lib.Die('this script must be run as root')
1055
1056 Crossdev.Load(options.reconfig)
1057 UpdateToolchains(options.usepkg, options.deleteold, options.hostonly,
Gilad Arnold8195b532015-04-07 10:56:30 +03001058 options.reconfig, targets_wanted, boards_wanted,
1059 bricks_wanted)
Mike Frysinger35247af2012-11-16 18:58:06 -05001060 Crossdev.Save()
1061
1062 return 0