xref: /nrf52832-nimble/rt-thread/tools/win32spawn.py (revision 104654410c56c573564690304ae786df310c91fc)
1*10465441SEvalZero#
2*10465441SEvalZero# File      : win32spawn.py
3*10465441SEvalZero# This file is part of RT-Thread RTOS
4*10465441SEvalZero# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
5*10465441SEvalZero#
6*10465441SEvalZero#  This program is free software; you can redistribute it and/or modify
7*10465441SEvalZero#  it under the terms of the GNU General Public License as published by
8*10465441SEvalZero#  the Free Software Foundation; either version 2 of the License, or
9*10465441SEvalZero#  (at your option) any later version.
10*10465441SEvalZero#
11*10465441SEvalZero#  This program is distributed in the hope that it will be useful,
12*10465441SEvalZero#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13*10465441SEvalZero#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*10465441SEvalZero#  GNU General Public License for more details.
15*10465441SEvalZero#
16*10465441SEvalZero#  You should have received a copy of the GNU General Public License along
17*10465441SEvalZero#  with this program; if not, write to the Free Software Foundation, Inc.,
18*10465441SEvalZero#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*10465441SEvalZero#
20*10465441SEvalZero# Change Logs:
21*10465441SEvalZero# Date           Author       Notes
22*10465441SEvalZero# 2015-01-20     Bernard      Add copyright information
23*10465441SEvalZero#
24*10465441SEvalZero
25*10465441SEvalZeroimport os
26*10465441SEvalZeroimport threading
27*10465441SEvalZeroimport Queue
28*10465441SEvalZero
29*10465441SEvalZero# Windows import
30*10465441SEvalZeroimport win32file
31*10465441SEvalZeroimport win32pipe
32*10465441SEvalZeroimport win32api
33*10465441SEvalZeroimport win32con
34*10465441SEvalZeroimport win32security
35*10465441SEvalZeroimport win32process
36*10465441SEvalZeroimport win32event
37*10465441SEvalZero
38*10465441SEvalZeroclass Win32Spawn(object):
39*10465441SEvalZero    def __init__(self, cmd, shell=False):
40*10465441SEvalZero        self.queue = Queue.Queue()
41*10465441SEvalZero        self.is_terminated = False
42*10465441SEvalZero        self.wake_up_event = win32event.CreateEvent(None, 0, 0, None)
43*10465441SEvalZero
44*10465441SEvalZero        exec_dir = os.getcwd()
45*10465441SEvalZero        comspec = os.environ.get("COMSPEC", "cmd.exe")
46*10465441SEvalZero        cmd = comspec + ' /c ' + cmd
47*10465441SEvalZero
48*10465441SEvalZero        win32event.ResetEvent(self.wake_up_event)
49*10465441SEvalZero
50*10465441SEvalZero        currproc = win32api.GetCurrentProcess()
51*10465441SEvalZero
52*10465441SEvalZero        sa = win32security.SECURITY_ATTRIBUTES()
53*10465441SEvalZero        sa.bInheritHandle = 1
54*10465441SEvalZero
55*10465441SEvalZero        child_stdout_rd, child_stdout_wr = win32pipe.CreatePipe(sa, 0)
56*10465441SEvalZero        child_stdout_rd_dup = win32api.DuplicateHandle(currproc, child_stdout_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
57*10465441SEvalZero        win32file.CloseHandle(child_stdout_rd)
58*10465441SEvalZero
59*10465441SEvalZero        child_stderr_rd, child_stderr_wr = win32pipe.CreatePipe(sa, 0)
60*10465441SEvalZero        child_stderr_rd_dup = win32api.DuplicateHandle(currproc, child_stderr_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
61*10465441SEvalZero        win32file.CloseHandle(child_stderr_rd)
62*10465441SEvalZero
63*10465441SEvalZero        child_stdin_rd, child_stdin_wr = win32pipe.CreatePipe(sa, 0)
64*10465441SEvalZero        child_stdin_wr_dup = win32api.DuplicateHandle(currproc, child_stdin_wr, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
65*10465441SEvalZero        win32file.CloseHandle(child_stdin_wr)
66*10465441SEvalZero
67*10465441SEvalZero        startup_info = win32process.STARTUPINFO()
68*10465441SEvalZero        startup_info.hStdInput = child_stdin_rd
69*10465441SEvalZero        startup_info.hStdOutput = child_stdout_wr
70*10465441SEvalZero        startup_info.hStdError = child_stderr_wr
71*10465441SEvalZero        startup_info.dwFlags = win32process.STARTF_USESTDHANDLES
72*10465441SEvalZero
73*10465441SEvalZero        cr_flags = 0
74*10465441SEvalZero        cr_flags = win32process.CREATE_NEW_PROCESS_GROUP
75*10465441SEvalZero
76*10465441SEvalZero        env = os.environ.copy()
77*10465441SEvalZero        self.h_process, h_thread, dw_pid, dw_tid = win32process.CreateProcess(None, cmd, None, None, 1,
78*10465441SEvalZero                                                                              cr_flags, env, os.path.abspath(exec_dir),
79*10465441SEvalZero                                                                              startup_info)
80*10465441SEvalZero
81*10465441SEvalZero        win32api.CloseHandle(h_thread)
82*10465441SEvalZero
83*10465441SEvalZero        win32file.CloseHandle(child_stdin_rd)
84*10465441SEvalZero        win32file.CloseHandle(child_stdout_wr)
85*10465441SEvalZero        win32file.CloseHandle(child_stderr_wr)
86*10465441SEvalZero
87*10465441SEvalZero        self.__child_stdout = child_stdout_rd_dup
88*10465441SEvalZero        self.__child_stderr = child_stderr_rd_dup
89*10465441SEvalZero        self.__child_stdin = child_stdin_wr_dup
90*10465441SEvalZero
91*10465441SEvalZero        self.exit_code = -1
92*10465441SEvalZero
93*10465441SEvalZero    def close(self):
94*10465441SEvalZero        win32file.CloseHandle(self.__child_stdout)
95*10465441SEvalZero        win32file.CloseHandle(self.__child_stderr)
96*10465441SEvalZero        win32file.CloseHandle(self.__child_stdin)
97*10465441SEvalZero        win32api.CloseHandle(self.h_process)
98*10465441SEvalZero        win32api.CloseHandle(self.wake_up_event)
99*10465441SEvalZero
100*10465441SEvalZero    def kill_subprocess():
101*10465441SEvalZero        win32event.SetEvent(self.wake_up_event)
102*10465441SEvalZero
103*10465441SEvalZero    def sleep(secs):
104*10465441SEvalZero        win32event.ResetEvent(self.wake_up_event)
105*10465441SEvalZero        timeout = int(1000 * secs)
106*10465441SEvalZero        val = win32event.WaitForSingleObject(self.wake_up_event, timeout)
107*10465441SEvalZero        if val == win32event.WAIT_TIMEOUT:
108*10465441SEvalZero            return True
109*10465441SEvalZero        else:
110*10465441SEvalZero            # The wake_up_event must have been signalled
111*10465441SEvalZero            return False
112*10465441SEvalZero
113*10465441SEvalZero    def get(self, block=True, timeout=None):
114*10465441SEvalZero        return self.queue.get(block=block, timeout=timeout)
115*10465441SEvalZero
116*10465441SEvalZero    def qsize(self):
117*10465441SEvalZero        return self.queue.qsize()
118*10465441SEvalZero
119*10465441SEvalZero    def __wait_for_child(self):
120*10465441SEvalZero        # kick off threads to read from stdout and stderr of the child process
121*10465441SEvalZero        threading.Thread(target=self.__do_read, args=(self.__child_stdout, )).start()
122*10465441SEvalZero        threading.Thread(target=self.__do_read, args=(self.__child_stderr, )).start()
123*10465441SEvalZero
124*10465441SEvalZero        while True:
125*10465441SEvalZero            # block waiting for the process to finish or the interrupt to happen
126*10465441SEvalZero            handles = (self.wake_up_event, self.h_process)
127*10465441SEvalZero            val = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE)
128*10465441SEvalZero
129*10465441SEvalZero            if val >= win32event.WAIT_OBJECT_0 and val < win32event.WAIT_OBJECT_0 + len(handles):
130*10465441SEvalZero                handle = handles[val - win32event.WAIT_OBJECT_0]
131*10465441SEvalZero                if handle == self.wake_up_event:
132*10465441SEvalZero                    win32api.TerminateProcess(self.h_process, 1)
133*10465441SEvalZero                    win32event.ResetEvent(self.wake_up_event)
134*10465441SEvalZero                    return False
135*10465441SEvalZero                elif handle == self.h_process:
136*10465441SEvalZero                    # the process has ended naturally
137*10465441SEvalZero                    return True
138*10465441SEvalZero                else:
139*10465441SEvalZero                    assert False, "Unknown handle fired"
140*10465441SEvalZero            else:
141*10465441SEvalZero                assert False, "Unexpected return from WaitForMultipleObjects"
142*10465441SEvalZero
143*10465441SEvalZero    # Wait for job to finish. Since this method blocks, it can to be called from another thread.
144*10465441SEvalZero    # If the application wants to kill the process, it should call kill_subprocess().
145*10465441SEvalZero    def wait(self):
146*10465441SEvalZero        if not self.__wait_for_child():
147*10465441SEvalZero            # it's been killed
148*10465441SEvalZero            result = False
149*10465441SEvalZero        else:
150*10465441SEvalZero            # normal termination
151*10465441SEvalZero            self.exit_code = win32process.GetExitCodeProcess(self.h_process)
152*10465441SEvalZero            result = self.exit_code == 0
153*10465441SEvalZero        self.close()
154*10465441SEvalZero        self.is_terminated = True
155*10465441SEvalZero
156*10465441SEvalZero        return result
157*10465441SEvalZero
158*10465441SEvalZero    # This method gets called on a worker thread to read from either a stderr
159*10465441SEvalZero    # or stdout thread from the child process.
160*10465441SEvalZero    def __do_read(self, handle):
161*10465441SEvalZero        bytesToRead = 1024
162*10465441SEvalZero        while 1:
163*10465441SEvalZero            try:
164*10465441SEvalZero                finished = 0
165*10465441SEvalZero                hr, data = win32file.ReadFile(handle, bytesToRead, None)
166*10465441SEvalZero                if data:
167*10465441SEvalZero                    self.queue.put_nowait(data)
168*10465441SEvalZero            except win32api.error:
169*10465441SEvalZero                finished = 1
170*10465441SEvalZero
171*10465441SEvalZero            if finished:
172*10465441SEvalZero                return
173*10465441SEvalZero
174*10465441SEvalZero    def start_pipe(self):
175*10465441SEvalZero        def worker(pipe):
176*10465441SEvalZero            return pipe.wait()
177*10465441SEvalZero
178*10465441SEvalZero        thrd = threading.Thread(target=worker, args=(self, ))
179*10465441SEvalZero        thrd.start()
180