1 # Copyright 2013 The Chromium 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.
5 """Module for build host support."""
14 # Controls what verbosity level turns on command trail logging
18 class Host(cr.Plugin, cr.Plugin.Type):
19 """Base class for implementing cr hosts.
21 The host is the main access point to services provided by the machine cr
22 is running on. It exposes information about the machine, and runs external
23 commands on behalf of the actions.
27 super(Host, self).__init__()
30 """Detects whether this is the correct host implementation.
32 This method is overridden by the concrete implementations.
34 true if the plugin matches the machine it is running on.
39 def Select(cls, context):
40 for host in cls.Plugins():
44 def _Execute(self, context, command,
45 shell=False, capture=False, silent=False,
46 ignore_dry_run=False, return_status=False,
47 ignore_interrupt_signal=False):
48 """This is the only method that launches external programs.
50 It is a thin wrapper around subprocess.Popen that handles cr specific
51 issues. The command is expanded in the context, so that context variables
54 context: the cr context to run under.
55 command: the command to run.
56 shell: whether to run the command using the shell.
57 capture: controls wether the output of the command is captured.
58 ignore_dry_run: Normally, if the context is in dry run mode the command is
59 printed but not executed. This flag overrides that behaviour, causing
60 the command to be run anyway.
61 return_status: switches the function to returning the status code rather
63 ignore_interrupt_signal: Ignore the interrupt signal (i.e., Ctrl-C) while
64 the command is running. Useful for letting interactive programs manage
67 the status if return_status is true, or the output if capture is true,
71 command = [context.Substitute(arg) for arg in command if arg]
74 print 'Empty command passed to execute'
77 print ' '.join(command)
78 if context.verbose >= _TRAIL_VERBOSITY:
79 print 'Command expanded the following variables:'
80 for key, value in trail:
81 print ' ', key, '=', value
82 if ignore_dry_run or not context.dry_run:
87 out = open(os.devnull, "w")
91 env={k: str(v) for k, v in context.exported.items()},
94 print 'Failed to exec', command
95 # Don't log the trail if we already have
96 if context.verbose < _TRAIL_VERBOSITY:
97 print 'Variables used to build the command were:'
98 for key, value in trail:
99 print ' ', key, '=', value
102 if ignore_interrupt_signal:
103 signal.signal(signal.SIGINT, signal.SIG_IGN)
104 output, _ = p.communicate()
106 if ignore_interrupt_signal:
107 signal.signal(signal.SIGINT, signal.SIG_DFL)
112 if p.returncode != 0:
113 print 'Error {0} executing command {1}'.format(p.returncode, command)
118 @cr.Plugin.activemethod
119 def Shell(self, context, *command):
120 command = ' '.join([pipes.quote(arg) for arg in command])
121 return self._Execute(context, [command], shell=True,
122 ignore_interrupt_signal=True)
124 @cr.Plugin.activemethod
125 def Execute(self, context, *command):
126 return self._Execute(context, command, shell=False)
128 @cr.Plugin.activemethod
129 def ExecuteSilently(self, context, *command):
130 return self._Execute(context, command, shell=False, silent=True)
132 @cr.Plugin.activemethod
133 def CaptureShell(self, context, *command):
134 return self._Execute(context, command,
135 shell=True, capture=True, ignore_dry_run=True)
137 @cr.Plugin.activemethod
138 def Capture(self, context, *command):
139 return self._Execute(context, command, capture=True, ignore_dry_run=True)
141 @cr.Plugin.activemethod
142 def ExecuteStatus(self, context, *command):
143 return self._Execute(context, command,
144 ignore_dry_run=True, return_status=True)
146 @cr.Plugin.activemethod
147 def YesNo(self, question, default=True):
148 """Ask the user a yes no question
150 This blocks until the user responds.
152 question: The question string to show the user
153 default: True if the default response is Yes
155 True if the response was yes.
157 options = 'Y/n' if default else 'y/N'
158 result = raw_input(question + ' [' + options + '] ').lower()
161 return result in ['y', 'yes']
164 def SearchPath(cls, name):
165 """Searches the PATH for an executable.
168 name: the name of the binary to search for.
170 the set of executables found, or an empty list if none.
174 extensions.extend(os.environ.get('PATHEXT', '').split(os.pathsep))
175 for path in os.environ.get('PATH', '').split(os.pathsep):
176 partial = os.path.join(path, name)
177 for extension in extensions:
178 filename = partial + extension
179 if os.path.exists(filename) and filename not in result:
180 result.append(filename)