Package org.renjin.gnur

Source Code of org.renjin.gnur.GnurSourcesCompiler

package org.renjin.gnur;


import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import org.renjin.gcc.Gcc;
import org.renjin.gcc.GccException;
import org.renjin.gcc.GimpleCompiler;
import org.renjin.gcc.gimple.GimpleCompilationUnit;
import org.renjin.gcc.jimple.RealJimpleType;
import org.renjin.gcc.translate.type.struct.SimpleRecordType;
import org.renjin.gnur.sexp.GnuSEXP;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class GnurSourcesCompiler {

  private String packageName;
  private String className;
  private boolean verbose = true;
  private List<File> sources = Lists.newArrayList();
  private List<File> classPaths = Lists.newArrayList();
  private File jimpleDirectory = new File("target/jimple");
  private File gimpleDirectory = new File("target/gimple");
  private File workDirectory;
  private File outputDirectory = new File("target/classes");
 

  public void setPackageName(String packageName) {
    this.packageName = packageName;
  }
 
  public void setClassName(String className) {
    this.className = className;
  }
  public void setJimpleDirectory(File jimpleOutputDirectory) {
    this.jimpleDirectory = jimpleOutputDirectory;
  }

  public void setGimpleDirectory(File gimpleDirectory) {
    this.gimpleDirectory = gimpleDirectory;
  }

  public void setOutputDirectory(File outputDirectory) {
    this.outputDirectory = outputDirectory;
  }

  public void setVerbose(boolean verbose) {
    this.verbose = verbose;
  }

  public void setWorkDirectory(File workDir) {
    this.workDirectory = workDir;
  }

  public void addSources(File src) {
    if(src.exists() && src.listFiles() != null) {
      for(File file : src.listFiles()) {
        if(file.getName().endsWith(".c") || file.getName().endsWith(".f")) {
          sources.add(file);
        }
      }
    }
  }

  public void addClassPaths(List<File> paths) {
    classPaths.addAll(paths);
  }


  public void compile() throws Exception {

    if(!sources.isEmpty()) {

      workDirectory.mkdirs();
      jimpleDirectory.mkdirs();
      gimpleDirectory.mkdirs();

      if(checkUpToDate()) {
        return;
      }
     
      List<GimpleCompilationUnit> units = Lists.newArrayList();

      Gcc gcc = new Gcc(getWorkDirectory());
      gcc.extractPlugin();
      gcc.addIncludeDirectory(unpackIncludes());
      gcc.setGimpleOutputDir(gimpleDirectory);

      for(File sourceFile : sources) {
        GimpleCompilationUnit unit;
        try {
          unit = gcc.compileToGimple(sourceFile, "-std=gnu99");
        } catch(Exception e) {
          throw new GccException("Error compiling " + sourceFile + " to gimple: " + e.getMessage(), e);
        }

        try {
          units.add(unit);
        } catch(Exception e) {
          throw new RuntimeException("Exception parsing unit output of " + sourceFile, e);
        }
      }
     
      File jimpleOutput = new File("target/jimple");
      jimpleOutput.mkdirs();

      GimpleCompiler compiler = new GimpleCompiler();
      compiler.setJimpleOutputDirectory(jimpleDirectory);
      compiler.setOutputDirectory(outputDirectory);
   
      compiler.setPackageName(packageName);
      compiler.setClassName(className);
      compiler.addSootClassPaths(classPaths);
      compiler.setVerbose(verbose);

      compiler.getMethodTable().addMathLibrary();

      compiler.getMethodTable().addCallTranslator(new RallocTranslator());

      compiler.getMethodTable().addReferenceClass(Class.forName("org.renjin.appl.Appl"));

      Class distributionsClass = Class.forName("org.renjin.stats.internals.Distributions");
      compiler.getMethodTable().addReferenceClass(distributionsClass);
      compiler.getMethodTable().addMethod("Rf_dbeta",distributionsClass,"dbeta");
      compiler.getMethodTable().addMethod("Rf_pbeta",distributionsClass,"pbeta");

      compiler.getMethodTable().addReferenceClass(RenjinCApi.class);
      compiler.getMethodTable().addReferenceClass(Sort.class);

      compiler.provideType("SEXP_T", new SimpleRecordType(new RealJimpleType(GnuSEXP.class)));

      compiler.compile(units);
    }
  }

  private boolean checkUpToDate() {
    if(sourcesLastModified() < classLastModified()) {
      System.out.println(packageName + "." + className + "  is up to date, skipping GNU R sources compilation");
      return true;
    } else {
      return false;
    }
  }

  private long classLastModified() {
    File classFile = new File(outputDirectory.getAbsolutePath() + File.separator +
        packageName.replace('.', File.separatorChar) +
        File.separator + className + ".class");
    System.out.println("class file (" + classFile.getAbsolutePath() + ") last modified on " + new Date(classFile.lastModified()));
    return classFile.lastModified();
  }

  private long sourcesLastModified() {
    long lastModified = 0;
    for(File source: sources) {
      if(source.lastModified() > lastModified) {
        lastModified = source.lastModified();
      }
    }
    System.out.println("sources last modified: " + lastModified);

    return lastModified;
  }

  private File unpackIncludes() throws IOException {
   
    URL url = Resources.getResource("org/renjin/gnur/include/R.h");
    if(url.getProtocol().equals("file")) {
        return new File(url.getFile()).getParentFile();
    } else if(url.getProtocol().equals("jar")) {
     
      // file = file:/C:/Users/Alex/.m2/repository/org/renjin/renjin-gnur-compiler/0.7.0-SNAPSHOT/renjin-gnur-compiler-0.7.0-SNAPSHOT.jar!/org/renjin/gnur/include/R.h
      if(url.getFile().startsWith("file:")) {
         
        int fileStart = url.getFile().indexOf("!");
        String jarPath = url.getFile().substring("file:".length(), fileStart);
        String includePath = url.getFile().substring(fileStart+1+"/".length());
        includePath = includePath.substring(0, includePath.length() - "R.h".length());

        return extractToTemp(jarPath, includePath);
      }
    }
    throw new RuntimeException("Don't know how to unpack resources at "  + url);
  }

  private File extractToTemp(String jarPath, String includePath) throws IOException {
    File tempDir = getWorkDirectory();
    JarFile jar = new JarFile(jarPath);
    Enumeration<JarEntry> entries = jar.entries();
    while(entries.hasMoreElements()) {
      JarEntry entry = entries.nextElement();
      if(entry.getName().startsWith(includePath) && !entry.isDirectory()) {
        File target = new File(tempDir.getAbsolutePath() + File.separator +
            entry.getName().substring(includePath.length()).replace('/', File.separatorChar));
        target.getParentFile().mkdirs();

        //System.err.println("extracting to "  + target);

        InputStream in = jar.getInputStream(entry);
        FileOutputStream out = new FileOutputStream(target);
        ByteStreams.copy(in, out);
        out.close();
      }
    }
    return tempDir;
  }

  private File getWorkDirectory() {
    if(workDirectory == null) {
      workDirectory = Files.createTempDir();
    }
    return workDirectory;
  }

}
TOP

Related Classes of org.renjin.gnur.GnurSourcesCompiler

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.