blob: 475b1c2081dc547c649b64d7765963129a1bb75a [file] [log] [blame]
# -*- 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