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."""
13 # Controls what verbosity level turns on command trail logging
17 class Host(cr.Plugin, cr.Plugin.Type):
18 """Base class for implementing cr hosts.
20 The host is the main access point to services provided by the machine cr
21 is running on. It exposes information about the machine, and runs external
22 commands on behalf of the actions.
26 super(Host, self).__init__()
29 """Detects whether this is the correct host implementation.
31 This method is overridden by the concrete implementations.
33 true if the plugin matches the machine it is running on.
38 def Select(cls, context):
39 for host in cls.Plugins():
43 def _Execute(self, context, command,
44 shell=False, capture=False, silent=False,
45 ignore_dry_run=False, return_status=False):
46 """This is the only method that launches external programs.
48 It is a thin wrapper around subprocess.Popen that handles cr specific
49 issues. The command is expanded in the context, so that context variables
52 context: the cr context to run under.
53 command: the command to run.
54 shell: whether to run the command using the shell.
55 capture: controls wether the output of the command is captured.
56 ignore_dry_run: Normally, if the context is in dry run mode the command is
57 printed but not executed. This flag overrides that behaviour, causing
58 the command to be run anyway.
59 return_status: switches the function to returning the status code rather
62 the status if return_status is true, or the output if capture is true,
66 command = [context.Substitute(arg) for arg in command if arg]
69 print 'Empty command passed to execute'
72 print ' '.join(command)
73 if context.verbose >= _TRAIL_VERBOSITY:
74 print 'Command expanded the following variables:'
75 for key, value in trail:
76 print ' ', key, '=', value
77 if ignore_dry_run or not context.dry_run:
82 out = open(os.devnull, "w")
86 env={k: str(v) for k, v in context.exported.items()},
89 print 'Failed to exec', command
90 # Don't log the trail if we already have
91 if context.verbose < _TRAIL_VERBOSITY:
92 print 'Variables used to build the command were:'
93 for key, value in trail:
94 print ' ', key, '=', value
97 output, _ = p.communicate()
98 except KeyboardInterrupt:
107 if p.returncode != 0:
108 print 'Error {0} executing command {1}'.format(p.returncode, command)
113 @cr.Plugin.activemethod
114 def Shell(self, context, *command):
115 command = ' '.join([pipes.quote(arg) for arg in command])
116 return self._Execute(context, [command], shell=True)
118 @cr.Plugin.activemethod
119 def Execute(self, context, *command):
120 return self._Execute(context, command, shell=False)
122 @cr.Plugin.activemethod
123 def ExecuteSilently(self, context, *command):
124 return self._Execute(context, command, shell=False, silent=True)
126 @cr.Plugin.activemethod
127 def CaptureShell(self, context, *command):
128 return self._Execute(context, command,
129 shell=True, capture=True, ignore_dry_run=True)
131 @cr.Plugin.activemethod
132 def Capture(self, context, *command):
133 return self._Execute(context, command, capture=True, ignore_dry_run=True)
135 @cr.Plugin.activemethod
136 def ExecuteStatus(self, context, *command):
137 return self._Execute(context, command,
138 ignore_dry_run=True, return_status=True)
140 @cr.Plugin.activemethod
141 def YesNo(self, question, default=True):
142 """Ask the user a yes no question
144 This blocks until the user responds.
146 question: The question string to show the user
147 default: True if the default response is Yes
149 True if the response was yes.
151 options = 'Y/n' if default else 'y/N'
152 result = raw_input(question + ' [' + options + '] ').lower()
155 return result in ['y', 'yes']
158 def SearchPath(cls, name):
159 """Searches the PATH for an executable.
162 name: the name of the binary to search for.
164 the set of executables found, or an empty list if none.
168 extensions.extend(os.environ.get('PATHEXT', '').split(os.pathsep))
169 for path in os.environ.get('PATH', '').split(os.pathsep):
170 partial = os.path.join(path, name)
171 for extension in extensions:
172 filename = partial + extension
173 if os.path.exists(filename) and filename not in result:
174 result.append(filename)