new Run implementation - no DUB

This commit is contained in:
Vadim Lopatin 2015-12-09 12:18:32 +03:00
parent cfe4315c8f
commit 02d7d13379
8 changed files with 278 additions and 4 deletions

View File

@ -218,6 +218,8 @@
<Compile Include="src\dlangide\workspace\workspace.d" />
<Compile Include="src\ddebug\common\debugger.d" />
<Compile Include="src\ddebug\common\queue.d" />
<Compile Include="src\ddebug\common\execution.d" />
<Compile Include="src\ddebug\common\nodebug.d" />
<Compile Include="src\ddebug\gdb\gdbinterface.d" />
</ItemGroup>
<ItemGroup>

View File

@ -128,6 +128,8 @@
<Compile Include="src\dlangide\builders\extprocess.d" />
<Compile Include="src\ddebug\common\debugger.d" />
<Compile Include="src\ddebug\common\queue.d" />
<Compile Include="src\ddebug\common\execution.d" />
<Compile Include="src\ddebug\common\nodebug.d" />
<Compile Include="src\ddebug\gdb\gdbinterface.d" />
<Compile Include="src\ddebug\windows\debuginfo.d" />
<Compile Include="src\ddebug\windows\mago.d" />

View File

@ -419,6 +419,15 @@
</Folder>
</Folder>
<Folder name="ddebug">
<Folder name="common">
<File path="src\ddebug\common\debugger.d" />
<File path="src\ddebug\common\execution.d" />
<File path="src\ddebug\common\nodebug.d" />
<File path="src\ddebug\common\queue.d" />
</Folder>
<Folder name="gdbinterface">
<File path="src\ddebug\gdb\gdbinterface.d" />
</Folder>
<Folder name="windows">
<File path="src\ddebug\windows\debuginfo.d" />
<File path="src\ddebug\windows\mago.d" />

View File

@ -0,0 +1,31 @@
/**
Support for running stopping of project executable.
*/
module ddebug.common.execution;
enum ExecutionStatus {
NotStarted,
Running,
Finished, // finished normally
Killed, // killed
Error // error while trying to start program
}
interface ProgramExecutionStatusListener {
/// called when program execution is stopped
void onProgramExecutionStatus(ProgramExecution process, ExecutionStatus status, int exitCode);
}
interface ProgramExecution {
/// returns true if it's debugger
@property bool isDebugger();
/// executable file
@property string executableFile();
/// returns execution status
@property ExecutionStatus status();
/// start execution
bool run();
/// stop execution
bool stop();
}

146
src/ddebug/common/nodebug.d Normal file
View File

@ -0,0 +1,146 @@
module ddebug.common.nodebug;
import ddebug.common.execution;
import core.thread;
import std.process;
import dlangui.core.logger;
class ProgramExecutionNoDebug : Thread, ProgramExecution {
// parameters
protected string _executableFile;
protected string[] _args;
protected string _workDir;
protected string _externalConsole;
protected ProgramExecutionStatusListener _listener;
// status
protected Pid _pid;
protected ExecutionStatus _status = ExecutionStatus.NotStarted;
protected int _exitCode = 0;
/// initialize but do not run
this(string executable, string[] args, string workDir, string externalConsole, ProgramExecutionStatusListener listener) {
super(&threadFunc);
_executableFile = executable;
_args = args;
_workDir = workDir;
_externalConsole = externalConsole;
_listener = listener;
assert(_listener !is null);
}
~this() {
stop();
}
private bool isProcessActive() {
if (_pid is null)
return false;
auto res = tryWait(_pid);
if (res.terminated) {
Log.d("Process ", _executableFile, " is stopped");
_exitCode = wait(_pid);
_pid = Pid.init;
return false;
} else {
return true;
}
}
private void killProcess() {
if (_pid is null)
return;
try {
Log.d("Trying to kill process", _executableFile);
kill(_pid, 9);
Log.d("Waiting for process termination");
_exitCode = wait(_pid);
_pid = Pid.init;
Log.d("Killed");
} catch (Exception e) {
Log.d("Exception while killing process " ~ _executableFile, e);
_pid = Pid.init;
}
}
private void threadFunc() {
import std.stdio;
string[] params;
params ~= _executableFile;
params ~= _args;
File newstdin;
File newstdout;
File newstderr;
try {
_pid = spawnProcess(params, newstdin, newstdout, newstderr, null, Config.none, _workDir);
} catch (Exception e) {
Log.e("ProgramExecutionNoDebug: Failed to spawn process: ", e);
killProcess();
_status = ExecutionStatus.Error;
}
if (_status != ExecutionStatus.Error) {
// thread loop: poll process status
while (!_stopRequested) {
Thread.sleep(dur!"msecs"(50));
if (!isProcessActive()) {
_status = ExecutionStatus.Finished;
break;
}
}
if (_stopRequested) {
killProcess();
_status = ExecutionStatus.Killed;
}
}
// finished
_listener.onProgramExecutionStatus(this, _status, _exitCode);
}
// implement ProgramExecution interface
/// returns true if it's debugger
@property bool isDebugger() { return false; }
/// executable file
@property string executableFile() { return _executableFile; }
/// returns execution status
@property ExecutionStatus status() { return _status; }
/// start execution
bool run() {
if (_runRequested)
return false; // already running
_runRequested = true;
_threadStarted = true;
_status = ExecutionStatus.Running;
start();
return true;
}
/// stop execution (call from GUI thread)
bool stop() {
if (!_runRequested)
return false;
if (_stopRequested)
return true;
_stopRequested = true;
if (_threadStarted && !_threadJoined) {
_threadJoined = true;
join();
}
return true;
}
protected bool _threadStarted;
protected bool _threadJoined;
protected bool _stopRequested;
protected bool _runRequested;
}

View File

@ -31,6 +31,9 @@ import dlangide.workspace.project;
import dlangide.builders.builder;
import dlangide.tools.editorTool;
import ddebug.common.execution;
import ddebug.common.nodebug;
import std.conv;
import std.utf;
import std.algorithm;
@ -61,7 +64,7 @@ class BackgroundOperationWatcherTest : BackgroundOperationWatcher {
}
/// DIDE app frame
class IDEFrame : AppFrame {
class IDEFrame : AppFrame, ProgramExecutionStatusListener {
private ToolBarComboBox projectConfigurationCombo;
@ -72,6 +75,7 @@ class IDEFrame : AppFrame {
TabWidget _tabs;
DCDServer _dcdServer;
IDESettings _settings;
ProgramExecution _execution;
dstring frameWindowCaptionSuffix = "DLangIDE"d;
@ -84,6 +88,65 @@ class IDEFrame : AppFrame {
applySettings(_settings);
}
/// stop current program execution
void stopExecution() {
if (_execution) {
_execution.stop();
destroy(_execution);
_execution = null;
}
}
/// called when program execution is stopped
protected void onProgramExecutionStatus(ProgramExecution process, ExecutionStatus status, int exitCode) {
executeInUiThread(delegate() {
Log.d("onProgramExecutionStatus process: ", process.executableFile, " status: ", status, " exitCode: ", exitCode);
_execution = null;
// TODO: update state
switch(status) {
case ExecutionStatus.Error:
_logPanel.logLine("Cannot run program " ~ process.executableFile);
break;
case ExecutionStatus.Finished:
_logPanel.logLine("Program " ~ process.executableFile ~ " finished with exit code " ~ to!string(exitCode));
break;
case ExecutionStatus.Killed:
_logPanel.logLine("Program " ~ process.executableFile ~ " is killed");
break;
default:
_logPanel.logLine("Program " ~ process.executableFile ~ " is finished");
break;
}
});
}
protected void runProject() {
import std.file;
stopExecution();
if (!currentWorkspace)
return;
Project project = currentWorkspace.startupProject;
if (!project) {
window.showMessageBox(UIString("Cannot run project"d), UIString("Startup project is not specified"d));
return;
}
// build project
// TODO
string executableFileName = project.executableFileName;
if (!executableFileName || !exists(executableFileName) || !isFile(executableFileName)) {
window.showMessageBox(UIString("Cannot run project"d), UIString("Cannot find executable"d));
return;
}
string[] args;
string externalConsoleExecutable = null; // TODO
string workingDirectory = null; // TODO
// TODO: provide thread safe listener
_logPanel.logLine("Starting " ~ executableFileName);
_execution = new ProgramExecutionNoDebug(executableFileName, args, workingDirectory, externalConsoleExecutable, this);
_execution.run();
// TODO: update status
}
override protected void init() {
_appName = "dlangide";
//_editorTool = new DEditorTool(this);
@ -586,7 +649,8 @@ class IDEFrame : AppFrame {
case IDEActions.DebugStart:
case IDEActions.DebugStartNoDebug:
case IDEActions.DebugContinue:
buildProject(BuildOperation.Run);
runProject();
//buildProject(BuildOperation.Run);
return true;
case IDEActions.UpdateProjectDependencies:
buildProject(BuildOperation.Upgrade);
@ -980,6 +1044,7 @@ class IDEFrame : AppFrame {
/// called when main window is closing
void onWindowClose() {
Log.i("onWindowClose()");
stopExecution();
if (_dcdServer) {
if (_dcdServer.isRunning)
_dcdServer.stop();

View File

@ -438,6 +438,25 @@ class Project : WorkspaceItem {
return buildNormalizedPath(_filename.dirName, toUTF8(name) ~ WORKSPACE_EXTENSION);
}
@property bool isExecutable() {
// TODO: use targetType
return true;
}
/// return executable file name, or null if it's library project or executable is not found
@property string executableFileName() {
if (!isExecutable)
return null;
string exename = toUTF8(name);
// TODO: use targetName
version (Windows) {
exename = exename ~ ".exe";
}
// TODO: use targetPath
string exePath = buildNormalizedPath(_filename.dirName, "bin", exename);
return exePath;
}
ProjectFolder findItems(string[] srcPaths) {
ProjectFolder folder = new ProjectFolder(_filename);
folder.project = this;

View File

@ -33,7 +33,7 @@ Widget createAboutWidget()
res.addChild(new TextWidget(null, "(C) Vadim Lopatin, 2014"d));
res.addChild(new TextWidget(null, "http://github.com/buggins/dlangui"d));
Button closeButton = new Button("close", "Close"d);
closeButton.onClickListener = delegate(Widget src) {
closeButton.click = delegate(Widget src) {
Log.i("Closing window");
res.window.close();
return true;
@ -521,7 +521,7 @@ class StatusWidget : VerticalLayout {
ImageWidget image = new ImageWidget(null, "tetris_logo_big");
image.layoutWidth(FILL_PARENT).alignment(Align.Center).clickable(true);
image.onClickListener = delegate(Widget src) {
image.click = delegate(Widget src) {
_cup.handleAction(ACTION_PAUSE);
// about dialog when clicking on image
Window wnd = Platform.instance.createWindow("About...", window, WindowFlag.Modal);