Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / tools / cr / cr / base / host.py
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.
4
5 """Module for build host support."""
6
7 import os
8 import pipes
9 import signal
10 import subprocess
11
12 import cr
13
14 # Controls what verbosity level turns on command trail logging
15 _TRAIL_VERBOSITY = 2
16
17 def PrintTrail(trail):
18   print 'Command expanded the following variables:'
19   for key, value in trail:
20     if value == None:
21       value = ''
22     print '   ', key, '=', value
23
24
25 class Host(cr.Plugin, cr.Plugin.Type):
26   """Base class for implementing cr hosts.
27
28   The host is the main access point to services provided by the machine cr
29   is running on. It exposes information about the machine, and runs external
30   commands on behalf of the actions.
31   """
32
33   def __init__(self):
34     super(Host, self).__init__()
35
36   def Matches(self):
37     """Detects whether this is the correct host implementation.
38
39     This method is overridden by the concrete implementations.
40     Returns:
41       true if the plugin matches the machine it is running on.
42     """
43     return False
44
45   @classmethod
46   def Select(cls):
47     for host in cls.Plugins():
48       if host.Matches():
49         return host
50
51   def _Execute(self, command,
52                shell=False, capture=False, silent=False,
53                ignore_dry_run=False, return_status=False,
54                ignore_interrupt_signal=False):
55     """This is the only method that launches external programs.
56
57     It is a thin wrapper around subprocess.Popen that handles cr specific
58     issues. The command is expanded in the active context so that variables
59     are substituted.
60     Args:
61       command: the command to run.
62       shell: whether to run the command using the shell.
63       capture: controls wether the output of the command is captured.
64       ignore_dry_run: Normally, if the context is in dry run mode the command is
65         printed but not executed. This flag overrides that behaviour, causing
66         the command to be run anyway.
67       return_status: switches the function to returning the status code rather
68         the output.
69       ignore_interrupt_signal: Ignore the interrupt signal (i.e., Ctrl-C) while
70         the command is running. Useful for letting interactive programs manage
71         Ctrl-C by themselves.
72     Returns:
73       the status if return_status is true, or the output if capture is true,
74       otherwise nothing.
75     """
76     with cr.context.Trace():
77       command = [cr.context.Substitute(arg) for arg in command if arg]
78     trail = cr.context.trail
79     if not command:
80       print 'Empty command passed to execute'
81       exit(1)
82     if cr.context.verbose:
83       print ' '.join(command)
84       if cr.context.verbose >= _TRAIL_VERBOSITY:
85         PrintTrail(trail)
86     if ignore_dry_run or not cr.context.dry_run:
87       out = None
88       if capture:
89         out = subprocess.PIPE
90       elif silent:
91         out = open(os.devnull, "w")
92       try:
93         p = subprocess.Popen(
94             command, shell=shell,
95             env={k: str(v) for k, v in cr.context.exported.items()},
96             stdout=out)
97       except OSError:
98         print 'Failed to exec', command
99         # Don't log the trail if we already have
100         if cr.context.verbose < _TRAIL_VERBOSITY:
101           PrintTrail(trail)
102         exit(1)
103       try:
104         if ignore_interrupt_signal:
105           signal.signal(signal.SIGINT, signal.SIG_IGN)
106         output, _ = p.communicate()
107       finally:
108         if ignore_interrupt_signal:
109           signal.signal(signal.SIGINT, signal.SIG_DFL)
110         if silent:
111           out.close()
112       if return_status:
113         return p.returncode
114       if p.returncode != 0:
115         print 'Error {0} executing command {1}'.format(p.returncode, command)
116         exit(p.returncode)
117       return output or ''
118     return ''
119
120   @cr.Plugin.activemethod
121   def Shell(self, *command):
122     command = ' '.join([pipes.quote(arg) for arg in command])
123     return self._Execute([command], shell=True, ignore_interrupt_signal=True)
124
125   @cr.Plugin.activemethod
126   def Execute(self, *command):
127     return self._Execute(command, shell=False)
128
129   @cr.Plugin.activemethod
130   def ExecuteSilently(self, *command):
131     return self._Execute(command, shell=False, silent=True)
132
133   @cr.Plugin.activemethod
134   def CaptureShell(self, *command):
135     return self._Execute(command,
136                          shell=True, capture=True, ignore_dry_run=True)
137
138   @cr.Plugin.activemethod
139   def Capture(self, *command):
140     return self._Execute(command, capture=True, ignore_dry_run=True)
141
142   @cr.Plugin.activemethod
143   def ExecuteStatus(self, *command):
144     return self._Execute(command,
145                          ignore_dry_run=True, return_status=True)
146
147   @cr.Plugin.activemethod
148   def YesNo(self, question, default=True):
149     """Ask the user a yes no question
150
151     This blocks until the user responds.
152     Args:
153       question: The question string to show the user
154       default: True if the default response is Yes
155     Returns:
156       True if the response was yes.
157     """
158     options = 'Y/n' if default else 'y/N'
159     result = raw_input(question + ' [' + options + '] ').lower()
160     if result == '':
161       return default
162     return result in ['y', 'yes']
163
164   @classmethod
165   def SearchPath(cls, name, paths=[]):
166     """Searches the PATH for an executable.
167
168     Args:
169       name: the name of the binary to search for.
170     Returns:
171       the set of executables found, or an empty list if none.
172     """
173     result = []
174     extensions = ['']
175     extensions.extend(os.environ.get('PATHEXT', '').split(os.pathsep))
176     paths = [cr.context.Substitute(path) for path in paths if path]
177     paths = paths + os.environ.get('PATH', '').split(os.pathsep)
178     for path in paths:
179       partial = os.path.join(path, name)
180       for extension in extensions:
181         filename = partial + extension
182         if os.path.exists(filename) and filename not in result:
183           result.append(filename)
184     return result