Repository: mesonbuild/meson

Issue:
meson crashes when wine is misbehaving (subprocess.TimeoutExpired)
When using `wine` to run the tests and if either wine or the binary in question takes a long time to execute or crashes, `meson` freaks out. See the error below. When compiling and testing the same sources natively on Windows, things work fine and I get a user-friendly report saying that the test timed out.

```
> ninja test
[0/1] Running all tests.
Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mesonbuild/mtest.py", line 259, in run_single_test
    (stdo, stde) = p.communicate(timeout=timeout)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/subprocess.py", line 843, in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/subprocess.py", line 1515, in _communicate
    self._check_timeout(endtime, orig_timeout)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/subprocess.py", line 871, in _check_timeout
    raise TimeoutExpired(self.args, orig_timeout)
subprocess.TimeoutExpired: Command '['wine', '/path/to/build/mingw/test/simple/hello0.exe']' timed out after 30 seconds

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/bin/meson", line 37, in <module>
    sys.exit(main())
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/bin/meson", line 34, in main
    return mesonmain.run(sys.argv[1:], launcher)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mesonbuild/mesonmain.py", line 291, in run
    return mtest.run(remaining_args)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mesonbuild/mtest.py", line 619, in run
    return th.doit()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mesonbuild/mtest.py", line 359, in doit
    self.run_tests(tests)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mesonbuild/mtest.py", line 502, in run_tests
    self.drain_futures(futures)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mesonbuild/mtest.py", line 516, in drain_futures
    self.print_stats(numlen, tests, name, result.result(), i)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/concurrent/futures/_base.py", line 432, in result
    return self.__get_result()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/mesonbuild/mtest.py", line 270, in run_single_test
    os.killpg(os.getpgid(p.pid), signal.SIGKILL)
ProcessLookupError: [Errno 3] No such process
FAILED: meson-test 
/opt/local/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6 /opt/local/Library/Frameworks/Python.framework/Versions/3.6/bin/meson test --no-rebuild --print-errorlogs
ninja: build stopped: subcommand failed.
```


Discussion:

Resolving patch:
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -267,7 +267,13 @@ def run_single_test(self, wrap, test):
                 if is_windows():
                     subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)])
                 else:
-                    os.killpg(os.getpgid(p.pid), signal.SIGKILL)
+                    try:
+                        os.killpg(os.getpgid(p.pid), signal.SIGKILL)
+                    except ProcessLookupError:
+                        # Sometimes (e.g. with Wine) this happens.
+                        # There's nothing we can do (maybe the process
+                        # already died) so carry on.
+                        pass
                 (stdo, stde) = p.communicate()
             endtime = time.time()
             duration = endtime - starttime


Test patch: