/* * Copyright (c) [2016] [ <ether.camp> ] * This file is part of the ethereumJ library. * * The ethereumJ library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ethereumJ library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>. */ package org.ethereum.solidity.compiler; import com.google.common.base.Joiner; import org.ethereum.config.SystemProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.*; import java.util.ArrayList; import java.util.List; @Component public class SolidityCompiler { private Solc solc; private static SolidityCompiler INSTANCE; @Autowired public SolidityCompiler(SystemProperties config) { solc = new Solc(config); } public static Result compile(File sourceDirectory, boolean combinedJson, Options... options) throws IOException { return getInstance().compileSrc(sourceDirectory, false, combinedJson, options); } public enum Options { AST("ast"), BIN("bin"), INTERFACE("interface"), ABI("abi"), METADATA("metadata"); private String name; Options(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return name; } } public static class Result { public String errors; public String output; private boolean success = false; public Result(String errors, String output, boolean success) { this.errors = errors; this.output = output; this.success = success; } public boolean isFailed() { return !success; } } private static class ParallelReader extends Thread { private InputStream stream; private StringBuilder content = new StringBuilder(); ParallelReader(InputStream stream) { this.stream = stream; } public String getContent() { return getContent(true); } public synchronized String getContent(boolean waitForComplete) { if (waitForComplete) { while(stream != null) { try { wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } return content.toString(); } public void run() { try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { String line; while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } } catch (IOException ioe) { ioe.printStackTrace(); } finally { synchronized (this) { stream = null; notifyAll(); } } } } public static Result compile(byte[] source, boolean combinedJson, Options... options) throws IOException { return getInstance().compileSrc(source, false, combinedJson, options); } public Result compileSrc(File source, boolean optimize, boolean combinedJson, Options... options) throws IOException { List<String> commandParts = prepareCommandOptions(optimize, combinedJson, options); commandParts.add(source.getAbsolutePath()); ProcessBuilder processBuilder = new ProcessBuilder(commandParts) .directory(solc.getExecutable().getParentFile()); processBuilder.environment().put("LD_LIBRARY_PATH", solc.getExecutable().getParentFile().getCanonicalPath()); Process process = processBuilder.start(); ParallelReader error = new ParallelReader(process.getErrorStream()); ParallelReader output = new ParallelReader(process.getInputStream()); error.start(); output.start(); try { process.waitFor(); } catch (InterruptedException e) { throw new RuntimeException(e); } boolean success = process.exitValue() == 0; return new Result(error.getContent(), output.getContent(), success); } private List<String> prepareCommandOptions(boolean optimize, boolean combinedJson, Options[] options) throws IOException { List<String> commandParts = new ArrayList<>(); commandParts.add(solc.getExecutable().getCanonicalPath()); if (optimize) { commandParts.add("--optimize"); } if (combinedJson) { commandParts.add("--combined-json"); commandParts.add(Joiner.on(',').join(options)); } else { for (Options option : options) { commandParts.add("--" + option.getName()); } } return commandParts; } public Result compileSrc(byte[] source, boolean optimize, boolean combinedJson, Options... options) throws IOException { List<String> commandParts = prepareCommandOptions(optimize, combinedJson, options); ProcessBuilder processBuilder = new ProcessBuilder(commandParts) .directory(solc.getExecutable().getParentFile()); processBuilder.environment().put("LD_LIBRARY_PATH", solc.getExecutable().getParentFile().getCanonicalPath()); Process process = processBuilder.start(); try (BufferedOutputStream stream = new BufferedOutputStream(process.getOutputStream())) { stream.write(source); } ParallelReader error = new ParallelReader(process.getErrorStream()); ParallelReader output = new ParallelReader(process.getInputStream()); error.start(); output.start(); try { process.waitFor(); } catch (InterruptedException e) { throw new RuntimeException(e); } boolean success = process.exitValue() == 0; return new Result(error.getContent(), output.getContent(), success); } public static String runGetVersionOutput() throws IOException { List<String> commandParts = new ArrayList<>(); commandParts.add(getInstance().solc.getExecutable().getCanonicalPath()); commandParts.add("--version"); ProcessBuilder processBuilder = new ProcessBuilder(commandParts) .directory(getInstance().solc.getExecutable().getParentFile()); processBuilder.environment().put("LD_LIBRARY_PATH", getInstance().solc.getExecutable().getParentFile().getCanonicalPath()); Process process = processBuilder.start(); ParallelReader error = new ParallelReader(process.getErrorStream()); ParallelReader output = new ParallelReader(process.getInputStream()); error.start(); output.start(); try { process.waitFor(); } catch (InterruptedException e) { throw new RuntimeException(e); } if (process.exitValue() == 0) { return output.getContent(); } throw new RuntimeException("Problem getting solc version: " + error.getContent()); } public static SolidityCompiler getInstance() { if (INSTANCE == null) { INSTANCE = new SolidityCompiler(SystemProperties.getDefault()); } return INSTANCE; } }