blob: 53f72d04ff4adc28abdc4f1d156988c6060e6e47 [file] [log] [blame]
#!/usr/bin/python
#
# Copyright (c) 2011 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.
"""Main file for the chromite shell."""
# Python imports
import os
import sys
# Local imports
from chromite.lib import text_menu
import chromite.lib.cros_build_lib as cros_lib
from chromite.shell import utils
from chromite.shell.subcmds import build_cmd
from chromite.shell.subcmds import clean_cmd
from chromite.shell.subcmds import portage_cmds
from chromite.shell.subcmds import shell_cmd
from chromite.shell.subcmds import workon_cmd
from chromite.shell import chromite_env
from chromite.lib import operation
# Define command handlers and command strings.
#
# ORDER MATTERS here when we show the menu.
_COMMAND_HANDLERS = [
build_cmd.BuildCmd,
clean_cmd.CleanCmd,
portage_cmds.EbuildCmd,
portage_cmds.EmergeCmd,
portage_cmds.EqueryCmd,
portage_cmds.PortageqCmd,
shell_cmd.ShellCmd,
workon_cmd.WorkonCmd,
]
_COMMAND_STRS = [cls.__name__[:-len('Cmd')].lower()
for cls in _COMMAND_HANDLERS]
def _FindCommand(cmd_name):
"""Find the command that matches the given command name.
This tries to be smart. See the cmd_name parameter for details.
Args:
cmd_name: Can be any of the following:
1. The full name of a command. This is checked first so that if one
command name is a substring of another, you can still specify
the shorter spec name and know you won't get a menu (the exact
match prevents the menu).
2. A _prefix_ that will be used to pare-down a menu of commands
Can be the empty string to show a menu of all commands
Returns:
The command name.
"""
# Always make cmd_name lower. Commands are case-insensitive.
cmd_name = cmd_name.lower()
# If we're an exact match, we're done!
if cmd_name in _COMMAND_STRS:
return cmd_name
# Find ones that match and put them in a menu...
possible_cmds = []
possible_choices = []
for cmd_num, this_cmd in enumerate(_COMMAND_STRS):
if this_cmd.startswith(cmd_name):
handler = _COMMAND_HANDLERS[cmd_num]
assert hasattr(handler, '__doc__'), \
('All handlers must have docstrings: %s' % cmd_name)
desc = handler.__doc__.splitlines()[0]
possible_cmds.append(this_cmd)
possible_choices.append('%s - %s' % (this_cmd, desc))
if not possible_choices:
cros_lib.Die('No commands matched: "%s". '
'Try running with no arguments for a menu.' %
cmd_name)
if cmd_name and len(possible_choices) == 1:
# Avoid showing the user a menu if the user's search string matched exactly
# one item.
choice = 0
cros_lib.Info("Running command '%s'." % possible_cmds[choice])
else:
choice = text_menu.TextMenu(possible_choices, 'Which chromite command',
menu_width=0)
if choice is None:
cros_lib.Die('OK, cancelling...')
else:
return possible_cmds[choice]
def main():
"""Main function for the chromite shell."""
# Hack it so that argv[0] is 'chromite' so that it doesn't tell user to run
# 'main.py' in help commands...
# TODO(dianders): Remove this hack, since it is ugly. Shouldn't be needed
# once we change the way that the chromite wrapper works.
sys.argv[0] = 'chromite'
# Support EnterChroot().
did_resume = utils.ResumeEnterChrootIfNeeded(sys.argv)
if did_resume:
return
# TODO(dianders): Make help a little better. Specifically:
# 1. Add a command called 'help'
# 2. Make the help string below include command list and descriptions (like
# the menu, but without being interactive).
# 3. Make "help command" and "--help command" equivalent to "command --help".
help_str = (
"""Usage: %(prog)s [chromite_options] [cmd [args]]\n"""
"""\n"""
"""The chromite script is a wrapper to make it easy to do various\n"""
"""build tasks. For a list of commands, run without any arguments.\n"""
"""\n"""
"""Options:\n"""
""" -h, --help show this help message and exit\n"""
) % {'prog': os.path.basename(sys.argv[0])}
if not cros_lib.IsInsideChroot():
help_str += (
""" --chroot=CHROOT_NAME Chroot spec to use. Can be an absolute\n"""
""" path to a spec file or a substring of a\n"""
""" chroot spec name (without .spec suffix)\n"""
)
# We don't use OptionParser here, since options for different subcommands are
# so different. We just look for the chromite options here...
if sys.argv[1:2] == ['--help']:
print help_str
sys.exit(0)
else:
# Start by skipping argv[0]
argv = sys.argv[1:]
# Look for special "--chroot" argument to allow for alternate chroots
if not cros_lib.IsInsideChroot():
# Default chroot name...
chroot_name = 'chroot'
# Get chroot spec name if specified; trim argv down if needed...
if argv:
if argv[0].startswith('--chroot='):
_, chroot_name = argv[0].split('=', 2)
argv = argv[1:]
elif argv[0] == '--chroot':
if len(argv) < 2:
cros_lib.Die('Chroot not specified.')
chroot_name = argv[1]
argv = argv[2:]
chroot_spec_path = utils.FindSpec(chroot_name,
spec_type=utils.CHROOT_SPEC_TYPE)
cros_lib.Info('Using chroot "%s"' % os.path.relpath(chroot_spec_path))
chroot_config = utils.ReadConfig(chroot_spec_path)
else:
# Already in the chroot; no need to get config...
chroot_config = None
# Get command and arguments
if argv:
cmd_str = argv[0].lower()
argv = argv[1:]
else:
cmd_str = ''
# Validate the subcmd, popping a menu if needed.
cmd_str = _FindCommand(cmd_str)
# Set up the cros system.
cros_env = chromite_env.ChromiteEnv()
# Configure the operation setup.
oper = cros_env.GetOperation()
oper.SetVerbose(True)
oper.SetProgress(True)
# Finally, call the function w/ standard argv.
cmd_cls = _COMMAND_HANDLERS[_COMMAND_STRS.index(cmd_str)]
cmd_obj = cmd_cls()
cmd_obj.SetChromiteEnv(cros_env)
cmd_obj.Run([cmd_str] + argv, chroot_config=chroot_config)
if __name__ == '__main__':
main()