mirror of
https://github.com/peterdell/wudsn-ide.git
synced 2024-05-29 05:41:33 +00:00
802 lines
31 KiB
Java
802 lines
31 KiB
Java
/**
|
|
* Copyright (C) 2009 - 2021 <a href="https://www.wudsn.com" target="_top">Peter Dell</a>
|
|
*
|
|
* This file is part of WUDSN IDE.
|
|
*
|
|
* WUDSN IDE 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.
|
|
*
|
|
* WUDSN IDE 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 WUDSN IDE. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package com.wudsn.ide.lng.editor;
|
|
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.Date;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
import org.eclipse.core.resources.IFile;
|
|
import org.eclipse.core.resources.IMarker;
|
|
import org.eclipse.core.resources.IResource;
|
|
import org.eclipse.core.resources.ResourcesPlugin;
|
|
import org.eclipse.core.runtime.CoreException;
|
|
import org.eclipse.debug.core.DebugPlugin;
|
|
import org.eclipse.debug.core.IBreakpointManager;
|
|
import org.eclipse.debug.core.model.IBreakpoint;
|
|
import org.eclipse.swt.program.Program;
|
|
import org.eclipse.ui.IPageLayout;
|
|
import org.eclipse.ui.IViewReference;
|
|
import org.eclipse.ui.IWorkbenchPage;
|
|
import org.eclipse.ui.IWorkbenchPart;
|
|
import org.eclipse.ui.PartInitException;
|
|
import org.eclipse.ui.console.IConsoleConstants;
|
|
import org.eclipse.ui.console.IConsoleView;
|
|
import org.eclipse.ui.ide.IDE;
|
|
|
|
import com.wudsn.ide.base.common.FileUtility;
|
|
import com.wudsn.ide.base.common.HexUtility;
|
|
import com.wudsn.ide.base.common.MarkerUtility;
|
|
import com.wudsn.ide.base.common.NumberUtility;
|
|
import com.wudsn.ide.base.common.ProcessWithLogs;
|
|
import com.wudsn.ide.base.common.StringUtility;
|
|
import com.wudsn.ide.base.hardware.Hardware;
|
|
import com.wudsn.ide.lng.HardwareUtility;
|
|
import com.wudsn.ide.lng.LanguagePlugin;
|
|
import com.wudsn.ide.lng.LanguageUtility;
|
|
import com.wudsn.ide.lng.Texts;
|
|
import com.wudsn.ide.lng.breakpoint.LanguageBreakpoint;
|
|
import com.wudsn.ide.lng.compiler.Compiler;
|
|
import com.wudsn.ide.lng.compiler.CompilerConsole;
|
|
import com.wudsn.ide.lng.compiler.CompilerDefinition;
|
|
import com.wudsn.ide.lng.compiler.CompilerFileWriter;
|
|
import com.wudsn.ide.lng.compiler.CompilerFiles;
|
|
import com.wudsn.ide.lng.compiler.CompilerProcessLogParser;
|
|
import com.wudsn.ide.lng.compiler.CompilerProcessLogParser.Marker;
|
|
import com.wudsn.ide.lng.compiler.CompilerSymbol;
|
|
import com.wudsn.ide.lng.compiler.CompilerVariables;
|
|
import com.wudsn.ide.lng.preferences.CompilerRunPreferences;
|
|
import com.wudsn.ide.lng.runner.Runner;
|
|
import com.wudsn.ide.lng.runner.RunnerDefinition;
|
|
import com.wudsn.ide.lng.runner.RunnerId;
|
|
import com.wudsn.ide.lng.symbol.CompilerSymbolsView;
|
|
|
|
/**
|
|
* Implementation of the "Compile" command.
|
|
*
|
|
* @author Peter Dell
|
|
*/
|
|
final class LanguageEditorCompileCommand {
|
|
|
|
/**
|
|
* Commands.
|
|
*/
|
|
public static final String COMPILE = "com.wudsn.ide.lng.editor.LanguageEditorCompileCommand";
|
|
public static final String COMPILE_AND_RUN = "com.wudsn.ide.lng.editor.LanguageEditorCompileAndRunCommand";
|
|
public static final String COMPILE_AND_RUN_WITH = "com.wudsn.ide.lng.editor.LanguageEditorCompileAndRunWithCommand";
|
|
|
|
/**
|
|
* The owning plugin.
|
|
*/
|
|
private LanguagePlugin plugin;
|
|
|
|
/**
|
|
* Creation is private.
|
|
*/
|
|
private LanguageEditorCompileCommand() {
|
|
plugin = LanguagePlugin.getInstance();
|
|
}
|
|
|
|
/**
|
|
* Creates a message associated with the main source file of an
|
|
* {@link CompilerFiles} instance. The message is not bound to a line number.
|
|
*
|
|
* @param files The {@link CompilerFiles} not <code>null</code>.
|
|
* @param severity The message severity, see {@link IMarker#SEVERITY}
|
|
* @param message The message, may contain parameter "{0}" to "{9}". May be
|
|
* empty, not <code>null</code>.
|
|
* @param parameters The format parameters for the message, may be empty, not
|
|
* <code>null</code>.
|
|
*/
|
|
private void createMainSourceFileMessage(CompilerFiles files, int severity, String message, String... parameters) {
|
|
if (files == null) {
|
|
throw new IllegalArgumentException("Parameter 'files' must not be null.");
|
|
}
|
|
MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, severity, message, parameters);
|
|
}
|
|
|
|
/**
|
|
* Executes a compile command.
|
|
*
|
|
* @param languageEditor The language editor, not <code>null</code>.
|
|
* @param files The compiler files, not <code>null</code>.
|
|
* @param commandId The command id, see {@link #COMPILE},
|
|
* {@link #COMPILE_AND_RUN} ,
|
|
* {@link #COMPILE_AND_RUN_WITH}.
|
|
* @param runnerId The runner id, may be empty or <code>null</code>.
|
|
*
|
|
* @throws RuntimeException
|
|
*/
|
|
public static void execute(LanguageEditor languageEditor, CompilerFiles files, String commandId, String runnerId)
|
|
throws RuntimeException {
|
|
|
|
if (languageEditor == null) {
|
|
throw new IllegalArgumentException("Parameter 'languageEditor' must not be null.");
|
|
}
|
|
if (files == null) {
|
|
throw new IllegalArgumentException("Parameter 'files' must not be null.");
|
|
}
|
|
if (commandId == null) {
|
|
throw new IllegalArgumentException("Parameter 'commandId' must not be null.");
|
|
}
|
|
|
|
// Ensure all current changes in all open editors are saved.
|
|
if (!IDE.saveAllEditors(new IResource[] { ResourcesPlugin.getWorkspace().getRoot() }, false)) {
|
|
return;
|
|
}
|
|
|
|
languageEditor.doSave(null);
|
|
|
|
IWorkbenchPage page = languageEditor.getSite().getPage();
|
|
|
|
LanguageEditorCompileCommand instance;
|
|
instance = new LanguageEditorCompileCommand();
|
|
|
|
try {
|
|
instance.executeInternal(languageEditor, files, commandId, runnerId);
|
|
} catch (RuntimeException ex) {
|
|
throw ex;
|
|
}
|
|
|
|
try {
|
|
// Remember active editor.
|
|
IWorkbenchPart activePart = page.getActivePart();
|
|
|
|
// Show console.
|
|
CompilerConsole compilerConsole = instance.plugin.getCompilerConsole();
|
|
IConsoleView consoleView = (IConsoleView) page.showView(IConsoleConstants.ID_CONSOLE_VIEW);
|
|
compilerConsole.display(consoleView);
|
|
|
|
// Show problems view.
|
|
page.showView(IPageLayout.ID_PROBLEM_VIEW);
|
|
|
|
// Reactivate previously active editor.
|
|
page.activate(activePart);
|
|
|
|
} catch (PartInitException ex) {
|
|
throw new RuntimeException("Cannot show view.", ex);
|
|
}
|
|
}
|
|
|
|
private boolean executeInternal(LanguageEditor languageEditor, CompilerFiles files, String commandId,
|
|
String runnerId) {
|
|
|
|
if (languageEditor == null) {
|
|
throw new IllegalArgumentException("Parameter 'languageEditor' must not be null.");
|
|
}
|
|
if (files == null) {
|
|
throw new IllegalArgumentException("Parameter 'files' must not be null.");
|
|
}
|
|
if (commandId == null) {
|
|
throw new IllegalArgumentException("Parameter 'commandId' must not be null.");
|
|
}
|
|
|
|
LanguageEditorFilesLogic languageEditorFilesLogic = LanguageEditorFilesLogic.createInstance(languageEditor);
|
|
|
|
// Remove existing problem markers from all files.
|
|
if (!languageEditorFilesLogic.removeMarkers(files)) {
|
|
return false;
|
|
}
|
|
|
|
// Determine and check hardware.
|
|
Hardware hardware = languageEditorFilesLogic.getHardware(files);
|
|
if (hardware == null) {
|
|
return false;
|
|
}
|
|
|
|
// Check files based on the compiler definition.
|
|
if (!languageEditorFilesLogic.validateOutputFile(files)) {
|
|
return false;
|
|
}
|
|
|
|
// Create wrapper for run properties.
|
|
CompilerDefinition compilerDefinition = languageEditor.getCompilerDefinition();
|
|
CompilerRunPreferences compilerRunPreferences = new CompilerRunPreferences(
|
|
languageEditor.getLanguageHardwareCompilerPreferences(), files.mainSourceFile.languageProperties);
|
|
|
|
// Check if output file is modifiable in case it already exists.
|
|
long outputFileLastModified = -1;
|
|
if (files.outputFile.exists()) {
|
|
boolean canWrite = files.outputFile.canWrite();
|
|
if (canWrite) {
|
|
try {
|
|
FileOutputStream fos = new FileOutputStream(files.outputFile, true);
|
|
try {
|
|
fos.close();
|
|
} catch (IOException ex) {
|
|
plugin.logError("Cannot close file output stream", null, ex);
|
|
}
|
|
} catch (FileNotFoundException ex) {
|
|
canWrite = false;
|
|
}
|
|
|
|
}
|
|
if (!canWrite) {
|
|
// ERROR: Output file '{0}' cannot be opened for writing. End
|
|
// all applications which may keep the file open.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E106, files.outputFileName);
|
|
return false;
|
|
}
|
|
outputFileLastModified = files.outputFile.lastModified();
|
|
}
|
|
|
|
// Get and check path to compiler executable.
|
|
String compilerPreferencesText = LanguageUtility.getCompilerPreferencesText(compilerDefinition.getLanguage());
|
|
String compilerExecutablePath = languageEditor.getLanguagePreferences()
|
|
.getCompilerExecutablePathOrDefault(compilerDefinition);
|
|
if (StringUtility.isEmpty(compilerExecutablePath)) {
|
|
// ERROR: Path to {0} '{1}' executable is not set in the '{2}' preferences.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E100, compilerDefinition.getText(),
|
|
compilerDefinition.getName(), compilerPreferencesText);
|
|
return false;
|
|
}
|
|
File compilerExecutableFile = new File(compilerExecutablePath);
|
|
if (!compilerExecutableFile.exists()) {
|
|
// ERROR: Path to {0} '{1}' executable in the '{2}' preferences points to
|
|
// non-existing file '{3}'.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E103, compilerDefinition.getText(),
|
|
compilerDefinition.getName(), compilerPreferencesText, compilerExecutablePath);
|
|
return false;
|
|
}
|
|
|
|
// Get and check compiler executable parameters.
|
|
String compilerParameters = compilerRunPreferences.getParameters();
|
|
if (StringUtility.isEmpty(compilerParameters)) {
|
|
compilerParameters = compilerDefinition.getDefaultParameters();
|
|
}
|
|
|
|
// The parameters are first split and then substituted.
|
|
// This allows for parameters and file paths inner spaces to be used.
|
|
// In some case addition quotes must be places around parameters, for
|
|
// example for the "${sourceFilePath}". This can be used to avoid
|
|
// problems with absolute file path under Unix starting with "/" or path
|
|
// containing white spaces.
|
|
compilerParameters = compilerParameters.trim();
|
|
String compilerParameterArray[] = compilerParameters.split(" ");
|
|
if (compilerParameterArray.length == 0) {
|
|
// ERROR: The {0} '{1}' does not specify default parameters.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E101, compilerDefinition.getText(),
|
|
compilerDefinition.getName());
|
|
return false;
|
|
}
|
|
|
|
// From here on, the method is linear, i.e. there is no "return" until
|
|
// the end.
|
|
|
|
// Special handling for direct execution of ".jar" files.
|
|
String[] fullCommandLineArray;
|
|
int offset;
|
|
if (compilerExecutablePath.toLowerCase().endsWith(".jar")) {
|
|
offset = 3;
|
|
fullCommandLineArray = new String[offset + compilerParameterArray.length];
|
|
fullCommandLineArray[0] = "java";
|
|
fullCommandLineArray[1] = "-jar";
|
|
fullCommandLineArray[2] = compilerExecutablePath;
|
|
} else {
|
|
offset = 1;
|
|
fullCommandLineArray = new String[offset + compilerParameterArray.length];
|
|
fullCommandLineArray[0] = compilerExecutablePath;
|
|
}
|
|
|
|
// Map parameter with variables replacement.
|
|
for (int i = 0; i < compilerParameterArray.length; i++) {
|
|
String parameter = compilerParameterArray[i];
|
|
parameter = CompilerVariables.replaceVariables(parameter, files);
|
|
fullCommandLineArray[i + offset] = parameter;
|
|
}
|
|
|
|
ProcessWithLogs compilerProcess = new ProcessWithLogs(fullCommandLineArray, files.mainSourceFile.folder);
|
|
CompilerConsole compilerConsole = plugin.getCompilerConsole();
|
|
compilerConsole.println("");
|
|
compilerConsole.println("Compiling for hardware " + hardware.name() + " on "
|
|
+ new SimpleDateFormat().format(new Date()) + ": " + compilerProcess.getCommandArrayString());
|
|
|
|
try {
|
|
compilerProcess.exec(System.out, System.err, true);
|
|
} catch (IOException ex) {
|
|
// ERROR: Cannot execute {0} process '{1}' in working directory '{2}'. System
|
|
// error: {3}
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E105, compilerDefinition.getText(),
|
|
compilerProcess.getCommandArrayString(), compilerProcess.getWorkingDirectory().getPath(),
|
|
ex.getMessage());
|
|
}
|
|
|
|
// Refresh the output and the symbols file resource.
|
|
if (files.outputFolderPath.equals(files.mainSourceFile.folderPath)
|
|
|| files.outputFolderPath.equals(files.sourceFile.folderPath)) {
|
|
try {
|
|
IResource outputResource = files.mainSourceFile.iFile.getParent();
|
|
if (outputResource != null) {
|
|
outputResource.refreshLocal(IResource.DEPTH_ONE, null);
|
|
}
|
|
outputResource = files.sourceFile.iFile.getParent();
|
|
if (outputResource != null) {
|
|
outputResource.refreshLocal(IResource.DEPTH_ONE, null);
|
|
}
|
|
|
|
} catch (CoreException ex) {
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, ex.getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
compilerConsole.println("");
|
|
compilerConsole.println("Compiler '" + compilerDefinition.getName() + "' output:");
|
|
compilerConsole.println(compilerProcess.getErrorLog());
|
|
compilerConsole.println(compilerProcess.getOutputLog());
|
|
|
|
// Compiling is over, check the result.
|
|
Compiler compiler = languageEditor.getCompiler();
|
|
boolean compilerSuccess = compiler.isSuccessExitValue(compilerProcess.getExitValue());
|
|
if (compilerSuccess) {
|
|
if (files.outputFile.exists()) {
|
|
if (files.outputFile.length() > 0) {
|
|
if (files.outputFile.lastModified() != outputFileLastModified) {
|
|
// INFO: Output file '{0}' created or updated with {1}
|
|
// (${2}) bytes.
|
|
long fileLength = files.outputFile.length();
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_INFO, Texts.MESSAGE_I109,
|
|
files.outputFilePath, Long.toString(fileLength),
|
|
HexUtility.getLongValueHexString(fileLength));
|
|
|
|
// Handle disk images
|
|
CompilerFileWriter compilerFileWriter = HardwareUtility
|
|
.getCompilerFileWriter(compilerRunPreferences.getHardware());
|
|
if (!compilerFileWriter.createOrUpdateDiskImage(files)) {
|
|
return false;
|
|
}
|
|
|
|
if (commandId.equals(COMPILE_AND_RUN) || commandId.equals(COMPILE_AND_RUN_WITH)) {
|
|
|
|
openOutputFile(languageEditor, files, compilerRunPreferences, compilerConsole, runnerId);
|
|
|
|
}
|
|
} else {
|
|
// ERROR: Output file not updated. Check the error
|
|
// messages and the console log.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E108);
|
|
|
|
}
|
|
} else {
|
|
// ERROR: Output file created but empty. Check the error
|
|
// messages and the console log.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E126);
|
|
}
|
|
} else {
|
|
// ERROR: No output file created. Check the error messages and
|
|
// the
|
|
// console log.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E107);
|
|
}
|
|
}
|
|
|
|
// Output an additional message if the reason for the compiler's exit
|
|
// value is not already contained in the error messages.
|
|
boolean errorFound = parseLogs(languageEditor, files, compilerProcess);
|
|
if (!compilerSuccess && !errorFound) {
|
|
// ERROR: {0} process ended with return code {1}. Check the error messages and
|
|
// the console log.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E127, compilerDefinition.getText(),
|
|
NumberUtility.getLongValueDecimalString(compilerProcess.getExitValue()));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Creates or deletes the breakpoints file based on the runner.
|
|
*
|
|
* @param languageEditor The language editor, not <code>null</code>.
|
|
* @param files The compiler files, not <code>null</code>.
|
|
* @param runner The runner, not <code>null</code>.
|
|
*
|
|
* @since 1.6.1
|
|
*/
|
|
private void createBreakpointsFile(LanguageEditor languageEditor, CompilerFiles files, Runner runner) {
|
|
if (languageEditor == null) {
|
|
throw new IllegalArgumentException("Parameter 'languageEditor' must not be null.");
|
|
}
|
|
if (files == null) {
|
|
throw new IllegalArgumentException("Parameter 'files' must not be null.");
|
|
}
|
|
if (runner == null) {
|
|
throw new IllegalArgumentException("Parameter 'runner' must not be null.");
|
|
}
|
|
File breakpointsFile = runner.createBreakpointsFile(files);
|
|
IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
|
|
IBreakpoint breakpoints[];
|
|
if (breakpointManager.isEnabled()) {
|
|
breakpoints = breakpointManager.getBreakpoints(LanguageBreakpoint.DEBUG_MODEL_ID);
|
|
} else {
|
|
breakpoints = new IBreakpoint[0];
|
|
}
|
|
if (breakpointsFile == null) {
|
|
if (breakpoints.length > 0) {
|
|
// WARNING: Breakpoints will be ignored because the application
|
|
// '{0}' does not support passing source level breakpoints.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_WARNING, Texts.MESSAGE_W120,
|
|
new String[] { runner.getDefinition().getName() });
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If breakpoints are present, a breakpoints file is generated.
|
|
if (breakpoints.length >= 0) {
|
|
LanguageBreakpoint[] languageBreakpoints = new LanguageBreakpoint[breakpoints.length];
|
|
System.arraycopy(breakpoints, 0, languageBreakpoints, 0, breakpoints.length);
|
|
StringBuilder breakpointBuilder = new StringBuilder();
|
|
int activeBreakpointCount = runner.createBreakpointsFileContent(languageBreakpoints, breakpointBuilder);
|
|
try {
|
|
FileUtility.writeString(breakpointsFile, breakpointBuilder.toString());
|
|
} catch (CoreException ex) {
|
|
// ERROR: Cannot open breakpoints file '{0}' for output. System
|
|
// error: {1}
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E122,
|
|
new String[] { breakpointsFile.getPath(), ex.getMessage() });
|
|
return;
|
|
}
|
|
// INFO: Breakpoints file '{0}' created with {1} active breakpoints.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_INFO, Texts.MESSAGE_I121, new String[] {
|
|
breakpointsFile.getPath(), NumberUtility.getLongValueDecimalString(activeBreakpointCount) });
|
|
|
|
} else {
|
|
// If no breakpoints are present, the breakpoints file is deleted.
|
|
if (breakpointsFile.exists()) {
|
|
if (!breakpointsFile.delete()) {
|
|
// ERROR: Cannot delete empty breakpoints file '{0}'.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E123,
|
|
new String[] { breakpointsFile.getPath() });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void openOutputFile(LanguageEditor languageEditor, CompilerFiles files,
|
|
CompilerRunPreferences compilerRunPreferences, CompilerConsole compilerConsole, String runnerId) {
|
|
|
|
if (languageEditor == null) {
|
|
throw new IllegalArgumentException("Parameter 'languageEditor' must not be null.");
|
|
}
|
|
if (files == null) {
|
|
throw new IllegalArgumentException("Parameter 'files' must not be null.");
|
|
}
|
|
if (compilerRunPreferences == null) {
|
|
throw new IllegalArgumentException("Parameter 'compilerRunPreferences' must not be null.");
|
|
}
|
|
if (compilerConsole == null) {
|
|
throw new IllegalArgumentException("Parameter 'compilerConsole' must not be null.");
|
|
}
|
|
String[] fullCommandLineArray;
|
|
ProcessWithLogs runnerProcess;
|
|
|
|
fullCommandLineArray = null;
|
|
|
|
// If the runner id was no specified explicitly, the default from the
|
|
// preferences is used.
|
|
if (runnerId == null || StringUtility.isEmpty(runnerId)) {
|
|
runnerId = compilerRunPreferences.getRunnerId();
|
|
}
|
|
Hardware hardware = compilerRunPreferences.getHardware();
|
|
Runner runner;
|
|
runner = plugin.getRunnerRegistry().getRunner(hardware, runnerId);
|
|
if (runner == null) {
|
|
// ERROR: Definition for application '{0}' from the preferences of
|
|
// hardware '{1}' is not registered.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E116, runnerId,
|
|
hardware.toString());
|
|
return;
|
|
}
|
|
|
|
createBreakpointsFile(languageEditor, files, runner);
|
|
|
|
String runnerCommandLine;
|
|
runnerCommandLine = compilerRunPreferences.getRunnerCommandLine(runnerId);
|
|
if (StringUtility.isEmpty(runnerCommandLine)) {
|
|
runnerCommandLine = runner.getDefinition().getDefaultCommandLine();
|
|
}
|
|
runnerCommandLine = runnerCommandLine.trim();
|
|
|
|
// The parameters are first split and then substituted.
|
|
// This allows for parameters and file paths inner spaces to
|
|
// be used. In some case addition quotes must be places around
|
|
// parameters, for example for the "${outputFilePath}" for
|
|
// MADS. Otherwise using absolute file path under Unix starting
|
|
// "/" may cause conflicts.
|
|
String[] commandLineArray;
|
|
commandLineArray = runnerCommandLine.split(" ");
|
|
|
|
// Execution type: DEFAULT_APPLICATION
|
|
if (runnerId.equals(RunnerId.DEFAULT_APPLICATION)) {
|
|
String extension = files.outputFileName;
|
|
int index = extension.lastIndexOf('.');
|
|
if (index > 0) {
|
|
extension = extension.substring(index);
|
|
}
|
|
Program program = Program.findProgram(extension);
|
|
if (program == null) {
|
|
// ERROR: Cannot open output file '{0}' with the
|
|
// standard application since no application is
|
|
// registered for the file extension '{1}'.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E115, files.outputFilePath,
|
|
extension);
|
|
} else {
|
|
|
|
if (Program.launch(files.outputFilePath)) {
|
|
// INFO: Opening output file '{0}' with
|
|
// application
|
|
// '{1}'.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_INFO, Texts.MESSAGE_I118, files.outputFilePath,
|
|
program.getName());
|
|
|
|
compilerConsole.println("Running '" + runner.getDefinition().getName() + "': " + program.getName()
|
|
+ " " + files.outputFilePath);
|
|
} else {
|
|
// ERROR: Cannot open output file '{0}' with
|
|
// application '{1}'.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E119, files.outputFilePath,
|
|
program.getName());
|
|
}
|
|
}
|
|
}
|
|
// Execution type: pre-defined or USER_DEFINED_APPLICATION
|
|
else {
|
|
boolean error = false;
|
|
|
|
String runnerExecutablePath = compilerRunPreferences.getRunnerExecutablePath(runnerId);
|
|
if (runnerCommandLine.contains(RunnerDefinition.RUNNER_EXECUTABLE_PATH)) {
|
|
if (StringUtility.isEmpty(runnerExecutablePath)) {
|
|
// ERROR: Path to application executable is not
|
|
// set in the preferences of application '{0}'.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E112,
|
|
runner.getDefinition().getName());
|
|
error = true;
|
|
} else {
|
|
File runnerExecutableFile = new File(runnerExecutablePath);
|
|
if (!runnerExecutableFile.exists()) {
|
|
// ERROR: Path to '{0}' application
|
|
// executable in the preferences points to
|
|
// non-existing file '{1}'.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E114,
|
|
runner.getDefinition().getName(), runnerExecutablePath);
|
|
error = true;
|
|
}
|
|
}
|
|
}
|
|
if (!error) {
|
|
|
|
fullCommandLineArray = new String[commandLineArray.length];
|
|
for (int i = 0; i < commandLineArray.length; i++) {
|
|
String parameter = commandLineArray[i];
|
|
parameter = CompilerVariables.replaceVariables(parameter, files);
|
|
parameter = replaceRunnerParameters(parameter, runnerExecutablePath);
|
|
parameter = parameter.replace(RunnerDefinition.OUTPUT_FILE_PATH, files.outputFilePath);
|
|
fullCommandLineArray[i] = parameter;
|
|
}
|
|
|
|
// INFO: Opening output file '{0}' with application '{1}'.
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_INFO, Texts.MESSAGE_I118, files.outputFilePath,
|
|
runner.getDefinition().getName());
|
|
|
|
runnerProcess = new ProcessWithLogs(fullCommandLineArray, files.outputFolder);
|
|
|
|
compilerConsole.println(
|
|
"Running '" + runner.getDefinition().getName() + "': " + runnerProcess.getCommandArrayString());
|
|
|
|
try {
|
|
boolean wait = compilerRunPreferences.isRunnerWaitForCompletion(runnerId);
|
|
runnerProcess.exec(compilerConsole.getPrintStream(), compilerConsole.getPrintStream(), wait);
|
|
compilerConsole
|
|
.println("Application returned with exit code " + runnerProcess.getExitValue() + ".");
|
|
} catch (IOException ex) {
|
|
// ERROR: Cannot execute application '{0}' process '{1}' in
|
|
// working directory '{2}'. System error: {3}
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E113,
|
|
runner.getDefinition().getName(), runnerProcess.getCommandArrayString(),
|
|
runnerProcess.getWorkingDirectory().getPath(), ex.getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private String replaceRunnerParameters(String parameter, String runnerExecutablePath) {
|
|
if (parameter == null) {
|
|
throw new IllegalArgumentException("Parameter 'parameter' must not be null.");
|
|
}
|
|
if (runnerExecutablePath == null) {
|
|
throw new IllegalArgumentException("Parameter 'runnerExecutablePath' must not be null.");
|
|
}
|
|
parameter = parameter.replace(RunnerDefinition.RUNNER_EXECUTABLE_PATH, runnerExecutablePath);
|
|
return parameter;
|
|
}
|
|
|
|
private boolean parseLogs(LanguageEditor languageEditor, CompilerFiles files, ProcessWithLogs compileProcess) {
|
|
|
|
if (languageEditor == null) {
|
|
throw new IllegalArgumentException("Parameter 'languageEditor' must not be null.");
|
|
}
|
|
if (files == null) {
|
|
throw new IllegalArgumentException("Parameter 'files' must not be null.");
|
|
}
|
|
if (compileProcess == null) {
|
|
throw new IllegalArgumentException("Parameter 'compileProcess' must not be null.");
|
|
}
|
|
Compiler compiler = languageEditor.getCompiler();
|
|
CompilerProcessLogParser logParser = compiler.createLogParser();
|
|
|
|
// Line parser with main source file and logs.
|
|
logParser.setLogs(files, compileProcess.getOutputLog(), compileProcess.getErrorLog());
|
|
|
|
Set<Marker> set = new HashSet<Marker>();
|
|
List<IMarker> markers = new ArrayList<IMarker>();
|
|
while (logParser.nextMarker()) {
|
|
Marker markerProxy = logParser.getMarker();
|
|
while (markerProxy != null) { // Loop to add main marker and its
|
|
// detail markers
|
|
if (!set.contains(markerProxy)) {
|
|
set.add(markerProxy);
|
|
try {
|
|
IFile iFile = markerProxy.getIFile();
|
|
IMarker marker = iFile.createMarker(IMarker.PROBLEM);
|
|
marker.setAttribute(IMarker.SEVERITY, markerProxy.getSeverity());
|
|
marker.setAttribute(IMarker.MESSAGE, markerProxy.getMessage());
|
|
int lineNumber = markerProxy.getLineNumber();
|
|
if (lineNumber > 0) {
|
|
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
|
|
}
|
|
marker.setAttribute(IMarker.TRANSIENT, true);
|
|
markers.add(marker);
|
|
} catch (CoreException ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
}
|
|
markerProxy = markerProxy.getDetailMarker();
|
|
}
|
|
}
|
|
boolean errorOccurred = positionToFirstErrorOrWarning(languageEditor, markers);
|
|
|
|
parseCompilerSymbols(languageEditor, files, logParser);
|
|
|
|
return errorOccurred;
|
|
}
|
|
|
|
/**
|
|
* Positions to the first error or warning in any file for which markers have
|
|
* been created.
|
|
*
|
|
* @param languageEditor The language editor, not <code>null</code>. Used as
|
|
* basis for opening another editor when required.
|
|
* @param markers The modifiable list of marker, may be empty, not
|
|
* <code>null</code>.
|
|
* @return <code>true</code> if an error was found.
|
|
*/
|
|
private boolean positionToFirstErrorOrWarning(LanguageEditor languageEditor, List<IMarker> markers) {
|
|
|
|
if (languageEditor == null) {
|
|
throw new IllegalArgumentException("Parameter 'languageEditor' must not be null.");
|
|
}
|
|
if (markers == null) {
|
|
throw new IllegalArgumentException("Parameter 'markers' must not be null.");
|
|
}
|
|
|
|
Collections.sort(markers, new Comparator<IMarker>() {
|
|
|
|
@Override
|
|
public int compare(IMarker o1, IMarker o2) {
|
|
int result = o1.getResource().getFullPath().toString()
|
|
.compareTo(o2.getResource().getFullPath().toString());
|
|
if (result == 0) {
|
|
result = o1.getAttribute(IMarker.LINE_NUMBER, 0) - o2.getAttribute(IMarker.LINE_NUMBER, 0);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
});
|
|
|
|
String positioningMode = languageEditor.getLanguagePreferences().getEditorCompileCommandPositioningMode();
|
|
boolean ignoreWarnings = positioningMode.equals(LanguageEditorCompileCommandPositioningMode.FIRST_ERROR);
|
|
IMarker firstWarningMarker = null;
|
|
IMarker firstErrorMarker = null;
|
|
for (IMarker marker : markers) {
|
|
|
|
switch (marker.getAttribute(IMarker.SEVERITY, 0)) {
|
|
case IMarker.SEVERITY_WARNING:
|
|
if (!ignoreWarnings) {
|
|
|
|
if (firstWarningMarker == null) {
|
|
firstWarningMarker = marker;
|
|
}
|
|
|
|
}
|
|
break;
|
|
case IMarker.SEVERITY_ERROR:
|
|
if (firstErrorMarker == null) {
|
|
firstErrorMarker = marker;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
IMarker firstMarker = firstErrorMarker;
|
|
if (firstMarker == null) {
|
|
firstMarker = firstWarningMarker;
|
|
}
|
|
|
|
if (firstMarker != null) {
|
|
MarkerUtility.gotoMarker(languageEditor, firstMarker);
|
|
}
|
|
return firstErrorMarker != null;
|
|
}
|
|
|
|
private void parseCompilerSymbols(LanguageEditor languageEditor, CompilerFiles files,
|
|
CompilerProcessLogParser logParser) {
|
|
|
|
if (languageEditor == null)
|
|
throw new IllegalArgumentException("Parameter 'languageEditor' must not be null.");
|
|
if (files == null)
|
|
throw new IllegalArgumentException("Parameter 'files' must not be null.");
|
|
if (logParser == null)
|
|
throw new IllegalArgumentException("Parameter 'logParser' must not be null.");
|
|
|
|
List<CompilerSymbol> compilerSymbols;
|
|
|
|
compilerSymbols = new ArrayList<CompilerSymbol>();
|
|
try {
|
|
logParser.addCompilerSymbols(compilerSymbols);
|
|
} catch (RuntimeException ex) {
|
|
String message = ex.getMessage();
|
|
if (message == null) {
|
|
message = ex.getClass().getName();
|
|
}
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, message);
|
|
LanguagePlugin.getInstance().logError("Error in addCompilerSymbols()", null, ex);
|
|
|
|
} catch (CoreException ex) {
|
|
String message = ex.getMessage();
|
|
if (message == null) {
|
|
message = ex.getClass().getName();
|
|
}
|
|
createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, message);
|
|
|
|
}
|
|
|
|
// Display symbols.
|
|
IViewReference[] references = languageEditor.getSite().getPage().getViewReferences();
|
|
for (IViewReference reference : references) {
|
|
if (reference.getId().equals(CompilerSymbolsView.ID)) {
|
|
CompilerSymbolsView compilerSymbolsView = (CompilerSymbolsView) reference.getView(true);
|
|
if (compilerSymbolsView != null) {
|
|
compilerSymbolsView.setSymbols(files, compilerSymbols);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|