All Downloads are FREE. Search and download functionalities are using the official Maven repository.

editor.shipit.ShipIt Maven / Gradle / Ivy

There is a newer version: 1.18.1
Show newest version
package editor.shipit;

import editor.LabFrame;
import editor.util.Experiment;
import java.io.File;
import java.nio.file.Path;
import gw.util.PathUtil;
import gw.lang.Gosu;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;

/**
 */
public class ShipIt
{
  private static ShipIt INSTANCE;

  private JarOutputStream _jo;
  private Set _entries;
  private List _jars;


  public static ShipIt instance()
  {
    return INSTANCE == null ? INSTANCE = new ShipIt() : INSTANCE;
  }

  public boolean shipIt( Experiment experiment )
  {
    ShipItDialog dlg = new ShipItDialog( experiment );
    dlg.setVisible( true );
    String programName = dlg.getProgramName();
    if( programName == null )
    {
      return false;
    }
    String strProgramName = dlg.getProgramName();

    List progClasspath = new ArrayList<>();
    Path outFile = PathUtil.create( experiment.getExperimentDir(), experiment.getName() + ".jar" );
    if( PathUtil.exists( outFile ) )
    {
      PathUtil.delete( outFile );
    }

    _entries = new HashSet<>();
    _jars = new ArrayList<>();
    try
    {
      BufferedOutputStream bo = new BufferedOutputStream( PathUtil.createOutputStream( outFile ) );
      _jo = new JarOutputStream( bo );

      // Add source paths
      AddExperimentFilesAndDependencies( experiment, progClasspath );

      // Create the Main.class file
      JarEntry je = new JarEntry( "Launcher.class" );
      _jo.putNextEntry( je );
      InputStream inMain = getClass().getResource( "/Launcher.class" ).openStream();
      writeBytes( inMain );
      inMain.close();

      // Bundle Gosu
      boolean bBundleGosu = dlg.isBundleGosu();
      if( bBundleGosu )
      {
        bundleGosu( progClasspath );
      }

      // Precompile classes
      boolean bCompile = dlg.isPrecompiled();
      if( bCompile )
      {
        if( !ExperimentBuild.instance().rebuild( this::addPrecompiledClass ) )
        {
          _jo.close();
          PathUtil.delete( outFile );
          return false;
        }
      }

      // Create the jar-repo.txt file
      createJarRepoFile();

      // Create the main.properties file
      createMainPropertiesFile( strProgramName, bBundleGosu );

      // Create the MANIFEST
      makeManifest();

      _jo.close();

      LabFrame.openFileOrDir( outFile );

      return true;
    }
    catch( Exception e )
    {
      throw new RuntimeException( e );
    }
  }

  private boolean addPrecompiledClass( CompiledClass cs )
  {
    // Create the Main.class file
    String javaName = cs.getType().getJavaName();
    javaName = javaName.replace( '.', '/' ) + ".class";
    JarEntry je = new JarEntry( javaName );
    try
    {
      byte[] bytes = cs.getBytes();
      if( bytes == null || bytes.length == 0 )
      {
        return false;
      }
      else
      {
        _jo.putNextEntry( je );
        _jo.write( bytes );
      }
    }
    catch( IOException e )
    {
      throw new RuntimeException( e );
    }
    return true;
  }

  private void makeManifest() throws IOException
  {
    JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
    _jo.putNextEntry( je );
    Writer writer = new OutputStreamWriter( _jo );
    writer.write( "Manifest-Version: 1.0\n" +
                  "Main-Class: Launcher\n" );
    writer.flush();
  }

  private void bundleGosu( List progClasspath ) throws IOException
  {
    String[] classpath = System.getProperty( "java.class.path" ).split( File.pathSeparator );
    for( String path : classpath )
    {
      if( path.contains( "gw-asm-all" ) ||
          path.contains( "gosu-core" ) ||
          path.contains( "manifold" ) ||
          path.contains( "tools.jar" ) )
      {
        addClasspathEntry( progClasspath, PathUtil.create( path ) );
      }
    }
  }

  private void AddExperimentFilesAndDependencies( Experiment experiment, List progClasspath ) throws IOException
  {
    String javaHomePath = System.getProperty( "java.home" );

    List srcPaths = experiment.getSourcePath();
    for( String path : srcPaths )
    {
      if( path.startsWith( javaHomePath ) )
      {
        // don't pack jre jars
        continue;
      }

      addClasspathEntry( progClasspath, PathUtil.create( path ) );
    }
  }

  private void createMainPropertiesFile( String strProgramName, boolean bBundleGosu ) throws IOException
  {
    JarEntry je = new JarEntry( "main.properties" );
    _jo.putNextEntry( je );
    Writer writer = new OutputStreamWriter( _jo );
    writer.write( "Program=" + strProgramName + "\n" +
                  (bBundleGosu
                   ? "BundledGosu=true\n"
                   : "") +
                  "Classpath=\n" );
    writer.flush();
  }

  private void addClasspathEntry( List progClasspath, Path csr ) throws IOException
  {
    if( PathUtil.isDirectory( csr ) )
    {
      for( Path fileOrDir: PathUtil.listFiles( PathUtil.getAbsolutePath( csr ) ) )
      {
        addEntry( fileOrDir, "" );
        progClasspath.add( PathUtil.getAbsolutePathName( fileOrDir ) );
      }
    }
    else
    {
      String lowercaseName = PathUtil.getName( csr ).toLowerCase();
      if( lowercaseName.endsWith( ".jar" ) || lowercaseName.endsWith( ".zip" ) )
      {
        addJarEntry( csr );
        //addZipEntry( new ZipFile( csr ) );
      }
    }
  }

  private void addJarEntry( Path jarFile ) throws IOException
  {
    addEntry( jarFile, Gosu.JAR_REPO_DIR );
    _jars.add( PathUtil.getName( jarFile ) );
  }

  private void createJarRepoFile() throws IOException
  {
    JarEntry je;
    je = new JarEntry( Gosu.JAR_REPO_DIR + '/' + Gosu.JAR_REPO_TXT );
    _jo.putNextEntry( je );
    Writer writer = new OutputStreamWriter( _jo );
    writer.write( UUID.randomUUID().toString() + "\n" );
    for( String jar: _jars )
    {
      writer.write( jar + "\n" );
    }
    writer.flush();
  }

  private void addEntry( Path file, String strPath )
  {
    if( PathUtil.isDirectory( file ) )
    {
      String strDir = (strPath.length() > 0 ? strPath + '/' : "") + PathUtil.getName( file );
      for( Path csr: PathUtil.listFiles( file ) )
      {
        addEntry( csr, strDir );
      }
    }
    else if( PathUtil.isFile( file ) )
    {
      if( PathUtil.getName( file ).toLowerCase().equals( "manifest.mf" ) )
      {
        //## todo: maybe merge all manifests into one?  but really the corresponding path should be in a jar, not unjarred files
        return;
      }

      String strFile = (strPath.length() > 0 ? strPath + '/' : "") + PathUtil.getName( file );
      if( _entries.contains( strFile ) )
      {
        return;
      }
      _entries.add( strFile );
      JarEntry je = new JarEntry( strFile );
      try
      {
        _jo.putNextEntry( je );
        InputStream in = PathUtil.createInputStream( file );
        writeBytes( in );
        in.close();
      }
      catch( IOException e )
      {
        throw new RuntimeException( e );
      }
    }
    else
    {
      throw new RuntimeException( file + " is not a file" );
    }
  }

  private void writeBytes( InputStream in ) throws IOException
  {
    byte[] buf = new byte[1024];
    while( true )
    {
      int iCount = in.read( buf, 0, buf.length );
      if( iCount <= 0 )
      {
        break;
      }
      _jo.write( buf, 0, iCount );
    }
  }

//  private void addZipEntry( ZipFile file ) throws IOException
//  {
//    Enumeration en = file.entries();
//    while( en.hasMoreElements() )
//    {
//      ZipEntry entry = en.nextElement();
//      if( !entry.isDirectory() && !entry.getName().toLowerCase().endsWith( "manifest.mf" ) )
//      {
//        String strFile = entry.getName();
//        if( _entries.contains( strFile ) )
//        {
//          return;
//        }
//        _entries.add( strFile );
//        JarEntry je = new JarEntry( strFile );
//        _jo.putNextEntry( je );
//        InputStream in = file.getInputStream( entry );
//        byte[] buf = new byte[1024];
//        while( true )
//        {
//          int iCount = in.read( buf, 0, buf.length );
//          if( iCount <= 0 )
//          {
//            break;
//          }
//          _jo.write( buf, 0, iCount );
//        }
//        in.close();
//      }
//    }
//  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy