package org.rubypeople.rdt.internal.launching; import java.io.File; import java.io.FileWriter; import java.io.IOException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.Launch; import org.eclipse.debug.core.model.IProcess; import org.rubypeople.rdt.core.util.Util; import org.rubypeople.rdt.launching.AbstractVMInstall; import org.rubypeople.rdt.launching.IVMInstallType; import org.rubypeople.rdt.launching.IVMRunner; public class StandardVM extends AbstractVMInstall { private static final String FINISHED_MARKER = "finished.txt"; private static final int FIVE_MINUTES = 5 * 60 * 1000; private static final String VERSION_TXT = "version.txt"; private static final int CORE_STUBS_VERSION = 3; private Job coreStubJob; public StandardVM(IVMInstallType type, String id) { super(type, id); } @Override public IVMRunner getVMRunner(String mode) { if (ILaunchManager.RUN_MODE.equals(mode)) { IVMRunner runner = new StandardVMRunner(); runner.setVMInstall(this); return runner; } else if (ILaunchManager.DEBUG_MODE.equals(mode)) { IVMRunner runner = null; if (useRDebug()) { runner = new RDebugVMDebugger(); } else { runner = new StandardVMDebugger(); } runner.setVMInstall(this); return runner; } else if (ILaunchManager.PROFILE_MODE.equals(mode)) { return getVMRunner(this, mode); } return null; } protected boolean useRDebug() { return LaunchingPlugin.getDefault().getPluginPreferences().getBoolean(PreferenceConstants.USE_RUBY_DEBUG); } public String getRubyVersion() { IVMInstallType installType = getVMInstallType(); File installLocation = getInstallLocation(); if (installLocation != null) { File executable = installType.findExecutable(installLocation); if (executable != null) { String vmVersion = installType.getVMVersion(installLocation, executable); // strip off extra info StringBuffer version = new StringBuffer(); for (int i = 0; i < vmVersion.length(); i++) { char ch = vmVersion.charAt(i); if (Character.isDigit(ch) || ch == '.') { version.append(ch); } else { break; } } if (version.length() > 0) { return version.toString(); } } } return null; } public String getPlatform() { IVMInstallType installType = getVMInstallType(); File installLocation = getInstallLocation(); if (installLocation != null) { File executable = installType.findExecutable(installLocation); if (executable != null) { String platform = installType.getVMPlatform(installLocation, executable); if (platform != null) return platform; } } return "ruby"; } @Override public IPath[] getLibraryLocations() { IPath[] paths = super.getLibraryLocations(); if (paths != null) { // TODO Make sure the core stubs are generated! generateCoreStubs(getVMInstallType().findExecutable(getInstallLocation())); return paths; } return getDefaultLibraryLocations(); } private IPath[] getDefaultLibraryLocations() { IPath[] dflts = getVMInstallType().getDefaultLibraryLocations(getInstallLocation()); IPath coreStubsPath = generateCoreStubs(getVMInstallType().findExecutable(getInstallLocation())); if (coreStubsPath == null) { return dflts; } IPath[] paths = new IPath[dflts.length + 1]; for (int i = 0; i < dflts.length; i++) { paths[i] = dflts[i]; } paths[dflts.length] = coreStubsPath; return paths; } /** * Launch a ruby script to generate core class stubs for use in RDT internally (since Ruby core stuff is not in any * scripts, they're built into the VM in C code). * * @param rubyExecutable * @return an IPath pointing to the directory containing the core library stubs */ private IPath generateCoreStubs(final File rubyExecutable) { if (rubyExecutable == null) return null; // locate the script to generate our core stubs final File coreStubber = LaunchingPlugin .getFileInPlugin(new Path("ruby").append("standard_vm").append("core_stubber.rb")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if (coreStubber == null || !coreStubber.exists()) { LaunchingPlugin.log("Was unable to get path to core stub script (or didn't exist)"); return null; } final IPath stubFolder = LaunchingPlugin.getDefault().getStateLocation().append(getId()).append("lib"); //$NON-NLS-1$ if (stubFolder.toFile().exists() && stubFolder.append(FINISHED_MARKER).toFile().exists()) // finished before { // If version doesn't match current, then wipe the stubs and re-generate int version = getCoreStubsVersion(stubFolder); if (version == CORE_STUBS_VERSION) { return stubFolder; // we've already created the stubs for this VM } // Delete the old stubs delete(stubFolder.toFile()); // Insert new version file writeNewVersion(stubFolder); } stubFolder.toFile().mkdirs(); // Make the directory structure to throw the files into if (coreStubJob != null) { IStatus result = coreStubJob.getResult(); if (result == null) { // currently running. Let's just return... return stubFolder; } // job has finished. let's kill it and re-schedule because something went wrong. coreStubJob.cancel(); } coreStubJob = new Job("Generating core library stubs...") { @Override protected IStatus run(IProgressMonitor monitor) { String rubyExecutablePath = rubyExecutable.getAbsolutePath(); // FIXME JRuby core stub generation is broken in windows!!! rubyExecutablePath = rubyExecutablePath.replace("rubyw", "ruby"); // Use ruby.exe, not rubyw! String[] cmdLine = new String[] { rubyExecutablePath, coreStubber.getAbsolutePath(), stubFolder.toOSString() }; if (monitor.isCanceled()) return Status.CANCEL_STATUS; Process p = null; try { p = Runtime.getRuntime().exec(cmdLine); IProcess process = DebugPlugin.newProcess(new Launch(null, ILaunchManager.RUN_MODE, null), p, "Core Classes Stub Generation"); //$NON-NLS-1$ long start = System.currentTimeMillis(); while (!process.isTerminated()) { Thread.yield(); if (monitor.isCanceled() || (System.currentTimeMillis() > (start + (FIVE_MINUTES)))) { p.destroy(); return Status.CANCEL_STATUS; } } int exitValue = p.exitValue(); if (exitValue == 0) { // Add file which just marks we were able to finish stubFolder.append(FINISHED_MARKER).toFile().createNewFile(); } } catch (IOException ioe) { LaunchingPlugin.log(ioe); } finally { if (p != null) { p.destroy(); } } return Status.OK_STATUS; } }; coreStubJob.schedule(); return stubFolder; } private void writeNewVersion(IPath stubFolder) { stubFolder.toFile().mkdirs(); File versionFile = stubFolder.append(VERSION_TXT).toFile(); FileWriter writer = null; try { versionFile.createNewFile(); writer = new FileWriter(versionFile); writer.write(Integer.toString(CORE_STUBS_VERSION)); } catch (IOException e) { LaunchingPlugin.log(e); } finally { try { if (writer != null) writer.close(); } catch (IOException e) { // ignore } } } private void delete(File file) { if (file.isDirectory()) { File[] children = file.listFiles(); for (int i = 0; i < children.length; i++) { delete(children[i]); } } file.delete(); file.deleteOnExit(); } private int getCoreStubsVersion(IPath stubFolder) { try { File versionFile = stubFolder.append(VERSION_TXT).toFile(); if (!versionFile.exists()) { return 1; } String raw = new String(Util.getFileCharContent(versionFile, null)); return Integer.parseInt(raw); } catch (NumberFormatException e) { LaunchingPlugin.log(e); } catch (IOException e) { LaunchingPlugin.log(e); } return CORE_STUBS_VERSION; } }