]> source.dussan.org Git - aspectj.git/commitdiff
ICommand adapter for Ant taskdef is simple for the non-incremental case and ugly...
authorwisberg <wisberg>
Sat, 24 May 2003 09:30:29 +0000 (09:30 +0000)
committerwisberg <wisberg>
Sat, 24 May 2003 09:30:29 +0000 (09:30 +0000)
testing/src/org/aspectj/testing/taskdefs/AjcTaskCompileCommand.java [new file with mode: 0644]

diff --git a/testing/src/org/aspectj/testing/taskdefs/AjcTaskCompileCommand.java b/testing/src/org/aspectj/testing/taskdefs/AjcTaskCompileCommand.java
new file mode 100644 (file)
index 0000000..6a10c07
--- /dev/null
@@ -0,0 +1,294 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Common Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/cpl-v10.html 
+ *  
+ * Contributors: 
+ *     Wes Isberg     initial implementation 
+ * ******************************************************************/
+
+package org.aspectj.testing.taskdefs;
+
+import java.awt.Frame;
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.List;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.Project;
+import org.aspectj.bridge.*;
+import org.aspectj.tools.ant.taskdefs.AjcTask;
+import org.aspectj.util.*;
+import org.aspectj.util.FileUtil;
+
+/**
+ * Drive tests using the Ant taskdef.
+ * The non-incremental case is quite easy to implement,
+ * but incremental compiles require running the compiler
+ * in another thread using an incremental tag file.
+ * This is imprecise because it assumes
+ * incremental compiles are complete 
+ * when messages stop coming from the compiler.
+ * Also, the client should call quit() when done compiling
+ * to halt the process.
+ * XXX build up ICommand with quit (and done-with-last-invocation?)
+ *     to avoid the kludge workarounds
+ */
+public class AjcTaskCompileCommand implements ICommand {
+    /** 
+     * 20 seconds of quiet in message holder 
+     * before we assume incremental compile is done 
+     */
+    public static int COMPILE_SECONDS_WITHOUT_MESSAGES = 20;
+    
+    /** 5 minutes maximum time to wait for a compile to complete */
+    public static int COMPILE_MAX_SECONDS = 300;
+
+    /**
+     * Wait until this holder has not changed the number of messages
+     * in secs seconds, as a weak form of determining when the
+     * compile has completed.
+     * XXX implement "compile-complete" message instead.
+     * @param holder the IMessageHolder to wait for
+     * @param seconds the number of seconds that the messages should be the same
+     * @param timeoutSeconds the int number of seconds after which to time out
+     * @return true if the messages quiesced before the timeout
+     *   false if parameters are 0 or negative or if
+     *   seconds => timeoutSeconds
+     */
+    static boolean waitUntilMessagesQuiet(
+            IMessageHolder holder, 
+            int seconds,
+            int timeoutSeconds) {
+        LangUtil.throwIaxIfNull(holder, "holder");
+        if ((0 >= timeoutSeconds) || (0 >= seconds)
+            || (timeoutSeconds <= seconds)) {
+            return false;
+        }
+        long curTime = System.currentTimeMillis();
+        final long timeout = curTime + (timeoutSeconds*1000);
+        final Thread thread = Thread.currentThread();
+        final long targetQuietTime = 1000 * seconds;
+        int numMessages = holder.numMessages(null, true);
+        long numMessagesTime = curTime;
+        // check for new messages every .5 to 3 seconds
+        final long checkInterval;
+        {
+            long interval = seconds / 10;
+            if (interval > 3000) {
+                interval = 3000;
+            } else if (interval < 200) {
+                interval = 500;
+            }
+            checkInterval = interval;
+        }
+        while ((curTime < timeout)
+                && (curTime < (numMessagesTime + targetQuietTime))) {
+            // delay until next check
+            long nextCheck = curTime + checkInterval;
+            while (nextCheck > curTime) {
+                try {
+                    thread.sleep(nextCheck - curTime);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+                curTime = System.currentTimeMillis();            
+            }
+            int newNumMessages = holder.numMessages(null, true);
+            if (newNumMessages != numMessages) {
+                numMessages = newNumMessages;
+                numMessagesTime = curTime;
+            }
+        }
+        return (curTime >= (numMessagesTime + targetQuietTime));
+    }
+    
+    // single-threaded driver
+    MessageHandler messages = new MessageHandler();
+    AjcTask ajcTask;
+    File incrementalFile;
+    Thread incrementalCompileThread;
+
+    /** 
+     * Stop waiting for any further incremental compiles.
+     * Safe to call in non-incremental modes.
+     */
+    public void quit() { // XXX requires downcast from ICommand, need validator,IMessageHandler interface
+        AjcTask task = ajcTask;
+        if (null != task) {
+            task.quit(); // signal task to quit, thread to die
+            ajcTask = null; // XXX need for cleanup?
+        }        
+        updateIncrementalFile(false, true);
+        Thread thread = incrementalCompileThread;
+        if (null != thread) {
+            if (thread.isAlive()) {
+                try {
+                    thread.join(3000);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+                if (thread.isAlive()) {
+                    String s = "WARNING: abandoning undying thread ";
+                    System.err.println(s + thread.getName());
+                }
+            }
+            incrementalCompileThread = null;
+        }
+    }
+    
+    // --------- ICommand interface
+    public boolean runCommand(String[] args, IMessageHandler handler) {
+        return (makeAjcTask(args, handler) 
+            && doCommand(handler, false));
+    }
+
+    /**
+     * Fails if called before runCommand or if
+     * not in incremental mode or if the command
+     * included an incremental file entry.
+     * @return
+     */
+    public boolean repeatCommand(IMessageHandler handler) {
+        return doCommand(handler, true);
+    }
+    
+    protected boolean doCommand(IMessageHandler handler, boolean repeat) {
+        messages.clearMessages();
+        if (null == ajcTask) {
+            MessageUtil.fail(messages, "ajcTask null - repeat without do");
+        }
+        try {
+            // normal, non-incremental case
+            if (!repeat && (null == incrementalFile)) {
+                ajcTask.execute();
+            // rest is for incremental mode
+            } else if (null == incrementalFile)  {
+                MessageUtil.fail(messages, "incremental mode not specified");
+            } else if (!updateIncrementalFile(false, false)) {
+                MessageUtil.fail(messages, "can't update incremental file");
+            } else if (!repeat) { // first incremental compile
+                incrementalCompileThread = new Thread(
+                    new Runnable() {
+                        public void run() {
+                            ajcTask.execute();
+                        }
+                    }, "AjcTaskCompileCommand-incremental");
+                incrementalCompileThread.start();
+                waitUntilMessagesQuiet(messages, 10, 180);
+            } else {
+                Thread thread = incrementalCompileThread;
+                if (null == thread) {
+                    MessageUtil.fail(messages, "incremental process stopped");
+                } else if (!thread.isAlive()) {
+                    MessageUtil.fail(messages, "incremental process dead");
+                } else {
+                    waitUntilMessagesQuiet(messages, 10, 180);
+                }
+            }
+        } catch (BuildException e) {
+            Throwable t = e.getCause();
+            while (t instanceof BuildException) {
+                t = ((BuildException) t).getCause();
+            }
+            if (null == t) {
+                t = e;
+            }
+            MessageUtil.abort(messages, "BuildException " + e.getMessage(), t);
+        } finally {
+            MessageUtil.handleAll(handler, messages, false);
+        }
+       return (0 == messages.numMessages(IMessage.ERROR, true)); 
+    }
+
+
+    protected boolean makeAjcTask(String[] args, IMessageHandler handler) {
+        ajcTask = null;
+        incrementalFile = null;
+        AjcTask result = null;
+        try {
+            result = new AjcTask();
+            Project project = new Project();
+            project.setName("AjcTaskCompileCommand");
+            result.setProject(project);
+            result.setMessageHolder(messages);
+            // XXX argh - have to strip out "-incremental"
+            // because tools.ajc.Main privileges it over tagfile
+            ArrayList newArgs = new ArrayList();
+            boolean incremental = false;
+            for (int i = 0; i < args.length; i++) {
+                if ("-incremental".equals(args[i])) {
+                    incremental = true;
+                } else if ("-XincrementalFile".equals(args[i])) {
+                    // CommandController.TAG_FILE_OPTION = "-XincrementalFile";
+                    incremental = true;
+                    i++;
+                } else {
+                    newArgs.add(args[i]);
+                }
+            }
+            if (incremental) {
+                args = (String[]) newArgs.toArray(new String[0]);
+            }
+                                
+            result.readArguments(args);
+            
+            if (incremental || result.isInIncrementalMode()) {
+                // these should be impossible...
+                if (result.isInIncrementalFileMode()) {
+                    String m = "incremental file set in command";
+                    MessageUtil.fail(handler, m);
+                    return false;
+                } else if (null != incrementalFile) {
+                    String m = "incremental file set already";
+                    MessageUtil.fail(handler, m);
+                    return false;
+                }
+                String prefix = "AjcTaskCompileCommand_makeAjcTask";
+                File tmpDir = FileUtil.getTempDir(prefix);
+                incrementalFile = new File(tmpDir, "tagfile.tmp");
+                boolean create = true;
+                boolean delete = false;
+                updateIncrementalFile(create, delete);
+                result.setTagFile(incrementalFile);
+            }
+            // do not throw BuildException on error
+            result.setFailonerror(false);
+            ajcTask = result;
+        } catch (BuildException e) {
+            MessageUtil.abort(handler,"setting up AjcTask", e);
+        }
+        return (null != ajcTask);
+    }
+    
+    protected boolean updateIncrementalFile(boolean create, boolean delete) {
+        File file = incrementalFile;
+        if (delete) {
+            try {
+                return (null == file)
+                    || !file.exists() 
+                    || file.delete();
+            } finally {
+                incrementalFile = null;
+            }
+        }
+        if (null == file) {
+            return false;
+        }
+        if (file.exists()) {
+            return file.setLastModified(System.currentTimeMillis());
+        } else {
+            try {
+                return create && file.createNewFile();
+            } catch (IOException e) { // XXX warn in verbose mode?
+                return false;
+            }
+        }
+    }
+    
+}
+