From: aclement Date: Tue, 9 Jan 2007 09:37:29 +0000 (+0000) Subject: test and fix for 166580: multiple output locations and incremental compilation X-Git-Tag: Root_extensions~58 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a4d58bfa053406735465eb73ea74920194519fdc;p=aspectj.git test and fix for 166580: multiple output locations and incremental compilation --- diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/CompilationResultDestinationManager.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/CompilationResultDestinationManager.java index 5c7c30d4a..5173dbe23 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/CompilationResultDestinationManager.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/compiler/CompilationResultDestinationManager.java @@ -12,6 +12,7 @@ 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 @@ -43,4 +44,15 @@ public interface CompilationResultDestinationManager { */ 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, /bin). This is + * where classes which are on the inpath will be placed. + */ + File getDefaultOutputLocation(); + } diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java index c8e670028..4d71345d8 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java @@ -29,6 +29,7 @@ import java.util.Iterator; 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; @@ -95,8 +96,8 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc 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; @@ -554,9 +555,16 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc 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(); } } @@ -583,36 +591,91 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc 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(""); ps.println(""); - 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(""); - } + } } ps.println(""); ps.println(""); 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() { @@ -725,15 +788,20 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc 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); } @@ -742,7 +810,7 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc 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 @@ -750,8 +818,7 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc // 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); @@ -955,7 +1022,7 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc } 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), @@ -1012,16 +1079,17 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc 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); } } } @@ -1188,6 +1256,10 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc 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(); diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjState.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjState.java index 24baedad5..103a3b899 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjState.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjState.java @@ -156,10 +156,17 @@ public class AjState { */ private Map/**/ 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 /**/ 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 @@ -193,7 +200,7 @@ public class AjState { /** * Returns false if a batch build is needed. */ - boolean prepareForNextBuild(AjBuildConfig newBuildConfig) { + public boolean prepareForNextBuild(AjBuildConfig newBuildConfig) { currentBuildTime = System.currentTimeMillis(); if (!maybeIncremental()) { @@ -384,30 +391,50 @@ public class AjState { 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; } @@ -425,10 +452,20 @@ public class AjState { 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; @@ -447,7 +484,9 @@ public class AjState { // addDependentsOf(file); // } - thisTime.addAll(addedFiles); + if(addedFiles != null) { + thisTime.addAll(addedFiles); + } deleteClassFiles(); deleteResources(); @@ -514,11 +553,19 @@ public class AjState { } 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); @@ -571,21 +618,29 @@ public class AjState { // 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 + } } } } @@ -617,7 +672,14 @@ public class AjState { 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); @@ -1031,7 +1093,8 @@ public class AjState { } 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)); @@ -1247,12 +1310,12 @@ public class AjState { 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 @@ -1267,4 +1330,8 @@ public class AjState { public IBinaryType checkPreviousBuild(String name) { return (IBinaryType)resolvedTypeStructuresFromLastBuild.get(name); } + + public AjBuildManager getAjBuildManager() { + return buildManager; + } } diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/IncrementalStateManager.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/IncrementalStateManager.java index b9f2c9662..19f481eab 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/IncrementalStateManager.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/IncrementalStateManager.java @@ -15,8 +15,10 @@ import java.io.File; 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; @@ -77,15 +79,28 @@ public class IncrementalStateManager { 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;