package org.aspectj.ajdt.internal.compiler;
import java.io.File;
+import java.util.List;
/**
* acts as a bridge from ajde's OutputLocationManager interface to the compiler internals
*/
File getOutputLocationForResource(File resource);
+ /**
+ * Return a list of all output locations handled by this OutputLocationManager
+ */
+ List /*File*/ getAllOutputLocations();
+
+ /**
+ * Return the default output location (for example, <my_project>/bin). This is
+ * where classes which are on the inpath will be placed.
+ */
+ File getDefaultOutputLocation();
+
}
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
private static final String CROSSREFS_FILE_NAME = "build.lst";
private static final String CANT_WRITE_RESULT = "unable to write compilation result";
private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
- static final boolean COPY_INPATH_DIR_RESOURCES = false;
- // AJDT doesn't want this check, so Main enables it.
+ public static boolean COPY_INPATH_DIR_RESOURCES = false;
+ // AJDT doesn't want this check, so Main enables it.
private static boolean DO_RUNTIME_VERSION_CHECK = false;
// If runtime version check fails, warn or fail? (unset?)
static final boolean FAIL_IF_RUNTIME_NOT_FOUND = false;
private void writeManifest () throws IOException {
Manifest manifest = getWeaver().getManifest(false);
if (manifest != null && zos == null) {
- OutputStream fos =
- FileUtil.makeOutputStream(new File(buildConfig.getOutputDir(),MANIFEST_NAME));
- manifest.write(fos);
+ File outputDir = buildConfig.getOutputDir();
+ if (buildConfig.getCompilationResultDestinationManager() != null) {
+ // Manifests are only written if we have a jar on the inpath. Therefore,
+ // we write the manifest to the defaultOutputLocation because this is
+ // where we sent the classes that were on the inpath
+ outputDir = buildConfig.getCompilationResultDestinationManager().getDefaultOutputLocation();
+ }
+ if (outputDir == null) return;
+ OutputStream fos = FileUtil.makeOutputStream(new File(outputDir,MANIFEST_NAME));
+ manifest.write(fos);
fos.close();
}
}
if (ignoreOutxml) return;
String filename = buildConfig.getOutxmlName();
-// System.err.println("? AjBuildManager.writeOutxmlFile() outxml=" + filename);
-// System.err.println("? AjBuildManager.writeOutxmlFile() outputDir=" + buildConfig.getOutputDir());
-
+ // System.err.println("? AjBuildManager.writeOutxmlFile() outxml=" + filename);
+
+ Map outputDirsAndAspects = findOutputDirsForAspects();
+ Set outputDirs = outputDirsAndAspects.entrySet();
+ for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ File outputDir = (File) entry.getKey();
+ List aspects = (List) entry.getValue();
+ ByteArrayOutputStream baos = getOutxmlContents(aspects);
+ if (zos != null) {
+ ZipEntry newEntry = new ZipEntry(filename);
+
+ zos.putNextEntry(newEntry);
+ zos.write(baos.toByteArray());
+ zos.closeEntry();
+ } else {
+ OutputStream fos = FileUtil.makeOutputStream(new File(outputDir, filename));
+ fos.write(baos.toByteArray());
+ fos.close();
+ }
+ }
+ }
+
+ private ByteArrayOutputStream getOutxmlContents(List aspectNames) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
ps.println("<aspectj>");
ps.println("<aspects>");
- if (state.getAspectNames() != null) {
- for (Iterator i = state.getAspectNames().iterator(); i.hasNext();) {
- String name = (String)i.next();
+ if (aspectNames != null) {
+ for (Iterator i = aspectNames.iterator(); i.hasNext();) {
+ String name = (String) i.next();
ps.println("<aspect name=\"" + name + "\"/>");
- }
+ }
}
ps.println("</aspects>");
ps.println("</aspectj>");
ps.println();
ps.close();
+ return baos;
+ }
- if (zos != null) {
- ZipEntry newEntry = new ZipEntry(filename);
-
- zos.putNextEntry(newEntry);
- zos.write(baos.toByteArray());
- zos.closeEntry();
+ /**
+ * Returns a map where the keys are File objects corresponding to
+ * all the output directories and the values are a list of aspects
+ * which are sent to that ouptut directory
+ */
+ private Map /* File --> List (String) */findOutputDirsForAspects() {
+ Map outputDirsToAspects = new HashMap();
+ Map aspectNamesToFileNames = state.getAspectNamesToFileNameMap();
+ if (buildConfig.getCompilationResultDestinationManager() == null
+ || buildConfig.getCompilationResultDestinationManager().getAllOutputLocations().size() == 1) {
+ // we only have one output directory...which simplifies things
+ File outputDir = buildConfig.getOutputDir();
+ if (buildConfig.getCompilationResultDestinationManager() != null) {
+ outputDir = buildConfig.getCompilationResultDestinationManager().getDefaultOutputLocation();
+ }
+ List aspectNames = new ArrayList();
+ if (aspectNamesToFileNames != null) {
+ Set keys = aspectNamesToFileNames.keySet();
+ for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
+ String name = (String) iterator.next();
+ aspectNames.add(name);
+ }
+ }
+ outputDirsToAspects.put(outputDir, aspectNames);
} else {
- OutputStream fos =
- FileUtil.makeOutputStream(new File(buildConfig.getOutputDir(),filename));
- fos.write(baos.toByteArray());
- fos.close();
+ List outputDirs = buildConfig.getCompilationResultDestinationManager().getAllOutputLocations();
+ for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
+ File outputDir = (File) iterator.next();
+ outputDirsToAspects.put(outputDir,new ArrayList());
+ }
+ Set entrySet = aspectNamesToFileNames.entrySet();
+ for (Iterator iterator = entrySet.iterator(); iterator.hasNext();) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ String aspectName = (String) entry.getKey();
+ char[] fileName = (char[]) entry.getValue();
+ File outputDir = buildConfig.getCompilationResultDestinationManager()
+ .getOutputLocationForClass(new File(new String(fileName)));
+ if(!outputDirsToAspects.containsKey(outputDir)) {
+ outputDirsToAspects.put(outputDir,new ArrayList());
+ }
+ ((List)outputDirsToAspects.get(outputDir)).add(aspectName);
+ }
}
+ return outputDirsToAspects;
}
// public static void dumprels() {
bcelWeaver.addLibraryJarFile(f);
}
}
-
-// String lintMode = buildConfig.getLintMode();
-
-
-
- //??? incremental issues
- for (Iterator i = buildConfig.getInJars().iterator(); i.hasNext(); ) {
- File inJar = (File)i.next();
- List unwovenClasses = bcelWeaver.addJarFile(inJar, buildConfig.getOutputDir(),false);
+
+ // String lintMode = buildConfig.getLintMode();
+
+ File outputDir = buildConfig.getOutputDir();
+ if (outputDir == null
+ && buildConfig.getCompilationResultDestinationManager() != null) {
+ // send all output from injars and inpath to the default output location
+ // (will also later send the manifest there too)
+ outputDir = buildConfig.getCompilationResultDestinationManager().getDefaultOutputLocation();
+ }
+ // ??? incremental issues
+ for (Iterator i = buildConfig.getInJars().iterator(); i.hasNext();) {
+ File inJar = (File) i.next();
+ List unwovenClasses = bcelWeaver.addJarFile(inJar, outputDir, false);
state.recordBinarySource(inJar.getPath(), unwovenClasses);
}
if (!inPathElement.isDirectory()) {
// its a jar file on the inpath
// the weaver method can actually handle dirs, but we don't call it, see next block
- List unwovenClasses = bcelWeaver.addJarFile(inPathElement,buildConfig.getOutputDir(),true);
+ List unwovenClasses = bcelWeaver.addJarFile(inPathElement,outputDir,true);
state.recordBinarySource(inPathElement.getPath(),unwovenClasses);
} else {
// add each class file in an in-dir individually, this gives us the best error reporting
// class file changes in indirs.
File[] binSrcs = FileUtil.listFiles(inPathElement, binarySourceFilter);
for (int j = 0; j < binSrcs.length; j++) {
- UnwovenClassFile ucf =
- bcelWeaver.addClassFile(binSrcs[j], inPathElement, buildConfig.getOutputDir());
+ UnwovenClassFile ucf = bcelWeaver.addClassFile(binSrcs[j], inPathElement, outputDir);
List ucfl = new ArrayList();
ucfl.add(ucf);
state.recordBinarySource(binSrcs[j].getPath(),ucfl);
} else {
writeZipEntry(classFile,filename);
}
- if (shouldAddAspectName) addAspectName(classname);
+ if (shouldAddAspectName) addAspectName(classname, unitResult.getFileName());
} catch (IOException ex) {
IMessage message = EclipseAdapterUtils.makeErrorMessage(
new String(unitResult.fileName),
zos.closeEntry();
}
- private void addAspectName (String name) {
+ private void addAspectName (String name, char[] fileContainingAspect) {
BcelWorld world = getBcelWorld();
ResolvedType type = world.resolve(name);
// System.err.println("? writeAspectName() type=" + type);
if (type.isAspect()) {
- if (state.getAspectNames() == null) {
- state.initializeAspectNamesList();
+ if (state.getAspectNamesToFileNameMap() == null) {
+ state.initializeAspectNamesToFileNameMap();
}
- if (!state.getAspectNames().contains(name)) {
- state.getAspectNames().add(name);
+ if (!state.getAspectNamesToFileNameMap().containsKey(name)) {
+ state.getAspectNamesToFileNameMap().put(name,
+ fileContainingAspect);
}
}
}
String filename = new String(eclipseClassFileName);
filename = filename.replace('/', File.separatorChar) + ".class";
File destinationPath = buildConfig.getOutputDir();
+ if (buildConfig.getCompilationResultDestinationManager() != null) {
+ File f = new File(new String(result.getFileName()));
+ destinationPath = buildConfig.getCompilationResultDestinationManager().getOutputLocationForClass(f);
+ }
String outFile;
if (destinationPath == null) {
outFile = new File(filename).getName();
*/
private Map/*<String, File>*/ classesFromName = new HashMap();
+ /**
+ * Populated by AjBuildManager to record the aspects with the file name in which they're
+ * contained. This is later used when writing the outxml file in AjBuildManager. Need
+ * to record the file name because want to write an outxml file for each of the output
+ * directories and in order to ask the OutputLocationManager for the output location
+ * for a given aspect we need the file in which it is contained.
+ */
+ private Map /*<String, char[]>*/ aspectsFromFileNames;
private List/*File*/ compiledSourceFiles = new ArrayList();
private List/*String*/ resources = new ArrayList();
- private List/*String*/ aspectNames;
// these are references created on a particular compile run - when looping round in
// addAffectedSourceFiles(), if some have been created then we look at which source files
/**
* Returns false if a batch build is needed.
*/
- boolean prepareForNextBuild(AjBuildConfig newBuildConfig) {
+ public boolean prepareForNextBuild(AjBuildConfig newBuildConfig) {
currentBuildTime = System.currentTimeMillis();
if (!maybeIncremental()) {
private boolean pathChange(AjBuildConfig oldConfig, AjBuildConfig newConfig) {
boolean changed = false;
+
+ List oldOutputLocs = getOutputLocations(oldConfig);
+
List oldClasspath = oldConfig.getClasspath();
List newClasspath = newConfig.getClasspath();
if (stateListener!=null) stateListener.aboutToCompareClasspaths(oldClasspath,newClasspath);
- if (changed(oldClasspath,newClasspath,true,oldConfig.getOutputDir())) return true;
+ if (changed(oldClasspath,newClasspath,true,oldOutputLocs)) return true;
List oldAspectpath = oldConfig.getAspectpath();
List newAspectpath = newConfig.getAspectpath();
- if (changed(oldAspectpath,newAspectpath,true,oldConfig.getOutputDir())) return true;
+ if (changed(oldAspectpath,newAspectpath,true,oldOutputLocs)) return true;
List oldInJars = oldConfig.getInJars();
List newInJars = newConfig.getInJars();
- if (changed(oldInJars,newInJars,false,oldConfig.getOutputDir())) return true;
+ if (changed(oldInJars,newInJars,false,oldOutputLocs)) return true;
List oldInPath = oldConfig.getInpath();
List newInPath = newConfig.getInpath();
- if (changed(oldInPath, newInPath,false,oldConfig.getOutputDir())) return true;
+ if (changed(oldInPath, newInPath,false,oldOutputLocs)) return true;
return changed;
}
- private boolean changed(List oldPath, List newPath, boolean checkClassFiles, File oldOutputLocation) {
+ private List /*File*/ getOutputLocations(AjBuildConfig config) {
+ List outputLocs = new ArrayList();
+ if (config.getOutputDir() != null) {
+ try {
+ outputLocs.add(config.getOutputDir().getCanonicalFile());
+ } catch (IOException e) {}
+ }
+ if (config.getCompilationResultDestinationManager() != null) {
+ List dirs = config.getCompilationResultDestinationManager().getAllOutputLocations();
+ for (Iterator iterator = dirs.iterator(); iterator.hasNext();) {
+ File f = (File) iterator.next();
+ try {
+ if (!outputLocs.contains(f.getCanonicalFile())) {
+ outputLocs.add(f.getCanonicalFile());
+ }
+
+ } catch (IOException e) {}
+ }
+ }
+ return outputLocs;
+ }
+
+ private boolean changed(List oldPath, List newPath, boolean checkClassFiles, List outputLocs) {
if (oldPath == null) oldPath = new ArrayList();
if (newPath == null) newPath = new ArrayList();
- try {
- if (oldOutputLocation != null) {
- oldOutputLocation = oldOutputLocation.getCanonicalFile();
- }
- } catch(IOException ex) { /* we did our best...*/ }
if (oldPath.size() != newPath.size()) {
return true;
}
if (f.exists() && !f.isDirectory() && (f.lastModified() >= lastSuccessfulBuildTime)) {
return true;
}
- if (f.exists() && f.isDirectory() && checkClassFiles && !(f.equals(oldOutputLocation))) {
- boolean b= classFileChangedInDirSinceLastBuild(f);
- if (b && stateListener!=null) stateListener.detectedClassChangeInThisDir(f);
- if (b) return true;
+ if (f.exists() && f.isDirectory() && checkClassFiles) {
+ boolean foundMatch = false;
+ for (Iterator iterator = outputLocs.iterator(); iterator
+ .hasNext();) {
+ File dir = (File) iterator.next();
+ if (f.equals(dir)) {
+ foundMatch = true;
+ }
+ }
+ if (!foundMatch) {
+ boolean b= classFileChangedInDirSinceLastBuild(f);
+ if (b && stateListener!=null) stateListener.detectedClassChangeInThisDir(f);
+ if (b) return true;
+ }
}
}
return false;
// addDependentsOf(file);
// }
- thisTime.addAll(addedFiles);
+ if(addedFiles != null) {
+ thisTime.addAll(addedFiles);
+ }
deleteClassFiles();
deleteResources();
}
for (Iterator iter = resources.iterator(); iter.hasNext();) {
String resource = (String) iter.next();
- new File(buildConfig.getOutputDir(),resource).delete();
+ List outputDirs = getOutputLocations(buildConfig);
+ for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
+ File dir = (File) iterator.next();
+ File f = new File(dir,resource);
+ if (f.exists()) {
+ f.delete();
+ }
+ }
}
}
private void deleteClassFiles() {
+ if (deletedFiles == null) return;
for (Iterator i = deletedFiles.iterator(); i.hasNext(); ) {
File deletedFile = (File)i.next();
addDependentsOf(deletedFile);
// oldResources need to be deleted...
for (Iterator iter = oldResources.iterator(); iter.hasNext();) {
String victim = (String) iter.next();
- File f = new File(buildConfig.getOutputDir(),victim);
- if (f.exists()) {
- f.delete();
- }
- resources.remove(victim);
+ List outputDirs = getOutputLocations(buildConfig);
+ for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
+ File dir = (File) iterator.next();
+ File f = new File(dir,victim);
+ if (f.exists()) {
+ f.delete();
+ }
+ resources.remove(victim);
+ }
}
}
private void maybeDeleteResource(String resName, List oldResources) {
if (resources.contains(resName)) {
oldResources.remove(resName);
- File source = new File(buildConfig.getOutputDir(),resName);
- if ((source != null) && (source.exists()) &&
- (source.lastModified() >= lastSuccessfulBuildTime)) {
- resources.remove(resName); // will ensure it is re-copied
+ List outputDirs = getOutputLocations(buildConfig);
+ for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
+ File dir = (File) iterator.next();
+ File source = new File(dir,resName);
+ if ((source != null) && (source.exists()) &&
+ (source.lastModified() >= lastSuccessfulBuildTime)) {
+ resources.remove(resName); // will ensure it is re-copied
+ }
}
}
}
private UnwovenClassFile createUnwovenClassFile(AjBuildConfig.BinarySourceFile bsf) {
UnwovenClassFile ucf = null;
try {
- ucf = weaver.addClassFile(bsf.binSrc, bsf.fromInPathDirectory, buildConfig.getOutputDir());
+ File outputDir = buildConfig.getOutputDir();
+ if (buildConfig.getCompilationResultDestinationManager() != null) {
+ // createUnwovenClassFile is called only for classes that are on the inpath,
+ // all inpath classes are put in the defaultOutputLocation, therefore,
+ // this is the output dir
+ outputDir = buildConfig.getCompilationResultDestinationManager().getDefaultOutputLocation();
+ }
+ ucf = weaver.addClassFile(bsf.binSrc, bsf.fromInPathDirectory, outputDir);
} catch(IOException ex) {
IMessage msg = new Message("can't read class file " + bsf.binSrc.getPath(),
new SourceLocation(bsf.binSrc,0),false);
}
protected void addAffectedSourceFiles(List addTo, List lastTimeSources) {
- if (qualifiedStrings.isEmpty() && simpleStrings.isEmpty()) return;
+ if (qualifiedStrings == null || simpleStrings == null ||
+ (qualifiedStrings.isEmpty() && simpleStrings.isEmpty())) return;
if (listenerDefined()) getListener().recordDecision("Examining whether any other files now need compilation based just compiling: '"+stringifyList(lastTimeSources)+"'");
// the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
char[][][] qualifiedNames = ReferenceCollection.internQualifiedNames(makeStringSet(qualifiedStrings));
buildManager.setStructureModel(null);
}
- public List getAspectNames() {
- return aspectNames;
+ public Map getAspectNamesToFileNameMap() {
+ return aspectsFromFileNames;
}
-
- public void initializeAspectNamesList() {
- this.aspectNames = new LinkedList();
+
+ public void initializeAspectNamesToFileNameMap() {
+ this.aspectsFromFileNames = new HashMap();
}
// Will allow us to record decisions made during incremental processing, hopefully aid in debugging
public IBinaryType checkPreviousBuild(String name) {
return (IBinaryType)resolvedTypeStructuresFromLastBuild.get(name);
}
+
+ public AjBuildManager getAjBuildManager() {
+ return buildManager;
+ }
}
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
+import org.aspectj.ajdt.internal.compiler.CompilationResultDestinationManager;
import org.aspectj.asm.AsmManager;
continue;
}
File outputDir = ajbc.getOutputDir();
- if (outputDir == null) {
+ if (outputDir != null && outputDir.equals(location)) {
+ if (debugIncrementalStates) System.err.println("< findStateManagingOutputLocation("+location+") returning "+element);
+ return element;
+ }
+ CompilationResultDestinationManager outputManager = ajbc.getCompilationResultDestinationManager();
+ if (outputManager != null) {
+ List outputDirs = outputManager.getAllOutputLocations();
+ for (Iterator iterator = outputDirs.iterator(); iterator
+ .hasNext();) {
+ File dir = (File) iterator.next();
+ if (dir.equals(location)) {
+ if (debugIncrementalStates) System.err.println("< findStateManagingOutputLocation("+location+") returning "+element);
+ return element;
+ }
+ }
+ }
+ if (outputDir == null && outputManager == null) {
// FIXME why can it ever be null? due to using outjar?
- if (debugIncrementalStates) System.err.println(" output directory for "+ajbc+" is null");
+ if (debugIncrementalStates) System.err.println(" output directory and output location manager for "+ajbc+" are null");
continue;
}
- if (outputDir.equals(location)) {
- if (debugIncrementalStates) System.err.println("< findStateManagingOutputLocation("+location+") returning "+element);
- return element;
- }
+
}
if (debugIncrementalStates) System.err.println("< findStateManagingOutputLocation("+location+") returning null");
return null;