/* * Copyright (C) 2015 RoboVM AB * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. */ package org.robovm.idea.running; import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionResult; import com.intellij.execution.Executor; import com.intellij.execution.configurations.CommandLineState; import com.intellij.execution.configurations.RunProfileState; import com.intellij.execution.process.*; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.util.ReflectionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.robovm.compiler.AppCompiler; import org.robovm.compiler.config.Config; import org.robovm.compiler.config.OS; import org.robovm.compiler.target.LaunchParameters; import org.robovm.compiler.target.ios.DeviceType; import org.robovm.compiler.target.ios.IOSSimulatorLaunchParameters; import org.robovm.compiler.util.io.Fifos; import org.robovm.compiler.util.io.OpenOnReadFileInputStream; import org.robovm.idea.RoboVmPlugin; import org.robovm.idea.compilation.RoboVmCompileTask; import java.io.*; import java.util.List; public class RoboVmRunProfileState extends CommandLineState { public RoboVmRunProfileState(ExecutionEnvironment environment) { super(environment); } protected ProcessHandler executeRun() throws Throwable { RoboVmRunConfiguration runConfig = (RoboVmRunConfiguration)getEnvironment().getRunnerAndConfigurationSettings().getConfiguration(); Config config = runConfig.getConfig(); AppCompiler compiler = runConfig.getCompiler(); RoboVmPlugin.logInfo(getEnvironment().getProject(), "Launching executable"); String mainTypeName = config.getMainClass(); LaunchParameters launchParameters = config.getTarget().createLaunchParameters(); customizeLaunchParameters(runConfig, config, launchParameters); launchParameters.setArguments(runConfig.getProgramArguments()); // launch plugin may proxy stdout/stderr fifo, which // it then writes to. Need to save the original fifos File stdOutFifo = launchParameters.getStdoutFifo(); File stdErrFifo = launchParameters.getStderrFifo(); PipedInputStream pipedIn = new PipedInputStream(); PipedOutputStream pipedOut = new PipedOutputStream(pipedIn); Process process = compiler.launchAsync(launchParameters, pipedIn); if (stdOutFifo != null || stdErrFifo != null) { InputStream stdoutStream = null; InputStream stderrStream = null; if (launchParameters.getStdoutFifo() != null) { stdoutStream = new OpenOnReadFileInputStream(stdOutFifo); } if (launchParameters.getStderrFifo() != null) { stderrStream = new OpenOnReadFileInputStream(stdErrFifo); } process = new ProcessProxy(process, pipedOut, stdoutStream, stderrStream, compiler); } RoboVmPlugin.logInfo(getEnvironment().getProject(), "Launch done"); final OSProcessHandler processHandler = new ColoredProcessHandler(process, null); ProcessTerminatedListener.attach(processHandler); return processHandler; } protected void customizeLaunchParameters(RoboVmRunConfiguration runConfig, Config config, LaunchParameters launchParameters) throws IOException { launchParameters.setStdoutFifo(Fifos.mkfifo("stdout")); launchParameters.setStderrFifo(Fifos.mkfifo("stderr")); if(config.getOs() != OS.ios) { if(runConfig.getWorkingDir() != null && !runConfig.getWorkingDir().isEmpty()) { launchParameters.setWorkingDirectory(new File(runConfig.getWorkingDir())); } } else { if(launchParameters instanceof IOSSimulatorLaunchParameters) { IOSSimulatorLaunchParameters simParams = (IOSSimulatorLaunchParameters)launchParameters; for(DeviceType type: DeviceType.listDeviceTypes()) { if (type.getDeviceName().equals(runConfig.getSimulatorName())) { simParams.setDeviceType(type); } } } } } @NotNull @Override protected ProcessHandler startProcess() throws ExecutionException { try { if (getEnvironment().getExecutor().getId().equals(RoboVmRunner.RUN_EXECUTOR)) { return executeRun(); } else if (getEnvironment().getExecutor().getId().equals(RoboVmRunner.DEBUG_EXECUTOR)) { return executeRun(); } else { return null; } } catch(Throwable t) { RoboVmPlugin.logErrorThrowable(getEnvironment().getProject(), "Couldn't start application", t, true); return null; } } private static class ProcessProxy extends Process implements SelfKiller { private final Process target; private final OutputStream outputStream; private final InputStream inputStream; private final InputStream errorStream; private final AppCompiler appCompiler; private volatile boolean cleanedUp = false; private int pid; ProcessProxy(Process target, OutputStream outputStream, InputStream inputStream, InputStream errorStream, AppCompiler appCompiler) { this.target = target; this.outputStream = outputStream; this.inputStream = inputStream; this.errorStream = errorStream; this.appCompiler = appCompiler; } public void destroy() { synchronized(this) { if(!cleanedUp) { appCompiler.launchAsyncCleanup(); cleanedUp = true; } } target.destroy(); } public boolean equals(Object obj) { return target.equals(obj); } public int exitValue() { return target.exitValue(); } public InputStream getErrorStream() { if (errorStream != null) { return errorStream; } return target.getErrorStream(); } public InputStream getInputStream() { if (inputStream != null) { return inputStream; } return target.getInputStream(); } public OutputStream getOutputStream() { if (outputStream != null) { return outputStream; } return target.getOutputStream(); } public int hashCode() { return target.hashCode(); } public String toString() { return target.toString(); } public int waitFor() { try { return target.waitFor(); } catch (Throwable t) { synchronized(this) { if(!cleanedUp) { appCompiler.launchAsyncCleanup(); cleanedUp = true; } } throw new RuntimeException(t); } } } }