diff mxtool/mx.py @ 15528:a3f897fb3289

Merge.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Mon, 05 May 2014 22:18:46 +0200
parents 1f28c463e452
children 406a94c03ffa
line wrap: on
line diff
--- a/mxtool/mx.py	Sat May 03 21:46:35 2014 +0200
+++ b/mxtool/mx.py	Mon May 05 22:18:46 2014 +0200
@@ -1300,8 +1300,21 @@
         time.sleep(delay)
 
 # Makes the current subprocess accessible to the abort() function
-# This is a tuple of the Popen object and args.
-_currentSubprocess = (None, None)
+# This is a list of tuples of the subprocess.Popen or
+# multiprocessing.Process object and args.
+_currentSubprocesses = []
+
+def _addSubprocess(p, args):
+    entry = (p, args)
+    _currentSubprocesses.append(entry)
+    return entry
+
+def _removeSubprocess(entry):
+    if entry and entry in _currentSubprocesses:
+        try:
+            _currentSubprocesses.remove(entry)
+        except:
+            pass
 
 def waitOn(p):
     if get_os() == 'windows':
@@ -1340,8 +1353,7 @@
     if timeout is None and _opts.ptimeout != 0:
         timeout = _opts.ptimeout
 
-    global _currentSubprocess
-
+    sub = None
     try:
         # On Unix, the new subprocess should be in a separate group so that a timeout alarm
         # can use os.killpg() to kill the whole subprocess group
@@ -1359,7 +1371,7 @@
         stdout = out if not callable(out) else subprocess.PIPE
         stderr = err if not callable(err) else subprocess.PIPE
         p = subprocess.Popen(args, cwd=cwd, stdout=stdout, stderr=stderr, preexec_fn=preexec_fn, creationflags=creationflags, env=env)
-        _currentSubprocess = (p, args)
+        sub = _addSubprocess(p, args)
         if callable(out):
             t = Thread(target=redirect, args=(p.stdout, out))
             t.daemon = True  # thread dies with the program
@@ -1382,7 +1394,7 @@
     except KeyboardInterrupt:
         abort(1)
     finally:
-        _currentSubprocess = (None, None)
+        _removeSubprocess(sub)
 
     if retcode and nonZeroIsFatal:
         if _opts.verbose:
@@ -1663,21 +1675,20 @@
     return result
 
 def _send_sigquit():
-    p, args = _currentSubprocess
-
-    def _isJava():
-        if args:
-            name = args[0].split(os.sep)[-1]
-            return name == "java"
-        return False
-
-    if p is not None and _isJava():
-        if get_os() == 'windows':
-            log("mx: implement me! want to send SIGQUIT to my child process")
-        else:
-            _kill_process_group(p.pid, sig=signal.SIGQUIT)
-        time.sleep(0.1)
-
+    for p, args in _currentSubprocesses:
+
+        def _isJava():
+            if args:
+                name = args[0].split(os.sep)[-1]
+                return name == "java"
+            return False
+
+        if p is not None and _isJava():
+            if get_os() == 'windows':
+                log("mx: implement me! want to send SIGQUIT to my child process")
+            else:
+                _kill_process_group(p.pid, sig=signal.SIGQUIT)
+            time.sleep(0.1)
 
 def abort(codeOrMessage):
     """
@@ -1692,12 +1703,14 @@
 
     # import traceback
     # traceback.print_stack()
-    p, _ = _currentSubprocess
-    if p is not None:
-        if get_os() == 'windows':
-            p.kill()
-        else:
-            _kill_process_group(p.pid, signal.SIGKILL)
+    for p, args in _currentSubprocesses:
+        try:
+            if get_os() == 'windows':
+                p.terminate()
+            else:
+                _kill_process_group(p.pid, signal.SIGKILL)
+        except BaseException as e:
+            log('error while killing subprocess {} "{}": {}'.format(p.pid, ' '.join(args), e))
 
     raise SystemExit(codeOrMessage)
 
@@ -1798,7 +1811,7 @@
     return get_env('JDT', join(_primary_suite.mxDir, 'ecj.jar'))
 
 class JavaCompileTask:
-    def __init__(self, args, proj, reason, javafilelist, jdk, outputDir, deps):
+    def __init__(self, args, proj, reason, javafilelist, jdk, outputDir, jdtJar, deps):
         self.proj = proj
         self.reason = reason
         self.javafilelist = javafilelist
@@ -1806,6 +1819,7 @@
         self.jdk = jdk
         self.outputDir = outputDir
         self.done = False
+        self.jdtJar = jdtJar
         self.args = args
 
     def __str__(self):
@@ -1840,20 +1854,8 @@
         cp = classpath(self.proj.name, includeSelf=True)
         toBeDeleted = [argfileName]
 
-        jdtJar = None
-        if not args.javac and args.jdt is not None:
-            if not args.jdt.endswith('.jar'):
-                abort('Path for Eclipse batch compiler does not look like a jar file: ' + args.jdt)
-            jdtJar = args.jdt
-            if not exists(jdtJar):
-                if os.path.abspath(jdtJar) == os.path.abspath(_defaultEcjPath()) and get_env('JDT', None) is None:
-                    # Silently ignore JDT if default location is used but does not exist
-                    jdtJar = None
-                else:
-                    abort('Eclipse batch compiler jar does not exist: ' + args.jdt)
-
         try:
-            if not jdtJar:
+            if not self.jdtJar:
                 mainJava = java()
                 if not args.error_prone:
                     self.logCompilation('javac')
@@ -1878,7 +1880,7 @@
             else:
                 self.logCompilation('JDT')
 
-                jdtVmArgs = ['-Xmx1g', '-jar', jdtJar]
+                jdtVmArgs = ['-Xmx1g', '-jar', self.jdtJar]
 
                 jdtArgs = ['-' + compliance,
                          '-cp', cp, '-g', '-enableJavadoc',
@@ -1946,12 +1948,23 @@
     compilerSelect.add_argument('--jdt', help='path to ecj.jar, the Eclipse batch compiler', default=_defaultEcjPath(), metavar='<path>')
     compilerSelect.add_argument('--force-javac', action='store_true', dest='javac', help='use javac despite ecj.jar is found or not')
 
-
     if suppliedParser:
         parser.add_argument('remainder', nargs=REMAINDER, metavar='...')
 
     args = parser.parse_args(args)
 
+    jdtJar = None
+    if not args.javac and args.jdt is not None:
+        if not args.jdt.endswith('.jar'):
+            abort('Path for Eclipse batch compiler does not look like a jar file: ' + args.jdt)
+        jdtJar = args.jdt
+        if not exists(jdtJar):
+            if os.path.abspath(jdtJar) == os.path.abspath(_defaultEcjPath()) and get_env('JDT', None) is None:
+                # Silently ignore JDT if default location is used but does not exist
+                jdtJar = None
+            else:
+                abort('Eclipse batch compiler jar does not exist: ' + args.jdt)
+
     if args.only is not None:
         # N.B. This build will not include dependencies including annotation processor dependencies
         sortedProjects = [project(name) for name in args.only.split(',')]
@@ -2087,7 +2100,7 @@
             logv('[no Java sources for {0} - skipping]'.format(p.name))
             continue
 
-        task = JavaCompileTask(args, p, buildReason, javafilelist, jdk, outputDir, taskDeps)
+        task = JavaCompileTask(args, p, buildReason, javafilelist, jdk, outputDir, jdtJar, taskDeps)
 
         if args.parallelize:
             # Best to initialize class paths on main process
@@ -2113,6 +2126,7 @@
                 if t.proc.is_alive():
                     active.append(t)
                 else:
+                    _removeSubprocess(t.sub)
                     if t.proc.exitcode != 0:
                         return ([], joinTasks(tasks))
             return (active, [])
@@ -2126,10 +2140,18 @@
                     task._d = max([remainingDepsDepth(t) for t in incompleteDeps]) + 1
             return task._d
 
+        def compareTasks(t1, t2):
+            d = remainingDepsDepth(t1) - remainingDepsDepth(t2)
+            if d == 0:
+                t1Work = (1 + len(t1.proj.annotation_processors())) * len(t1.javafilelist)
+                t2Work = (1 + len(t2.proj.annotation_processors())) * len(t2.javafilelist)
+                d = t1Work - t2Work
+            return d
+
         def sortWorklist(tasks):
             for t in tasks:
                 t._d = None
-            return sorted(tasks, lambda x, y: remainingDepsDepth(x) - remainingDepsDepth(y))
+            return sorted(tasks, compareTasks)
 
         import multiprocessing
         cpus = multiprocessing.cpu_count()
@@ -2162,6 +2184,7 @@
                     task.proc = multiprocessing.Process(target=executeTask, args=(task,))
                     task.proc.start()
                     active.append(task)
+                    task.sub = _addSubprocess(task.proc, ['JavaCompileTask', str(task)])
                 if len(active) == cpus:
                     break