package org.dlangplugin.run;
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.GeneralCommandLine;
import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.filters.TextConsoleBuilderImpl;
import com.intellij.execution.process.*;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.CompilerModuleExtension;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import org.dlangplugin.run.exception.ModuleNotFoundException;
import org.dlangplugin.run.exception.NoSourcesException;
import org.dlangplugin.run.exception.NoValidDLangSdkFound;
import org.dlangplugin.sdk.DLangSdkType;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;
public class DLangRunDmdState extends CommandLineState implements ProcessListener {
private DLangRunDmdConfiguration config;
private Executor executor;
protected DLangRunDmdState(@NotNull ExecutionEnvironment environment, @NotNull DLangRunDmdConfiguration config) {
super(environment);
this.config = config;
}
@NotNull
@Override
public ExecutionResult execute(@NotNull Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
TextConsoleBuilder consoleBuilder = new TextConsoleBuilderImpl(config.getProject());
setConsoleBuilder(consoleBuilder);
this.executor = executor;
return super.execute(executor, runner);
}
@NotNull
@Override
protected ProcessHandler startProcess() throws ExecutionException {
try {
GeneralCommandLine dmdCommandLine = getDmdCommandLine(config);
OSProcessHandler handler = new OSProcessHandler(dmdCommandLine.createProcess(), dmdCommandLine.getCommandLineString());
handler.addProcessListener(this);
return handler;
}
catch (NoValidDLangSdkFound e) {
throw new ExecutionException("No valid DMD SDK found!");
}
catch(NoSourcesException e) {
throw new ExecutionException("No D Language source files found in directory: "+e.getSourcesRoot());
}
catch(ModuleNotFoundException e) {
throw new ExecutionException("Run configuration has no module selected.");
} catch (ExecutionException e) {
String message = e.getMessage();
boolean isEmpty = message.equals("Executable is not specified");
boolean notCorrect = message.startsWith("Cannot run program");
if (isEmpty || notCorrect) {
Notifications.Bus.notify(
new Notification("DMD run configuration", "DMD settings",
"DMD executable path is " + (isEmpty ? "empty" : "not specified correctly") +
"<br/><a href='configure'>Configure</a>",
NotificationType.ERROR), config.getProject());
}
throw e;
}
}
/** Build command line:
<code>
dmd @compilerArguments.txt
</code>
* "compilerArguments.txt" is temporary file with DMD compiler arguments separated with '\n'. E.g.
<code>
-release
-unittest
-od{objFilesDir}
-of{outputFilePath}
sourceFile1.d
sourceFile2.d
</code>
*/
private GeneralCommandLine getDmdCommandLine(DLangRunDmdConfiguration config)
throws ModuleNotFoundException, NoValidDLangSdkFound, NoSourcesException, ExecutionException
{
Module module = config.getConfigurationModule().getModule();
if(module == null) {
throw new ModuleNotFoundException();
}
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
Sdk sdk = moduleRootManager.getSdk();
if(sdk==null || !(sdk.getSdkType() instanceof DLangSdkType)) {
throw new NoValidDLangSdkFound();
}
List<String> dmdParameters = DLangDmdConfigToArgsConverter.getDmdParameters(config, module);
GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.withWorkDirectory(config.getProject().getBasePath());
commandLine.setExePath(DLangSdkType.getDmdPath(sdk));
commandLine.addParameter(tempFileWithParameters(dmdParameters));
return commandLine;
}
//Create file with the list of all dmdParameters
private String tempFileWithParameters(List<String> dmdParameters) throws ExecutionException {
String sep = System.lineSeparator();
File tmpFile;
try {
tmpFile = FileUtil.createTempFile("dmd", "src");
OutputStreamWriter output = new OutputStreamWriter(new FileOutputStream(tmpFile), "UTF-8");
for(String srcFilePath : dmdParameters) {
output.write(srcFilePath);
output.write(sep);
}
output.close();
}
catch(IOException exc) {
throw new ExecutionException("Can't create temporary file with arguments", exc);
}
return "@"+tmpFile.getAbsolutePath();
}
/* Implementations of ProcessListener interface methods */
@Override
public void startNotified(ProcessEvent event) {
//skip
}
@Override
public void processTerminated(ProcessEvent event) {
if(event.getExitCode() == 0) {
event.getProcessHandler().notifyTextAvailable("* Compilation successful", ProcessOutputTypes.SYSTEM);
}
else {
event.getProcessHandler().notifyTextAvailable("* Compilation finished with errors", ProcessOutputTypes.SYSTEM);
}
}
@Override
public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
//skip
}
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
//skip
}
}