1 module ddebug.common.nodebug; 2 3 import ddebug.common.execution; 4 5 import core.thread; 6 import std.process; 7 import dlangui.core.logger; 8 9 class ProgramExecutionNoDebug : Thread, ProgramExecution { 10 11 // parameters 12 /// provides _executableFile, _executableArgs, _executableWorkingDir, _executableEnvVars parameters and setter function setExecutableParams 13 mixin ExecutableParams; 14 /// provides _terminalExecutable, _terminalTty, setTerminalExecutable, and setTerminalTty 15 mixin TerminalParams; 16 17 protected ProgramExecutionStatusListener _listener; 18 void setProgramExecutionStatusListener(ProgramExecutionStatusListener listener) { 19 _listener = listener; 20 } 21 22 // status 23 protected Pid _pid; 24 protected ExecutionStatus _status = ExecutionStatus.NotStarted; 25 protected int _exitCode = 0; 26 27 /// initialize but do not run 28 this() { 29 super(&threadFunc); 30 } 31 32 ~this() { 33 stop(); 34 } 35 36 private bool isProcessActive() { 37 if (_pid is null) 38 return false; 39 auto res = tryWait(_pid); 40 if (res.terminated) { 41 Log.d("Process ", _executableFile, " is stopped"); 42 _exitCode = wait(_pid); 43 _pid = Pid.init; 44 return false; 45 } else { 46 return true; 47 } 48 } 49 50 private void killProcess() { 51 if (_pid is null) 52 return; 53 try { 54 Log.d("Trying to kill process", _executableFile); 55 kill(_pid, 9); 56 Log.d("Waiting for process termination"); 57 _exitCode = wait(_pid); 58 _pid = Pid.init; 59 Log.d("Killed"); 60 } catch (Exception e) { 61 Log.d("Exception while killing process " ~ _executableFile, e); 62 _pid = Pid.init; 63 } 64 } 65 66 private void threadFunc() { 67 import std.stdio; 68 import std.array: empty; 69 70 // prepare parameter list 71 string[] params; 72 params ~= _executableFile; 73 params ~= _executableArgs; 74 75 // external console support 76 if (!_terminalExecutable.empty) { 77 string cmdline = escapeShellCommand(params); 78 string shellScript = ` 79 rm $0 80 ` ~ cmdline ~ ` 81 exit_code=$? 82 echo " 83 ----------------------- 84 (program returned exit code: $exit_code)" 85 echo "Press return to continue..." 86 dummy_var="" 87 read dummy_var 88 exit $exit_code 89 `; 90 static import std.file; 91 static import std.path; 92 std.file.write(std.path.buildPath(_executableWorkingDir, "dlangide_run_script.sh"), shellScript); 93 string setExecFlagCommand = escapeShellCommand("chmod", "+x", "dlangide_run_script.sh"); 94 spawnShell(setExecFlagCommand, stdin, stdout, stderr, null, Config.none, _executableWorkingDir); 95 params = [_terminalExecutable, "-e", "./dlangide_run_script.sh"]; 96 } 97 98 File newstdin; 99 File newstdout; 100 File newstderr; 101 version (Windows) { 102 } else { 103 newstdin = stdin; 104 newstdout = stdout; 105 newstderr = stderr; 106 } 107 try { 108 _pid = spawnProcess(params, newstdin, newstdout, newstderr, null, Config.none, _executableWorkingDir); 109 } catch (Exception e) { 110 Log.e("ProgramExecutionNoDebug: Failed to spawn process: ", e); 111 killProcess(); 112 _status = ExecutionStatus.Error; 113 } 114 115 if (_status != ExecutionStatus.Error) { 116 // thread loop: poll process status 117 while (!_stopRequested) { 118 Thread.sleep(dur!"msecs"(50)); 119 if (!isProcessActive()) { 120 _status = ExecutionStatus.Finished; 121 break; 122 } 123 } 124 if (_stopRequested) { 125 killProcess(); 126 _status = ExecutionStatus.Killed; 127 } 128 } 129 130 // finished 131 Log.d("ProgramExecutionNoDebug: finished, execution status: ", _status); 132 _listener.onProgramExecutionStatus(this, _status, _exitCode); 133 } 134 135 // implement ProgramExecution interface 136 137 /// returns true if it's debugger 138 @property bool isDebugger() { return false; } 139 140 /// returns true if it's mago debugger 141 @property bool isMagoDebugger() { return false; } 142 143 /// executable file 144 @property string executableFile() { return _executableFile; } 145 146 /// returns execution status 147 @property ExecutionStatus status() { return _status; } 148 149 /// start execution 150 void run() { 151 if (_runRequested) 152 return; // already running 153 assert(_listener !is null); 154 _runRequested = true; 155 _threadStarted = true; 156 _status = ExecutionStatus.Running; 157 start(); 158 } 159 160 /// stop execution (call from GUI thread) 161 void stop() { 162 if (!_runRequested) 163 return; 164 if (_stopRequested) 165 return; 166 _stopRequested = true; 167 if (_threadStarted && !_threadJoined) { 168 _threadJoined = true; 169 join(); 170 } 171 } 172 173 protected bool _threadStarted; 174 protected bool _threadJoined; 175 protected bool _stopRequested; 176 protected bool _runRequested; 177 }