blob: d55e83b3955fae3fa06a4768407f09ec9d2e52e4 [file] [log] [blame]
Doug Andersonf0c73952011-01-18 13:46:07 -08001#!/usr/bin/python
Doug Andersone91900d2011-01-26 11:37:17 -08002#
3# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
Doug Andersonf0c73952011-01-18 13:46:07 -08004# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
Doug Andersone91900d2011-01-26 11:37:17 -08007"""Main file for the chromite shell."""
Doug Andersonf0c73952011-01-18 13:46:07 -08008
9# Python imports
Doug Andersonf0c73952011-01-18 13:46:07 -080010import os
11import sys
Doug Andersonf0c73952011-01-18 13:46:07 -080012
13
Doug Anderson4b6d3962011-01-21 09:41:50 -080014# Local imports
Doug Andersone91900d2011-01-26 11:37:17 -080015from chromite.lib import text_menu
Doug Andersona9b10902011-02-01 17:54:31 -080016import chromite.lib.cros_build_lib as cros_lib
17from chromite.shell import utils
18from chromite.shell.subcmds import build_cmd
19from chromite.shell.subcmds import clean_cmd
20from chromite.shell.subcmds import portage_cmds
21from chromite.shell.subcmds import shell_cmd
22from chromite.shell.subcmds import workon_cmd
Simon Glass21599602011-02-11 13:57:29 -080023from chromite.shell import chromite_env
24from chromite.lib import operation
Doug Andersonf0c73952011-01-18 13:46:07 -080025
26
Doug Andersona9b10902011-02-01 17:54:31 -080027# Define command handlers and command strings.
Doug Andersonf0c73952011-01-18 13:46:07 -080028#
29# ORDER MATTERS here when we show the menu.
Doug Andersonf0c73952011-01-18 13:46:07 -080030_COMMAND_HANDLERS = [
Doug Andersona9b10902011-02-01 17:54:31 -080031 build_cmd.BuildCmd,
32 clean_cmd.CleanCmd,
33 portage_cmds.EbuildCmd,
34 portage_cmds.EmergeCmd,
35 portage_cmds.EqueryCmd,
36 portage_cmds.PortageqCmd,
37 shell_cmd.ShellCmd,
38 workon_cmd.WorkonCmd,
Doug Andersonf0c73952011-01-18 13:46:07 -080039]
Doug Andersona9b10902011-02-01 17:54:31 -080040_COMMAND_STRS = [cls.__name__[:-len('Cmd')].lower()
41 for cls in _COMMAND_HANDLERS]
Doug Andersonf0c73952011-01-18 13:46:07 -080042
43
44def _FindCommand(cmd_name):
45 """Find the command that matches the given command name.
46
47 This tries to be smart. See the cmd_name parameter for details.
48
49 Args:
50 cmd_name: Can be any of the following:
51 1. The full name of a command. This is checked first so that if one
52 command name is a substring of another, you can still specify
53 the shorter spec name and know you won't get a menu (the exact
54 match prevents the menu).
55 2. A _prefix_ that will be used to pare-down a menu of commands
56 Can be the empty string to show a menu of all commands
57
58 Returns:
59 The command name.
60 """
61 # Always make cmd_name lower. Commands are case-insensitive.
62 cmd_name = cmd_name.lower()
63
64 # If we're an exact match, we're done!
65 if cmd_name in _COMMAND_STRS:
66 return cmd_name
67
68 # Find ones that match and put them in a menu...
69 possible_cmds = []
70 possible_choices = []
71 for cmd_num, this_cmd in enumerate(_COMMAND_STRS):
72 if this_cmd.startswith(cmd_name):
Doug Andersona9b10902011-02-01 17:54:31 -080073 handler = _COMMAND_HANDLERS[cmd_num]
Doug Andersonf0c73952011-01-18 13:46:07 -080074 assert hasattr(handler, '__doc__'), \
75 ('All handlers must have docstrings: %s' % cmd_name)
76 desc = handler.__doc__.splitlines()[0]
77
78 possible_cmds.append(this_cmd)
79 possible_choices.append('%s - %s' % (this_cmd, desc))
80
81 if not possible_choices:
Doug Andersona9b10902011-02-01 17:54:31 -080082 cros_lib.Die('No commands matched: "%s". '
Doug Andersonf0c73952011-01-18 13:46:07 -080083 'Try running with no arguments for a menu.' %
84 cmd_name)
85
86 if cmd_name and len(possible_choices) == 1:
87 # Avoid showing the user a menu if the user's search string matched exactly
88 # one item.
89 choice = 0
Doug Andersonf0c73952011-01-18 13:46:07 -080090 else:
91 choice = text_menu.TextMenu(possible_choices, 'Which chromite command',
92 menu_width=0)
93
94 if choice is None:
Doug Andersona9b10902011-02-01 17:54:31 -080095 cros_lib.Die('OK, cancelling...')
Doug Andersonf0c73952011-01-18 13:46:07 -080096 else:
97 return possible_cmds[choice]
98
99
Doug Andersonf0c73952011-01-18 13:46:07 -0800100def main():
Doug Andersona9b10902011-02-01 17:54:31 -0800101 """Main function for the chromite shell."""
102
103 # Hack it so that argv[0] is 'chromite' so that it doesn't tell user to run
104 # 'main.py' in help commands...
105 # TODO(dianders): Remove this hack, since it is ugly. Shouldn't be needed
106 # once we change the way that the chromite wrapper works.
107 sys.argv[0] = 'chromite'
108
109 # Support EnterChroot().
Simon Glass5329b932011-03-14 16:49:04 -0700110 # This may raise a ChromiteError if the child dies, so we must handle this.
111 try:
112 did_resume = utils.ResumeEnterChrootIfNeeded(sys.argv)
113 if did_resume:
114 return
115 except chromite_env.ChromiteError:
116 # The error has been reported, but we must exit indicating failure
117 sys.exit(1)
Doug Andersona9b10902011-02-01 17:54:31 -0800118
Doug Andersonf0c73952011-01-18 13:46:07 -0800119 # TODO(dianders): Make help a little better. Specifically:
120 # 1. Add a command called 'help'
121 # 2. Make the help string below include command list and descriptions (like
122 # the menu, but without being interactive).
123 # 3. Make "help command" and "--help command" equivalent to "command --help".
124 help_str = (
125 """Usage: %(prog)s [chromite_options] [cmd [args]]\n"""
126 """\n"""
127 """The chromite script is a wrapper to make it easy to do various\n"""
128 """build tasks. For a list of commands, run without any arguments.\n"""
129 """\n"""
130 """Options:\n"""
131 """ -h, --help show this help message and exit\n"""
132 ) % {'prog': os.path.basename(sys.argv[0])}
Doug Andersona9b10902011-02-01 17:54:31 -0800133 if not cros_lib.IsInsideChroot():
Doug Andersonf0c73952011-01-18 13:46:07 -0800134 help_str += (
135 """ --chroot=CHROOT_NAME Chroot spec to use. Can be an absolute\n"""
136 """ path to a spec file or a substring of a\n"""
137 """ chroot spec name (without .spec suffix)\n"""
138 )
139
140 # We don't use OptionParser here, since options for different subcommands are
141 # so different. We just look for the chromite options here...
Simon Glass5329b932011-03-14 16:49:04 -0700142 # TODO(sjg): I think we should use OptionParser for two reasons:
143 # 1. It allows us to find out what options/paths are in the scripts
144 # 2. It prevents people from adding new options to underlying scripts
145 # so that Chromite diverges through no fault of the authors.
Doug Andersonf0c73952011-01-18 13:46:07 -0800146 if sys.argv[1:2] == ['--help']:
147 print help_str
148 sys.exit(0)
Doug Andersonf0c73952011-01-18 13:46:07 -0800149 else:
150 # Start by skipping argv[0]
151 argv = sys.argv[1:]
152
Simon Glass5329b932011-03-14 16:49:04 -0700153 # Set up the cros system.
154 cros_env = chromite_env.ChromiteEnv()
155
156 # Configure the operation setup.
157 oper = cros_env.GetOperation()
158 oper.verbose = True
159 oper.progress = True
160
161 # Do we want to be quiet? This is just a hack / demo
162 if argv and argv[0] == '-q':
163 oper.verbose = False
164 argv = argv[1:]
165
Doug Andersonf0c73952011-01-18 13:46:07 -0800166 # Look for special "--chroot" argument to allow for alternate chroots
Doug Andersona9b10902011-02-01 17:54:31 -0800167 if not cros_lib.IsInsideChroot():
Doug Andersonf0c73952011-01-18 13:46:07 -0800168 # Default chroot name...
169 chroot_name = 'chroot'
170
171 # Get chroot spec name if specified; trim argv down if needed...
172 if argv:
173 if argv[0].startswith('--chroot='):
174 _, chroot_name = argv[0].split('=', 2)
175 argv = argv[1:]
176 elif argv[0] == '--chroot':
177 if len(argv) < 2:
Doug Andersona9b10902011-02-01 17:54:31 -0800178 cros_lib.Die('Chroot not specified.')
Doug Andersonf0c73952011-01-18 13:46:07 -0800179
180 chroot_name = argv[1]
181 argv = argv[2:]
182
Doug Andersona9b10902011-02-01 17:54:31 -0800183 chroot_spec_path = utils.FindSpec(chroot_name,
184 spec_type=utils.CHROOT_SPEC_TYPE)
Doug Andersonf0c73952011-01-18 13:46:07 -0800185
Simon Glass5329b932011-03-14 16:49:04 -0700186 oper.Info('Using chroot "%s"' % os.path.relpath(chroot_spec_path))
Doug Andersonf0c73952011-01-18 13:46:07 -0800187
Doug Andersona9b10902011-02-01 17:54:31 -0800188 chroot_config = utils.ReadConfig(chroot_spec_path)
Doug Andersonf0c73952011-01-18 13:46:07 -0800189 else:
190 # Already in the chroot; no need to get config...
191 chroot_config = None
192
193 # Get command and arguments
194 if argv:
Doug Anderson4b6d3962011-01-21 09:41:50 -0800195 cmd_str = argv[0].lower()
Doug Andersonf0c73952011-01-18 13:46:07 -0800196 argv = argv[1:]
197 else:
Doug Anderson4b6d3962011-01-21 09:41:50 -0800198 cmd_str = ''
Doug Andersonf0c73952011-01-18 13:46:07 -0800199
Doug Andersone91900d2011-01-26 11:37:17 -0800200 # Validate the subcmd, popping a menu if needed.
Doug Anderson4b6d3962011-01-21 09:41:50 -0800201 cmd_str = _FindCommand(cmd_str)
Simon Glass5329b932011-03-14 16:49:04 -0700202 oper.Info("Running command '%s'." % cmd_str)
Simon Glass21599602011-02-11 13:57:29 -0800203
Doug Andersonf0c73952011-01-18 13:46:07 -0800204 # Finally, call the function w/ standard argv.
Doug Andersona9b10902011-02-01 17:54:31 -0800205 cmd_cls = _COMMAND_HANDLERS[_COMMAND_STRS.index(cmd_str)]
Doug Anderson4b6d3962011-01-21 09:41:50 -0800206 cmd_obj = cmd_cls()
Simon Glass21599602011-02-11 13:57:29 -0800207 cmd_obj.SetChromiteEnv(cros_env)
Simon Glass5329b932011-03-14 16:49:04 -0700208 try:
209 cmd_obj.Run([cmd_str] + argv, chroot_config=chroot_config)
Doug Andersonf0c73952011-01-18 13:46:07 -0800210
Simon Glass5329b932011-03-14 16:49:04 -0700211 # Handle an error in one of the scripts: print a message and exit.
212 except chromite_env.ChromiteError, msg:
213 sys.exit(1)
Doug Andersonf0c73952011-01-18 13:46:07 -0800214
215if __name__ == '__main__':
216 main()