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.

21 年之前
21 年之前
21 年之前
21 年之前
21 年之前
21 年之前
21 年之前
21 年之前
21 年之前
21 年之前
14 年之前
14 年之前
21 年之前
21 年之前
21 年之前
14 年之前
21 年之前
21 年之前
21 年之前
21 年之前
21 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  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. * PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.tools.ajc;
  13. import java.io.File;
  14. import java.io.FileOutputStream;
  15. import java.io.IOException;
  16. import java.io.PrintStream;
  17. import java.util.Arrays;
  18. import java.util.Date;
  19. import java.util.List;
  20. import org.aspectj.ajdt.internal.core.builder.AjBuildManager;
  21. import org.aspectj.bridge.AbortException;
  22. import org.aspectj.bridge.ICommand;
  23. import org.aspectj.bridge.IMessage;
  24. import org.aspectj.bridge.IMessage.Kind;
  25. import org.aspectj.bridge.IMessageHandler;
  26. import org.aspectj.bridge.IMessageHolder;
  27. import org.aspectj.bridge.ISourceLocation;
  28. import org.aspectj.bridge.Message;
  29. import org.aspectj.bridge.MessageHandler;
  30. import org.aspectj.bridge.MessageUtil;
  31. import org.aspectj.bridge.ReflectionFactory;
  32. import org.aspectj.bridge.Version;
  33. import org.aspectj.bridge.context.CompilationAndWeavingContext;
  34. import org.aspectj.util.FileUtil;
  35. import org.aspectj.util.LangUtil;
  36. import org.aspectj.weaver.Dump;
  37. /**
  38. * Programmatic and command-line interface to AspectJ compiler. The compiler is an ICommand obtained by reflection. Not thread-safe.
  39. * By default, messages are printed as they are emitted; info messages go to the output stream, and warnings and errors go to the
  40. * error stream.
  41. * <p>
  42. * Clients can handle all messages by registering a holder:
  43. *
  44. * <pre>
  45. * Main main = new Main();
  46. * IMessageHolder holder = new MessageHandler();
  47. * main.setHolder(holder);
  48. * </pre>
  49. *
  50. * Clients can get control after each command completes by installing a Runnable:
  51. *
  52. * <pre>
  53. * main.setCompletionRunner(new Runnable() {..});
  54. * </pre>
  55. *
  56. */
  57. public class Main {
  58. /** Header used when rendering exceptions for users */
  59. public static final String THROWN_PREFIX = "Exception thrown from AspectJ " + Version.getText() + LangUtil.EOL + "" + LangUtil.EOL
  60. + "This might be logged as a bug already -- find current bugs at" + LangUtil.EOL
  61. + " http://bugs.eclipse.org/bugs/buglist.cgi?product=AspectJ&component=Compiler" + LangUtil.EOL + "" + LangUtil.EOL
  62. + "Bugs for exceptions thrown have titles File:line from the top stack, " + LangUtil.EOL
  63. + "e.g., \"SomeFile.java:243\"" + LangUtil.EOL + "" + LangUtil.EOL
  64. + "If you don't find the exception below in a bug, please add a new bug" + LangUtil.EOL
  65. + "at http://bugs.eclipse.org/bugs/enter_bug.cgi?product=AspectJ" + LangUtil.EOL
  66. + "To make the bug a priority, please include a test program" + LangUtil.EOL + "that can reproduce this exception."
  67. + LangUtil.EOL;
  68. private static final String OUT_OF_MEMORY_MSG = "AspectJ " + Version.getText() + " ran out of memory during compilation:"
  69. + LangUtil.EOL + LangUtil.EOL + "Please increase the memory available to ajc by editing the ajc script " + LangUtil.EOL
  70. + "found in your AspectJ installation directory. The -Xmx parameter value" + LangUtil.EOL
  71. + "should be increased from 64M (default) to 128M or even 256M." + LangUtil.EOL + LangUtil.EOL
  72. + "See the AspectJ FAQ available from the documentation link" + LangUtil.EOL
  73. + "on the AspectJ home page at http://www.eclipse.org/aspectj";
  74. private static final String MESSAGE_HOLDER_OPTION = "-messageHolder";
  75. /** @param args the String[] of command-line arguments */
  76. public static void main(String[] args) throws IOException {
  77. new Main().runMain(args, true);
  78. }
  79. /**
  80. * Convenience method to run ajc and collect String lists of messages. This can be reflectively invoked with the List collecting
  81. * parameters supplied by a parent class loader. The String messages take the same form as command-line messages.
  82. *
  83. * @param args the String[] args to pass to the compiler
  84. * @param useSystemExit if true and errors, return System.exit(errs)
  85. * @param fails the List sink, if any, for String failure (or worse) messages
  86. * @param errors the List sink, if any, for String error messages
  87. * @param warnings the List sink, if any, for String warning messages
  88. * @param info the List sink, if any, for String info messages
  89. * @return number of messages reported with level ERROR or above
  90. * @throws any unchecked exceptions the compiler does
  91. */
  92. public static int bareMain(String[] args, boolean useSystemExit, List fails, List errors, List warnings, List infos) {
  93. Main main = new Main();
  94. MessageHandler holder = new MessageHandler();
  95. main.setHolder(holder);
  96. try {
  97. main.runMain(args, useSystemExit);
  98. } finally {
  99. readMessages(holder, IMessage.FAIL, true, fails);
  100. readMessages(holder, IMessage.ERROR, false, errors);
  101. readMessages(holder, IMessage.WARNING, false, warnings);
  102. readMessages(holder, IMessage.INFO, false, infos);
  103. }
  104. return holder.numMessages(IMessage.ERROR, true);
  105. }
  106. /** Read messages of a given kind into a List as String */
  107. private static void readMessages(IMessageHolder holder, IMessage.Kind kind, boolean orGreater, List sink) {
  108. if ((null == sink) || (null == holder)) {
  109. return;
  110. }
  111. IMessage[] messages = holder.getMessages(kind, orGreater);
  112. if (!LangUtil.isEmpty(messages)) {
  113. for (int i = 0; i < messages.length; i++) {
  114. sink.add(MessagePrinter.render(messages[i]));
  115. }
  116. }
  117. }
  118. /**
  119. * @return String rendering throwable as compiler error for user/console, including information on how to report as a bug.
  120. * @throws NullPointerException if thrown is null
  121. */
  122. public static String renderExceptionForUser(Throwable thrown) {
  123. String m = thrown.getMessage();
  124. return THROWN_PREFIX + (null != m ? m + "\n" : "") + "\n" + CompilationAndWeavingContext.getCurrentContext()
  125. + LangUtil.renderException(thrown, true);
  126. }
  127. private static String parmInArgs(String flag, String[] args) {
  128. int loc = 1 + (null == args ? -1 : Arrays.asList(args).indexOf(flag));
  129. return ((0 == loc) || (args.length <= loc) ? null : args[loc]);
  130. }
  131. private static boolean flagInArgs(String flag, String[] args) {
  132. return ((null != args) && (Arrays.asList(args).indexOf(flag) != -1));
  133. }
  134. /**
  135. * append nothing if numItems is 0, numItems + label + (numItems > 1? "s" : "") otherwise, prefixing with " " if sink has
  136. * content
  137. */
  138. private static void appendNLabel(StringBuffer sink, String label, int numItems) {
  139. if (0 == numItems) {
  140. return;
  141. }
  142. if (0 < sink.length()) {
  143. sink.append(", ");
  144. }
  145. sink.append(numItems + " ");
  146. if (!LangUtil.isEmpty(label)) {
  147. sink.append(label);
  148. }
  149. if (1 < numItems) {
  150. sink.append("s");
  151. }
  152. }
  153. /** control iteration/continuation for command (compiler) */
  154. protected CommandController controller;
  155. /** ReflectionFactory identifier for command (compiler) */
  156. protected String commandName;
  157. protected ICommand command;
  158. /** client-set message sink */
  159. private IMessageHolder clientHolder;
  160. /** internally-set message sink */
  161. protected final MessageHandler ourHandler;
  162. private int lastFails;
  163. private int lastErrors;
  164. /** if not null, run this synchronously after each compile completes */
  165. private Runnable completionRunner;
  166. public Main() {
  167. controller = new CommandController();
  168. commandName = ReflectionFactory.ECLIPSE;
  169. CompilationAndWeavingContext.setMultiThreaded(false);
  170. try {
  171. String value = System.getProperty("aspectj.multithreaded");
  172. if (value != null && value.equalsIgnoreCase("true")) {
  173. CompilationAndWeavingContext.setMultiThreaded(true);
  174. }
  175. } catch (Exception e) {
  176. // silent
  177. }
  178. ourHandler = new MessageHandler(true);
  179. }
  180. public MessageHandler getMessageHandler() {
  181. return ourHandler;
  182. }
  183. // for unit testing...
  184. void setController(CommandController controller) {
  185. this.controller = controller;
  186. }
  187. public void setCommand(ICommand command) {
  188. this.command = command;
  189. }
  190. /**
  191. * Run without throwing exceptions but optionally using System.exit(..). This sets up a message handler which emits messages
  192. * immediately, so report(boolean, IMessageHandler) only reports total number of errors or warnings.
  193. *
  194. * @param args the String[] command line for the compiler
  195. * @param useSystemExit if true, use System.exit(int) to complete unless one of the args is -noExit. and signal result (0 no
  196. * exceptions/error, <0 exceptions, >0 compiler errors).
  197. */
  198. public void runMain(String[] args, boolean useSystemExit) {
  199. // Urk - default no check for AJDT, enabled here for Ant, command-line
  200. AjBuildManager.enableRuntimeVersionCheck(this);
  201. final boolean verbose = flagInArgs("-verbose", args);
  202. final boolean timers = flagInArgs("-timers", args);
  203. if (null == this.clientHolder) {
  204. this.clientHolder = checkForCustomMessageHolder(args);
  205. }
  206. IMessageHolder holder = clientHolder;
  207. if (null == holder) {
  208. holder = ourHandler;
  209. if (verbose) {
  210. ourHandler.setInterceptor(MessagePrinter.VERBOSE);
  211. } else {
  212. ourHandler.ignore(IMessage.INFO);
  213. ourHandler.setInterceptor(MessagePrinter.TERSE);
  214. }
  215. }
  216. // make sure we handle out of memory gracefully...
  217. try {
  218. // byte[] b = new byte[100000000]; for testing OoME only!
  219. long stime = System.currentTimeMillis();
  220. // uncomment next line to pause before startup (attach jconsole)
  221. // try {Thread.sleep(5000); }catch(Exception e) {}
  222. run(args, holder);
  223. long etime = System.currentTimeMillis();
  224. if (timers) {
  225. System.out.println("Compiler took " + (etime - stime) + "ms");
  226. }
  227. holder.handleMessage(MessageUtil.info("Compiler took " + (etime - stime) + "ms"));
  228. // uncomment next line to pause at end (keeps jconsole alive!)
  229. // try { System.in.read(); } catch (Exception e) {}
  230. } catch (OutOfMemoryError outOfMemory) {
  231. IMessage outOfMemoryMessage = new Message(OUT_OF_MEMORY_MSG, null, true);
  232. holder.handleMessage(outOfMemoryMessage);
  233. System.exit(-1); // we can't reasonably continue from this point.
  234. } finally {
  235. CompilationAndWeavingContext.reset();
  236. Dump.reset();
  237. }
  238. boolean skipExit = false;
  239. if (useSystemExit && !LangUtil.isEmpty(args)) { // sigh - pluck -noExit
  240. for (int i = 0; i < args.length; i++) {
  241. if ("-noExit".equals(args[i])) {
  242. skipExit = true;
  243. break;
  244. }
  245. }
  246. }
  247. if (useSystemExit && !skipExit) {
  248. systemExit(holder);
  249. }
  250. }
  251. // put calls around run() call above to allowing connecting jconsole
  252. // private void pause(int ms) {
  253. // try {
  254. // System.err.println("Pausing for "+ms+"ms");
  255. // System.gc();
  256. // Thread.sleep(ms);
  257. // System.gc();
  258. // System.err.println("Continuing");
  259. // } catch (Exception e) {}
  260. // }
  261. /**
  262. * @param args
  263. */
  264. private IMessageHolder checkForCustomMessageHolder(String[] args) {
  265. IMessageHolder holder = null;
  266. final String customMessageHolder = parmInArgs(MESSAGE_HOLDER_OPTION, args);
  267. if (customMessageHolder != null) {
  268. try {
  269. holder = (IMessageHolder) Class.forName(customMessageHolder).newInstance();
  270. } catch (Exception ex) {
  271. holder = ourHandler;
  272. throw new AbortException("Failed to create custom message holder of class '" + customMessageHolder + "' : " + ex);
  273. }
  274. }
  275. return holder;
  276. }
  277. /**
  278. * Run without using System.exit(..), putting all messages in holder:
  279. * <ul>
  280. * <li>ERROR: compiler error</li>
  281. * <li>WARNING: compiler warning</li>
  282. * <li>FAIL: command error (bad arguments, exception thrown)</li>
  283. * </ul>
  284. * This handles incremental behavior:
  285. * <ul>
  286. * <li>If args include "-incremental", repeat for every input char until 'q' is entered.
  287. * <li>
  288. * <li>If args include "-incrementalTagFile {file}", repeat every time we detect that {file} modification time has changed.</li>
  289. * <li>Either way, list files recompiled each time if args includes "-verbose".</li>
  290. * <li>Exit when the commmand/compiler throws any Throwable.</li>
  291. * </ul>
  292. * When complete, this contains all the messages of the final run of the command and/or any FAIL messages produced in running
  293. * the command, including any Throwable thrown by the command itself.
  294. *
  295. * @param args the String[] command line for the compiler
  296. * @param holder the MessageHandler sink for messages.
  297. */
  298. public void run(String[] args, IMessageHolder holder) {
  299. PrintStream logStream = null;
  300. FileOutputStream fos = null;
  301. String logFileName = parmInArgs("-log", args);
  302. if (null != logFileName) {
  303. File logFile = new File(logFileName);
  304. try {
  305. logFile.createNewFile();
  306. fos = new FileOutputStream(logFileName, true);
  307. logStream = new PrintStream(fos, true);
  308. } catch (Exception e) {
  309. fail(holder, "Couldn't open log file: " + logFileName, e);
  310. }
  311. Date now = new Date();
  312. logStream.println(now.toString());
  313. if (flagInArgs("-verbose", args)) {
  314. ourHandler.setInterceptor(new LogModeMessagePrinter(true, logStream));
  315. } else {
  316. ourHandler.ignore(IMessage.INFO);
  317. ourHandler.setInterceptor(new LogModeMessagePrinter(false, logStream));
  318. }
  319. holder = ourHandler;
  320. }
  321. if (LangUtil.isEmpty(args)) {
  322. args = new String[] { "-?" };
  323. } else if (controller.running()) {
  324. fail(holder, "already running with controller: " + controller, null);
  325. return;
  326. }
  327. args = controller.init(args, holder);
  328. if (0 < holder.numMessages(IMessage.ERROR, true)) {
  329. return;
  330. }
  331. if (command == null) {
  332. command = ReflectionFactory.makeCommand(commandName, holder);
  333. }
  334. if (0 < holder.numMessages(IMessage.ERROR, true)) {
  335. return;
  336. }
  337. try {
  338. outer: while (true) {
  339. boolean passed = command.runCommand(args, holder);
  340. if (report(passed, holder) && controller.incremental()) {
  341. while (controller.doRepeatCommand(command)) {
  342. holder.clearMessages();
  343. if (controller.buildFresh()) {
  344. continue outer;
  345. } else {
  346. passed = command.repeatCommand(holder);
  347. }
  348. if (!report(passed, holder)) {
  349. break;
  350. }
  351. }
  352. }
  353. break;
  354. }
  355. } catch (AbortException ae) {
  356. if (ae.isSilent()) {
  357. quit();
  358. } else {
  359. IMessage message = ae.getIMessage();
  360. Throwable thrown = ae.getThrown();
  361. if (null == thrown) { // toss AbortException wrapper
  362. if (null != message) {
  363. holder.handleMessage(message);
  364. } else {
  365. fail(holder, "abort without message", ae);
  366. }
  367. } else if (null == message) {
  368. fail(holder, "aborted", thrown);
  369. } else {
  370. String mssg = MessageUtil.MESSAGE_MOST.renderToString(message);
  371. fail(holder, mssg, thrown);
  372. }
  373. }
  374. } catch (Throwable t) {
  375. fail(holder, "unexpected exception", t);
  376. } finally {
  377. if (logStream != null) {
  378. logStream.close();
  379. logStream = null;
  380. }
  381. if (fos != null) {
  382. try {
  383. fos.close();
  384. } catch (IOException e) {
  385. fail(holder, "unexpected exception", e);
  386. }
  387. fos = null;
  388. }
  389. command = null;
  390. }
  391. }
  392. /** call this to stop after the next iteration of incremental compile */
  393. public void quit() {
  394. controller.quit();
  395. }
  396. /**
  397. * Set holder to be passed all messages. When holder is set, messages will not be printed by default.
  398. *
  399. * @param holder the IMessageHolder sink for all messages (use null to restore default behavior)
  400. */
  401. public void setHolder(IMessageHolder holder) {
  402. clientHolder = holder;
  403. }
  404. public IMessageHolder getHolder() {
  405. return clientHolder;
  406. }
  407. /**
  408. * Install a Runnable to be invoked synchronously after each compile completes.
  409. *
  410. * @param runner the Runnable to invoke - null to disable
  411. */
  412. public void setCompletionRunner(Runnable runner) {
  413. this.completionRunner = runner;
  414. }
  415. /**
  416. * Call System.exit(int) with values derived from the number of failures/aborts or errors in messages.
  417. *
  418. * @param messages the IMessageHolder to interrogate.
  419. * @param messages
  420. */
  421. protected void systemExit(IMessageHolder messages) {
  422. int num = lastFails; // messages.numMessages(IMessage.FAIL, true);
  423. if (0 < num) {
  424. System.exit(-num);
  425. }
  426. num = lastErrors; // messages.numMessages(IMessage.ERROR, false);
  427. if (0 < num) {
  428. System.exit(num);
  429. }
  430. System.exit(0);
  431. }
  432. /** Messages to the user */
  433. protected void outMessage(String message) { // XXX coordinate with MessagePrinter
  434. System.out.print(message);
  435. System.out.flush();
  436. }
  437. /**
  438. * Report results from a (possibly-incremental) compile run. This delegates to any reportHandler or otherwise prints summary
  439. * counts of errors/warnings to System.err (if any errors) or System.out (if only warnings). WARNING: this silently ignores
  440. * other messages like FAIL, but clears the handler of all messages when returning true. XXX false
  441. *
  442. * This implementation ignores the pass parameter but clears the holder after reporting on the assumption messages were
  443. * handled/printed already. (ignoring UnsupportedOperationException from holder.clearMessages()).
  444. *
  445. * @param pass true result of the command
  446. * @param holder IMessageHolder with messages from the command
  447. * @see reportCommandResults(IMessageHolder)
  448. * @return false if the process should abort
  449. */
  450. protected boolean report(boolean pass, IMessageHolder holder) {
  451. lastFails = holder.numMessages(IMessage.FAIL, true);
  452. boolean result = (0 == lastFails);
  453. final Runnable runner = completionRunner;
  454. if (null != runner) {
  455. runner.run();
  456. }
  457. if (holder == ourHandler) {
  458. lastErrors = holder.numMessages(IMessage.ERROR, false);
  459. int warnings = holder.numMessages(IMessage.WARNING, false);
  460. StringBuffer sb = new StringBuffer();
  461. appendNLabel(sb, "fail|abort", lastFails);
  462. appendNLabel(sb, "error", lastErrors);
  463. appendNLabel(sb, "warning", warnings);
  464. if (0 < sb.length()) {
  465. PrintStream out = (0 < (lastErrors + lastFails) ? System.err : System.out);
  466. out.println(""); // XXX "wrote class file" messages no eol?
  467. out.println(sb.toString());
  468. }
  469. }
  470. return result;
  471. }
  472. /** convenience API to make fail messages (without MessageUtils's fail prefix) */
  473. protected static void fail(IMessageHandler handler, String message, Throwable thrown) {
  474. handler.handleMessage(new Message(message, IMessage.FAIL, thrown, null));
  475. }
  476. /**
  477. * interceptor IMessageHandler to print as we go. This formats all messages to the user.
  478. */
  479. public static class MessagePrinter implements IMessageHandler {
  480. public static final IMessageHandler VERBOSE = new MessagePrinter(true);
  481. public static final IMessageHandler TERSE = new MessagePrinter(false);
  482. final boolean verbose;
  483. protected MessagePrinter(boolean verbose) {
  484. this.verbose = verbose;
  485. }
  486. /**
  487. * Print errors and warnings to System.err, and optionally info to System.out, rendering message String only.
  488. *
  489. * @return false always
  490. */
  491. @Override
  492. public boolean handleMessage(IMessage message) {
  493. if (null != message) {
  494. PrintStream out = getStreamFor(message.getKind());
  495. if (null != out) {
  496. out.println(render(message));
  497. }
  498. }
  499. return false;
  500. }
  501. /**
  502. * Render message differently. If abort, then prefix stack trace with feedback request. If the actual message is empty, then
  503. * use toString on the whole. Prefix message part with file:line; If it has context, suffix message with context.
  504. *
  505. * @param message the IMessage to render
  506. * @return String rendering IMessage (never null)
  507. */
  508. public static String render(IMessage message) {
  509. // IMessage.Kind kind = message.getKind();
  510. StringBuffer sb = new StringBuffer();
  511. String text = message.getMessage();
  512. if (text.equals(AbortException.NO_MESSAGE_TEXT)) {
  513. text = null;
  514. }
  515. boolean toString = (LangUtil.isEmpty(text));
  516. if (toString) {
  517. text = message.toString();
  518. }
  519. ISourceLocation loc = message.getSourceLocation();
  520. String context = null;
  521. if (null != loc) {
  522. File file = loc.getSourceFile();
  523. if (null != file) {
  524. String name = file.getName();
  525. if (!toString || (-1 == text.indexOf(name))) {
  526. sb.append(FileUtil.getBestPath(file));
  527. if (loc.getLine() > 0) {
  528. sb.append(":" + loc.getLine());
  529. }
  530. int col = loc.getColumn();
  531. if (0 < col) {
  532. sb.append(":" + col);
  533. }
  534. sb.append(" ");
  535. }
  536. }
  537. context = loc.getContext();
  538. }
  539. // per Wes' suggestion on dev...
  540. if (message.getKind() == IMessage.ERROR) {
  541. sb.append("[error] ");
  542. } else if (message.getKind() == IMessage.WARNING) {
  543. sb.append("[warning] ");
  544. }
  545. sb.append(text);
  546. if (null != context) {
  547. sb.append(LangUtil.EOL);
  548. sb.append(context);
  549. }
  550. String details = message.getDetails();
  551. if (details != null) {
  552. sb.append(LangUtil.EOL);
  553. sb.append('\t');
  554. sb.append(details);
  555. }
  556. Throwable thrown = message.getThrown();
  557. if (null != thrown) {
  558. sb.append(LangUtil.EOL);
  559. sb.append(Main.renderExceptionForUser(thrown));
  560. }
  561. if (message.getExtraSourceLocations().isEmpty()) {
  562. return sb.toString();
  563. } else {
  564. return MessageUtil.addExtraSourceLocations(message, sb.toString());
  565. }
  566. }
  567. @Override
  568. public boolean isIgnoring(IMessage.Kind kind) {
  569. return (null != getStreamFor(kind));
  570. }
  571. /**
  572. * No-op
  573. *
  574. * @see org.aspectj.bridge.IMessageHandler#isIgnoring(org.aspectj.bridge.IMessage.Kind)
  575. * @param kind
  576. */
  577. @Override
  578. public void dontIgnore(IMessage.Kind kind) {
  579. }
  580. /**
  581. * @return System.err for FAIL, ABORT, ERROR, and WARNING, System.out for INFO if -verbose and WEAVEINFO if -showWeaveInfo.
  582. */
  583. protected PrintStream getStreamFor(IMessage.Kind kind) {
  584. if (IMessage.WARNING.isSameOrLessThan(kind)) {
  585. return System.err;
  586. } else if (verbose && IMessage.INFO.equals(kind)) {
  587. return System.out;
  588. } else if (IMessage.WEAVEINFO.equals(kind)) {
  589. return System.out;
  590. } else {
  591. return null;
  592. }
  593. }
  594. /**
  595. * No-op
  596. *
  597. * @see org.aspectj.bridge.IMessageHandler#ignore(org.aspectj.bridge.IMessage.Kind)
  598. * @param kind
  599. */
  600. @Override
  601. public void ignore(Kind kind) {
  602. }
  603. }
  604. public static class LogModeMessagePrinter extends MessagePrinter {
  605. protected final PrintStream logStream;
  606. public LogModeMessagePrinter(boolean verbose, PrintStream logStream) {
  607. super(verbose);
  608. this.logStream = logStream;
  609. }
  610. @Override
  611. protected PrintStream getStreamFor(IMessage.Kind kind) {
  612. if (IMessage.WARNING.isSameOrLessThan(kind)) {
  613. return logStream;
  614. } else if (verbose && IMessage.INFO.equals(kind)) {
  615. return logStream;
  616. } else if (IMessage.WEAVEINFO.equals(kind)) {
  617. return logStream;
  618. } else {
  619. return null;
  620. }
  621. }
  622. }
  623. /** controller for repeatable command delays until input or file changed or removed */
  624. public static class CommandController {
  625. public static String TAG_FILE_OPTION = "-XincrementalFile";
  626. public static String INCREMENTAL_OPTION = "-incremental";
  627. /** maximum 10-minute delay between filesystem checks */
  628. public static long MAX_DELAY = 1000 * 600;
  629. /** default 5-second delay between filesystem checks */
  630. public static long DEFAULT_DELAY = 1000 * 5;
  631. /** @see init(String[]) */
  632. private static String[][] OPTIONS = new String[][] { new String[] { INCREMENTAL_OPTION },
  633. new String[] { TAG_FILE_OPTION, null } };
  634. /** true between init(String[]) and doRepeatCommand() that returns false */
  635. private boolean running;
  636. /** true after quit() called */
  637. private boolean quit;
  638. /** true if incremental mode, waiting for input other than 'q' */
  639. private boolean incremental;
  640. /** true if incremental mode, waiting for file to change (repeat) or disappear (quit) */
  641. private File tagFile;
  642. /** last modification time for tagFile as of last command - 0 to start */
  643. private long fileModTime;
  644. /** delay between filesystem checks for tagFile modification time */
  645. private long delay;
  646. /** true just after user types 'r' for rebuild */
  647. private boolean buildFresh;
  648. public CommandController() {
  649. delay = DEFAULT_DELAY;
  650. }
  651. /**
  652. * @param argList read and strip incremental args from this
  653. * @param sink IMessageHandler for error messages
  654. * @return String[] remainder of args
  655. */
  656. public String[] init(String[] args, IMessageHandler sink) {
  657. running = true;
  658. // String[] unused;
  659. if (!LangUtil.isEmpty(args)) {
  660. String[][] options = LangUtil.copyStrings(OPTIONS);
  661. /* unused = */LangUtil.extractOptions(args, options);
  662. incremental = (null != options[0][0]);
  663. if (null != options[1][0]) {
  664. File file = new File(options[1][1]);
  665. if (!file.exists()) {
  666. MessageUtil.abort(sink, "tag file does not exist: " + file);
  667. } else {
  668. tagFile = file;
  669. fileModTime = tagFile.lastModified();
  670. }
  671. }
  672. }
  673. return args;
  674. }
  675. /**
  676. * @return true if init(String[]) called but doRepeatCommand has not returned false
  677. */
  678. public boolean running() {
  679. return running;
  680. }
  681. /** @param delay milliseconds between filesystem checks */
  682. public void setDelay(long delay) {
  683. if ((delay > -1) && (delay < MAX_DELAY)) {
  684. this.delay = delay;
  685. }
  686. }
  687. /** @return true if INCREMENTAL_OPTION or TAG_FILE_OPTION was in args */
  688. public boolean incremental() {
  689. return (incremental || (null != tagFile));
  690. }
  691. /** @return true if INCREMENTAL_OPTION was in args */
  692. public boolean commandLineIncremental() {
  693. return incremental;
  694. }
  695. public void quit() {
  696. if (!quit) {
  697. quit = true;
  698. }
  699. }
  700. /** @return true just after user typed 'r' */
  701. boolean buildFresh() {
  702. return buildFresh;
  703. }
  704. /** @return false if we should quit, true to do another command */
  705. boolean doRepeatCommand(ICommand command) {
  706. if (!running) {
  707. return false;
  708. }
  709. boolean result = false;
  710. if (quit) {
  711. result = false;
  712. } else if (incremental) {
  713. try {
  714. if (buildFresh) { // reset before input request
  715. buildFresh = false;
  716. }
  717. System.out.println(" press enter to recompile, r to rebuild, q to quit: ");
  718. System.out.flush();
  719. // boolean doMore = false;
  720. // seek for one q or a series of [\n\r]...
  721. do {
  722. int input = System.in.read();
  723. if ('q' == input) {
  724. break; // result = false;
  725. } else if ('r' == input) {
  726. buildFresh = true;
  727. result = true;
  728. } else if (('\n' == input) || ('\r' == input)) {
  729. result = true;
  730. } // else eat anything else
  731. } while (!result);
  732. System.in.skip(Integer.MAX_VALUE);
  733. } catch (IOException e) { // XXX silence for error?
  734. result = false;
  735. }
  736. } else if (null != tagFile) {
  737. long curModTime;
  738. while (true) {
  739. if (!tagFile.exists()) {
  740. result = false;
  741. break;
  742. } else if (fileModTime == (curModTime = tagFile.lastModified())) {
  743. fileCheckDelay();
  744. } else {
  745. fileModTime = curModTime;
  746. result = true;
  747. break;
  748. }
  749. }
  750. } // else, not incremental - false
  751. if (!result && running) {
  752. running = false;
  753. }
  754. return result;
  755. }
  756. /** delay between filesystem checks, returning if quit is set */
  757. protected void fileCheckDelay() {
  758. // final Thread thread = Thread.currentThread();
  759. long targetTime = System.currentTimeMillis() + delay;
  760. // long curTime;
  761. while (targetTime > System.currentTimeMillis()) {
  762. if (quit) {
  763. return;
  764. }
  765. try {
  766. Thread.sleep(300);
  767. } // 1/3-second delta for quit check
  768. catch (InterruptedException e) {
  769. }
  770. }
  771. }
  772. }
  773. }