/*
* 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;
}
}