Package org.aspectj.ajdt.internal.core.builder

Source Code of org.aspectj.ajdt.internal.core.builder.AjBuildManager

/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
* Contributors:
*     PARC     initial implementation
* ******************************************************************/


package org.aspectj.ajdt.internal.core.builder;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;

import org.aspectj.ajdt.internal.compiler.AjCompilerAdapter;
import org.aspectj.ajdt.internal.compiler.IBinarySourceProvider;
import org.aspectj.ajdt.internal.compiler.ICompilerAdapter;
import org.aspectj.ajdt.internal.compiler.ICompilerAdapterFactory;
import org.aspectj.ajdt.internal.compiler.IIntermediateResultsRequestor;
import org.aspectj.ajdt.internal.compiler.IOutputClassFileNameProvider;
import org.aspectj.ajdt.internal.compiler.InterimCompilationResult;
import org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment;
import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
import org.aspectj.ajdt.internal.compiler.problem.AjProblemReporter;
import org.aspectj.asm.AsmManager;
import org.aspectj.asm.IHierarchy;
import org.aspectj.asm.IProgramElement;
import org.aspectj.asm.internal.ProgramElement;
import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.CountingMessageHandler;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.IProgressListener;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.bridge.Version;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.bridge.context.ContextFormatter;
import org.aspectj.bridge.context.ContextToken;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.core.compiler.IProblem;
import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile;
import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
import org.aspectj.org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.aspectj.org.eclipse.jdt.internal.compiler.ICompilerRequestor;
import org.aspectj.org.eclipse.jdt.internal.compiler.IProblemFactory;
import org.aspectj.org.eclipse.jdt.internal.compiler.batch.ClasspathDirectory;
import org.aspectj.org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
import org.aspectj.org.eclipse.jdt.internal.compiler.batch.FileSystem;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser;
import org.aspectj.org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.aspectj.org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.aspectj.util.FileUtil;
import org.aspectj.weaver.Dump;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.BcelWeaver;
import org.aspectj.weaver.bcel.BcelWorld;
import org.aspectj.weaver.bcel.UnwovenClassFile;
import org.eclipse.core.runtime.OperationCanceledException;
//import org.aspectj.org.eclipse.jdt.internal.compiler.util.HashtableOfObject;

public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourceProvider,ICompilerAdapterFactory {
  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;
    static final boolean FAIL_IF_RUNTIME_NOT_FOUND = false;
   
    private static final FileFilter binarySourceFilter =
    new FileFilter() {
      public boolean accept(File f) {
        return f.getName().endsWith(".class");
      }};
     
  /**
   * This builder is static so that it can be subclassed and reset.  However, note
   * that there is only one builder present, so if two extendsion reset it, only
   * the latter will get used.
   */
  private static AsmHierarchyBuilder asmHierarchyBuilder = new AsmHierarchyBuilder();
 
  static {
    CompilationAndWeavingContext.registerFormatter(
        CompilationAndWeavingContext.BATCH_BUILD, new AjBuildContexFormatter());
    CompilationAndWeavingContext.registerFormatter(
        CompilationAndWeavingContext.INCREMENTAL_BUILD, new AjBuildContexFormatter());   
  }
 
  private IProgressListener progressListener = null;
 
  private int compiledCount;
  private int sourceFileCount;

  private JarOutputStream zos;
  private boolean batchCompile = true;
  private INameEnvironment environment;
 
  private Map /* String -> List<UCF>*/ binarySourcesForTheNextCompile = new HashMap();
 
  // FIXME asc should this really be in here?
  private IHierarchy structureModel;
  public AjBuildConfig buildConfig;
  private List aspectNames = new LinkedList();
 
  AjState state = new AjState(this);
   
 
  public BcelWeaver getWeaver() { return state.getWeaver();}
  public BcelWorld getBcelWorld() { return state.getBcelWorld();}
 
  public CountingMessageHandler handler;

  public AjBuildManager(IMessageHandler holder) {
    super();
        this.handler = CountingMessageHandler.makeCountingMessageHandler(holder);
  }

    /** @return true if we should generate a model as a side-effect */
    public boolean doGenerateModel() {
        return buildConfig.isGenerateModelMode();
    }

  public boolean batchBuild(
        AjBuildConfig buildConfig,
        IMessageHandler baseHandler)
        throws IOException, AbortException {
        return doBuild(buildConfig, baseHandler, true);
    }

    public boolean incrementalBuild(
        AjBuildConfig buildConfig,
        IMessageHandler baseHandler)
        throws IOException, AbortException {
        return doBuild(buildConfig, baseHandler, false);
    }
   

    /** @throws AbortException if check for runtime fails */
    protected boolean doBuild(
        AjBuildConfig buildConfig,
        IMessageHandler baseHandler,
        boolean batch) throws IOException, AbortException {
        boolean ret = true;
      batchCompile = batch;
     
      int phase = batch ? CompilationAndWeavingContext.BATCH_BUILD : CompilationAndWeavingContext.INCREMENTAL_BUILD;
      ContextToken ct = CompilationAndWeavingContext.enteringPhase(phase ,buildConfig);
        try {
          if (batch) {
            this.state = new AjState(this);
          }
         
            boolean canIncremental = state.prepareForNextBuild(buildConfig);
            if (!canIncremental && !batch) { // retry as batch?
                 CompilationAndWeavingContext.leavingPhase(ct);
              return doBuild(buildConfig, baseHandler, true);
            }
            this.handler =
                CountingMessageHandler.makeCountingMessageHandler(baseHandler);
            // XXX duplicate, no? remove?
            String check = checkRtJar(buildConfig);
            if (check != null) {
                if (FAIL_IF_RUNTIME_NOT_FOUND) {
                    MessageUtil.error(handler, check);
                     CompilationAndWeavingContext.leavingPhase(ct);
                    return false;
                } else {
                    MessageUtil.warn(handler, check);
                }
            }
            // if (batch) {
                setBuildConfig(buildConfig);
            //}
            if (batch || !AsmManager.attemptIncrementalModelRepairs) {
//                if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode()) {
                  setupModel(buildConfig);
//                }
            }
            if (batch) {
                initBcelWorld(handler);
            }
            if (handler.hasErrors()) {
                 CompilationAndWeavingContext.leavingPhase(ct);
                return false;
            }
           
            if (buildConfig.getOutputJar() != null) {
              if (!openOutputStream(buildConfig.getOutputJar())) {
                     CompilationAndWeavingContext.leavingPhase(ct);
                     return false;
              }
            }
           
            if (batch) {
                // System.err.println("XXXX batch: " + buildConfig.getFiles());
                if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode()) { 
                    getWorld().setModel(AsmManager.getDefault().getHierarchy());
                    // in incremental build, only get updated model?
                }
                binarySourcesForTheNextCompile = state.getBinaryFilesToCompile(true);
                performCompilation(buildConfig.getFiles());
                if (handler.hasErrors()) {
                     CompilationAndWeavingContext.leavingPhase(ct);
                    return false;
                }

        if (AsmManager.isReporting())
            AsmManager.getDefault().reportModelInfo("After a batch build");
   
            } else {
// done already?
//                if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode()) { 
//                    bcelWorld.setModel(StructureModelManager.INSTANCE.getStructureModel());
//                }
                // System.err.println("XXXX start inc ");
                binarySourcesForTheNextCompile = state.getBinaryFilesToCompile(true);
                List files = state.getFilesToCompile(true);
        if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode())
        if (AsmManager.attemptIncrementalModelRepairs)
            AsmManager.getDefault().processDelta(files,state.addedFiles,state.deletedFiles);
                boolean hereWeGoAgain = !(files.isEmpty() && binarySourcesForTheNextCompile.isEmpty());
                for (int i = 0; (i < 5) && hereWeGoAgain; i++) {
                    // System.err.println("XXXX inc: " + files);
              
                    performCompilation(files);
                    if (handler.hasErrors() || (progressListener!=null && progressListener.isCancelledRequested())) {
                         CompilationAndWeavingContext.leavingPhase(ct);
                        return false;
                    }
                    binarySourcesForTheNextCompile = state.getBinaryFilesToCompile(false);
                    files = state.getFilesToCompile(false);
                    hereWeGoAgain = !(files.isEmpty() && binarySourcesForTheNextCompile.isEmpty());
                    // TODO Andy - Needs some thought here...
                    // I think here we might want to pass empty addedFiles/deletedFiles as they were
                    // dealt with on the first call to processDelta - we are going through this loop
                    // again because in compiling something we found something else we needed to
                    // rebuild.  But what case causes this?
                    if (hereWeGoAgain) {
            if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode())
              if (AsmManager.attemptIncrementalModelRepairs)
              AsmManager.getDefault().processDelta(files,state.addedFiles,state.deletedFiles);
                    }
                }
                if (!files.isEmpty()) {
                     CompilationAndWeavingContext.leavingPhase(ct);
                    return batchBuild(buildConfig, baseHandler);
                } else {               
                  if (AsmManager.isReporting())
                AsmManager.getDefault().reportModelInfo("After an incremental build");
                }
            }

            // XXX not in Mik's incremental
            if (buildConfig.isEmacsSymMode()) {
                new org.aspectj.ajdt.internal.core.builder.EmacsStructureModelManager().externalizeModel();
            }
           
            // for bug 113554: support ajsym file generation for command line builds
            if (buildConfig.isGenerateCrossRefsMode()) {
                String configFileProxy = buildConfig.getOutputDir().getAbsolutePath()
                + File.separator
                + CROSSREFS_FILE_NAME;
              AsmManager.getDefault().writeStructureModel(configFileProxy);
            }
           
            // have to tell state we succeeded or next is not incremental
            state.successfulCompile(buildConfig,batch);

            copyResourcesToDestination();
           
            if (buildConfig.getOutxmlName() != null) {
              writeOutxmlFile();
            }
           
            /*boolean weaved = *///weaveAndGenerateClassFiles();
            // if not weaved, then no-op build, no model changes
            // but always returns true
            // XXX weaved not in Mik's incremental
            if (buildConfig.isGenerateModelMode()) {
                AsmManager.getDefault().fireModelUpdated()
            }
             CompilationAndWeavingContext.leavingPhase(ct);
           
        } finally {
          if (zos != null) {
            closeOutputStream(buildConfig.getOutputJar());
          }
            ret = !handler.hasErrors();
            getBcelWorld().tidyUp();
            // bug 59895, don't release reference to handler as may be needed by a nested call
            //handler = null;
        }
        return ret;
    }
   

  private boolean openOutputStream(File outJar)  {
    try {
      OutputStream os = FileUtil.makeOutputStream(buildConfig.getOutputJar());
      zos = new JarOutputStream(os,getWeaver().getManifest(true));
    } catch (IOException ex) {
      IMessage message =
        new Message("Unable to open outjar "
                + outJar.getPath()
                + "(" + ex.getMessage()
                + ")",
              new SourceLocation(outJar,0),
              true);
      handler.handleMessage(message);
      return false;
    }
    return true;
  }

  private void closeOutputStream(File outJar) {
    try {
      if (zos != null) zos.close();
      zos = null;
     
      /* Ensure we don't write an incomplete JAR bug-71339 */
      if (handler.hasErrors()) {
        outJar.delete();
      }
    } catch (IOException ex) {
      IMessage message =
        new Message("Unable to write outjar "
                + outJar.getPath()
                + "(" + ex.getMessage()
                + ")",
              new SourceLocation(outJar,0),
              true);
      handler.handleMessage(message);
    }
  }

 
  private void copyResourcesToDestination() throws IOException {
    // resources that we need to copy are contained in the injars and inpath only
    for (Iterator i = buildConfig.getInJars().iterator(); i.hasNext(); ) {
      File inJar = (File)i.next();
      copyResourcesFromJarFile(inJar);
    }
   
    for (Iterator i = buildConfig.getInpath().iterator(); i.hasNext(); ) {
      File inPathElement = (File)i.next();
      if (inPathElement.isDirectory()) {       
        copyResourcesFromDirectory(inPathElement);
      } else {
        copyResourcesFromJarFile(inPathElement);
      }
    } 
   
    if (buildConfig.getSourcePathResources() != null) {
      for (Iterator i = buildConfig.getSourcePathResources().keySet().iterator(); i.hasNext(); ) {
        String resource = (String)i.next();
        File from = (File)buildConfig.getSourcePathResources().get(resource);
        copyResourcesFromFile(from,resource,from);
      }
    }
   
    writeManifest();
    }
 
  private void copyResourcesFromJarFile(File jarFile) throws IOException {
    JarInputStream inStream = null;
    try {
      inStream = new JarInputStream(new FileInputStream(jarFile));
      while (true) {
        ZipEntry entry = inStream.getNextEntry();
        if (entry == null) break;
     
        String filename = entry.getName();
//        System.out.println("? copyResourcesFromJarFile() filename='" + filename +"'");
 
        if (!entry.isDirectory() && acceptResource(filename)) {
          byte[] bytes = FileUtil.readAsByteArray(inStream);
          writeResource(filename,bytes,jarFile);
        }
 
        inStream.closeEntry();
      }
    } finally {
      if (inStream != null) inStream.close();
    }
  }
 
  private void copyResourcesFromDirectory(File dir) throws IOException {
    if (!COPY_INPATH_DIR_RESOURCES) return;
    // Get a list of all files (i.e. everything that isnt a directory)
    File[] files = FileUtil.listFiles(dir,new FileFilter() {
      public boolean accept(File f) {
        boolean accept = !(f.isDirectory() || f.getName().endsWith(".class")) ;
        return accept;
      }
    });
   
    // For each file, add it either as a real .class file or as a resource
    for (int i = 0; i < files.length; i++) {
      // ASSERT: files[i].getAbsolutePath().startsWith(inFile.getAbsolutePath()
      // or we are in trouble...
      String filename = files[i].getAbsolutePath().substring(
                          dir.getAbsolutePath().length()+1);
      copyResourcesFromFile(files[i],filename,dir);
    }   
  }
 
  private void copyResourcesFromFile(File f,String filename,File src) throws IOException {
    if (!acceptResource(filename)) return;
    FileInputStream fis = null;
    try {
      fis = new FileInputStream(f);
      byte[] bytes = FileUtil.readAsByteArray(fis);
      // String relativePath = files[i].getPath();
     
      writeResource(filename,bytes,src);
    } finally {
      if (fis != null) fis.close();
   
  }
   
  private void writeResource(String filename, byte[] content, File srcLocation) throws IOException {
    if (state.resources.contains(filename)) {
      IMessage msg = new Message("duplicate resource: '" + filename + "'",
                     IMessage.WARNING,
                     null,
                     new SourceLocation(srcLocation,0));
      handler.handleMessage(msg);
      return;
    }
    if (zos != null) {
      ZipEntry newEntry = new ZipEntry(filename)//??? get compression scheme right
     
      zos.putNextEntry(newEntry);
      zos.write(content);
      zos.closeEntry();
    } else {
      OutputStream fos =
        FileUtil.makeOutputStream(new File(buildConfig.getOutputDir(),filename));
      fos.write(content);
      fos.close();
    }
    state.resources.add(filename);
  }
 
  /*
   * If we are writing to an output directory copy the manifest but only
   * if we already have one
   */   
  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)
      fos.close();
    }
  }

  private boolean acceptResource(String resourceName) {
    if
        (resourceName.startsWith("CVS/")) ||
        (resourceName.indexOf("/CVS/") != -1) ||
        (resourceName.endsWith("/CVS")) ||
        (resourceName.endsWith(".class")) ||
        (resourceName.toUpperCase().equals(MANIFEST_NAME))
        )
    {
      return false;
    } else {
      return true;
    }
  }
 
  private void writeOutxmlFile () throws IOException {
    String filename = buildConfig.getOutxmlName();
//    System.err.println("? AjBuildManager.writeOutxmlFile() outxml=" + filename);
//    System.err.println("? AjBuildManager.writeOutxmlFile() outputDir=" + buildConfig.getOutputDir());
   
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(baos);
    ps.println("<aspectj>");
    ps.println("<aspects>");
    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();

    if (zos != null) {
      ZipEntry newEntry = new ZipEntry(filename);
     
      zos.putNextEntry(newEntry);
      zos.write(baos.toByteArray());
      zos.closeEntry();
    } else {
      OutputStream fos =
        FileUtil.makeOutputStream(new File(buildConfig.getOutputDir(),filename));
      fos.write(baos.toByteArray());
      fos.close();
    }
  }
 
//  public static void dumprels() {
//    IRelationshipMap irm = AsmManager.getDefault().getRelationshipMap();
//    int ctr = 1;
//    Set entries = irm.getEntries();
//    for (Iterator iter = entries.iterator(); iter.hasNext();) {
//      String hid = (String) iter.next();
//      List rels =  irm.get(hid);
//      for (Iterator iterator = rels.iterator(); iterator.hasNext();) {
//        IRelationship ir = (IRelationship) iterator.next();
//        List targets = ir.getTargets();
//        for (Iterator iterator2 = targets.iterator();
//          iterator2.hasNext();
//          ) {
//          String thid = (String) iterator2.next();
//          System.err.println("Hid:"+(ctr++)+":(targets="+targets.size()+") "+hid+" ("+ir.getName()+") "+thid);
//        }
//      }
//    }
//  }
 
 
    /**
     * Responsible for managing the ASM model between builds.  Contains the policy for
     * maintaining the persistance of elements in the model.
     *
     * This code is driven before each 'fresh' (batch) build to create
     * a new model.
     */
     private void setupModel(AjBuildConfig config) {
       AsmManager.setCreatingModel(config.isEmacsSymMode() || config.isGenerateModelMode());
       if (!AsmManager.isCreatingModel()) return;

    AsmManager.getDefault().createNewASM();
    // AsmManager.getDefault().getRelationshipMap().clear();
    IHierarchy model = AsmManager.getDefault().getHierarchy();
        String rootLabel = "<root>";
         
   
        IProgramElement.Kind kind = IProgramElement.Kind.FILE_JAVA;
        if (buildConfig.getConfigFile() != null) {
             rootLabel = buildConfig.getConfigFile().getName();
             model.setConfigFile(buildConfig.getConfigFile().getAbsolutePath());
             kind = IProgramElement.Kind.FILE_LST; 
        }
        model.setRoot(new ProgramElement(rootLabel, kind, new ArrayList()));
               
        model.setFileMap(new HashMap());
        setStructureModel(model);
    state.setStructureModel(model);
    state.setRelationshipMap(AsmManager.getDefault().getRelationshipMap());
    }
   
//   
//    private void dumplist(List l) {
//      System.err.println("---- "+l.size());
//      for (int i =0 ;i<l.size();i++) System.err.println(i+"\t "+l.get(i));
//    }
//    private void accumulateFileNodes(IProgramElement ipe,List store) {
//      if (ipe.getKind()==IProgramElement.Kind.FILE_JAVA ||
//          ipe.getKind()==IProgramElement.Kind.FILE_ASPECTJ) {
//            if (!ipe.getName().equals("<root>")) {
//              store.add(ipe);
//              return;
//            }
//      }
//      for (Iterator i = ipe.getChildren().iterator();i.hasNext();) {
//        accumulateFileNodes((IProgramElement)i.next(),store);
//      }
//    }
   
    /** init only on initial batch compile? no file-specific options */
  private void initBcelWorld(IMessageHandler handler) throws IOException {
    List cp = buildConfig.getBootclasspath();
    cp.addAll(buildConfig.getClasspath());
    BcelWorld bcelWorld = new BcelWorld(cp, handler, null);
    bcelWorld.setBehaveInJava5Way(buildConfig.getBehaveInJava5Way());
    bcelWorld.setTargetAspectjRuntimeLevel(buildConfig.getTargetAspectjRuntimeLevel());
    bcelWorld.setXnoInline(buildConfig.isXnoInline());
    bcelWorld.setXlazyTjp(buildConfig.isXlazyTjp());
    bcelWorld.setXHasMemberSupportEnabled(buildConfig.isXHasMemberEnabled());
    bcelWorld.setPinpointMode(buildConfig.isXdevPinpoint());
    BcelWeaver bcelWeaver = new BcelWeaver(bcelWorld);
    state.setWorld(bcelWorld);
    state.setWeaver(bcelWeaver);
    state.binarySourceFiles = new HashMap();
   
    for (Iterator i = buildConfig.getAspectpath().iterator(); i.hasNext();) {
      File f = (File) i.next();
      bcelWeaver.addLibraryJarFile(f);
    }
   
//    String lintMode = buildConfig.getLintMode();
   
    if (buildConfig.getLintMode().equals(AjBuildConfig.AJLINT_DEFAULT)) {
      bcelWorld.getLint().loadDefaultProperties();
    } else {
      bcelWorld.getLint().setAll(buildConfig.getLintMode());
    }
   
    if (buildConfig.getLintSpecFile() != null) {
      bcelWorld.getLint().setFromProperties(buildConfig.getLintSpecFile());
    }
   
    //??? incremental issues
    for (Iterator i = buildConfig.getInJars().iterator(); i.hasNext(); ) {
      File inJar = (File)i.next();
      List unwovenClasses = bcelWeaver.addJarFile(inJar, buildConfig.getOutputDir(),false);
      state.binarySourceFiles.put(inJar.getPath(), unwovenClasses);
    }
   
    for (Iterator i = buildConfig.getInpath().iterator(); i.hasNext(); ) {
      File inPathElement = (File)i.next();
      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);
        state.binarySourceFiles.put(inPathElement.getPath(),unwovenClasses);
      } else {
        // add each class file in an in-dir individually, this gives us the best error reporting
        // (they are like 'source' files then), and enables a cleaner incremental treatment of
        // 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());
          List ucfl = new ArrayList();
          ucfl.add(ucf);
          state.binarySourceFiles.put(binSrcs[j].getPath(),ucfl);
        }
      }
    }
   
    bcelWeaver.setReweavableMode(buildConfig.isXNotReweavable());

    //check for org.aspectj.runtime.JoinPoint
    ResolvedType joinPoint = bcelWorld.resolve("org.aspectj.lang.JoinPoint");
    if (joinPoint.isMissing()) {
      IMessage message =
        new Message("classpath error: unable to find org.aspectj.lang.JoinPoint (check that aspectjrt.jar is in your classpath)",
              null,
              true);
        handler.handleMessage(message);
    }
  }
 
  public World getWorld() {
    return getBcelWorld();
  }
 
  void addAspectClassFilesToWeaver(List addedClassFiles) throws IOException {
    for (Iterator i = addedClassFiles.iterator(); i.hasNext(); ) {
      UnwovenClassFile classFile = (UnwovenClassFile) i.next();
      getWeaver().addClassFile(classFile);
    }
  }

//  public boolean weaveAndGenerateClassFiles() throws IOException {
//    handler.handleMessage(MessageUtil.info("weaving"));
//    if (progressListener != null) progressListener.setText("weaving aspects");
//    bcelWeaver.setProgressListener(progressListener, 0.5, 0.5/state.addedClassFiles.size());
//    //!!! doesn't provide intermediate progress during weaving
//    // XXX add all aspects even during incremental builds?
//        addAspectClassFilesToWeaver(state.addedClassFiles);
//    if (buildConfig.isNoWeave()) {
//      if (buildConfig.getOutputJar() != null) {
//        bcelWeaver.dumpUnwoven(buildConfig.getOutputJar());
//      } else {
//        bcelWeaver.dumpUnwoven();
//        bcelWeaver.dumpResourcesToOutPath();
//      }
//    } else {
//      if (buildConfig.getOutputJar() != null) {
//        bcelWeaver.weave(buildConfig.getOutputJar());
//      } else {
//        bcelWeaver.weave();
//        bcelWeaver.dumpResourcesToOutPath();
//      }
//    }
//    if (progressListener != null) progressListener.setProgress(1.0);
//    return true;
//        //return messageAdapter.getErrorCount() == 0; //!javaBuilder.notifier.anyErrors();
//  }
 
  public FileSystem getLibraryAccess(String[] classpaths, String[] filenames) {
    String defaultEncoding = buildConfig.getOptions().defaultEncoding;
    if ("".equals(defaultEncoding)) //$NON-NLS-1$
      defaultEncoding = null; //$NON-NLS-1$ 
    // Bug 46671: We need an array as long as the number of elements in the classpath - *even though* not every
    // element of the classpath is likely to be a directory.  If we ensure every element of the array is set to
    // only look for BINARY, then we make sure that for any classpath element that is a directory, we won't build
    // a classpathDirectory object that will attempt to look for source when it can't find binary.
    int[] classpathModes = new int[classpaths.length];
    for (int i =0 ;i<classpaths.length;i++) classpathModes[i]=ClasspathDirectory.BINARY;
    return new FileSystem(classpaths, filenames, defaultEncoding,classpathModes);
  }
 
  public IProblemFactory getProblemFactory() {
    return new DefaultProblemFactory(Locale.getDefault());
  }
   
  /*
   *  Build the set of compilation source units
   */
  public CompilationUnit[] getCompilationUnits(String[] filenames, String[] encodings) {
    int fileCount = filenames.length;
    CompilationUnit[] units = new CompilationUnit[fileCount];
//    HashtableOfObject knownFileNames = new HashtableOfObject(fileCount);

    String defaultEncoding = buildConfig.getOptions().defaultEncoding;
    if ("".equals(defaultEncoding)) //$NON-NLS-1$
      defaultEncoding = null; //$NON-NLS-1$

    for (int i = 0; i < fileCount; i++) {
      String encoding = encodings[i];
      if (encoding == null)
        encoding = defaultEncoding;
      units[i] = new CompilationUnit(null, filenames[i], encoding);
    }
    return units;
  }
   
  public String extractDestinationPathFromSourceFile(CompilationResult result) {
    ICompilationUnit compilationUnit = result.compilationUnit;
    if (compilationUnit != null) {
      char[] fileName = compilationUnit.getFileName();
      int lastIndex = CharOperation.lastIndexOf(java.io.File.separatorChar, fileName);
      if (lastIndex == -1) {
        return System.getProperty("user.dir"); //$NON-NLS-1$
      }
      return new String(CharOperation.subarray(fileName, 0, lastIndex));
    }
    return System.getProperty("user.dir"); //$NON-NLS-1$
  }
   
   
  public void performCompilation(List files) {
    if (progressListener != null) {
      compiledCount=0;
      sourceFileCount = files.size();
      progressListener.setText("compiling source files");
    }
    //System.err.println("got files: " + files);
    String[] filenames = new String[files.size()];
    String[] encodings = new String[files.size()];
    //System.err.println("filename: " + this.filenames);
    for (int i=0; i < files.size(); i++) {
      filenames[i] = ((File)files.get(i)).getPath();
    }
   
    List cps = buildConfig.getFullClasspath();
    Dump.saveFullClasspath(cps);
    String[] classpaths = new String[cps.size()];
    for (int i=0; i < cps.size(); i++) {
      classpaths[i] = (String)cps.get(i);
    }
   
    //System.out.println("compiling");
    environment = getLibraryAccess(classpaths, filenames);
   
    if (!state.classesFromName.isEmpty()) {
      environment = new StatefulNameEnvironment(environment, state.classesFromName);
    }
   
    org.aspectj.ajdt.internal.compiler.CompilerAdapter.setCompilerAdapterFactory(this);
    org.aspectj.org.eclipse.jdt.internal.compiler.Compiler compiler =
      new org.aspectj.org.eclipse.jdt.internal.compiler.Compiler(environment,
          DefaultErrorHandlingPolicies.proceedWithAllProblems(),
            buildConfig.getOptions().getMap(),
          getBatchRequestor(),
          getProblemFactory());
   
    CompilerOptions options = compiler.options;

    options.produceReferenceInfo = true; //TODO turn off when not needed
   
    try {
       compiler.compile(getCompilationUnits(filenames, encodings));
    } catch (OperationCanceledException oce) {
      handler.handleMessage(new Message("build cancelled:"+oce.getMessage(),IMessage.WARNING,null,null));
    }
    // cleanup
    environment.cleanup();
    environment = null;
  }

  /*
   * Answer the component to which will be handed back compilation results from the compiler
   */
  public IIntermediateResultsRequestor getInterimResultRequestor() {
    return new IIntermediateResultsRequestor() {
      public void acceptResult(InterimCompilationResult result) {
        if (progressListener != null) {
          compiledCount++;
          progressListener.setProgress((compiledCount/2.0)/sourceFileCount);
          progressListener.setText("compiled: " + result.fileName());
        }
        state.noteResult(result);
       
        if (progressListener!=null && progressListener.isCancelledRequested()) {
          throw new AbortCompilation(true,
            new OperationCanceledException("Compilation cancelled as requested"));
        }
      }
    };
  }
 
  public ICompilerRequestor getBatchRequestor() {
    return new ICompilerRequestor() {
     
      public void acceptResult(CompilationResult unitResult) {
        // end of compile, must now write the results to the output destination
        // this is either a jar file or a file in a directory
        if (!(unitResult.hasErrors() && !proceedOnError())) {     
          Collection classFiles = unitResult.compiledTypes.values();
          boolean shouldAddAspectName = (buildConfig.getOutxmlName() != null);
          for (Iterator iter = classFiles.iterator(); iter.hasNext();) {
            ClassFile classFile = (ClassFile) iter.next();         
            String filename = new String(classFile.fileName());
            String classname = filename.replace('/', '.');
            filename = filename.replace('/', File.separatorChar) + ".class";
            try {
              if (buildConfig.getOutputJar() == null) {
                writeDirectoryEntry(unitResult, classFile,filename);
              } else {
                writeZipEntry(classFile,filename);
              }
              if (shouldAddAspectName) addAspectName(classname);
            } catch (IOException ex) {
              IMessage message = EclipseAdapterUtils.makeErrorMessage(
                  new String(unitResult.fileName),
                  CANT_WRITE_RESULT,
                  ex);
              handler.handleMessage(message);
            }

          }
        }
       
        if (unitResult.hasProblems() || unitResult.hasTasks()) {
          IProblem[] problems = unitResult.getAllProblems();
          for (int i=0; i < problems.length; i++) {
            IMessage message =
              EclipseAdapterUtils.makeMessage(unitResult.compilationUnit, problems[i]);
            handler.handleMessage(message);
          }
        }

      }
     
      private void writeDirectoryEntry(
          CompilationResult unitResult,
          ClassFile classFile,
          String filename)
      throws IOException {
        File destinationPath = buildConfig.getOutputDir();
        String outFile;
        if (destinationPath == null) {
          outFile = new File(filename).getName();
          outFile = new File(extractDestinationPathFromSourceFile(unitResult), outFile).getPath();
        } else {
          outFile = new File(destinationPath, filename).getPath();
        }
        BufferedOutputStream os =
          FileUtil.makeOutputStream(new File(outFile));
        os.write(classFile.getBytes());
        os.close();
      }
     
      private void writeZipEntry(ClassFile classFile, String name)
      throws IOException {
        name = name.replace(File.separatorChar,'/');
        ZipEntry newEntry = new ZipEntry(name)//??? get compression scheme right
       
        zos.putNextEntry(newEntry);
        zos.write(classFile.getBytes());
        zos.closeEntry();
      }
     
      private void addAspectName (String name) {
        BcelWorld world = getBcelWorld();
        ResolvedType type = world.resolve(name);
//        System.err.println("? writeAspectName() type=" + type);
        if (type.isAspect()) {
          aspectNames.add(name);
        }
      }
    };
  }
 
  protected boolean proceedOnError() {
    return buildConfig.getProceedOnError();
  }

//  public void noteClassFiles(AjCompiler.InterimResult result) {
//    if (result == null) return;
//    CompilationResult unitResult = result.result;
//    String sourceFileName = result.fileName();
//    if (!(unitResult.hasErrors() && !proceedOnError())) {
//      List unwovenClassFiles = new ArrayList();
//      Enumeration classFiles = unitResult.compiledTypes.elements();
//      while (classFiles.hasMoreElements()) {
//        ClassFile classFile = (ClassFile) classFiles.nextElement();
//        String filename = new String(classFile.fileName());
//        filename = filename.replace('/', File.separatorChar) + ".class";
//       
//        File destinationPath = buildConfig.getOutputDir();
//        if (destinationPath == null) {
//          filename = new File(filename).getName();
//          filename = new File(extractDestinationPathFromSourceFile(unitResult), filename).getPath();
//        } else {
//          filename = new File(destinationPath, filename).getPath();
//        }
//       
//        //System.out.println("classfile: " + filename);
//        unwovenClassFiles.add(new UnwovenClassFile(filename, classFile.getBytes()));
//      }
//      state.noteClassesFromFile(unitResult, sourceFileName, unwovenClassFiles);
////      System.out.println("file: " + sourceFileName);
////      for (int i=0; i < unitResult.simpleNameReferences.length; i++) {
////        System.out.println("simple: " + new String(unitResult.simpleNameReferences[i]));
////      }
////      for (int i=0; i < unitResult.qualifiedReferences.length; i++) {
////        System.out.println("qualified: " +
////          new String(CharOperation.concatWith(unitResult.qualifiedReferences[i], '/')));
////      }
//    } else {
//      state.noteClassesFromFile(null, sourceFileName, Collections.EMPTY_LIST);
//    }
//  }
//   

  private void setBuildConfig(AjBuildConfig buildConfig) {
    this.buildConfig = buildConfig;
    handler.reset();
  }
 
  String makeClasspathString(AjBuildConfig buildConfig) {
    if (buildConfig == null || buildConfig.getFullClasspath() == null) return "";
    StringBuffer buf = new StringBuffer();
    boolean first = true;
    for (Iterator it = buildConfig.getFullClasspath().iterator(); it.hasNext(); ) {
      if (first) { first = false; }
      else { buf.append(File.pathSeparator); }
      buf.append(it.next().toString());
    }
    return buf.toString();
  }
 
 
  /**
   * This will return null if aspectjrt.jar is present and has the correct version.
   * Otherwise it will return a string message indicating the problem.
   */
  public String checkRtJar(AjBuildConfig buildConfig) {
        // omitting dev info
    if (Version.text.equals(Version.DEVELOPMENT)) {
      // in the development version we can't do this test usefully
//      MessageUtil.info(holder, "running development version of aspectj compiler");
      return null;
    }
   
    if (buildConfig == null || buildConfig.getFullClasspath() == null) return "no classpath specified";
   
    String ret = null;
    for (Iterator it = buildConfig.getFullClasspath().iterator(); it.hasNext(); ) {
      File p = new File( (String)it.next() );
      // pr112830, allow variations on aspectjrt.jar of the form aspectjrtXXXXXX.jar
      if (p.isFile() && p.getName().startsWith("aspectjrt") && p.getName().endsWith(".jar")) {

        try {
                    String version = null;
                    Manifest manifest = new JarFile(p).getManifest();
                    if (manifest == null) {
                      ret = "no manifest found in " + p.getAbsolutePath() +
                ", expected " + Version.text;
                      continue;
                    }
                    Attributes attr = manifest.getAttributes("org/aspectj/lang/");
                    if (null != attr) {
                        version = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
                        if (null != version) {
                            version = version.trim();
                        }
                    }
          // assume that users of development aspectjrt.jar know what they're doing
          if (Version.DEVELOPMENT.equals(version)) {
//            MessageUtil.info(holder,
//              "running with development version of aspectjrt.jar in " +
//              p.getAbsolutePath());
                        return null;
          } else if (!Version.text.equals(version)) {
            ret =  "bad version number found in " + p.getAbsolutePath() +
                " expected " + Version.text + " found " + version;
            continue;
          }
        } catch (IOException ioe) {
          ret = "bad jar file found in " + p.getAbsolutePath() + " error: " + ioe;
        }
        return null; // this is the "OK" return value!
      } else {
        // might want to catch other classpath errors
      }
    }
   
    if (ret != null) return ret; // last error found in potentially matching jars...
   
    return "couldn't find aspectjrt.jar on classpath, checked: " + makeClasspathString(buildConfig);
  }
 

  public String toString() {
    StringBuffer buf = new StringBuffer();
    buf.append("AjBuildManager(");
    buf.append(")");
    return buf.toString();
  }


  public void setStructureModel(IHierarchy structureModel) {
    this.structureModel = structureModel;
  }

  /**
   * Returns null if there is no structure model
   */
  public IHierarchy getStructureModel() {
    return structureModel;
  }
   
  public IProgressListener getProgressListener() {
    return progressListener;
  }

  public void setProgressListener(IProgressListener progressListener) {
    this.progressListener = progressListener;
  }
 
 
  /* (non-Javadoc)
   * @see org.aspectj.ajdt.internal.compiler.AjCompiler.IOutputClassFileNameProvider#getOutputClassFileName(char[])
   */
  public String getOutputClassFileName(char[] eclipseClassFileName, CompilationResult result) {
    String filename = new String(eclipseClassFileName);
    filename = filename.replace('/', File.separatorChar) + ".class";
    File destinationPath = buildConfig.getOutputDir();
    String outFile;
    if (destinationPath == null) {
      outFile = new File(filename).getName();
      outFile = new File(extractDestinationPathFromSourceFile(result), outFile).getPath();
    } else {
      outFile = new File(destinationPath, filename).getPath();
    }
    return outFile;   
  }

  /* (non-Javadoc)
   * @see org.eclipse.jdt.internal.compiler.ICompilerAdapterFactory#getAdapter(org.eclipse.jdt.internal.compiler.Compiler)
   */
  public ICompilerAdapter getAdapter(org.aspectj.org.eclipse.jdt.internal.compiler.Compiler forCompiler) {
    // complete compiler config and return a suitable adapter...
    AjProblemReporter pr =
      new AjProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(),
                  forCompiler.options, getProblemFactory());
   
    forCompiler.problemReporter = pr;
     
    AjLookupEnvironment le =
      new AjLookupEnvironment(forCompiler, forCompiler.options, pr, environment);
    EclipseFactory factory = new EclipseFactory(le,this);
    le.factory = factory;
    pr.factory = factory;
   
    forCompiler.lookupEnvironment = le;
   
    forCompiler.parser =
      new Parser(
        pr,
        forCompiler.options.parseLiteralExpressionsAsConstants);
   
    return new AjCompilerAdapter(forCompiler,batchCompile,getBcelWorld(),getWeaver(),
            factory,
            getInterimResultRequestor(),
            progressListener,
            this,  // IOutputFilenameProvider
            this,  // IBinarySourceProvider
            state.binarySourceFiles,
            state.resultsFromFile.values(),
            buildConfig.isNoWeave(),
            buildConfig.getProceedOnError(),
            buildConfig.isNoAtAspectJAnnotationProcessing());
  }

  /* (non-Javadoc)
   * @see org.aspectj.ajdt.internal.compiler.IBinarySourceProvider#getBinarySourcesForThisWeave()
   */
  public Map getBinarySourcesForThisWeave() {
    return binarySourcesForTheNextCompile;
  }

    public static AsmHierarchyBuilder getAsmHierarchyBuilder() {
        return asmHierarchyBuilder;
    }

    /**
     * Override the the default hierarchy builder.
     */
    public static void setAsmHierarchyBuilder(AsmHierarchyBuilder newBuilder) {
        asmHierarchyBuilder = newBuilder;
    }
   
    public AjState getState() {
        return state;
    }

  public void setState(AjState buildState) {
    state = buildState;
  }
 
  private static class AjBuildContexFormatter implements ContextFormatter {

    public String formatEntry(int phaseId, Object data) {
      StringBuffer sb = new StringBuffer();
      if (phaseId == CompilationAndWeavingContext.BATCH_BUILD) {
        sb.append("batch building ");
      } else {
        sb.append("incrementally building ");
      }
      AjBuildConfig config = (AjBuildConfig) data;
      List classpath = config.getClasspath();
      sb.append("with classpath: ");
      for (Iterator iter = classpath.iterator(); iter.hasNext();) {
        sb.append(iter.next().toString());
        sb.append(File.pathSeparator);       
      }
      return sb.toString();
    }
   
  }
}

TOP

Related Classes of org.aspectj.ajdt.internal.core.builder.AjBuildManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.