blob: 0fb4a2ddb90c0503d2bb646563f61c70e33344a5 [file] [log] [blame]
Doug Anderson4b6d3962011-01-21 09:41:50 -08001# Copyright (c) 2011 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
5"""ChromiteCmd abstract class and related functions."""
6
Doug Andersona9b10902011-02-01 17:54:31 -08007# Python imports
8import os
9import sys
10
11# Local imports
12import chromite.lib.cros_build_lib as cros_lib
13from chromite.shell import utils
14
Doug Anderson4b6d3962011-01-21 09:41:50 -080015
16class ChromiteCmd(object):
17 """The abstract base class of commands listed at the top level of chromite."""
18
19 def __init__(self):
20 """ChromiteCmd constructor."""
21 super(ChromiteCmd, self).__init__()
22
Doug Anderson4b6d3962011-01-21 09:41:50 -080023 def Run(self, raw_argv, chroot_config=None):
24 """Run the command.
25
26 All subclasses must implement this.
27
28 Args:
29 raw_argv: Command line arguments, including this command's name, but not
30 the chromite command name or chromite options.
31 chroot_config: A SafeConfigParser for the chroot config; or None chromite
32 was called from within the chroot.
33 """
34 # Must be implemented by subclass...
35 raise NotImplementedError()
Doug Andersona9b10902011-02-01 17:54:31 -080036
37
38class WrappedChrootCmd(ChromiteCmd):
39 """Superclass for any command that is simply wrapped by chromite.
40
41 These are commands where:
42 - We parse the command line only enough to figure out what board they
43 want. All othe command line parsing is handled by the wrapped command.
44 Because of this, the board name _needs_ to be specified first.
45 - Everything else (arg parsing, help, etc) is handled by the wrapped command.
46 The usage string will be a little messed up, but hopefully that's OK.
47 """
48
Doug Andersona17e0102011-02-07 13:16:55 -080049 def __init__(self, target_cmd, host_cmd, need_args=False, env_whitelist=None):
Doug Andersona9b10902011-02-01 17:54:31 -080050 """WrappedChrootCmd constructor.
51
52 Args:
53 target_cmd: We'll put this at the start of argv when calling a target
54 command. We'll substiture %s with the target.
55 Like - ['my_command-%s'] or ['my_command', '--board=%s']
56 host_cmd: We'll put this at the start of argv when calling a host command.
57 Like - ['my_command'] or ['sudo', 'my_command']
58 need_args: If True, we'll prompt for arguments if they weren't specified.
59 This makes the most sense when someone runs chromite with no arguments
60 and then walks through the menus. It's not ideal, but less sucky than
61 just quitting.
Doug Anderson143e7032011-02-04 15:05:37 -080062 env_whitelist: This is a whitelist of environment variables that will be
63 read from the current environment and passed through to the command.
64 Note that this doesn't matter much when we start inside the chroot,
65 but matters a lot when we transition into the chroot (since the
66 environment is reset when that happens).
67 Useful for portage commands. Like: ['USE', 'FEATURES']
Doug Andersona9b10902011-02-01 17:54:31 -080068 """
69 # Call superclass constructor.
70 super(WrappedChrootCmd, self).__init__()
71
72 # Save away params for use later in Run().
73 self._target_cmd = target_cmd
74 self._host_cmd = host_cmd
75 self._need_args = need_args
76
Doug Anderson143e7032011-02-04 15:05:37 -080077 # Handle the env_whitelist. We need to do this in __init__ rather than in
78 # Run(), since we want the environment vars from outside the chroot.
Doug Andersona17e0102011-02-07 13:16:55 -080079 if env_whitelist is None:
80 self._env_to_add = {}
81 else:
82 self._env_to_add = dict((key, os.environ[key]) for key in env_whitelist
83 if key in os.environ)
Doug Anderson143e7032011-02-04 15:05:37 -080084
Doug Andersona9b10902011-02-01 17:54:31 -080085 def Run(self, raw_argv, chroot_config=None, argv=None, build_config=None):
86 """Run the command.
87
88 Args:
89 raw_argv: Command line arguments, including this command's name, but not
90 the chromite command name or chromite options.
91 chroot_config: A SafeConfigParser for the chroot config; or None chromite
92 was called from within the chroot.
93 argv: None when called normally, but contains argv with board stripped off
94 if we call ourselves with utils.EnterChroot().
95 build_config: None when called normally, but contains the SafeConfigParser
96 for the build config if we call ourselves with utils.EnterChroot().
97 Note that even when called through utils.EnterChroot(), could still
98 be None if user chose 'HOST' as the target.
99 """
100 # If we didn't get called through EnterChroot, we need to read the build
101 # config.
102 if argv is None:
103 # We look for the build config without calling OptionParser. This means
104 # that the board _needs_ to be first (if it's specified) and all options
105 # will be passed straight to our subcommand.
106 argv, build_config = utils.GetBuildConfigFromArgs(raw_argv[1:])
107
108 # Enter the chroot if needed...
109 if not cros_lib.IsInsideChroot():
110 utils.EnterChroot(chroot_config, (self, 'Run'), raw_argv, argv=argv,
111 build_config=build_config)
112 else:
113 # We'll put CWD as src/scripts when running the command. Since everyone
114 # running by hand has their cwd there, it is probably the safest.
115 cwd = os.path.join(utils.SRCROOT_PATH, 'src', 'scripts')
116
117 # Get command to call. If build_config is None, it means host.
118 if build_config is None:
119 argv_prefix = self._host_cmd
120 else:
121 # Make argv_prefix w/ target.
122 target_name = build_config.get('BUILD', 'target')
123 argv_prefix = [arg % target_name for arg in self._target_cmd]
124
125 # Not a great way to to specify arguments, but works for now... Wrapped
126 # commands are not wonderful interfaces anyway...
127 if self._need_args and not argv:
128 while True:
129 sys.stderr.write('arg %d (blank to exit): ' % (len(argv)+1))
130 arg = raw_input()
131 if not arg:
132 break
133 argv.append(arg)
134
135 # Add the prefix...
136 argv = argv_prefix + argv
137
Doug Anderson143e7032011-02-04 15:05:37 -0800138 # Update the environment with anything from the whitelist.
139 env = os.environ.copy()
140 env.update(self._env_to_add)
141
Doug Anderson60d0e4f2011-02-07 11:15:26 -0800142 # Run ignoring errors (since some commands might return errors from
143 # things like --help).
144 #
145 # TODO(dianders): "cros_workon --help" used to return errors, but that
146 # has been fixed. Are there any other places where errors should
147 # be ignored? If not, we should remove the error_ok parameter.
Doug Anderson143e7032011-02-04 15:05:37 -0800148 cros_lib.RunCommand(argv, cwd=cwd, ignore_sigint=True, error_ok=True,
149 env=env)