Mercurial > hg > truffle
comparison mxtool/mx.py @ 4158:f3a50640333b
Added support for specifying a timeout when running an external command.
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Thu, 22 Dec 2011 22:51:37 +0100 |
parents | c78bace5086a |
children | 8c507a8dd6a4 |
comparison
equal
deleted
inserted
replaced
4157:b26279781d95 | 4158:f3a50640333b |
---|---|
85 # | 85 # |
86 # Other properties can be specified for projects and libraries for use by extension commands. | 86 # Other properties can be specified for projects and libraries for use by extension commands. |
87 # | 87 # |
88 # Values can use environment variables with Bash syntax (e.g. ${HOME}). | 88 # Values can use environment variables with Bash syntax (e.g. ${HOME}). |
89 | 89 |
90 import sys | 90 import sys, os, errno, time, subprocess, shlex, types, urllib2, contextlib, StringIO, zipfile |
91 import os | 91 import shutil, fnmatch, re, xml.dom.minidom |
92 import subprocess | |
93 from collections import Callable | 92 from collections import Callable |
94 from threading import Thread | 93 from threading import Thread |
95 from argparse import ArgumentParser, REMAINDER | 94 from argparse import ArgumentParser, REMAINDER |
96 from os.path import join, dirname, exists, getmtime, isabs, expandvars, isdir | 95 from os.path import join, dirname, exists, getmtime, isabs, expandvars, isdir |
97 import shlex | |
98 import types | |
99 import urllib2 | |
100 import contextlib | |
101 import StringIO | |
102 import zipfile | |
103 import shutil, fnmatch, re, xml.dom.minidom | |
104 | 96 |
105 DEFAULT_JAVA_ARGS = '-ea -Xss2m -Xmx1g' | 97 DEFAULT_JAVA_ARGS = '-ea -Xss2m -Xmx1g' |
106 | 98 |
107 _projects = dict() | 99 _projects = dict() |
108 _libs = dict() | 100 _libs = dict() |
460 self.add_argument('--J', dest='java_args', help='Java VM arguments (e.g. --J @-dsa)', metavar='@<args>', default=DEFAULT_JAVA_ARGS) | 452 self.add_argument('--J', dest='java_args', help='Java VM arguments (e.g. --J @-dsa)', metavar='@<args>', default=DEFAULT_JAVA_ARGS) |
461 self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[]) | 453 self.add_argument('--Jp', action='append', dest='java_args_pfx', help='prefix Java VM arguments (e.g. --Jp @-dsa)', metavar='@<args>', default=[]) |
462 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[]) | 454 self.add_argument('--Ja', action='append', dest='java_args_sfx', help='suffix Java VM arguments (e.g. --Ja @-dsa)', metavar='@<args>', default=[]) |
463 self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~')) | 455 self.add_argument('--user-home', help='users home directory', metavar='<path>', default=os.path.expanduser('~')) |
464 self.add_argument('--java-home', help='JDK installation directory (must be JDK 6 or later)', metavar='<path>') | 456 self.add_argument('--java-home', help='JDK installation directory (must be JDK 6 or later)', metavar='<path>') |
457 self.add_argument('--timeout', help='Timeout (in seconds) for subprocesses', type=int, default=0, metavar='<secs>') | |
465 | 458 |
466 def _parse_cmd_line(self, args=None): | 459 def _parse_cmd_line(self, args=None): |
467 if args is None: | 460 if args is None: |
468 args = sys.argv[1:] | 461 args = sys.argv[1:] |
469 | 462 |
504 return _java | 497 return _java |
505 | 498 |
506 def run_java(args, nonZeroIsFatal=True, out=None, err=None, cwd=None): | 499 def run_java(args, nonZeroIsFatal=True, out=None, err=None, cwd=None): |
507 return run(java().format_cmd(args), nonZeroIsFatal=nonZeroIsFatal, out=out, err=err, cwd=cwd) | 500 return run(java().format_cmd(args), nonZeroIsFatal=nonZeroIsFatal, out=out, err=err, cwd=cwd) |
508 | 501 |
509 def run(args, nonZeroIsFatal=True, out=None, err=None, cwd=None): | 502 def _waitWithTimeout(process, args, timeout): |
503 def _waitpid(pid): | |
504 while True: | |
505 try: | |
506 return os.waitpid(pid, os.WNOHANG) | |
507 except OSError, e: | |
508 if e.errno == errno.EINTR: | |
509 continue | |
510 raise | |
511 | |
512 end = time.time() + timeout | |
513 delay = 0.0005 | |
514 while True: | |
515 (pid, _) = _waitpid(process.pid) | |
516 if pid == process.pid: | |
517 return process.wait() | |
518 remaining = end - time.time() | |
519 if remaining <= 0: | |
520 process.kill() | |
521 abort('Process timed out after {0} seconds: {1}'.format(timeout, ' '.join(args))) | |
522 delay = min(delay * 2, remaining, .05) | |
523 time.sleep(delay) | |
524 | |
525 def run(args, nonZeroIsFatal=True, out=None, err=None, cwd=None, timeout=None): | |
510 """ | 526 """ |
511 Run a command in a subprocess, wait for it to complete and return the exit status of the process. | 527 Run a command in a subprocess, wait for it to complete and return the exit status of the process. |
512 If the exit status is non-zero and `nonZeroIsFatal` is true, then the program is exited with | 528 If the exit status is non-zero and `nonZeroIsFatal` is true, then mx is exited with |
513 the same exit status. | 529 the same exit status. |
514 Each line of the standard output and error streams of the subprocess are redirected to the | 530 Each line of the standard output and error streams of the subprocess are redirected to the |
515 provided out and err functions if they are not None. | 531 provided out and err functions if they are not None. |
516 """ | 532 """ |
517 | 533 |
520 assert isinstance(arg, types.StringTypes), 'argument is not a string: ' + str(arg) | 536 assert isinstance(arg, types.StringTypes), 'argument is not a string: ' + str(arg) |
521 | 537 |
522 if _opts.verbose: | 538 if _opts.verbose: |
523 log(' '.join(args)) | 539 log(' '.join(args)) |
524 | 540 |
541 if timeout is None and _opts.timeout != 0: | |
542 timeout = _opts.timeout | |
543 | |
525 try: | 544 try: |
526 if out is None and err is None: | 545 if out is None and err is None and timeout is None: |
527 retcode = subprocess.call(args, cwd=cwd) | 546 retcode = subprocess.call(args, cwd=cwd) |
528 else: | 547 else: |
529 def redirect(stream, f): | 548 def redirect(stream, f): |
530 for line in iter(stream.readline, ''): | 549 for line in iter(stream.readline, ''): |
531 f(line) | 550 f(line) |
537 t.start() | 556 t.start() |
538 if err is not None: | 557 if err is not None: |
539 t = Thread(target=redirect, args=(p.stderr, err)) | 558 t = Thread(target=redirect, args=(p.stderr, err)) |
540 t.daemon = True # thread dies with the program | 559 t.daemon = True # thread dies with the program |
541 t.start() | 560 t.start() |
542 retcode = p.wait() | 561 if timeout is None: |
562 retcode = p.wait() | |
563 else: | |
564 if get_os() == 'windows': | |
565 abort('Use of timeout not (yet) supported on Windows') | |
566 retcode = _waitWithTimeout(p, args, timeout) | |
543 except OSError as e: | 567 except OSError as e: |
544 log('Error executing \'' + ' '.join(args) + '\': ' + str(e)) | 568 log('Error executing \'' + ' '.join(args) + '\': ' + str(e)) |
545 if _opts.verbose: | 569 if _opts.verbose: |
546 raise e | 570 raise e |
547 abort(e.errno) | 571 abort(e.errno) |