/*
* Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.antcontrib.walls;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.util.JAXPUtils;
import org.xml.sax.HandlerBase;
import org.xml.sax.Parser;
import org.xml.sax.SAXException;
/*
* Created on Aug 24, 2003
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
/**
* FILL IN JAVADOC HERE
*
* @author Dean Hiller(dean@xsoftware.biz)
*/
public class CompileWithWalls extends Task {
private boolean setWallsTwice = false;
private boolean setJavacTwice = false;
private Walls walls;
private Javac javac;
private File wallsFile;
private File tempBuildDir;
private Map packagesNeedingCompiling = new HashMap();
private SAXException cachedSAXException = null;
private IOException cachedIOException = null;
public void setIntermediaryBuildDir(File f) {
tempBuildDir = f;
}
public File getIntermediaryBuildDir() {
return tempBuildDir;
}
public void setWalls(File f) {
this.wallsFile = f;
Parser parser = JAXPUtils.getParser();
HandlerBase hb = new WallsFileHandler(this, wallsFile);
parser.setDocumentHandler(hb);
parser.setEntityResolver(hb);
parser.setErrorHandler(hb);
parser.setDTDHandler(hb);
try {
log("about to start parsing walls file", Project.MSG_INFO);
parser.parse(wallsFile.toURL().toExternalForm());
} catch (SAXException e) {
cachedSAXException = e;
throw new ParsingWallsException("Problem parsing walls file attached:", e);
} catch (IOException e) {
cachedIOException = e;
throw new ParsingWallsException("IOException on walls file attached:", e);
}
}
public File getWalls() {
return wallsFile;
}
public Walls createWalls() {
if (walls != null)
setWallsTwice = true;
walls = new Walls();
return walls;
}
public Javac createJavac() {
if (javac != null)
setJavacTwice = true;
javac = new Javac();
return javac;
}
public void execute() throws BuildException {
if(cachedIOException != null)
throw new BuildException(cachedIOException, getLocation());
else if(cachedSAXException != null)
throw new BuildException(cachedSAXException, getLocation());
else if(tempBuildDir == null)
throw new BuildException(
"intermediaryBuildDir attribute must be specified on the compilewithwalls element"
, getLocation());
else if (javac == null)
throw new BuildException(
"There must be a nested javac element",
getLocation());
else if (walls == null)
throw new BuildException(
"There must be a nested walls element",
getLocation());
else if (setWallsTwice)
throw new BuildException(
"compilewithwalls task only supports one nested walls element or one walls attribute",
getLocation());
else if (setJavacTwice)
throw new BuildException(
"compilewithwalls task only supports one nested javac element",
getLocation());
getProject().addTaskDefinition("SilentMove", SilentMove.class);
getProject().addTaskDefinition("SilentCopy", SilentCopy.class);
File destDir = javac.getDestdir();
Path src = javac.getSrcdir();
if(src == null)
throw new BuildException("Javac inside compilewithwalls must have a srcdir specified");
String[] list = src.list();
File[] tempSrcDirs1 = new File[list.length];
for(int i = 0; i < list.length; i++) {
tempSrcDirs1[i] = getProject().resolveFile(list[i]);
}
String[] classpaths = new String[0];
if(javac.getClasspath() != null)
classpaths = javac.getClasspath().list();
File temp = null;
for(int i = 0; i < classpaths.length; i++) {
temp = new File(classpaths[i]);
if(temp.isDirectory()) {
for(int n = 0; n < tempSrcDirs1.length; n++) {
if(tempSrcDirs1[n].compareTo(temp) == 0)
throw new BuildException("The classpath cannot contain any of the\n"
+"src directories, but it does.\n"
+"srcdir="+tempSrcDirs1[n]);
}
}
}
//get rid of non-existent srcDirs
List srcDirs2 = new ArrayList();
for(int i = 0; i < tempSrcDirs1.length; i++) {
if(tempSrcDirs1[i].exists())
srcDirs2.add(tempSrcDirs1[i]);
}
if (destDir == null)
throw new BuildException(
"destdir was not specified in nested javac task",
getLocation());
//make sure tempBuildDir is not inside destDir or we are in trouble!!
if(file1IsChildOfFile2(tempBuildDir, destDir))
throw new BuildException("intermediaryBuildDir attribute cannot be specified\n"
+"to be the same as destdir or inside desdir of the javac task.\n"
+"This is an intermediary build directory only used by the\n"
+"compilewithwalls task, not the class file output directory.\n"
+"The class file output directory is specified in javac's destdir attribute", getLocation());
//create the tempBuildDir if it doesn't exist.
if(!tempBuildDir.exists()) {
tempBuildDir.mkdirs();
log("created direction="+tempBuildDir, Project.MSG_VERBOSE);
}
Iterator iter = walls.getPackagesToCompile();
while (iter.hasNext()) {
Package toCompile = (Package)iter.next();
File buildSpace = toCompile.getBuildSpace(tempBuildDir);
if(!buildSpace.exists()) {
buildSpace.mkdir();
log("created directory="+buildSpace, Project.MSG_VERBOSE);
}
FileSet javaIncludes2 =
toCompile.getJavaCopyFileSet(getProject(), getLocation());
for(int i = 0; i < srcDirs2.size(); i++) {
File srcDir = (File)srcDirs2.get(i);
javaIncludes2.setDir(srcDir);
log(toCompile.getPackage()+": sourceDir["+i+"]="+srcDir+" destDir="+buildSpace, Project.MSG_VERBOSE);
copyFiles(srcDir, buildSpace, javaIncludes2);
}
Path srcDir2 = toCompile.getSrcPath(tempBuildDir, getProject());
Path classPath = toCompile.getClasspath(tempBuildDir, getProject());
if(javac.getClasspath() != null)
classPath.addExisting(javac.getClasspath());
//unfortunately, we cannot clear the SrcDir in Javac, so we have to clone
//instead of just reusing the other Javac....this means added params in
//future releases will be missed unless this task is kept up to date.
//need to convert to reflection later so we don't need to keep this up to
//date.
Javac buildSpaceJavac = new Javac();
buildSpaceJavac.setProject(getProject());
buildSpaceJavac.setOwningTarget(getOwningTarget());
buildSpaceJavac.setTaskName(getTaskName());
log(toCompile.getPackage()+": Compiling");
log(toCompile.getPackage()+": sourceDir="+srcDir2, Project.MSG_VERBOSE);
log(toCompile.getPackage()+": classPath="+classPath, Project.MSG_VERBOSE);
log(toCompile.getPackage()+": destDir="+buildSpace, Project.MSG_VERBOSE);
buildSpaceJavac.setSrcdir(srcDir2);
buildSpaceJavac.setDestdir(buildSpace);
//includes not used...ie. ignored
//includesfile not used
//excludes not used
//excludesfile not used
buildSpaceJavac.setClasspath(classPath);
//sourcepath not used
buildSpaceJavac.setBootclasspath(javac.getBootclasspath());
//classpath not used..redefined by us
//sourcepathref not used...redefined by us.
//bootclasspathref was already copied above(see javac and you will understand)
buildSpaceJavac.setExtdirs(javac.getExtdirs());
buildSpaceJavac.setEncoding(javac.getEncoding());
buildSpaceJavac.setNowarn(javac.getNowarn());
buildSpaceJavac.setDebug(javac.getDebug());
buildSpaceJavac.setDebugLevel(javac.getDebugLevel());
buildSpaceJavac.setOptimize(javac.getOptimize());
buildSpaceJavac.setDeprecation(javac.getDeprecation());
buildSpaceJavac.setTarget(javac.getTarget());
buildSpaceJavac.setVerbose(javac.getVerbose());
buildSpaceJavac.setDepend(javac.getDepend());
buildSpaceJavac.setIncludeantruntime(javac.getIncludeantruntime());
buildSpaceJavac.setIncludejavaruntime(javac.getIncludejavaruntime());
buildSpaceJavac.setFork(javac.isForkedJavac());
buildSpaceJavac.setExecutable(javac.getJavacExecutable());
buildSpaceJavac.setMemoryInitialSize(javac.getMemoryInitialSize());
buildSpaceJavac.setMemoryMaximumSize(javac.getMemoryMaximumSize());
buildSpaceJavac.setFailonerror(javac.getFailonerror());
buildSpaceJavac.setSource(javac.getSource());
buildSpaceJavac.setCompiler(javac.getCompiler());
Javac.ImplementationSpecificArgument arg;
String[] args = javac.getCurrentCompilerArgs();
if(args != null) {
for(int i = 0; i < args.length;i++) {
arg = buildSpaceJavac.createCompilerArg();
arg.setValue(args[i]);
}
}
buildSpaceJavac.setProject(getProject());
buildSpaceJavac.perform();
//copy class files to javac's destDir where the user wants the class files
copyFiles(buildSpace, destDir, toCompile.getClassCopyFileSet(getProject(), getLocation()));
}
}
/**
* @param tempBuildDir
* @param destDir
* @return
*/
private boolean file1IsChildOfFile2(File tempBuildDir, File destDir) {
File parent = tempBuildDir;
for(int i = 0; i < 1000; i++) {
if(parent.compareTo(destDir) == 0)
return true;
parent = parent.getParentFile();
if(parent == null)
return false;
}
throw new RuntimeException("You either have more than 1000 directories in"
+"\nyour heirarchy or this is a bug, please report. parent="+parent+" destDir="+destDir);
}
/**
* Move java or class files to temp files or moves the temp files
* back to java or class files. This must be done because javac
* is too nice and sticks already compiled classes and ones depended
* on in the classpath destroying the compile time wall. This way,
* we can keep the wall up.
*
* @param srcDir Directory to copy files from/to(Usually the java files dir or the class files dir)
* @param fileset The fileset of files to include in the move.
* @param moveToTempFile true if moving src files to temp files, false if moving temp files
* back to src files.
* @param isJavaFiles true if we are moving .java files, false if we are moving .class files.
*/
private void copyFiles(
File srcDir,
File destDir,
FileSet fileset) {
fileset.setDir(srcDir);
if (!srcDir.exists())
throw new BuildException(
"Directory=" + srcDir + " does not exist",
getLocation());
//before we do this, we have to move all files not
//in the above fileset to xxx.java.ant-tempfile
//so that they don't get dragged into the compile
//This way we don't miss anything and all the dependencies
//are listed or the compile will break.
Copy move = (Copy)getProject().createTask("SilentCopy");
move.setProject(getProject());
move.setOwningTarget(getOwningTarget());
move.setTaskName(getTaskName());
move.setLocation(getLocation());
move.setTodir(destDir);
// move.setOverwrite(true);
move.addFileset(fileset);
move.perform();
}
public void log(String msg, int level) {
super.log(msg, level);
}
//until 1.3 is deprecated, this is a cheat to chain exceptions.
private class ParsingWallsException extends RuntimeException {
private String message;
public ParsingWallsException(String message, Throwable cause) {
super(message);
this.message = message+"\n";
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
cause.printStackTrace(pw);
this.message += sw;
}
public String getMessage() {
return message;
}
public String toString() {
return getMessage();
}
}
}