Upstream version 5.34.104.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
18 class Host(cr.Plugin, cr.Plugin.Type):
19   """Base class for implementing cr hosts.
20
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.
24   """
25
26   def __init__(self):
27     super(Host, self).__init__()
28
29   def Matches(self):
30     """Detects whether this is the correct host implementation.
31
32     This method is overridden by the concrete implementations.
33     Returns:
34       true if the plugin matches the machine it is running on.
35     """
36     return False
37
38   @classmethod
39   def Select(cls, context):
40     for host in cls.Plugins():
41       if host.Matches():
42         return host
43
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.
49
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
52     are substituted.
53     Args:
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
62         the output.
63       ignore_interrupt_signal: Ignore the interrupt signal (i.e., Ctrl-C) while
64         the command is running. Useful for letting interactive programs manage
65         Ctrl-C by themselves.
66     Returns:
67       the status if return_status is true, or the output if capture is true,
68       otherwise nothing.
69     """
70     with context.Trace():
71       command = [context.Substitute(arg) for arg in command if arg]
72     trail = context.trail
73     if not command:
74       print 'Empty command passed to execute'
75       exit(1)
76     if context.verbose:
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:
83       out = None
84       if capture:
85         out = subprocess.PIPE
86       elif silent:
87         out = open(os.devnull, "w")
88       try:
89         p = subprocess.Popen(
90             command, shell=shell,
91             env={k: str(v) for k, v in context.exported.items()},
92             stdout=out)
93       except OSError:
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
100         exit(1)
101       try:
102         if ignore_interrupt_signal:
103           signal.signal(signal.SIGINT, signal.SIG_IGN)
104         output, _ = p.communicate()
105       finally:
106         if ignore_interrupt_signal:
107           signal.signal(signal.SIGINT, signal.SIG_DFL)
108         if silent:
109           out.close()
110       if return_status:
111         return p.returncode
112       if p.returncode != 0:
113         print 'Error {0} executing command {1}'.format(p.returncode, command)
114         exit(p.returncode)
115       return output or ''
116     return ''
117
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)
123
124   @cr.Plugin.activemethod
125   def Execute(self, context, *command):
126     return self._Execute(context, command, shell=False)
127
128   @cr.Plugin.activemethod
129   def ExecuteSilently(self, context, *command):
130     return self._Execute(context, command, shell=False, silent=True)
131
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)
136
137   @cr.Plugin.activemethod
138   def Capture(self, context, *command):
139     return self._Execute(context, command, capture=True, ignore_dry_run=True)
140
141   @cr.Plugin.activemethod
142   def ExecuteStatus(self, context, *command):
143     return self._Execute(context, command,
144                          ignore_dry_run=True, return_status=True)
145
146   @cr.Plugin.activemethod
147   def YesNo(self, question, default=True):
148     """Ask the user a yes no question
149
150     This blocks until the user responds.
151     Args:
152       question: The question string to show the user
153       default: True if the default response is Yes
154     Returns:
155       True if the response was yes.
156     """
157     options = 'Y/n' if default else 'y/N'
158     result = raw_input(question + ' [' + options + '] ').lower()
159     if result == '':
160       return default
161     return result in ['y', 'yes']
162
163   @classmethod
164   def SearchPath(cls, name):
165     """Searches the PATH for an executable.
166
167     Args:
168       name: the name of the binary to search for.
169     Returns:
170       the set of executables found, or an empty list if none.
171     """
172     result = []
173     extensions = ['']
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)
181     return result