Package org.renjin.gcc

Source Code of org.renjin.gcc.Gcc$OutputCollector

package org.renjin.gcc;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
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.gimple.CallingConventions;
import org.renjin.gcc.gimple.GimpleCompilationUnit;
import org.renjin.gcc.gimple.GimpleFunction;
import org.renjin.gcc.gimple.GimpleParser;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

public class Gcc {

  private File workingDirectory;
  private File pluginLibrary;

  private List<File> includeDirectories = Lists.newArrayList();

  private static final Logger LOGGER = Logger.getLogger(Gcc.class.getName());

  private boolean debug;
  private File gimpleOutputDir;
 
  public Gcc() {
    workingDirectory = Files.createTempDir();
    gimpleOutputDir = workingDirectory;
  }
 
  public Gcc(File workingDirectory) {
    this.workingDirectory = workingDirectory;
    gimpleOutputDir = workingDirectory;
  }

  public void setPluginLibrary(File pluginLibrary) {
    this.pluginLibrary = pluginLibrary;
  }

  public void setDebug(boolean debug) {
    this.debug = debug;
  }

  public void setGimpleOutputDir(File gimpleOutputDir) {
    this.gimpleOutputDir = gimpleOutputDir;
    this.gimpleOutputDir.mkdirs();
  }

  public File getGimpleOutputDir() {
    return gimpleOutputDir;
  }

  public GimpleCompilationUnit compileToGimple(File source, String... compilerFlags) throws IOException {
   
    checkEnvironment();
   
    List<String> arguments = Lists.newArrayList();
   
    // cross compile to i386 so that our pointers are 32-bits
    // rather than 64-bit. We use arrays to back pointers,
    // and java arrays can only be indexed by 32-bit integers
    if(is64bit()) {
      arguments.add("-m32");
    }

    // for debugging preprocessor output
//    arguments.add("-E");
//    arguments.add("-P");

    arguments.add("-c"); // compile only, do not link
    arguments.add("-S"); // stop at assembly generation
    arguments.addAll(Arrays.asList(compilerFlags));
    // command.add("-O9"); // highest optimization

    arguments.add("-fdump-tree-gimple-verbose-raw-vops");
    arguments.add("-save-temps");

    // Enable our plugin which dumps the Gimple as JSON
    // to standard out


    arguments.add("-fplugin=" + pluginLibrary.getAbsolutePath());

    File gimpleFile = new File(gimpleOutputDir, source.getName() + ".gimple");
    arguments.add("-fplugin-arg-bridge-json-output-file=" +
        gimpleFile.getAbsolutePath());

    for (File includeDir : includeDirectories) {
      arguments.add("-I");
      arguments.add(includeDir.getAbsolutePath());
    }

    arguments.add(source.getAbsolutePath());

    LOGGER.info("Executing " + Joiner.on(" ").join(arguments));

    callGcc(arguments);

    GimpleParser parser = new GimpleParser();
    GimpleCompilationUnit unit = parser.parse(gimpleFile);
    for(GimpleFunction fn: unit.getFunctions()) {
      fn.setCallingConvention(CallingConventions.fromFile(source));
    }
    return unit;
  }

  /**
   * Executes GCC and returns the standard output and error
   * @param arguments
   * @return
   * @throws IOException
   * @throws GccException if the GCC process does not exit successfully
   */
  private String callGcc(List<String> arguments) throws IOException {
    List<String> command = Lists.newArrayList();
    command.add("gcc-4.6");
    command.addAll(arguments);

    Process gcc = new ProcessBuilder().command(command).directory(workingDirectory).redirectErrorStream(true).start();

    OutputCollector outputCollector = new OutputCollector(gcc);
    Thread collectorThread = new Thread(outputCollector);
    collectorThread.start();

    try {
      gcc.waitFor();
      collectorThread.join();
    } catch (InterruptedException e) {
      throw new GccException("Compiler interrupted");
    }

    if (gcc.exitValue() != 0) {

      if(outputCollector.getOutput().contains("error trying to exec 'f951': execvp: No such file or directory")) {
        throw new GccException("Compilation failed: Fortran compiler is missing:\n" + outputCollector.getOutput());
      }

      throw new GccException("Compilation failed:\n" + outputCollector.getOutput());
    }
    return outputCollector.getOutput();
  }

  private void checkEnvironment() {
    if(PlatformUtils.OS == PlatformUtils.OSType.WINDOWS) {
      throw new GccException("Sorry, gcc-bridge does not work on Windows/Cygwin because of problems building \n" +
              "and linking the required gcc plugin. You can still compile on a *NIX platform and use the " +
              "resulting pure-Java class files on any platform.");
    }
  }

  private boolean is64bit() {
    return Strings.nullToEmpty(System.getProperty("os.arch")).contains("64");
  }

  public void addIncludeDirectory(File path) {
    includeDirectories.add(path);
  }
 
  public void extractPlugin() throws IOException {
   
    if(!Strings.isNullOrEmpty(System.getProperty("gcc.bridge.plugin"))) {
      pluginLibrary = new File(System.getProperty("gcc.bridge.plugin"));
      if(pluginLibrary.exists()) {
        System.err.println("Using bridge.so at " + pluginLibrary.getAbsolutePath());
        return;
      } else {
        System.err.println("bridge.so does not exist at " + pluginLibrary.getAbsolutePath());
      }
    }
   
    String libraryName = PlatformUtils.getPortableLibraryName("gcc-bridge");
   
   
    URL pluginResource;
    try {
      pluginResource = Resources.getResource("org/renjin/gcc/" + libraryName);
    } catch(IllegalArgumentException e) {
      throw new GccException("Could not find a bundled version of the gcc plugin for your platform.\n" +
              "(Was expecting: /org/renjin/gcc/" +  libraryName + " on the classpath.)\n" +
              "You will need to build it yourself and specify the path to the binary. ");
    }
   
    pluginLibrary = new File(workingDirectory, "bridge" + PlatformUtils.getExtension());
   
    Resources.asByteSource(pluginResource).copyTo(Files.asByteSink(pluginLibrary));

    pluginLibrary.deleteOnExit();
  }
 
  public void checkVersion() {
    try {
      String versionOutput = callGcc(Arrays.asList("--version"));
      if (!versionOutput.contains("4.6.3")) {
        System.err.println("WARNING: gcc-bridge has been tested against 4.6.3, other versions may not work correctly.");
      }
    } catch (IOException e) {
      throw new GccException("Failed to start GCC: " + e.getMessage() + ".\n" +
              "Make sure gcc 4.6.3 is installed." );
    }
  }

  private static class OutputCollector implements Runnable {

    private Process process;
    private String output;

    private OutputCollector(Process process) {
      this.process = process;
    }

    @Override
    public void run() {
      try {
        output = new String(ByteStreams.toByteArray(process.getInputStream()));
      } catch (IOException e) {

      }
    }

    private String getOutput() {
      return output;
    }
  }

}
TOP

Related Classes of org.renjin.gcc.Gcc$OutputCollector

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.