Java tutorial
/* * The MIT License * * Copyright 2013 Przemyslaw Walkowiak <przemkovv@gmail.com>. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package net.przemkovv.sphinx.compiler; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.przemkovv.sphinx.Utils; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Przemyslaw Walkowiak <przemkovv@gmail.com> */ public final class GXXCompiler implements ICompiler { static Logger logger = LoggerFactory.getLogger(GXXCompiler.class); private final String tmp_dir_prefix; private File compiler_cmd; private File prepare_env_cmd; private String tmp_dir; public GXXCompiler() throws InvalidCompilerException, CompilerNonAvailableException { tmp_dir_prefix = "gxx_"; try { Configuration config = new PropertiesConfiguration( "net/przemkovv/sphinx/compiler/gxx_compiler.properties"); String cmds[] = config.getStringArray("net.przemkovv.sphinx.compiler.gxx.cmd"); String prepare_envs[] = config.getStringArray("net.przemkovv.sphinx.compiler.gxx.prepare_env"); compiler_cmd = null; prepare_env_cmd = null; for (int i = 0; i < cmds.length; i++) { File temp_cmd = new File(cmds[i]); if (temp_cmd.exists() && temp_cmd.canExecute()) { compiler_cmd = temp_cmd; if (!prepare_envs[i].isEmpty()) { prepare_env_cmd = new File(prepare_envs[i]); } break; } } if (compiler_cmd == null) { throw new CompilerNonAvailableException("Compiler G++ is not available."); } CompilerInfo info = getCompilerInfo(); if (info != null) { logger.debug("Found g++ Compiler. Version: {}, Vendor: {}", info.version, info.vendor); } tmp_dir = System.getProperty("java.io.tmpdir"); logger.debug("Temporary directory: {}", tmp_dir); } catch (ConfigurationException ex) { throw new CompilerNonAvailableException("Couldn't find configuration for compiler compiler G++.", ex); } } @Override public String getCompilerPath() { return compiler_cmd.getAbsolutePath(); } private final ExecutorService pool = Executors.newFixedThreadPool(10); private CompilationResult runCompiler(List<File> files, File output_file, List<String> args, File working_dir) throws InvalidCompilerException { // TODO: extract the main functionality of running compiler try { if (output_file == null) { output_file = new File(working_dir, RandomStringUtils.randomAlphanumeric(8) + ".exe"); } ArrayList<String> cmd = new ArrayList<>(); if (prepare_env_cmd != null) { cmd.add(prepare_env_cmd.getAbsolutePath()); cmd.add("&&"); } cmd.add(compiler_cmd.getAbsolutePath()); cmd.add("-o" + output_file.getAbsolutePath()); if (args != null) { cmd.addAll(args); } return compile(cmd, files, output_file); } catch (IOException | InterruptedException ex) { throw new InvalidCompilerException(ex); } catch (ExecutionException ex) { java.util.logging.Logger.getLogger(GXXCompiler.class.getName()).log(Level.SEVERE, null, ex); } return null; } private CompilationResult compile(ArrayList<String> cmd, List<File> files, File output_file) throws IOException, ExecutionException, InterruptedException { if (files != null) { for (File file : files) { if (FilenameUtils.isExtension(file.getName(), "cpp") || FilenameUtils.isExtension(file.getName(), "c")) { cmd.add(file.getAbsolutePath()); } } } logger.debug("Compiler command line: {}", cmd); final Process process = Runtime.getRuntime().exec(cmd.toArray(new String[cmd.size()]), null, output_file.getParentFile()); Future<String> output_error_result = pool.submit(new Callable<String>() { @Override public String call() throws Exception { return Utils.readAllFromInputStream(process.getErrorStream()); } }); Future<String> output_result = pool.submit(new Callable<String>() { @Override public String call() throws Exception { return Utils.readAllFromInputStream(process.getInputStream()); } }); process.waitFor(); CompilationResult result = new CompilationResult(); result.output_errror = output_error_result.get(); result.output = output_result.get(); result.exit_value = process.exitValue(); result.executable_file = output_file; result.prepare_env = prepare_env_cmd; return result; } @Override public CompilerInfo getCompilerInfo() throws InvalidCompilerException { CompilerInfo info = null; List<String> args = new ArrayList<>(); args.add("--version"); CompilationResult result = runCompiler(null, null, args, null); if (result.output == null || result.output.isEmpty()) { throw new InvalidCompilerException("invalid output from compiler."); } String first_line = new Scanner(result.output).nextLine(); Pattern version_pattern = Pattern.compile("^g\\+\\+.* \\((.*)\\) (.*)$"); Matcher m = version_pattern.matcher(first_line); if (m.find()) { info = new CompilerInfo(); info.version = m.group(2); info.vendor = m.group(1); } else { throw new InvalidCompilerException("couldn't find the compiler version."); } return info; } @Override public Boolean checkCompiler() { try { return getCompilerInfo() != null; } catch (InvalidCompilerException ex) { return false; } } @Override public CompilationResult compile(String source) throws InvalidCompilerException, IOException { return compile(source, null); } @Override public CompilationResult compile(String source, String executable_name) throws InvalidCompilerException, IOException { File dir = Utils.createRandomTempDirectory(tmp_dir_prefix, tmp_dir); List<File> source_files = new ArrayList<>(); source_files.add(new File(dir, "main.cpp")); FileUtils.writeStringToFile(source_files.get(0), source); CompilationResult result = compile(source_files, executable_name); return result; } @Override public CompilationResult compile(List<File> source_files) throws InvalidCompilerException, IOException { return compile(source_files, null); } @Override public CompilationResult compile(List<File> source_files, String executable_name) throws InvalidCompilerException, IOException { File working_dir = source_files.get(0).getParentFile(); // TODO: implement in the better way.. File executable_file = null; if (executable_name != null && !executable_name.isEmpty()) { executable_file = new File(working_dir, executable_name); } List<String> args = new ArrayList<>(); CompilationResult result = runCompiler(source_files, executable_file, args, working_dir); logger.debug("Compiler output: {}", result.output); logger.debug("Compiler error output: {}", result.output_errror); return result; } }