REGRESSION(r263625): [WPE][GTK] MiniBrowser output no longer includes stderr and...
[WebKit-https.git] / Tools / Scripts / webkitpy / common / system / abstractexecutive.py
1 # Copyright (C) 2017 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1.  Redistributions of source code must retain the above copyright
7 #     notice, this list of conditions and the following disclaimer.
8 # 2.  Redistributions in binary form must reproduce the above copyright
9 #     notice, this list of conditions and the following disclaimer in the
10 #     documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
13 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
16 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23 import sys
24 import time
25
26 from webkitpy.common.system.filesystem import FileSystem
27 from webkitpy.common import unicode_compatibility
28
29 class AbstractExecutive(object):
30
31     def run_and_throw_if_fail(self, args, quiet=False, decode_output=True, **kwargs):
32         raise NotImplementedError('subclasses must implement')
33
34     def cpu_count(self):
35         raise NotImplementedError('subclasses must implement')
36
37     @staticmethod
38     def interpreter_for_script(script_path, fs=None):
39         fs = fs or FileSystem()
40         lines = fs.read_text_file(script_path).splitlines()
41         if not len(lines):
42             return None
43         first_line = lines[0]
44         if not first_line.startswith('#!'):
45             return None
46         if first_line.find('python') > -1:
47             return sys.executable
48         if first_line.find('perl') > -1:
49             return 'perl'
50         if first_line.find('ruby') > -1:
51             return 'ruby'
52         return None
53
54     @staticmethod
55     def shell_command_for_script(script_path, fs=None):
56         fs = fs or FileSystem()
57         # Win32 does not support shebang. We need to detect the interpreter ourself.
58         if sys.platform.startswith('win'):
59             interpreter = AbstractExecutive.interpreter_for_script(script_path, fs)
60             if interpreter:
61                 return [interpreter, script_path]
62         return [script_path]
63
64     def kill_process(self, pid):
65         raise NotImplementedError('subclasses must implement')
66
67     def check_running_pid(self, pid):
68         raise NotImplementedError('subclasses must implement')
69
70     def running_pids(self, process_name_filter=None):
71         raise NotImplementedError('subclasses must implement')
72
73     def wait_newest(self, process_name_filter=None):
74         if not process_name_filter:
75             process_name_filter = lambda process_name: True
76
77         running_pids = self.running_pids(process_name_filter)
78         if not running_pids:
79             return
80         pid = running_pids[-1]
81
82         while self.check_running_pid(pid):
83             time.sleep(0.25)
84
85     def wait_limited(self, pid, limit_in_seconds=None, check_frequency_in_seconds=None):
86         seconds_left = limit_in_seconds or 10
87         sleep_length = check_frequency_in_seconds or 1
88         while seconds_left > 0 and self.check_running_pid(pid):
89             seconds_left -= sleep_length
90             time.sleep(sleep_length)
91
92     def interrupt(self, pid):
93         raise NotImplementedError('subclasses must implement')
94
95     @staticmethod
96     def default_error_handler(error):
97         raise error
98
99     @staticmethod
100     def ignore_error(error):
101         pass
102
103     def _stringify_args(self, args):
104         return map(unicode_compatibility.unicode, args)
105
106     def command_for_printing(self, args):
107         """Returns a print-ready string representing command args.
108         The string should be copy/paste ready for execution in a shell."""
109         args = self._stringify_args(args)
110         return unicode_compatibility.decode_if_necessary(unicode_compatibility.encode_if_necessary(' '.join(args), 'unicode_escape'))
111
112     def run_command(self, args, cwd=None, env=None, input=None, stdout=None, error_handler=None, ignore_errors=False,
113         return_exit_code=False, return_stderr=True, decode_output=True):
114         raise NotImplementedError('subclasses must implement')
115
116     def popen(self, args, **kwargs):
117         raise NotImplementedError('subclasses must implement')
118
119     def run_in_parallel(self, command_lines_and_cwds, processes=None):
120         raise NotImplementedError('subclasses must implement')