| # -*- coding: utf-8 -*- |
| # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """This script moves ebuilds between 'stable' and 'live' states. |
| |
| By default 'stable' ebuilds point at and build from source at the |
| last known good commit. Moving an ebuild to 'live' (via cros_workon start) |
| is intended to support development. The current source tip is fetched, |
| source modified and built using the unstable 'live' (9999) ebuild. |
| """ |
| |
| from __future__ import print_function |
| |
| import sys |
| |
| from chromite.cli import command |
| from chromite.lib import build_target_lib |
| from chromite.lib import commandline |
| from chromite.lib import cros_build_lib |
| from chromite.lib import terminal |
| from chromite.lib import workon_helper |
| |
| assert sys.version_info >= (3, 6), 'This module requires Python 3.6+' |
| |
| # These would preferably be class attributes, but it's difficult to make class |
| # attributes refer to each other with nested generators in class declarations. |
| _ACTIONS = ( |
| ('start', 'Moves an ebuild to live (intended to support development)'), |
| ('stop', 'Moves an ebuild to stable (use last known good)'), |
| ('info', 'Print package name, repo name, and source directory.'), |
| ('list', 'List of live ebuilds (workon ebuilds if --all)'), |
| ('list-all', 'List all of the live ebuilds for all setup boards'), |
| ('iterate', 'For each ebuild, cd to the source dir and run a command'), |
| ) |
| |
| # Formatting for the "Actions" epilog section. |
| _fill = max(len(a[0]) for a in _ACTIONS) + 3 |
| _action_epilog = '\n'.join( |
| ' %s%s' % (a[0].ljust(_fill, ' '), a[1]) for a in _ACTIONS) |
| |
| |
| @command.CommandDecorator('workon') |
| class WorkonCommand(command.CliCommand): |
| """Forces rebuilds of worked on packages from the local source.""" |
| |
| EPILOG = f""" |
| Actions: |
| {_action_epilog} |
| |
| Examples: |
| There is some support for automatically locating ebuilds. The following two |
| commands are equivalent. |
| cros workon start chromeos-base/authpolicy --build-target eve |
| cros workon start authpolicy -b eve |
| |
| Start working on a package (always build from source): |
| cros workon start authpolicy -b eve |
| |
| Stop working on a package (use last known good version): |
| cros workon stop authpolicy -b eve |
| |
| Stop working on all packages for a build target: |
| cros workon stop --all -b eve |
| |
| Due to argparse limitations, the positional arguments must be together. |
| Currently, the following will not parse correctly: |
| cros workon stop -b eve authpolicy |
| """ |
| |
| @classmethod |
| def AddParser(cls, parser: commandline.ArgumentParser): |
| """Build the parser. |
| |
| Args: |
| parser: The parser. |
| """ |
| super().AddParser(parser) |
| |
| # The current argparse limitations mean we cannot correctly parse all |
| # variations of the arguments, currently the positional arguments must |
| # be listed together, e.g. `cros workon start -b eve package` will |
| # not parse as you might expect. This has been addressed in python |
| # 3.7 with intermixed parsing (ArgumentParse.parse_intermixed_args). |
| # See: https://docs.python.org/3/library/argparse.html#intermixed-parsing |
| # TODO: Add support for intermixed parsing when we are on 3.7. |
| parser.add_argument( |
| 'action', choices=[a[0] for a in _ACTIONS], help='Action to run.') |
| parser.add_argument( |
| 'packages', nargs='*', help='The packages to run the action against.') |
| |
| target_group = parser.add_mutually_exclusive_group() |
| target_group.add_argument( |
| '-b', |
| '--board', |
| '--build-target', |
| dest='build_target_name', |
| help='The name of the build target whose package is being worked on.') |
| target_group.add_argument( |
| '--host', |
| default=False, |
| action='store_true', |
| help='Use the host (sdk) instead of a build target.') |
| |
| parser.add_argument( |
| '--command', |
| default='git status', |
| dest='iterate_command', |
| help='The command to be run by iterate.') |
| |
| filter_group = parser.add_mutually_exclusive_group() |
| filter_group.add_argument( |
| '--workon_only', |
| default=False, |
| action='store_true', |
| help='Apply to packages that have a workon ebuild only.') |
| filter_group.add_argument( |
| '--all', |
| default=False, |
| action='store_true', |
| help='Apply to all possible packages for the given command.') |
| |
| return parser |
| |
| def Run(self): |
| if self.options.build_target_name: |
| self.options.build_target = build_target_lib.BuildTarget( |
| self.options.build_target_name) |
| else: |
| self.options.build_target = None |
| |
| self.options.Freeze() |
| |
| has_target = self.options.host or self.options.build_target |
| needs_target = self.options.action != 'list-all' |
| if needs_target and not has_target: |
| cros_build_lib.Die(f'{self.options.action} requires a build target or ' |
| 'specifying the host.') |
| |
| commandline.RunInsideChroot(self) |
| |
| if self.options.action == 'list-all': |
| build_target_to_packages = workon_helper.ListAllWorkedOnAtoms() |
| color = terminal.Color() |
| for build_target_name in sorted(build_target_to_packages): |
| print(color.Start(color.GREEN) + build_target_name + ':' + color.Stop()) |
| for package in build_target_to_packages[build_target_name]: |
| print(' ' + package) |
| print('') |
| return 0 |
| |
| if self.options.build_target: |
| target = self.options.build_target.name |
| sysroot = self.options.build_target.root |
| else: |
| target = 'host' |
| sysroot = '/' |
| |
| helper = workon_helper.WorkonHelper(sysroot, target) |
| try: |
| if self.options.action == 'start': |
| helper.StartWorkingOnPackages( |
| self.options.packages, |
| use_all=self.options.all, |
| use_workon_only=self.options.workon_only) |
| elif self.options.action == 'stop': |
| helper.StopWorkingOnPackages( |
| self.options.packages, |
| use_all=self.options.all, |
| use_workon_only=self.options.workon_only) |
| elif self.options.action == 'info': |
| triples = helper.GetPackageInfo( |
| self.options.packages, |
| use_all=self.options.all, |
| use_workon_only=self.options.workon_only) |
| for package, repos, paths in triples: |
| print(package, ','.join(repos), ','.join(paths)) |
| elif self.options.action == 'list': |
| packages = helper.ListAtoms( |
| use_all=self.options.all, use_workon_only=self.options.workon_only) |
| if packages: |
| print('\n'.join(packages)) |
| elif self.options.action == 'iterate': |
| helper.RunCommandInPackages( |
| self.options.packages, |
| self.options.iterate_command, |
| use_all=self.options.all, |
| use_workon_only=self.options.workon_only) |
| else: |
| cros_build_lib.Die(f'No implementation for {self.options.action}') |
| except workon_helper.WorkonError as e: |
| cros_build_lib.Die(e) |
| return 0 |