Package ch.mtSystems.gcjStubber.model

Source Code of ch.mtSystems.gcjStubber.model.ObjectFileFilter

/*
*   GcjStubber - A stub creator for GCJ (JNC).
*   Copyright (C) 2007  Marco Trudel <mtrudel@gmx.ch>
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package ch.mtSystems.gcjStubber.model;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import ch.mtSystems.gcjStubber.model.stubCreator.FullPublicInterfaceStubCreator;
import ch.mtSystems.gcjStubber.model.stubCreator.MinimalStubCreator;
import ch.mtSystems.gcjStubber.model.stubCreator.MinimalWithInheritanceStubCreator;
import ch.mtSystems.gcjStubber.model.stubCreator.StubCreator;


/**
* StubsGenerator runs through all objects of libgcj.a and tries, with the StubCreators,
* to create the smallest possible stub for each object.
*/
public class StubsGenerator
{
  private List<StubsGeneratorListener> listeners = new LinkedList<StubsGeneratorListener>();
 
  private boolean stop = false;
  private File gcjDir, stubsDir;
  private String[] compilationArguments;

  private File helloWorldDotJava, helloWorldDotExe;
  private int origHelloWorldDotExeSize;
  private File libgcjDotSpec, libgcjDotA, libgcjDotJar;
  private File cmdGcj, cmdAr, cmdNm;


  // --------------- public methods ---------------
 
  /**
   * Add a listener.
   *
   * @param listener The listener to add.
   */
  public void addListener(StubsGeneratorListener listener)
  {
    listeners.add(listener);
  }

  /**
   * Remove a listener.
   *
   * @param listener The listener to remove.
   */
  public void removeListener(StubsGeneratorListener listener)
  {
    listeners.remove(listener);
  }

  /**
   * Tries to create stubs for all objects in libgcj.a (from gcjDir).
   * Informs about the progress through StubsGeneratorListeners.
   *
   * @param gcjDir The directory of the GCJ to stub.
   * @param stubsDir The output directory; where to place the stubs.
   * @param compilationArguments Custom arguments for compilation with GCJ.
   */
  public void createStubs(File gcjDir, File stubsDir, String[] compilationArguments)
  {
    this.gcjDir = gcjDir;
    this.stubsDir = stubsDir;
    this.compilationArguments = compilationArguments;

    helloWorldDotJava = new File(stubsDir, "HelloWorld.java");
    helloWorldDotExe = new File(stubsDir, "HelloWorld.exe");

    for(StubsGeneratorListener l : listeners) l.started();
    boolean restoreLibgcjDotSpec = false;

    try
    {
      log("Checking GCJ directory... ");
      if(!checkGcj()) return;
      log("Ok\n");

      log("Checking Output directory... ");
      if(!checkStubsDir()) return;
      log("Ok\n");

      log("Extracting libgcj.a to \"" + stubsDir + "\"... ");
      if(!extractLibgcjDotA()) return;
      log("Ok\n");

      log("Creating \"HelloWorld.java\" (used for stubbing)... ");
      if(!writeHelloWorld()) return;
      log("Ok\n");
     
      log("Adapting libgcj.spec for stubbing... ");
      if(!updateLibgcjDotSpec(true)) return;
      restoreLibgcjDotSpec = true;
      log("Ok\n");

      log("Compile HelloWorld (for statistics)... ");
      if(!compileHelloWorld()) return;
      log("Ok\n");
     
      // create all stubs
      File[] dirContent = stubsDir.listFiles(new ObjectFileFilter());
      Arrays.sort(dirContent);
      for(int i=0; i<dirContent.length && !stop; i++)
      {
        log((i+1) + "/" + dirContent.length + ": Handling \"" + dirContent[i].getName() + "\"...\n");
        if(!createStubForObject(dirContent[i], (i+1), dirContent.length)) return;
      }

      log("Cleaning up... ");
      if(!cleanup()) return;
      log("Ok\n");
    } finally
    {
      if(restoreLibgcjDotSpec)
      {
        log("Restoring libgcj.spec... ");
        log(updateLibgcjDotSpec(false) ? "Ok\n" : "Failed!\n");
      }

      for(StubsGeneratorListener l : listeners) l.done();
      stop = false;
    }
  }
 
  /**
   * Stops stub-creating at the next possible save state.
   * Please note that this can need up to one minute. So the
   * GUI should be adjusted.
   */
  public void stopCreatingStubs()
  {
    stop = true;
  }


  // --------------- private methods --------------

  /**
   * Sends the message to all registered listeners.
   */
  private void log(String line)
  {
    for(StubsGeneratorListener l : listeners) l.actionDone(line);
  }
 
  /**
   * Check if the provided GCJ contains a usable GCJ.
   *
   * @return true if it's ok. false otherwise.
   */
  private boolean checkGcj()
  {
    boolean linux = System.getProperty("os.name").equals("Linux");

    libgcjDotSpec = new File(gcjDir, "lib/libgcj.spec");
    if(!libgcjDotSpec.exists())
    {
      log("Invalid:\n   libgcj.spec not found!\n");
      return false;
    }

    libgcjDotA = new File(gcjDir, "lib/libgcj.a");
    if(!libgcjDotA.exists())
    {
      log("Invalid:\n   libgcj.a not found!\n");
      return false;
    }

    libgcjDotJar = new File(gcjDir, "share/java/libgcj-4.3.0.jar");
    if(!libgcjDotJar.exists())
    {
      log("Invalid:\n   libgcj.jar not found!\n");
      return false;
    }

    String name = linux ? "gcj" : "gcj.exe";
    cmdGcj = new File(gcjDir, "bin/" + name);
    if(!cmdGcj.exists())
    {
      log("Invalid:\n   " + name + " not found!\n");
      return false;
    }
   
    name = linux ? "ar" : "ar.exe";
    cmdAr = new File(gcjDir, "bin/" + name);
    if(!cmdAr.exists())
    {
      log("Invalid:\n   " + name + " not found!\n");
      return false;
    }

    name = linux ? "nm" : "nm.exe";
    cmdNm = new File(gcjDir, "bin/" + name);
    if(!cmdNm.exists())
    {
      log("Invalid:\n   " + name + " not found!\n");
      return false;
    }

    return true;
  }
 
  /**
   * Ensures that the output (stubs) directory exists and is empty.
   *
   * @return true if it's ok. false otherwise.
   */
  private boolean checkStubsDir()
  {
    // stubsDir needs to exist and be empty
    if(!stubsDir.exists())
    {
      if(!stubsDir.mkdirs())
      {
        log("Invalid:\n   Creating it failed!\n");
        return false;
      }
    } else
    {
      if(stubsDir.listFiles().length > 0)
      {
        log("Invalid:\n   It needs to be empty!\n");
        return false;
      }
    }

    return true;
  }
 
  /**
   * Extracts libgcj.a into the stubs directory.
   *
   * @return true if extracting was successfully. false otherwise.
   */
  private boolean extractLibgcjDotA()
  {
    try
    {
      String[] cmd = { cmdAr.toString(), "x", libgcjDotA.toString() };
      CommandExecutor commandExecutor = new CommandExecutor(cmd, stubsDir);
      commandExecutor.execute();
 
      if(commandExecutor.getOutput().length != 0 || commandExecutor.getError().length != 0)
      {
        log("Failed (1):\n");
        for(String s : commandExecutor.getOutput()) log("   [stdout] " + s + "\n");
        for(String s : commandExecutor.getError()) log("   [stderr] " + s + "\n");
        return false;
      }
     
      return true;
    } catch(Exception ex)
    {
      log("Exception occured:\n   " + ex.getMessage() + "\n");
      return false;
    }
  }
 
  /**
   * Writes a simple HelloWorld java file.
   *
   * @return true if it worked. false otherwise.
   */
  private boolean writeHelloWorld()
  {
    try
    {
      FileWriter fw = new FileWriter(helloWorldDotJava);
      fw.write("public class HelloWorld\n");
      fw.write("{\n");
      fw.write("  public static void main(String[] args)\n");
      fw.write("  {\n");
      fw.write("    System.out.println(\"HelloWorld\");\n");
      fw.write("  }\n");
      fw.write("}\n");
      fw.flush();
      fw.close();
     
      return true;
    } catch(Exception ex)
    {
      log("Exception occured:\n   " + ex.getMessage() + "\n");
      return false;
    }
  }
 
  /**
   * Compiles the helloWorld file and gets it's size for the statistics.
   * 
   * @return true if it worked. false otherwise.
   */
  private boolean compileHelloWorld()
  {
    try
    {
      List<String> cmd = new LinkedList<String>();
      cmd.add(cmdGcj.toString());
      cmd.add("-s");
      cmd.add("--main=HelloWorld");
      cmd.add("-o" + helloWorldDotExe.toString());
      cmd.add(helloWorldDotJava.toString());
      for(File f : stubsDir.listFiles(new ObjectFileFilter())) cmd.add(f.toString());
      for(String arg : compilationArguments) cmd.add(arg);
     
      // compile
      CommandExecutor commandExecutor = new CommandExecutor(cmd.toArray(new String[0]), stubsDir);
      commandExecutor.execute();
      if(commandExecutor.getOutput().length != 0 || commandExecutor.getError().length != 0)
      {
        log("Failed (2):\n");
        for(String s : commandExecutor.getOutput()) log("   [stdout] " + s + "\n");
        for(String s : commandExecutor.getError()) log("   [stderr] " + s + "\n");
        return false;
      }
     
      // try execution
      commandExecutor = new CommandExecutor(helloWorldDotExe.toString(), stubsDir);
      commandExecutor.execute();
      if(commandExecutor.getOutput().length != 1 ||
          !commandExecutor.getOutput()[0].equals("HelloWorld") ||
          commandExecutor.getError().length != 0)
      {
        log("Failed (3):\n");
        for(String s : commandExecutor.getOutput()) log("   [stdout] " + s + "\n");
        for(String s : commandExecutor.getError()) log("   [stderr] " + s + "\n");
        return false;
      }

      origHelloWorldDotExeSize = (int)helloWorldDotExe.length();
      return true;
    } catch(Exception ex)
    {
      log("Exception occured:\n   " + ex.getMessage() + "\n");
      return false;
    }
  }

  /**
   * Updates libgcj.spec to exclude/include libgcj.a (-lgcj).
   *
   * @param removeGcjArchive Wether to exclude or restore again the usage of libgcj.a.
   * @return true if it worked. false otherwise.
   */
  private boolean updateLibgcjDotSpec(boolean removeGcjArchive)
  {
    try
    {
      BufferedReader br = new BufferedReader(new FileReader(libgcjDotSpec));
      List<String> lines = new LinkedList<String>();
      for(String line = br.readLine(); line != null; line = br.readLine()) lines.add(line);
      br.close();
     
      FileWriter fileWriter = new FileWriter(libgcjDotSpec);
      boolean replaced = false;
      for(int i=0; i<lines.size(); i++)
      {
        String line = lines.get(i);

        if(removeGcjArchive)
        {
          int index = line.indexOf("-lgcj");
          if(index > -1)
          {
            fileWriter.write("## GcjStubber ## ");
            fileWriter.write(line);
            fileWriter.write("\n");

            fileWriter.write(line.replaceAll("-lgcj", ""));
            fileWriter.write("\n");

            replaced = true;
            continue;
          }
        } else
        {
          if(line.startsWith("## GcjStubber ## "))
          {
            fileWriter.write(line.substring(17));
            fileWriter.write("\n");

            i++;
            replaced = true;
            continue;
          }
        }

        fileWriter.write(line);
        fileWriter.write("\n");
      }
      fileWriter.flush();
      fileWriter.close();

      if(!replaced)
      {
        log("Library entry not found!");
        return false;
      }

      return true;
    } catch(Exception ex)
    {
      log("Exception occured:\n   " + ex.getMessage() + "\n");
      return false;
    }
  }
 
  /**
   * Creates a stub for the given object.
   *
   * @param fObj The object to create a stub for.
   * @param objectIndex The index of the object (1 .. totalCount).
   * @param totalCount The total number of objects to stub.
   * @return false if stubbing should be aborted, true otherwise.
   */
  private boolean createStubForObject(File fObj, int objectIndex, int totalCount)
  {
    File fObjTmp = new File(fObj.getParentFile(), fObj.getName()+".bak");
    int phaseProcessed = 0;
    boolean renameBack = false;

    try
   
      // check if the object contains java classes or only resources
      ClassesInObjectLister lister = new ClassesInObjectLister(cmdNm);
      Set<String> classesInObject = lister.getClassesInObject(fObj);
      if(classesInObject.size() == 0)
      {
        for(StubsGeneratorListener l : listeners)
        {
          l.processed(fObj.getName(), 0, -1, null, -1, objectIndex, totalCount);
        }
        return true;
      }
      phaseProcessed = 1;
     
      // remove the current object from the compilation
      if(!fObj.renameTo(fObjTmp)) throw new Exception("rename failed");
      renameBack = true;
     
      // try to compile the HelloWorld, will get the "missing references"
      List<String> cmd = new LinkedList<String>();
      cmd.add(cmdGcj.toString());
      cmd.add("-s");
      cmd.add("-w");
      cmd.add("--main=HelloWorld");
      cmd.add("-o" + helloWorldDotExe.toString());
      cmd.add(helloWorldDotJava.toString());
      for(File f : stubsDir.listFiles(new ObjectFileFilter())) cmd.add(f.toString());
      for(String arg : compilationArguments) cmd.add(arg);

      CommandExecutor commandExecutor = new CommandExecutor(cmd.toArray(new String[0]), stubsDir);
      commandExecutor.execute();
      if(commandExecutor.getOutput().length != 0)
      {
        log("   Unexpected stdout on first compilation:\n");
        for(String s : commandExecutor.getOutput()) log("   " + s + "\n");
        return false;
      }

      String[] saError = commandExecutor.getError();
      if(saError.length == 0)
      {
        for(StubsGeneratorListener l : listeners)
        {
          l.processed(fObj.getName(), 1, -1, null, -1, objectIndex, totalCount);
        }
        return true;
      }
      phaseProcessed = 2;

      // now create the stub
      UnresolvedReferenceParser parser = new UnresolvedReferenceParser(saError, classesInObject, libgcjDotJar);
      MissingClass[] missingClasses = parser.parse();

      File stubJar = new File(fObj.getParentFile(), "stub-" + fObj.getName() + ".jar");
      File stubObject = new File(fObj.getParentFile(), "stub-" + fObj.getName() + ".a");
      cmd.add(stubObject.toString());
      cmd.add("-I");
      cmd.add(stubJar.toString());

      for(int i=0; i < 3; i++)
      {
        phaseProcessed = i + 2;

        try
        {
          StubCreator stubCreator;
          if(i == 0)
          {
            stubCreator = new MinimalStubCreator(missingClasses, stubJar,
                stubObject, cmdGcj, new File(stubsDir, "tmp"), libgcjDotJar, classesInObject);
          } else if(i == 1)
          {
            stubCreator = new MinimalWithInheritanceStubCreator(missingClasses,
                stubJar, stubObject, cmdGcj, new File(stubsDir, "tmp"), libgcjDotJar, classesInObject);
          } else if(i == 2)
          {
            stubCreator = new FullPublicInterfaceStubCreator(missingClasses,
                stubJar, stubObject, cmdGcj, new File(stubsDir, "tmp"), libgcjDotJar, classesInObject);
          } else
          {
            throw new Exception("Can't be here?!");
          }
          stubCreator.create();
 
          // compile again, has to work with the created stub
          commandExecutor = new CommandExecutor(cmd.toArray(new String[0]), stubsDir);
          commandExecutor.execute();
          if(commandExecutor.getOutput().length != 0 || commandExecutor.getError().length != 0)
          {
            StringBuffer sb = new StringBuffer("Unexpected output on compilation with stub:\n");
            for(String s : commandExecutor.getOutput()) sb.append("[stdout] " + s + "\n");
            for(String s : commandExecutor.getError()) sb.append("[stderr] " + s + "\n");
            sb.deleteCharAt(sb.length()-1);
           
            for(StubsGeneratorListener l : listeners)
            {
              l.processed(fObj.getName(), phaseProcessed, 2, sb.toString(), -1, objectIndex, totalCount);
            }
            continue;
          }
   
          // check if the compiled exe works
          commandExecutor = new CommandExecutor(helloWorldDotExe.toString(), stubsDir);
          commandExecutor.execute();
          if(commandExecutor.getOutput().length != 1 ||
              !commandExecutor.getOutput()[0].equals("HelloWorld") ||
              commandExecutor.getError().length != 0)
          {
            StringBuffer sb = new StringBuffer("Unexpected HelloWorld output:\n");
            for(String s : commandExecutor.getOutput()) sb.append("[stdout] " + s + "\n");
            for(String s : commandExecutor.getError()) sb.append("[stderr] " + s + "\n");
            if(sb.length() > 0) sb.deleteCharAt(sb.length()-1);
           
            for(StubsGeneratorListener l : listeners)
            {
              l.processed(fObj.getName(), phaseProcessed, 1, sb.toString(), -1, objectIndex, totalCount);
            }
            continue;
          }
 
          // stub works
          int saving = origHelloWorldDotExeSize - (int)helloWorldDotExe.length();
          for(StubsGeneratorListener l : listeners)
          {
            l.processed(fObj.getName(), phaseProcessed, 0, null, saving, objectIndex, totalCount);
          }
          return true;
        } catch(Exception ex)
        {
          //ex.printStackTrace();

          for(StubsGeneratorListener l : listeners)
          {
            l.processed(fObj.getName(), phaseProcessed, 2, "Failed with exception: " + ex.getMessage(),
                -1, objectIndex, totalCount);
          }
        }
      }

      return true; // no stubCreator worked
    } catch(Exception ex)
    {
      //ex.printStackTrace();

      for(StubsGeneratorListener l : listeners)
      {
        l.processed(fObj.getName(), phaseProcessed, 2, "Failed with exception: " + ex.getMessage(),
            -1, objectIndex, totalCount);
      }
      return true;
    } finally
    {
      // add the current object back to the compilation
      if(renameBack && !fObjTmp.renameTo(fObj))
      {
        log("   Renaming \"" + fObjTmp.getName() + "\" back to \"" + fObj.getName() + "\" failed!!!");
        return false;
      }
    }
  }
 
  /**
   * Deletes all files that have been generated during stubbing and are no
   * longer needed.
   *
   * @return true if it worked. false otherwise.
   */
  private boolean cleanup()
  {
    if(helloWorldDotJava.exists() && !helloWorldDotJava.delete())
    {
      log("Deleting \"" + helloWorldDotJava.getName() + "\" failed!\n");
      return false;
    }
   
    if(helloWorldDotExe.exists() && !helloWorldDotExe.delete())
    {
      log("Deleting \"" + helloWorldDotExe.getName() + "\" failed!\n");
      return false;
    }

    for(File f : stubsDir.listFiles(new ObjectFileFilter()))
    {
      if(!f.delete())
      {
        log("Deleting \"" + f.getName() + "\" failed!\n");
        return false;
      }
    }
    return true;
  }


  // --------------- Singleton ---------------
 
  private static StubsGenerator stubsGenerator = new StubsGenerator();
 
  public static StubsGenerator getStubsGenerator()
  {
    return stubsGenerator;
  }
}

class ObjectFileFilter implements FileFilter
{
  public boolean accept(File pathname)
  {
    return pathname.getName().endsWith(".o");
  }
}
TOP

Related Classes of ch.mtSystems.gcjStubber.model.ObjectFileFilter

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.