/******************************************************************************* * Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://robocode.sourceforge.net/license/epl-v10.html * * Contributors: * Mathew A. Nelson * - Initial API and implementation * Flemming N. Larsen * - Changed to use the Eclipse Compiler for Java (ECJ) *******************************************************************************/ package net.sf.robocode.ui.editor; import net.sf.robocode.core.ContainerBase; import net.sf.robocode.io.FileUtil; import net.sf.robocode.io.Logger; import net.sf.robocode.manager.IVersionManagerBase; import static net.sf.robocode.io.Logger.logError; import static net.sf.robocode.io.Logger.logMessage; import net.sf.robocode.ui.dialog.ConsoleDialog; import net.sf.robocode.ui.dialog.WindowUtil; import javax.swing.*; import java.io.*; /** * @author Mathew A. Nelson (original) * @author Flemming N. Larsen (contributor) */ public class RobocodeCompilerFactory { private static final String COMPILER_CLASSPATH = "-classpath " + getJavaLib() + File.pathSeparator + "libs" + File.separator + "robocode.jar" + File.pathSeparator + FileUtil.quoteFileName(FileUtil.getRobotsDir().toString()); private CompilerProperties compilerProperties; public RobocodeCompiler createCompiler(RobocodeEditor editor) { compilerProperties = null; if (getCompilerProperties().getCompilerBinary() == null || getCompilerProperties().getCompilerBinary().length() == 0) { if (configureCompiler(editor)) { return new RobocodeCompiler(editor, getCompilerProperties().getCompilerBinary(), getCompilerProperties().getCompilerOptions(), getCompilerProperties().getCompilerClasspath()); } logError("Unable to create compiler."); return null; } return new RobocodeCompiler(editor, getCompilerProperties().getCompilerBinary(), getCompilerProperties().getCompilerOptions(), getCompilerProperties().getCompilerClasspath()); } public CompilerProperties getCompilerProperties() { if (compilerProperties == null) { compilerProperties = new CompilerProperties(); FileInputStream in = null; File file = null; try { file = FileUtil.getCompilerConfigFile(); in = new FileInputStream(file); compilerProperties.load(in); if (compilerProperties.getRobocodeVersion() == null) { logMessage("Setting up new compiler."); compilerProperties.setCompilerBinary(""); } } catch (FileNotFoundException e) { logMessage("Compiler configuration file was not found. A new one will be created."); } catch (IOException e) { logError("Error while reading " + file, e); } finally { if (in != null) { try { in.close(); } catch (IOException ignored) {} } } } return compilerProperties; } private static String getJavaLib() { String javahome = System.getProperty("java.home"); String javalib; if (System.getProperty("os.name").indexOf("Mac") == 0) { javalib = new File(javahome).getParentFile().getPath() + "/Classes/classes.jar"; } else { javalib = javahome + "/lib/rt.jar"; } return FileUtil.quoteFileName(javalib); } public boolean configureCompiler(RobocodeEditor editor) { ConsoleDialog console = new ConsoleDialog(editor, "Setting up compiler", false); console.setSize(500, 400); console.getOkButton().setEnabled(false); console.setText("Please wait while Robocode sets up a compiler for you...\n\n"); WindowUtil.centerShow(editor, console); console.append("Setting up compiler\n"); console.append("Java home is " + System.getProperty("java.home") + "\n\n"); String compilerName = "Java Compiler (javac)"; String compilerBinary = "javac"; String compilerOptions = "-deprecation -g -source 1.5"; boolean javacOK = testCompiler(compilerName, compilerBinary, console); boolean ecjOK = false; if (javacOK) { int rc = JOptionPane.showConfirmDialog(editor, "Robocode has found a working javac (Java Compiler) on this system.\nWould you like to use it?\n" + "(If you click No, Robocode will use the build-in Eclipse Compiler for Java (ECJ))", "Confirm javac", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (rc == JOptionPane.NO_OPTION) { javacOK = false; } } if (!javacOK) { compilerName = "Eclipse Compiler for Java (ECJ)"; compilerBinary = "java -jar compilers/ecj.jar"; ecjOK = testCompiler("Eclipse Compiler for Java (ECJ)", compilerBinary, console); } boolean compilerOK = javacOK || ecjOK; if (compilerOK) { console.append("\nCompiler has been set up successfully.\nClick OK to continue.\n"); } else { final String errorText = "Could not set up a working compiler for Robocode.\n" + "Please consult the console window for errors.\n\n" + "For help with this, please post to Help forum here:\n" + "http://sourceforge.net/projects/robocode/forums/forum/116459"; console.append("\nUnable to set up a working compiler for Robocode.\n"); JOptionPane.showMessageDialog(editor, errorText, "Error", JOptionPane.ERROR_MESSAGE); compilerBinary = ""; compilerOptions = ""; } getCompilerProperties().setRobocodeVersion(ContainerBase.getComponent(IVersionManagerBase.class).getVersion()); getCompilerProperties().setCompilerBinary(compilerBinary); getCompilerProperties().setCompilerOptions(compilerOptions); getCompilerProperties().setCompilerClasspath(COMPILER_CLASSPATH); saveCompilerProperties(); console.scrollToBottom(); console.getOkButton().setEnabled(true); return compilerOK; } public void saveCompilerProperties() { FileOutputStream out = null; try { out = new FileOutputStream(FileUtil.getCompilerConfigFile()); getCompilerProperties().store(out, "Robocode Compiler Properties"); } catch (IOException e) { Logger.logError(e); } finally { FileUtil.cleanupStream(out); } } /** * Tests a compiler by trying to let it compile the CompilerTest.java file. * * @param friendlyName friendly name of the compiler to test. * @param filepath the file path of the compiler. * @param console the console which outputs the result. * @return true if the compiler was found and did compile the test file; false otherwise. */ private static boolean testCompiler(String friendlyName, String filepath, ConsoleDialog console) { console.append("Testing compile with " + friendlyName + "\n"); boolean result = false; try { String cmdAndArgs = filepath + " compilers/CompilerTest.java"; // Must be split command and arguments individually ProcessBuilder pb = new ProcessBuilder(cmdAndArgs.split(" ")); pb.directory(FileUtil.getCwd()); pb.redirectErrorStream(true); // we can use p.getInputStream() Process p = pb.start(); // The waitFor() must done after reading the input and error stream of the process console.processStream(p.getInputStream()); p.waitFor(); result = (p.exitValue() == 0); } catch (IOException e) { logError(e); } catch (InterruptedException e) { // Immediately reasserts the exception by interrupting the caller thread itself Thread.currentThread().interrupt(); } if (result) { console.append(friendlyName + " was found and is working.\n"); } else { console.append(friendlyName + " does not exists or cannot compile.\n"); } return result; } }