123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875 |
- /* *******************************************************************
- * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License v 2.0
- * which accompanies this distribution and is available at
- * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
- *
- * Contributors:
- * PARC initial implementation
- * ******************************************************************/
-
- package org.aspectj.tools.ajc;
-
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.PrintStream;
- import java.util.Arrays;
- import java.util.Date;
- import java.util.List;
-
- import org.aspectj.ajdt.internal.core.builder.AjBuildManager;
- import org.aspectj.bridge.AbortException;
- import org.aspectj.bridge.ICommand;
- import org.aspectj.bridge.IMessage;
- import org.aspectj.bridge.IMessage.Kind;
- import org.aspectj.bridge.IMessageHandler;
- import org.aspectj.bridge.IMessageHolder;
- import org.aspectj.bridge.ISourceLocation;
- import org.aspectj.bridge.Message;
- import org.aspectj.bridge.MessageHandler;
- import org.aspectj.bridge.MessageUtil;
- import org.aspectj.bridge.ReflectionFactory;
- import org.aspectj.bridge.Version;
- import org.aspectj.bridge.context.CompilationAndWeavingContext;
- import org.aspectj.util.FileUtil;
- import org.aspectj.util.LangUtil;
- import org.aspectj.weaver.Dump;
-
- import javax.lang.model.SourceVersion;
-
- /**
- * Programmatic and command-line interface to AspectJ compiler. The compiler is an ICommand obtained by reflection. Not thread-safe.
- * By default, messages are printed as they are emitted; info messages go to the output stream, and warnings and errors go to the
- * error stream.
- * <p>
- * Clients can handle all messages by registering a holder:
- *
- * <pre>
- * Main main = new Main();
- * IMessageHolder holder = new MessageHandler();
- * main.setHolder(holder);
- * </pre>
- *
- * Clients can get control after each command completes by installing a Runnable:
- *
- * <pre>
- * main.setCompletionRunner(new Runnable() {..});
- * </pre>
- *
- */
- public class Main {
- /** Header used when rendering exceptions for users */
- public static final String THROWN_PREFIX = "Exception thrown from AspectJ " + Version.getText() + LangUtil.EOL + "" + LangUtil.EOL
- + "This might be logged as a bug already -- find current bugs at" + LangUtil.EOL
- + " https://bugs.eclipse.org/bugs/buglist.cgi?product=AspectJ&component=Compiler" + LangUtil.EOL + "" + LangUtil.EOL
- + "Bugs for exceptions thrown have titles File:line from the top stack, " + LangUtil.EOL
- + "e.g., \"SomeFile.java:243\"" + LangUtil.EOL + "" + LangUtil.EOL
- + "If you don't find the exception below in a bug, please add a new bug" + LangUtil.EOL
- + "at https://bugs.eclipse.org/bugs/enter_bug.cgi?product=AspectJ" + LangUtil.EOL
- + "To make the bug a priority, please include a test program" + LangUtil.EOL + "that can reproduce this exception."
- + LangUtil.EOL;
-
- private static final String OUT_OF_MEMORY_MSG = "AspectJ " + Version.getText() + " ran out of memory during compilation:"
- + LangUtil.EOL + LangUtil.EOL + "Please increase the memory available to ajc by editing the ajc script " + LangUtil.EOL
- + "found in your AspectJ installation directory. The -Xmx parameter value" + LangUtil.EOL
- + "should be increased from 64M (default) to 128M or even 256M." + LangUtil.EOL + LangUtil.EOL
- + "See the AspectJ FAQ available from the documentation link" + LangUtil.EOL
- + "on the AspectJ home page at https://www.eclipse.org/aspectj";
-
- private static final String MESSAGE_HOLDER_OPTION = "-messageHolder";
-
- // Minimal Java runtime version necessary to run AJC
- // TODO: Update value, if Eclipse JDT Core raises compilation target level.
- private static final int MINIMAL_JRE_VERSION = 17;
- private static final String MINIMAL_JRE_VERSION_ERROR =
- "The AspectJ compiler needs at least Java runtime " + MINIMAL_JRE_VERSION;
-
- /** @param args the String[] of command-line arguments */
- public static void main(String[] args) throws IOException {
- new Main().runMain(args, true);
- }
-
- /**
- * Convenience method to run ajc and collect String lists of messages. This can be reflectively invoked with the List collecting
- * parameters supplied by a parent class loader. The String messages take the same form as command-line messages.
- * This method does not catch unchecked exceptions thrown by the compiler.
- *
- * @param args the String[] args to pass to the compiler
- * @param useSystemExit if true and errors, return System.exit(errs)
- * @param fails the List sink, if any, for String failure (or worse) messages
- * @param errors the List sink, if any, for String error messages
- * @param warnings the List sink, if any, for String warning messages
- * @param infos the List sink, if any, for String info messages
- * @param usages the List sink, if any, for String usage messages
- * @return number of messages reported with level ERROR or above
- */
- public static int bareMain(
- String[] args, boolean useSystemExit,
- List<String> fails, List<String> errors, List<String> warnings, List<String> infos, List<String> usages
- ) {
- Main main = new Main();
- MessageHandler holder = new MessageHandler();
- main.setHolder(holder);
- try {
- main.runMain(args, useSystemExit);
- } finally {
- readMessages(holder, IMessage.FAIL, true, fails);
- readMessages(holder, IMessage.ERROR, false, errors);
- readMessages(holder, IMessage.WARNING, false, warnings);
- readMessages(holder, IMessage.INFO, false, infos);
- readMessages(holder, IMessage.USAGE, false, usages);
- }
- return holder.numMessages(IMessage.ERROR, true);
- }
-
- /** Read messages of a given kind into a List as String */
- private static void readMessages(IMessageHolder holder, IMessage.Kind kind, boolean orGreater, List<String> sink) {
- if ((null == sink) || (null == holder)) {
- return;
- }
- IMessage[] messages = holder.getMessages(kind, orGreater);
- if (!LangUtil.isEmpty(messages)) {
- for (IMessage message : messages) {
- sink.add(MessagePrinter.render(message));
- }
- }
- }
-
- /**
- * @return String rendering throwable as compiler error for user/console, including information on how to report as a bug.
- * @throws NullPointerException if thrown is null
- */
- public static String renderExceptionForUser(Throwable thrown) {
- String m = thrown.getMessage();
- return THROWN_PREFIX + (null != m ? m + "\n" : "") + "\n" + CompilationAndWeavingContext.getCurrentContext()
- + LangUtil.renderException(thrown, true);
- }
-
- private static String parmInArgs(String flag, String[] args) {
- int loc = 1 + (null == args ? -1 : Arrays.asList(args).indexOf(flag));
- return ((0 == loc) || (args.length <= loc) ? null : args[loc]);
- }
-
- private static boolean flagInArgs(String flag, String[] args) {
- return ((null != args) && (Arrays.asList(args).contains(flag)));
- }
-
- /**
- * append nothing if numItems is 0, numItems + label + (numItems > 1? "s" : "") otherwise, prefixing with " " if sink has
- * content
- */
- private static void appendNLabel(StringBuilder sink, String label, int numItems) {
- if (0 == numItems) {
- return;
- }
- if (0 < sink.length()) {
- sink.append(", ");
- }
- sink.append(numItems).append(" ");
- if (!LangUtil.isEmpty(label)) {
- sink.append(label);
- }
- if (1 < numItems) {
- sink.append("s");
- }
- }
-
- /** control iteration/continuation for command (compiler) */
- protected CommandController controller;
-
- /** ReflectionFactory identifier for command (compiler) */
- protected String commandName;
-
- protected ICommand command;
-
- /** client-set message sink */
- private IMessageHolder clientHolder;
-
- /** internally-set message sink */
- protected final MessageHandler ourHandler;
-
- private int lastFails;
- private int lastErrors;
-
- /** if not null, run this synchronously after each compile completes */
- private Runnable completionRunner;
-
- public Main() {
- controller = new CommandController();
- commandName = ReflectionFactory.ECLIPSE;
- CompilationAndWeavingContext.setMultiThreaded(false);
- try {
- String value = System.getProperty("aspectj.multithreaded");
- if (value != null && value.equalsIgnoreCase("true")) {
- CompilationAndWeavingContext.setMultiThreaded(true);
- }
- } catch (Exception e) {
- // silent
- }
- ourHandler = new MessageHandler(true);
- }
-
- public MessageHandler getMessageHandler() {
- return ourHandler;
- }
-
- // for unit testing...
- void setController(CommandController controller) {
- this.controller = controller;
- }
-
- public void setCommand(ICommand command) {
- this.command = command;
- }
-
- /**
- * Run without throwing exceptions, but optionally using {@link System#exit(int)}. This sets up a message handler
- * which emits messages immediately, so {@link #report(boolean, IMessageHolder)} only reports the total number of
- * errors or warnings.
- *
- * @param args the compiler command line
- * @param useSystemExit if true, use {@link System#exit(int)} to complete unless one of the args is {@code -noExit},
- * and signal result (0 = no exceptions/error, <0 = exceptions, >0 = compiler errors).
- * Note: While some shells like Windows <i>cmd.exe</i> can correctly print negative exit codes
- * via {@code echo %errorlevel%"}, UNIX shells like Bash interpret them as positive byte values
- * modulo 256. E.g., exit code -1 will be printed as 127 using {@code echo $?}.
- */
- public void runMain(String[] args, boolean useSystemExit) {
- final boolean doExit = useSystemExit && !flagInArgs("-noExit", args);
-
- // This needs to be checked, before any classes using JDT Core classes are used for the first time. Otherwise, users
- // will see ugly UnsupportedClassVersionError stack traces, which they might or might not interpret correctly.
- // Therefore, interrupt AJC usage right here, even if it means that not even a usage page can be printed. It is
- // better to save users from subsequent problems later.
- if (SourceVersion.latest().ordinal() < MINIMAL_JRE_VERSION) {
- System.err.println(MINIMAL_JRE_VERSION_ERROR);
- if (doExit)
- System.exit(-1);
- return;
- }
-
- // Urk - default no check for AJDT, enabled here for Ant, command-line
- AjBuildManager.enableRuntimeVersionCheck(this);
- final boolean verbose = flagInArgs("-verbose", args);
- final boolean timers = flagInArgs("-timers", args);
- if (null == this.clientHolder) {
- this.clientHolder = checkForCustomMessageHolder(args);
- }
- IMessageHolder holder = clientHolder;
- if (null == holder) {
- holder = ourHandler;
- if (verbose) {
- ourHandler.setInterceptor(MessagePrinter.VERBOSE);
- } else {
- ourHandler.ignore(IMessage.INFO);
- ourHandler.setInterceptor(MessagePrinter.TERSE);
- }
- }
-
- // make sure we handle out of memory gracefully...
- try {
- // byte[] b = new byte[100000000]; for testing OoME only!
- long stime = System.currentTimeMillis();
- // uncomment next line to pause before startup (attach jconsole)
- // try {Thread.sleep(5000); }catch(Exception e) {}
- run(args, holder);
- long etime = System.currentTimeMillis();
- if (timers) {
- System.out.println("Compiler took " + (etime - stime) + "ms");
- }
- holder.handleMessage(MessageUtil.info("Compiler took " + (etime - stime) + "ms"));
- // uncomment next line to pause at end (keeps jconsole alive!)
- // try { System.in.read(); } catch (Exception e) {}
- } catch (OutOfMemoryError outOfMemory) {
- IMessage outOfMemoryMessage = new Message(OUT_OF_MEMORY_MSG, null, true);
- holder.handleMessage(outOfMemoryMessage);
- System.exit(-1); // we can't reasonably continue from this point.
- } finally {
- CompilationAndWeavingContext.reset();
- Dump.reset();
- }
-
- if (doExit)
- systemExit();
- }
-
- // put calls around run() call above to allowing connecting jconsole
- // private void pause(int ms) {
- // try {
- // System.err.println("Pausing for "+ms+"ms");
- // System.gc();
- // Thread.sleep(ms);
- // System.gc();
- // System.err.println("Continuing");
- // } catch (Exception e) {}
- // }
-
- /**
- * @param args
- */
- private IMessageHolder checkForCustomMessageHolder(String[] args) {
- IMessageHolder holder = null;
- final String customMessageHolder = parmInArgs(MESSAGE_HOLDER_OPTION, args);
- if (customMessageHolder != null) {
- try {
- holder = (IMessageHolder) Class.forName(customMessageHolder).getDeclaredConstructor().newInstance();
- } catch (Exception ex) {
- throw new AbortException("Failed to create custom message holder of class '" + customMessageHolder + "' : " + ex);
- }
- }
- return holder;
- }
-
- /**
- * Run without using System.exit(..), putting all messages in holder:
- * <ul>
- * <li>ERROR: compiler error</li>
- * <li>WARNING: compiler warning</li>
- * <li>FAIL: command error (bad arguments, exception thrown)</li>
- * </ul>
- * This handles incremental behavior:
- * <ul>
- * <li>If args include "-incremental", repeat for every input char until 'q' is entered.
- * <li>If args include "-incrementalTagFile {file}", repeat every time we detect that {file} modification time has changed.</li>
- * <li>Either way, list files recompiled each time if args includes "-verbose".</li>
- * <li>Exit when the commmand/compiler throws any Throwable.</li>
- * </ul>
- * When complete, this contains all the messages of the final run of the command and/or any FAIL messages produced in running
- * the command, including any Throwable thrown by the command itself.
- *
- * @param args the String[] command line for the compiler
- * @param holder the MessageHandler sink for messages.
- */
- public void run(String[] args, IMessageHolder holder) {
-
- PrintStream logStream = null;
- FileOutputStream fos = null;
- String logFileName = parmInArgs("-log", args);
- if (null != logFileName) {
- File logFile = new File(logFileName);
- try {
- logFile.createNewFile();
- fos = new FileOutputStream(logFileName, true);
- logStream = new PrintStream(fos, true);
- } catch (Exception e) {
- fail(holder, "Couldn't open log file: " + logFileName, e);
- }
- Date now = new Date();
- logStream.println(now);
- if (flagInArgs("-verbose", args)) {
- ourHandler.setInterceptor(new LogModeMessagePrinter(true, logStream));
- } else {
- ourHandler.ignore(IMessage.INFO);
- ourHandler.setInterceptor(new LogModeMessagePrinter(false, logStream));
- }
- holder = ourHandler;
- }
-
- if (LangUtil.isEmpty(args)) {
- args = new String[] { "-?" };
- } else if (controller.running()) {
- fail(holder, "already running with controller: " + controller, null);
- return;
- }
- args = controller.init(args, holder);
- if (0 < holder.numMessages(IMessage.ERROR, true)) {
- return;
- }
- if (command == null) {
- command = ReflectionFactory.makeCommand(commandName, holder);
- }
- if (0 < holder.numMessages(IMessage.ERROR, true)) {
- return;
- }
- try {
- outer: while (true) {
- boolean passed = command.runCommand(args, holder);
- if (report(passed, holder) && controller.incremental()) {
- while (controller.doRepeatCommand(command)) {
- holder.clearMessages();
- if (controller.buildFresh()) {
- continue outer;
- } else {
- passed = command.repeatCommand(holder);
- }
- if (!report(passed, holder)) {
- break;
- }
- }
- }
- break;
- }
- } catch (AbortException ae) {
- if (ae.isSilent()) {
- quit();
- } else {
- IMessage message = ae.getIMessage();
- Throwable thrown = ae.getThrown();
- if (null == thrown) { // toss AbortException wrapper
- if (null != message) {
- holder.handleMessage(message);
- } else {
- fail(holder, "abort without message", ae);
- }
- } else if (null == message) {
- fail(holder, "aborted", thrown);
- } else {
- String mssg = MessageUtil.MESSAGE_MOST.renderToString(message);
- fail(holder, mssg, thrown);
- }
- }
- } catch (Throwable t) {
- fail(holder, "unexpected exception", t);
- } finally {
- if (logStream != null) {
- logStream.close();
- }
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException e) {
- fail(holder, "unexpected exception", e);
- }
- }
- command = null;
- }
- }
-
- /** call this to stop after the next iteration of incremental compile */
- public void quit() {
- controller.quit();
- }
-
- /**
- * Set holder to be passed all messages. When holder is set, messages will not be printed by default.
- *
- * @param holder the IMessageHolder sink for all messages (use null to restore default behavior)
- */
- public void setHolder(IMessageHolder holder) {
- clientHolder = holder;
- }
-
- public IMessageHolder getHolder() {
- return clientHolder;
- }
-
- /**
- * Install a Runnable to be invoked synchronously after each compile completes.
- *
- * @param runner the Runnable to invoke - null to disable
- */
- public void setCompletionRunner(Runnable runner) {
- this.completionRunner = runner;
- }
-
- /**
- * Call System.exit(int) with values derived from the number of failures/aborts or errors in messages.
- *
- */
- protected void systemExit() {
- int num = lastFails;
- if (0 < num) {
- System.exit(-num);
- }
- num = lastErrors;
- if (0 < num) {
- System.exit(num);
- }
- System.exit(0);
- }
-
-
- /** Messages to the user */
- protected void outMessage(String message) { // XXX coordinate with MessagePrinter
- System.out.print(message);
- System.out.flush();
- }
-
- /**
- * Report results from a (possibly-incremental) compile run. This delegates to any reportHandler or otherwise prints summary
- * counts of errors/warnings to System.err (if any errors) or System.out (if only warnings). WARNING: this silently ignores
- * other messages like FAIL, but clears the handler of all messages when returning true. XXX false
- *
- * This implementation ignores the pass parameter but clears the holder after reporting on the assumption messages were
- * handled/printed already. (ignoring UnsupportedOperationException from holder.clearMessages()).
- *
- * @param pass true result of the command
- * @param holder IMessageHolder with messages from the command
- * @return false if the process should abort
- */
- protected boolean report(boolean pass, IMessageHolder holder) {
- lastFails = holder.numMessages(IMessage.FAIL, true);
- boolean result = (0 == lastFails);
- final Runnable runner = completionRunner;
- if (null != runner) {
- runner.run();
- }
- if (holder == ourHandler) {
- lastErrors = holder.numMessages(IMessage.ERROR, false);
- int warnings = holder.numMessages(IMessage.WARNING, false);
- StringBuilder sb = new StringBuilder();
- appendNLabel(sb, "fail|abort", lastFails);
- appendNLabel(sb, "error", lastErrors);
- appendNLabel(sb, "warning", warnings);
- if (0 < sb.length()) {
- PrintStream out = (0 < (lastErrors + lastFails) ? System.err : System.out);
- out.println(""); // XXX "wrote class file" messages no eol?
- out.println(sb);
- }
- }
- return result;
- }
-
- /** convenience API to make fail messages (without MessageUtils's fail prefix) */
- protected static void fail(IMessageHandler handler, String message, Throwable thrown) {
- handler.handleMessage(new Message(message, IMessage.FAIL, thrown, null));
- }
-
- /**
- * interceptor IMessageHandler to print as we go. This formats all messages to the user.
- */
- public static class MessagePrinter implements IMessageHandler {
-
- public static final IMessageHandler VERBOSE = new MessagePrinter(true);
- public static final IMessageHandler TERSE = new MessagePrinter(false);
-
- final boolean verbose;
-
- protected MessagePrinter(boolean verbose) {
- this.verbose = verbose;
- }
-
- /**
- * Print errors and warnings to System.err, and optionally info to System.out, rendering message String only.
- *
- * @return false always
- */
- @Override
- public boolean handleMessage(IMessage message) {
- if (null != message) {
- PrintStream out = getStreamFor(message.getKind());
- if (null != out) {
- out.println(render(message));
- }
- }
- return false;
- }
-
- /**
- * Render message differently. If abort, then prefix stack trace with feedback request. If the actual message is empty, then
- * use toString on the whole. Prefix message part with file:line; If it has context, suffix message with context.
- *
- * @param message the IMessage to render
- * @return String rendering IMessage (never null)
- */
- public static String render(IMessage message) {
- // IMessage.Kind kind = message.getKind();
-
- StringBuilder sb = new StringBuilder();
- String text = message.getMessage();
- if (text.equals(AbortException.NO_MESSAGE_TEXT)) {
- text = null;
- }
- boolean toString = (LangUtil.isEmpty(text));
- if (toString) {
- text = message.toString();
- }
- ISourceLocation loc = message.getSourceLocation();
- String context = null;
- if (null != loc) {
- File file = loc.getSourceFile();
- if (null != file) {
- String name = file.getName();
- if (!toString || (!text.contains(name))) {
- sb.append(FileUtil.getBestPath(file));
- if (loc.getLine() > 0) {
- sb.append(":" + loc.getLine());
- }
- int col = loc.getColumn();
- if (0 < col) {
- sb.append(":").append(col);
- }
- sb.append(" ");
- }
- }
- context = loc.getContext();
- }
-
- // per Wes' suggestion on dev...
- if (message.getKind() == IMessage.ERROR) {
- sb.append("[error] ");
- } else if (message.getKind() == IMessage.WARNING) {
- sb.append("[warning] ");
- }
-
- sb.append(text);
- if (null != context) {
- sb.append(LangUtil.EOL);
- sb.append(context);
- }
-
- String details = message.getDetails();
- if (details != null) {
- sb.append(LangUtil.EOL);
- sb.append('\t');
- sb.append(details);
- }
- Throwable thrown = message.getThrown();
- if (null != thrown) {
- sb.append(LangUtil.EOL);
- sb.append(Main.renderExceptionForUser(thrown));
- }
-
- if (message.getExtraSourceLocations().isEmpty()) {
- return sb.toString();
- } else {
- return MessageUtil.addExtraSourceLocations(message, sb.toString());
- }
-
- }
-
- @Override
- public boolean isIgnoring(IMessage.Kind kind) {
- return (null != getStreamFor(kind));
- }
-
- /**
- * No-op
- *
- * @see org.aspectj.bridge.IMessageHandler#isIgnoring(org.aspectj.bridge.IMessage.Kind)
- * @param kind
- */
- @Override
- public void dontIgnore(IMessage.Kind kind) {
-
- }
-
- /**
- * @return System.err for FAIL, ABORT, ERROR, and WARNING, System.out for INFO if -verbose and WEAVEINFO if -showWeaveInfo.
- */
- protected PrintStream getStreamFor(IMessage.Kind kind) {
- if (IMessage.WARNING.isSameOrLessThan(kind)) {
- return System.err;
- } else if (verbose && IMessage.INFO.equals(kind)) {
- return System.out;
- } else if (IMessage.USAGE.equals(kind)) {
- return System.out;
- } else if (IMessage.WEAVEINFO.equals(kind)) {
- return System.out;
- } else {
- return null;
- }
- }
-
- /**
- * No-op
- *
- * @see org.aspectj.bridge.IMessageHandler#ignore(org.aspectj.bridge.IMessage.Kind)
- * @param kind
- */
- @Override
- public void ignore(Kind kind) {
- }
- }
-
- public static class LogModeMessagePrinter extends MessagePrinter {
-
- protected final PrintStream logStream;
-
- public LogModeMessagePrinter(boolean verbose, PrintStream logStream) {
- super(verbose);
- this.logStream = logStream;
- }
-
- @Override
- protected PrintStream getStreamFor(IMessage.Kind kind) {
- if (IMessage.WARNING.isSameOrLessThan(kind)) {
- return logStream;
- } else if (verbose && IMessage.INFO.equals(kind)) {
- return logStream;
- } else if (IMessage.WEAVEINFO.equals(kind)) {
- return logStream;
- } else {
- return null;
- }
- }
-
- }
-
- /** controller for repeatable command delays until input or file changed or removed */
- public static class CommandController {
- public static String TAG_FILE_OPTION = "-XincrementalFile";
- public static String INCREMENTAL_OPTION = "-incremental";
-
- /** maximum 10-minute delay between filesystem checks */
- public static long MAX_DELAY = 1000 * 600;
-
- /** default 5-second delay between filesystem checks */
- public static long DEFAULT_DELAY = 1000 * 5;
-
- /** @see init(String[]) */
- private static final String[][] OPTIONS = new String[][] {
- new String[] { INCREMENTAL_OPTION },
- new String[] { TAG_FILE_OPTION, null }
- };
-
- /** true between init(String[]) and doRepeatCommand() that returns false */
- private boolean running;
-
- /** true after quit() called */
- private boolean quit;
-
- /** true if incremental mode, waiting for input other than 'q' */
- private boolean incremental;
-
- /** true if incremental mode, waiting for file to change (repeat) or disappear (quit) */
- private File tagFile;
-
- /** last modification time for tagFile as of last command - 0 to start */
- private long fileModTime;
-
- /** delay between filesystem checks for tagFile modification time */
- private long delay;
-
- /** true just after user types 'r' for rebuild */
- private boolean buildFresh;
-
- public CommandController() {
- delay = DEFAULT_DELAY;
- }
-
- /**
- * @param args read and strip incremental args from this
- * @param sink IMessageHandler for error messages
- * @return String[] remainder of args
- */
- public String[] init(String[] args, IMessageHandler sink) {
- running = true;
- // String[] unused;
- if (!LangUtil.isEmpty(args)) {
- String[][] options = LangUtil.copyStrings(OPTIONS);
- /* unused = */LangUtil.extractOptions(args, options);
- incremental = (null != options[0][0]);
- if (null != options[1][0]) {
- File file = new File(options[1][1]);
- if (!file.exists()) {
- MessageUtil.abort(sink, "tag file does not exist: " + file);
- } else {
- tagFile = file;
- fileModTime = tagFile.lastModified();
- }
- }
- }
- return args;
- }
-
- /**
- * @return true if init(String[]) called but doRepeatCommand has not returned false
- */
- public boolean running() {
- return running;
- }
-
- /** @param delay milliseconds between filesystem checks */
- public void setDelay(long delay) {
- if ((delay > -1) && (delay < MAX_DELAY)) {
- this.delay = delay;
- }
- }
-
- /** @return true if INCREMENTAL_OPTION or TAG_FILE_OPTION was in args */
- public boolean incremental() {
- return (incremental || (null != tagFile));
- }
-
- /** @return true if INCREMENTAL_OPTION was in args */
- public boolean commandLineIncremental() {
- return incremental;
- }
-
- public void quit() {
- if (!quit) {
- quit = true;
- }
- }
-
- /** @return true just after user typed 'r' */
- boolean buildFresh() {
- return buildFresh;
- }
-
- /** @return false if we should quit, true to do another command */
- boolean doRepeatCommand(ICommand command) {
- if (!running) {
- return false;
- }
- boolean result = false;
- if (quit) {
- result = false;
- } else if (incremental) {
- try {
- if (buildFresh) { // reset before input request
- buildFresh = false;
- }
- System.out.println(" press enter to recompile, r to rebuild, q to quit: ");
- System.out.flush();
- // boolean doMore = false;
- // seek for one q or a series of [\n\r]...
- do {
- int input = System.in.read();
- if ('q' == input) {
- break; // result = false;
- } else if ('r' == input) {
- buildFresh = true;
- result = true;
- } else if (('\n' == input) || ('\r' == input)) {
- result = true;
- } // else eat anything else
- } while (!result);
- System.in.skip(Integer.MAX_VALUE);
- } catch (IOException e) { // XXX silence for error?
- result = false;
- }
- } else if (null != tagFile) {
- long curModTime;
- while (true) {
- if (!tagFile.exists()) {
- result = false;
- break;
- } else if (fileModTime == (curModTime = tagFile.lastModified())) {
- fileCheckDelay();
- } else {
- fileModTime = curModTime;
- result = true;
- break;
- }
- }
- } // else, not incremental - false
- if (!result && running) {
- running = false;
- }
- return result;
- }
-
- /** delay between filesystem checks, returning if quit is set */
- protected void fileCheckDelay() {
- // final Thread thread = Thread.currentThread();
- long targetTime = System.currentTimeMillis() + delay;
- // long curTime;
- while (targetTime > System.currentTimeMillis()) {
- if (quit) {
- return;
- }
- try {
- Thread.sleep(300);
- } // 1/3-second delta for quit check
- catch (InterruptedException e) {
- }
- }
- }
- }
-
- }
|