You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AjcTaskCompileCommand.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /* *******************************************************************
  2. * Copyright (c) 2003 Contributors.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Wes Isberg initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.testing.taskdefs;
  13. //import java.awt.Frame;
  14. import java.io.File;
  15. import java.io.IOException;
  16. //import java.lang.reflect.*;
  17. //import java.util.List;
  18. import java.util.ArrayList;
  19. import org.apache.tools.ant.BuildException;
  20. import org.apache.tools.ant.Project;
  21. import org.aspectj.bridge.ICommand;
  22. import org.aspectj.bridge.IMessage;
  23. import org.aspectj.bridge.IMessageHandler;
  24. import org.aspectj.bridge.IMessageHolder;
  25. import org.aspectj.bridge.MessageHandler;
  26. import org.aspectj.bridge.MessageUtil;
  27. import org.aspectj.tools.ant.taskdefs.AjcTask;
  28. import org.aspectj.util.FileUtil;
  29. import org.aspectj.util.LangUtil;
  30. /**
  31. * Drive tests using the Ant taskdef.
  32. * The non-incremental case is quite easy to implement,
  33. * but incremental compiles require running the compiler
  34. * in another thread using an incremental tag file.
  35. * This is imprecise because it assumes
  36. * incremental compiles are complete
  37. * when messages stop coming from the compiler.
  38. * Also, the client should call quit() when done compiling
  39. * to halt the process.
  40. * XXX build up ICommand with quit (and done-with-last-invocation?)
  41. * to avoid the kludge workarounds
  42. */
  43. public class AjcTaskCompileCommand implements ICommand {
  44. /**
  45. * 20 seconds of quiet in message holder
  46. * before we assume incremental compile is done
  47. */
  48. public static int COMPILE_SECONDS_WITHOUT_MESSAGES = 20;
  49. /** 5 minutes maximum time to wait for a compile to complete */
  50. public static int COMPILE_MAX_SECONDS = 300;
  51. /**
  52. * Wait until this holder has not changed the number of messages
  53. * in secs seconds, as a weak form of determining when the
  54. * compile has completed.
  55. * XXX implement "compile-complete" message instead.
  56. * @param holder the IMessageHolder to wait for
  57. * @param seconds the number of seconds that the messages should be the same
  58. * @param timeoutSeconds the int number of seconds after which to time out
  59. * @return true if the messages quiesced before the timeout
  60. * false if parameters are 0 or negative or if
  61. * seconds => timeoutSeconds
  62. */
  63. static boolean waitUntilMessagesQuiet(
  64. IMessageHolder holder,
  65. int seconds,
  66. int timeoutSeconds) {
  67. LangUtil.throwIaxIfNull(holder, "holder");
  68. if ((0 >= timeoutSeconds) || (0 >= seconds)
  69. || (timeoutSeconds <= seconds)) {
  70. return false;
  71. }
  72. long curTime = System.currentTimeMillis();
  73. final long timeout = curTime + (timeoutSeconds*1000);
  74. // final Thread thread = Thread.currentThread();
  75. final long targetQuietTime = 1000 * seconds;
  76. int numMessages = holder.numMessages(null, true);
  77. long numMessagesTime = curTime;
  78. // check for new messages every .5 to 3 seconds
  79. final long checkInterval;
  80. {
  81. long interval = seconds / 10;
  82. if (interval > 3000) {
  83. interval = 3000;
  84. } else if (interval < 200) {
  85. interval = 500;
  86. }
  87. checkInterval = interval;
  88. }
  89. while ((curTime < timeout)
  90. && (curTime < (numMessagesTime + targetQuietTime))) {
  91. // delay until next check
  92. long nextCheck = curTime + checkInterval;
  93. while (nextCheck > curTime) {
  94. try {
  95. Thread.sleep(nextCheck - curTime);
  96. } catch (InterruptedException e) {
  97. // ignore
  98. }
  99. curTime = System.currentTimeMillis();
  100. }
  101. int newNumMessages = holder.numMessages(null, true);
  102. if (newNumMessages != numMessages) {
  103. numMessages = newNumMessages;
  104. numMessagesTime = curTime;
  105. }
  106. }
  107. return (curTime >= (numMessagesTime + targetQuietTime));
  108. }
  109. // single-threaded driver
  110. MessageHandler messages = new MessageHandler();
  111. AjcTask ajcTask;
  112. File incrementalFile;
  113. Thread incrementalCompileThread;
  114. /**
  115. * Stop waiting for any further incremental compiles.
  116. * Safe to call in non-incremental modes.
  117. */
  118. public void quit() { // XXX requires downcast from ICommand, need validator,IMessageHandler interface
  119. AjcTask task = ajcTask;
  120. if (null != task) {
  121. task.quit(); // signal task to quit, thread to die
  122. ajcTask = null; // XXX need for cleanup?
  123. }
  124. updateIncrementalFile(false, true);
  125. Thread thread = incrementalCompileThread;
  126. if (null != thread) {
  127. if (thread.isAlive()) {
  128. try {
  129. thread.join(3000);
  130. } catch (InterruptedException e) {
  131. // ignore
  132. }
  133. if (thread.isAlive()) {
  134. String s = "WARNING: abandoning undying thread ";
  135. System.err.println(s + thread.getName());
  136. }
  137. }
  138. incrementalCompileThread = null;
  139. }
  140. }
  141. // --------- ICommand interface
  142. public boolean runCommand(String[] args, IMessageHandler handler) {
  143. return (makeAjcTask(args, handler)
  144. && doCommand(handler, false));
  145. }
  146. /**
  147. * Fails if called before runCommand or if
  148. * not in incremental mode or if the command
  149. * included an incremental file entry.
  150. * @return
  151. */
  152. public boolean repeatCommand(IMessageHandler handler) {
  153. return doCommand(handler, true);
  154. }
  155. protected boolean doCommand(IMessageHandler handler, boolean repeat) {
  156. messages.clearMessages();
  157. if (null == ajcTask) {
  158. MessageUtil.fail(messages, "ajcTask null - repeat without do");
  159. }
  160. try {
  161. // normal, non-incremental case
  162. if (!repeat && (null == incrementalFile)) {
  163. ajcTask.execute();
  164. // rest is for incremental mode
  165. } else if (null == incrementalFile) {
  166. MessageUtil.fail(messages, "incremental mode not specified");
  167. } else if (!updateIncrementalFile(false, false)) {
  168. MessageUtil.fail(messages, "can't update incremental file");
  169. } else if (!repeat) { // first incremental compile
  170. incrementalCompileThread = new Thread(
  171. new Runnable() {
  172. public void run() {
  173. ajcTask.execute();
  174. }
  175. }, "AjcTaskCompileCommand-incremental");
  176. incrementalCompileThread.start();
  177. waitUntilMessagesQuiet(messages, 10, 180);
  178. } else {
  179. Thread thread = incrementalCompileThread;
  180. if (null == thread) {
  181. MessageUtil.fail(messages, "incremental process stopped");
  182. } else if (!thread.isAlive()) {
  183. MessageUtil.fail(messages, "incremental process dead");
  184. } else {
  185. waitUntilMessagesQuiet(messages, 10, 180);
  186. }
  187. }
  188. } catch (BuildException e) {
  189. Throwable t = e.getCause();
  190. while (t instanceof BuildException) {
  191. t = ((BuildException) t).getCause();
  192. }
  193. if (null == t) {
  194. t = e;
  195. }
  196. MessageUtil.abort(messages, "BuildException " + e.getMessage(), t);
  197. } finally {
  198. MessageUtil.handleAll(handler, messages, false);
  199. }
  200. return (0 == messages.numMessages(IMessage.ERROR, true));
  201. }
  202. protected boolean makeAjcTask(String[] args, IMessageHandler handler) {
  203. ajcTask = null;
  204. incrementalFile = null;
  205. AjcTask result = null;
  206. try {
  207. result = new AjcTask();
  208. Project project = new Project();
  209. project.setName("AjcTaskCompileCommand");
  210. result.setProject(project);
  211. result.setMessageHolder(messages);
  212. // XXX argh - have to strip out "-incremental"
  213. // because tools.ajc.Main privileges it over tagfile
  214. ArrayList newArgs = new ArrayList();
  215. boolean incremental = false;
  216. for (int i = 0; i < args.length; i++) {
  217. if ("-incremental".equals(args[i])) {
  218. incremental = true;
  219. } else if ("-XincrementalFile".equals(args[i])) {
  220. // CommandController.TAG_FILE_OPTION = "-XincrementalFile";
  221. incremental = true;
  222. i++;
  223. } else {
  224. newArgs.add(args[i]);
  225. }
  226. }
  227. if (incremental) {
  228. args = (String[]) newArgs.toArray(new String[0]);
  229. }
  230. result.readArguments(args);
  231. if (incremental || result.isInIncrementalMode()) {
  232. // these should be impossible...
  233. if (result.isInIncrementalFileMode()) {
  234. String m = "incremental file set in command";
  235. MessageUtil.fail(handler, m);
  236. return false;
  237. } else if (null != incrementalFile) {
  238. String m = "incremental file set already";
  239. MessageUtil.fail(handler, m);
  240. return false;
  241. }
  242. String prefix = "AjcTaskCompileCommand_makeAjcTask";
  243. File tmpDir = FileUtil.getTempDir(prefix);
  244. incrementalFile = new File(tmpDir, "tagfile.tmp");
  245. boolean create = true;
  246. boolean delete = false;
  247. updateIncrementalFile(create, delete);
  248. result.setTagFile(incrementalFile);
  249. }
  250. // do not throw BuildException on error
  251. result.setFailonerror(false);
  252. ajcTask = result;
  253. } catch (BuildException e) {
  254. MessageUtil.abort(handler,"setting up AjcTask", e);
  255. }
  256. return (null != ajcTask);
  257. }
  258. protected boolean updateIncrementalFile(boolean create, boolean delete) {
  259. File file = incrementalFile;
  260. if (delete) {
  261. try {
  262. return (null == file)
  263. || !file.exists()
  264. || file.delete();
  265. } finally {
  266. incrementalFile = null;
  267. }
  268. }
  269. if (null == file) {
  270. return false;
  271. }
  272. if (file.exists()) {
  273. return file.setLastModified(System.currentTimeMillis());
  274. } else {
  275. try {
  276. return create && file.createNewFile();
  277. } catch (IOException e) { // XXX warn in verbose mode?
  278. return false;
  279. }
  280. }
  281. }
  282. }