123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- /* *******************************************************************
- * Copyright (c) 2004 IBM Corporation
- * 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:
- * Adrian Colyer,
- * ******************************************************************/
- package org.aspectj.tools.ajc;
-
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.FilenameFilter;
- import java.io.IOException;
- import java.io.PrintStream;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import java.util.StringTokenizer;
-
- import org.aspectj.asm.AsmManager;
- import org.aspectj.asm.IProgramElement;
- import org.aspectj.asm.IRelationship;
- import org.aspectj.asm.IRelationshipMap;
- import org.aspectj.asm.internal.Relationship;
- 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.MessageHandler;
- import org.aspectj.bridge.context.CompilationAndWeavingContext;
- import org.aspectj.testing.util.TestUtil;
- import org.aspectj.util.FileUtil;
-
- import static java.io.File.pathSeparator;
- import static java.io.File.separator;
- import static org.aspectj.tools.ajc.AjcTestCase.*;
-
- /**
- * The Ajc class is intended for use as part of a unit-test suite, it drives the AspectJ compiler and lets you check the compilation
- * results. Compilations run in a sandbox that is created in C:\temp\ajcSandbox or /tmp/ajcSandbox depending on your platform.
- * <p>
- * The expected usage of Ajc is through the TestCase superclass, AjcTestCase, which provides helper methods that conveniently drive
- * the base functions exposed by this class.
- * </p>
- *
- * @see org.aspectj.tools.ajc.AjcTestCase
- */
- public class Ajc {
-
- private static final String BUILD_OUTPUT_FOLDER = "target";
-
- public static final String outputFolder(String module) {
- return pathSeparator + ".." + separator + module + separator + BUILD_OUTPUT_FOLDER + separator + "classes";
- }
-
- public static final String outputFolders(String... modules) {
- StringBuilder s = new StringBuilder();
- for (String module: modules) {
- s.append(pathSeparator + ".." + separator + module + separator + BUILD_OUTPUT_FOLDER + separator + "classes");
- }
- return s.toString();
- }
-
- // ALSO SEE ANTSPEC AND AJCTESTCASE
- private static final String TESTER_PATH =
- outputFolder("testing-client")
- + outputFolder("runtime")
- + outputFolder("bcel-builder")
- + pathSeparator + CLASSPATH_JUNIT
- + pathSeparator + CLASSPATH_ASM
- + pathSeparator + CLASSPATH_ASM_COMMONS
- + outputFolder("bridge")
- + outputFolder("loadtime")
- + outputFolder("weaver")
- + outputFolder("org.aspectj.matcher")
- + outputFolder("bridge");
-
- private CompilationResult result;
- private File sandbox;
- private File baseDir;
- private final Main main;
- private String[] ajcArgs;
- private int incrementalStage = 10;
- private boolean shouldEmptySandbox = true;
- private final AjcCommandController controller;
- public static boolean verbose = System.getProperty("aspectj.tests.verbose", "true").equals("true");
-
- /**
- * Constructs a new Ajc instance, with a new AspectJ compiler inside.
- */
- public Ajc() {
- main = new Main();
- controller = new AjcCommandController();
- main.setController(controller);
- }
-
- /**
- * By default, each call to <code>compile</code> creates a new sandbox (C:\temp\ajcSandbox\ajtTestxxx.tmp, or
- * /tmp/ajcSandbox/ajcTestxxx.tmp depending on your platform). To write a test that performs multiple (non-incremental)
- * compiles, building on the results of previous compilations, set 'should empty sandbox' to false after the first compile,
- * which will cause subsequent compiles in the test to use the same directory and contents.
- */
- public void setShouldEmptySandbox(boolean empty) {
- this.shouldEmptySandbox = empty;
- }
-
- /**
- * Call the compiler with the given arguments (args are exactly the same as you would pass to ajc on the command-line). The
- * results of the compile are returned in a <code>CompilationResult</code>, which provides for easy testing of results.
- * <p>
- * The compilation happens in a sandbox (C:\temp\ajcSandbox\ajTestxxx.tmp or /tmp/ajcSandbox/ajcTestxxx.tmp depending on
- * platform). Compiler arguments are adapted to the sandbox as follows.
- * </p>
- * <p>
- * For every file or directory listed in an argument (source file, or component of inpath, aspectpath, sourceroots,
- * classpath,...), if the file is specified using an absolute path then it is left unchanged, but if the file is specified using
- * a relative path, and a base directory (see setBaseDir) has been provided, then files/directories are copied from the base
- * directory to the sandbox, and the compiler arguments adjusted to reflect their new location.
- * </p>
- * <p>
- * For example, given a baseDir of "tests/pr12345" and a compile command: "ajc src/A.java src/B.java", the files in
- *
- * <pre>
- * tests/pr12345/
- * src/
- * A.java
- * B.java
- * </pre>
- *
- * are copied to:
- *
- * <pre>
- * ajcSandbox/ajcTestxxx.tmp/
- * src/
- * A.java
- * B.java
- * </pre>
- * <p>
- * If no classpath is specified (no -classpath in the arguments) the classpath will be set to include the sandbox directory,
- * testing-client/bin (for the Tester class), and runtime/bin (for the AspectJ runtime). If a classpath <i>is</i> specified,
- * then any relative directories in it will be made relative to the sandbox, and the testing-client and runtime bin directories
- * are also added.
- * </p>
- * <p>
- * If no output directory is specified (no -d in the arguments), the output directory is set to the sandbox. If a directory is
- * specified, and the path is relative, it will be made relative to the sandbox.
- * </p>
- * <ul>
- * </ul>
- * </p>
- *
- * @param args The compiler arguments.
- * @return a CompilationResult object with all the messages produced by the compiler, a description of the ajc command that was
- * issued, and the standard output and error of the compile (excluding messages which are provided separately)
- * @throws IOException
- * @see org.aspectj.tools.ajc.CompilationResult
- */
- public CompilationResult compile(String[] args) throws IOException {
- incrementalStage = 10;
- return compile(args, false);
- }
-
- private CompilationResult compile(String[] args, boolean isIncremental) throws IOException {
- result = null;
- ajcArgs = args;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- PrintStream pout = new PrintStream(out);
- ByteArrayOutputStream err = new ByteArrayOutputStream();
- PrintStream perr = new PrintStream(err);
- PrintStream systemOut = System.out;
- PrintStream systemErr = System.err;
- System.setOut(pout);
- System.setErr(perr);
-
- List<IMessage> fails = new ArrayList<>();
- List<IMessage> errors = new ArrayList<>();
- List<IMessage> warnings = new ArrayList<>();
- List<IMessage> infos = new ArrayList<>();
- List<IMessage> weaves = new ArrayList<>();
-
- try {
- if (!isIncremental && shouldEmptySandbox) {
- sandbox = TestUtil.createEmptySandbox();
- }
- args = adjustToSandbox(args, !isIncremental);
- MessageHandler holder = new MessageHandler();
- holder.setInterceptor(new AbortInterceptor());
- main.setHolder(holder);
- if (incrementalStage == 10 && hasSpecifiedIncremental(args)) {
- // important to sleep after preparing the sandbox on first incremental stage (see notes in pr90806)
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- }
- }
- if (isIncremental) {
- controller.doIncremental(holder);
- } else {
- main.runMain(args, false);
- }
- addMessagesTo(infos, holder.getMessages(IMessage.INFO, false));
- addMessagesTo(warnings, holder.getWarnings());
- addMessagesTo(errors, holder.getErrors());
- addMessagesTo(fails, holder.getMessages(IMessage.FAIL, true));
- addMessagesTo(weaves, holder.getMessages(IMessage.WEAVEINFO, false));
- String stdOut = out.toString();
- String stdErr = err.toString();
- result = new CompilationResult(args, stdOut, stdErr, infos, errors, warnings, fails, weaves);
- } finally {
- System.setOut(systemOut);
- System.setErr(systemErr);
- }
- if (verbose) {
- System.err.println(result.getStandardError());
- System.out.println(result.getStandardOutput());
- System.out.println(result);
- }
- return result;
- }
-
- private boolean hasSpecifiedIncremental(String[] args) {
- if (args == null)
- return false;
- for (String arg : args) {
- if (arg.equals("-incremental"))
- return true;
- }
- return false;
- }
-
- /**
- * After compiling for the first time with compile(), if the -incremental option was specified you can do as many subsequent
- * incremental compiles as you like by calling this method.
- * <p>
- * Throws an IllegalStateException if you try and call this method without first doing a compile that specified the -incremental
- * option.
- * </p>
- *
- * @return A CompilationResult giving the results of the most recent increment.
- * @throws IOException
- */
- public CompilationResult doIncrementalCompile() throws IOException {
- if ((ajcArgs == null) || !isIncremental(ajcArgs)) {
- throw new IllegalStateException(
- "Can't do incremental compile unless -incremental specified and first compile has taken place");
- }
- incrementalStage += 10;
- return compile(ajcArgs, true);
- }
-
- /**
- * Return the result of the last compile or incremental compile. This is the same as the return value from the compile() or
- * doIncrementalCompile() methods.
- */
- public CompilationResult getLastCompilationResult() {
- return result;
- }
-
- /**
- * Get the sandbox directory used for the compilation.
- */
- public File getSandboxDirectory() {
- if (sandbox == null) {
- sandbox = TestUtil.createEmptySandbox();
- }
- return sandbox;
- }
-
- /**
- * Set the base directory relative to which all relative paths specified in the arguments to a compile will be interpreted.
- */
- public void setBaseDir(File dir) {
- if ((dir != null) && !dir.isDirectory())
- throw new IllegalArgumentException(dir.getPath() + " is not a directory: "+dir.getAbsolutePath());
- baseDir = dir;
- }
-
- private void addMessagesTo(List<IMessage> aList, IMessage[] messages) {
- Collections.addAll(aList, messages);
- }
-
- private boolean isIncremental(String[] args) {
- for (String arg : args) {
- if (arg.trim().equals("-incremental"))
- return true;
- }
- return false;
- }
-
- /**
- * Make every relative file name and dir be absolute under sandbox Add TESTER_PATH to classpath
- */
- private String[] adjustToSandbox(String[] args, boolean doCopy) throws IOException {
- String[] newArgs = new String[args.length];
- boolean hasClasspath = false;
- boolean hasOutdir = false;
- for (int i = 0; i < args.length; i++) {
- newArgs[i] = args[i];
- if (FileUtil.hasSourceSuffix(args[i])) {
- File f = new File(args[i]);
- // newArgs[i] = new File(baseDir,args[i]).getAbsolutePath(); // might be quicker?
- newArgs[i] = adjustFileOrDir(f, doCopy, false).getAbsolutePath();
- } else if (args[i].endsWith(".xml") && !args[i].startsWith("-")) {
- if (i > 0 && args[i - 1].equals("-outxmlfile")) {
- // dont adjust it
- } else {
- File f = new File(args[i]);
- // newArgs[i] = new File(baseDir,args[i]).getAbsolutePath(); // might be quicker?
- newArgs[i] = adjustFileOrDir(f, doCopy, false).getAbsolutePath();
- }
- } else {
- if ((args[i].equals("-aspectpath") || args[i].equals("-inpath") || args[i].equals("-injars")
- || args[i].equals("-outjar") || args[i].equals("-classpath") || args[i].equals("-sourceroots")
- || args[i].equals("-Xlintfile") || args[i].equals("-extdirs") || args[i].equals("-d"))
- && args.length > (i + 1))
- {
- newArgs[i] = args[i];
- StringBuilder buff = new StringBuilder();
- boolean copyThisTime = doCopy;
- if (args[i].equals("-d")) {
- copyThisTime = false;
- hasOutdir = true;
- }
- boolean isOutjar = args[i].equals("-outjar");
- StringTokenizer strTok = new StringTokenizer(args[++i], pathSeparator);
- while (strTok.hasMoreTokens()) {
- File f = new File(strTok.nextToken());
- buff.append(adjustFileOrDir(f, copyThisTime, isOutjar).getAbsolutePath());
- if (strTok.hasMoreTokens())
- buff.append(pathSeparator);
- }
- newArgs[i] = buff.toString();
- if (args[i - 1].equals("-classpath")) {
- hasClasspath = true;
- newArgs[i] = newArgs[i] + pathSeparator + TESTER_PATH + pathSeparator
- + getSandboxDirectory().getAbsolutePath();
- }
- } else {
- // could be resource file
- File f = new File(args[i]);
- if (f.exists()) {
- newArgs[i] = adjustFileOrDir(f, doCopy, false).getAbsolutePath();
- }
- }
- }
- }
- if (!hasClasspath) {
- String[] oldArgs = newArgs;
- newArgs = new String[oldArgs.length + 2];
- System.arraycopy(oldArgs, 0, newArgs, 0, oldArgs.length);
- newArgs[oldArgs.length] = "-classpath";
- newArgs[oldArgs.length + 1] = TESTER_PATH + pathSeparator + getSandboxDirectory().getAbsolutePath();
- }
- if (!hasOutdir) {
- String[] oldArgs = newArgs;
- newArgs = new String[oldArgs.length + 2];
- System.arraycopy(oldArgs, 0, newArgs, 0, oldArgs.length);
- newArgs[oldArgs.length] = "-d";
- newArgs[oldArgs.length + 1] = getSandboxDirectory().getPath();
- }
- return newArgs;
- }
-
- private File adjustFileOrDir(File from, boolean doCopy, boolean ensureDirsExist) throws IOException {
- File to = from;
- File ret = from;
- if (!from.isAbsolute()) {
- ret = new File(sandbox, from.getPath());
- File fromParent = from.getParentFile();
- String relativeToPath = (fromParent != null) ? (fromParent.getPath() + separator) : "";
- if (baseDir != null) {
- from = new File(baseDir, from.getPath());
- // if (ensureDirsExist) {
- // File toMkdir = (ret.getPath().endsWith(".jar") || ret.getPath().endsWith(".zip"))?ret.getParentFile():ret;
- // toMkdir.mkdirs();
- // }
- }
- if (!from.exists())
- return ret;
- if (doCopy) {
- // harness requires that any files with the same name, and a different extension,
- // get copied too (e.g. .out, .err, .event files)
- if (from.isFile()) {
- final String prefix = from.getName().substring(0, from.getName().lastIndexOf('.'));
- String[] toCopy = from.getParentFile().list(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- if (name.indexOf('.') == -1)
- return false;
- String toMatch = name.substring(0, name.lastIndexOf('.'));
- return (toMatch.equals(prefix));
- }
- });
- for (String s : toCopy) {
- String toPath = relativeToPath + s;
- to = new File(sandbox, toPath);
- FileUtil.copyFile(new File(from.getParentFile(), s), to);
- }
- } else {
- FileUtil.copyFile(from, ret);
- }
- }
- }
- return ret;
- }
-
- public static void dumpAJDEStructureModel(AsmManager model, String prefix) {
- dumpAJDEStructureModel(model, prefix, false);
- }
-
- public static void dumpAJDEStructureModel(AsmManager model, String prefix, boolean useHandles) {
- System.out.println("======================================");//$NON-NLS-1$
- System.out.println("start of AJDE structure model:" + prefix); //$NON-NLS-1$
-
- IRelationshipMap asmRelMap = model.getRelationshipMap();
- for (String sourceOfRelationship : asmRelMap.getEntries()) {
- System.err.println("Examining source relationship handle: " + sourceOfRelationship);
- List<IRelationship> relationships = null;
- if (useHandles) {
- relationships = asmRelMap.get(sourceOfRelationship);
- } else {
- IProgramElement ipe = model.getHierarchy().findElementForHandle(sourceOfRelationship);
- relationships = asmRelMap.get(ipe);
- }
- if (relationships != null) {
- for (IRelationship relationship : relationships) {
- Relationship rel = (Relationship) relationship;
- List<String> targets = rel.getTargets();
- for (String t : targets) {
- IProgramElement link = model.getHierarchy().findElementForHandle(t);
- System.out.println(""); //$NON-NLS-1$
- System.out.println(" sourceOfRelationship " + sourceOfRelationship); //$NON-NLS-1$
- System.out.println(" relationship " + rel.getName()); //$NON-NLS-1$
- System.out.println(" target " + link.getName()); //$NON-NLS-1$
- }
- }
-
- }
- }
- System.out.println("End of AJDE structure model"); //$NON-NLS-1$
- System.out.println("======================================");//$NON-NLS-1$
- }
- }
-
- /*
- * So that we can drive incremental compilation easily from a unit test.
- */
- class AjcCommandController extends Main.CommandController {
-
- private ICommand command;
-
- /*
- * (non-Javadoc)
- *
- * @see org.aspectj.tools.ajc.Main.CommandController#doRepeatCommand()
- */
- @Override
- boolean doRepeatCommand(ICommand command) {
- this.command = command;
- return false; // ensure that control returns to caller
- }
-
- /*
- * (non-Javadoc)
- *
- * @see org.aspectj.tools.ajc.Main.CommandController#running()
- */
- @Override
- public boolean running() {
- return false; // so that we can come back for more...
- }
-
- public void doIncremental(IMessageHandler handler) {
- if (command == null)
- throw new IllegalArgumentException("Can't repeat command until it has executed at least once!");
- command.repeatCommand(handler);
- }
- }
-
- class AbortInterceptor implements IMessageHandler {
-
- @Override
- public boolean handleMessage(IMessage message) throws AbortException {
- if (message.getKind() == IMessage.ABORT) {
- System.err.println("***** Abort Message Received ******");
- System.err.println(CompilationAndWeavingContext.getCurrentContext());
- System.err.println(message.getMessage());
- if (message.getThrown() != null) {
- System.err.println("caused by " + message.getThrown().toString());
- }
-
- } // allow message to accumulate...
- return false;
- }
-
- @Override
- public boolean isIgnoring(Kind kind) {
- if (kind != IMessage.ABORT)
- return true;
- return false;
- }
-
- @Override
- public void dontIgnore(Kind kind) {
- }
-
- @Override
- public void ignore(Kind kind) {
- }
- }
|