Ajc11CompilerAdapter.java

/* *******************************************************************
 * Copyright (c) 2003 Contributors.
 * 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:
 *     Wes Isberg     initial implementation
 * ******************************************************************/

package org.aspectj.tools.ant.taskdefs;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter;
import org.aspectj.util.FileUtil;

import java.io.File;
//import java.io.FileFilter;
//import java.io.FileWriter;
//import java.io.IOException;

/**
 * Adapt ajc to javac commands.
 * Note that the srcdirs set for javac are NOT passed on to ajc;
 * instead, the list of source files generated is passed to ajc.
 * <p>
 * Javac usually prunes the source file list based on the timestamps
 * of corresponding .class files, which is wrong for ajc which
 * requires all the files every time.  To work around this,
 * set the global property CLEAN ("build.compiler.clean") to delete
 * all .class files in the destination directory before compiling.
 * </p>
 *
 * <p><u>Warnings</u>:</p>
 * <ol>
 * <li>cleaning will not work if no destination directory
 *     is specified in the javac task.
 *     (RFE: find and kill .class files in source dirs?)</li>
 * <li>cleaning will makes stepwise build processes fail
 * if they depend on the results of the prior compilation being
 * in the same directory, since this deletes <strong>all</strong>
 * .class files.</li>
 * <li>If no files are out of date, then the adapter is <b>never</b> called
 *     and thus cannot gain control to clean out the destination dir.
 *     </li>
 * </ol>
 *
 * @author Wes Isberg
 * @since AspectJ 1.1, Ant 1.5.1
 */
public class Ajc11CompilerAdapter implements CompilerAdapter {

    /**
     * Define this system/project property to signal that the
     * destination directory should be cleaned
     * and javac reinvoked
     * to get the complete list of files every time.
     */
    public static final String CLEAN = "build.compiler.clean";

    /** track whether we re-called <code>javac.execute()</code> */
    private static final ThreadLocal<Boolean> inSelfCall = new ThreadLocal<Boolean>() {
        public Boolean initialValue() {
            return Boolean.FALSE;
        }
    };

    Javac javac;

    public void setJavac(Javac javac) {
        this.javac = javac;
        javac.setTaskName(javac.getTaskName() + " - ajc");
    }

    public boolean execute() throws BuildException {
        if (null == javac) {
            throw new IllegalStateException("null javac");
        }
        if (!(Boolean) inSelfCall.get()
            && afterCleaningDirs()) {
            // if we are not re-calling ourself and we cleaned dirs,
            // then re-call javac to get the list of all source files.
            inSelfCall.set(Boolean.TRUE);
            javac.execute();
            // javac re-invokes us after recalculating file list
        } else {
            try {
                AjcTask ajc = new AjcTask();
                String err = ajc.setupAjc(javac);
                if (null != err) {
                    throw new BuildException(err, javac.getLocation());
                }
                ajc.execute();
                // handles BuildException for failonerror, etc.
            } finally {
                inSelfCall.set(Boolean.FALSE);
            }
        }
        return true;
    }

    /**
     * If destDir exists and property CLEAN is set,
     * this cleans out the dest dir of any .class files,
     * and returns true to signal a recursive call.
     * @return true if destDir was cleaned.
     */
    private boolean afterCleaningDirs() {
        String clean = javac.getProject().getProperty(CLEAN);
        if (null == clean) {
            return false;
        }
        File destDir = javac.getDestdir();
        if (null == destDir) {
            javac.log(
                CLEAN + " specified, but no dest dir to clean",
                Project.MSG_WARN);
            return false;
        }
        javac.log(
            CLEAN + " cleaning .class files from " + destDir,
            Project.MSG_VERBOSE);
        FileUtil.deleteContents(
            destDir,
            FileUtil.DIRS_AND_WRITABLE_CLASSES,
            true);
        return true;
    }
}