Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[fix] kill: better kill, pid based

  • Loading branch information...
commit 0809b69237c55f0098071e8d6492f355f9f9d526 1 parent f499601
@OpaOnWindowsNow OpaOnWindowsNow authored
Showing with 302 additions and 30 deletions.
  1. +91 −30 commands.py
  2. +211 −0 task.py
View
121 commands.py
@@ -1,53 +1,114 @@
import os
import sublime, sublime_plugin
import sys
+import task, subprocess, signal
basename_exe = "opa_build"
-exe = basename_exe+".exe"
-def kill(self,dirname,exe):
+if sys.platform.startswith('win'):
+ exe_name = basename_exe+".js"
+else:
+ exe_name = basename_exe+".exe"
+
+launched = {}
+locations = {}
+
+def prog_name(prog):
if sys.platform.startswith('win'):
- self.view.window().run_command('exec', {'cmd': ["tskill "+os.path.splitext(exe)[0]], 'working_dir':dirname, 'shell':True} )
+ return prog+".exe"
+ else:
+ return prog
+
+def location(program):
+ if not (program in locations):
+ for path in os.environ["PATH"].split(os.pathsep):
+ pfile = os.path.join(path, program)
+ if os.path.isfile(pfile):
+ locations[program] = path
+ return path
+ raise Exception(program,'Not found')
else:
- self.view.window().run_command('exec', {'cmd': ["killall "+exe], 'working_dir':dirname, 'shell':True} )
+ return locations[program]
+
+def taskid(self,dirname,exe):
+ return dirname
+ #return str(self.view.id())
+def kill(self,dirname,exe):
+ id = taskid(self,dirname,exe)
+ print "Killing task" , id
+ if not(id in launched):
+ print "Nothing to do"
+ return
+ t = launched.pop(id)
+ if hasattr(t,"proc"):
+ pid = t.proc.proc.pid
+ else:
+ pid = None
+ if pid==None:
+ print "Nothing to do (No pid)"
+ return
+ if t.proc.proc.returncode!=None:
+ print "Nothing to do (Already returned)"
+ return
+ print "Killing pid" , pid
+ if sys.platform.startswith('win'):
+ si = subprocess.STARTUPINFO()
+ si.dwFlags = subprocess.STARTF_USESHOWWINDOW
+ si.wShowindow = subprocess.SW_HIDE
+ subprocess.call(['taskkill','/F', '/T', '/PID', str(pid)],startupinfo=si)
+ else:
+ os.killpg(pid, signal.SIGKILL)
+
def launch(self,dirname,exe):
exe = os.path.join(dirname,exe)
+ command = exe+" -p 2000"
+ env = os.environ.copy()
+ working_dir = dirname
if sys.platform.startswith('win'):
+ node = prog_name("node")
node_path = os.path.join(os.getenv("APPDATA"),"npm","node_modules")
- os.environ["NODE_PATH"] = node_path
- self.view.window().run_command('exec', {'cmd': ["node "+exe+" -p 2000"], 'working_dir':dirname, 'shell':True} )
+ env["NODE_PATH"] = node_path
+ command = node+ " " + command
else:
- self.view.window().run_command('exec', {'cmd': [exe+" -p 2000"], 'working_dir':dirname, 'shell':True} )
+ command = command
+ t = task.Task(self.view.window())
+ id = taskid(self,dirname,exe)
+ launched[id] = t
+ t.run(cmd=[command],working_dir=dirname,env=env)
+ launched[id] = t
+ print "Has launched", t.proc.proc.pid
-class RunOpaBuildCommand(sublime_plugin.TextCommand):
+def output(view,str):
+ print str
+ selection_was_at_end = (len(view.sel()) == 1 and view.sel()[0] == sublime.Region(view.size()))
+ view.set_read_only(False)
+ edit = view.begin_edit()
+ view.insert(edit, view.size(), str)
+ if selection_was_at_end:
+ view.show(view.size())
+ view.end_edit(edit)
+ view.set_read_only(True)
+
+class runOpaBuildCommand(sublime_plugin.TextCommand):
def run(self, view):
+ panel = self.view.window().get_output_panel("exec")
dirname = os.path.dirname(self.view.file_name())
- print "Will build & run", exe
+ exe = exe_name
+ output(panel, "Will build & run "+exe)
+ output(panel,"\nCompiling ...")
self.view.window().run_command('build')
+ output(panel,"done\n")
+ output(panel,"\nStoping previous launch ...")
kill(self,dirname,exe)
+ output(panel,"done\n")
+ output(panel,"Launching ...")
launch(self,dirname,exe)
+ #output(panel,"The application has started\n")
+
-class StopRunOpaBuildCommand(sublime_plugin.TextCommand):
+class stopRunOpaBuildCommand(sublime_plugin.TextCommand):
def run(self, view):
dirname = os.path.dirname(self.view.file_name())
- print "Will kill", exe
- kill(self,dirname,exe)
-
-#class CheckProjectCommand(sublime_plugin.WindowCommand):
-# def run(self, edit):
-# #get filename
- #for root, dirs, files in os.walk("."):
-# print "ROOT " , root
-# print "dirs " , dirs
-# print "files ", files
- # [dirs.remove(dir) for dir in dirs if dir in dirs_to_ignore]
- # paths = dirs
- # paths.extend(files)
- # for path in paths:
- # print "path = $path"
- # print "fullpath = $full_path"
- # full_path = os.path.join(root, path)
- # return
-
-
+ print "Will kill", exe_name
+ kill(self,dirname,exe_name)
View
211 task.py
@@ -0,0 +1,211 @@
+import sublime, sublime_plugin
+import os, sys
+import thread
+import subprocess
+import functools
+
+read_size = 2**15
+
+if sys.platform.startswith('win'):
+ read_size = 32
+
+# This is mostly copy of exec.py of sublime
+class ProcessListener(object):
+ def on_data(self, proc, data):
+ pass
+
+ def on_finished(self, proc):
+ pass
+
+# Encapsulates subprocess.Popen, forwarding stdout to a supplied
+# ProcessListener (on a separate thread)
+class AsyncProcess(object):
+ def __init__(self, arg_list, env, listener,
+ # "path" is an option in build systems
+ path="",
+ # "shell" is an options in build systems
+ shell=False):
+
+ self.listener = listener
+ self.killed = False
+
+ # Hide the console window on Windows
+ startupinfo = None
+ if os.name == "nt":
+ startupinfo = subprocess.STARTUPINFO()
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+
+ # Set temporary PATH to locate executable in arg_list
+ if path:
+ old_path = os.environ["PATH"]
+ # The user decides in the build system whether he wants to append $PATH
+ # or tuck it at the front: "$PATH;C:\\new\\path", "C:\\new\\path;$PATH"
+ os.environ["PATH"] = os.path.expandvars(path).encode(sys.getfilesystemencoding())
+
+ proc_env = os.environ.copy()
+ proc_env.update(env)
+ for k, v in proc_env.iteritems():
+ proc_env[k] = os.path.expandvars(v).encode(sys.getfilesystemencoding())
+
+ if sys.platform.startswith('win'):
+ self.proc = subprocess.Popen(arg_list, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, startupinfo=startupinfo, env=proc_env, shell=shell)
+ else:
+ self.proc = subprocess.Popen(arg_list, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, startupinfo=startupinfo, env=proc_env, shell=shell, preexec_fn=os.setsid)
+
+ if path:
+ os.environ["PATH"] = old_path
+
+ if self.proc.stdout:
+ thread.start_new_thread(self.read_stdout, ())
+
+ if self.proc.stderr:
+ thread.start_new_thread(self.read_stderr, ())
+
+ def kill(self):
+ if not self.killed:
+ self.killed = True
+ try:
+ self.proc.kill()
+ except:
+ print "Killed"
+ self.listener = None
+
+ def poll(self):
+ return self.proc.poll() == None
+
+ def read_stdout(self):
+ while True:
+ data = os.read(self.proc.stdout.fileno(), read_size)
+
+ if data != "":
+ if self.listener:
+ self.listener.on_data(self, data)
+ else:
+ self.proc.stdout.close()
+ if self.listener:
+ self.listener.on_finished(self)
+ break
+
+ def read_stderr(self):
+ while True:
+ data = os.read(self.proc.stderr.fileno(), read_size)
+
+ if data != "":
+ if self.listener:
+ self.listener.on_data(self, data)
+ else:
+ self.proc.stderr.close()
+ break
+
+class Task(sublime_plugin.WindowCommand, ProcessListener):
+ def run(self, cmd = [], encoding = "utf-8", env = {}, quiet = False, kill = False, working_dir="", shell=True):
+
+ if kill:
+ if self.proc:
+ self.proc.kill()
+ self.proc = None
+ self.append_data(None, "[Cancelled]")
+ return
+
+ if not hasattr(self, 'output_view'):
+ # Try not to call get_output_panel until the regexes are assigned
+ self.output_view = self.window.get_output_panel("exec")
+
+ # Default the to the current files directory if no working directory was given
+ if (working_dir == "" and self.window.active_view()
+ and self.window.active_view().file_name() != ""):
+ working_dir = os.path.dirname(self.window.active_view().file_name())
+
+ # Call get_output_panel a second time after assigning the above
+ # settings, so that it'll be picked up as a result buffer
+ self.window.get_output_panel("exec")
+
+ self.encoding = encoding
+ self.quiet = quiet
+
+ self.proc = None
+ if not self.quiet:
+ print "Task: " + " ".join(cmd)
+
+ self.window.run_command("show_panel", {"panel": "output.exec"})
+
+ merged_env = env.copy()
+ if self.window.active_view():
+ user_env = self.window.active_view().settings().get('build_env')
+ if user_env:
+ merged_env.update(user_env)
+
+ # Change to the working dir, rather than spawning the process with it,
+ # so that emitted working dir relative path names make sense
+ if working_dir != "":
+ os.chdir(working_dir)
+
+ err_type = OSError
+ if os.name == "nt":
+ err_type = WindowsError
+ self.cmd = cmd
+ try:
+ # Forward kwargs to AsyncProcess
+ self.proc = AsyncProcess(cmd, merged_env, self, working_dir, shell)
+ except err_type as e:
+ self.append_data(None, str(e) + "\n")
+ if not self.quiet:
+ self.append_data(None, "[Failed]")
+
+ def kill(self):
+ self.proc.kill()
+
+ def is_enabled(self, kill = False):
+ if kill:
+ return hasattr(self, 'proc') and self.proc and self.proc.poll()
+ else:
+ return True
+
+ def append_data(self, proc, data):
+ if hasattr(self, 'proc') and proc != self.proc:
+ # a second call to exec has been made before the first one
+ # finished, ignore it instead of intermingling the output.
+ if proc:
+ proc.kill()
+ return
+
+ try:
+ str = data.decode(self.encoding)
+ except:
+ str = "[Decode error - output not " + self.encoding + "]"
+ proc = None
+
+ # Normalize newlines, Sublime Text always uses a single \n separator
+ # in memory.
+ str = str.replace('\r\n', '\n').replace('\r', '\n')
+
+ selection_was_at_end = (len(self.output_view.sel()) == 1
+ and self.output_view.sel()[0]
+ == sublime.Region(self.output_view.size()))
+ self.output_view.set_read_only(False)
+ edit = self.output_view.begin_edit()
+ self.output_view.insert(edit, self.output_view.size(), str)
+ if selection_was_at_end:
+ self.output_view.show(self.output_view.size())
+ self.output_view.end_edit(edit)
+ self.output_view.set_read_only(True)
+
+ def finish(self, proc):
+ if not self.quiet:
+ self.append_data(proc, "[Finished "+''.join(self.cmd)+"]")
+ if proc != self.proc:
+ return
+
+ # Set the selection to the start, so that next_result will work as expected
+ edit = self.output_view.begin_edit()
+ self.output_view.sel().clear()
+ self.output_view.sel().add(sublime.Region(0))
+ self.output_view.end_edit(edit)
+
+ def on_data(self, proc, data):
+ sublime.set_timeout(functools.partial(self.append_data, proc, data), 0)
+
+ def on_finished(self, proc):
+ sublime.set_timeout(functools.partial(self.finish, proc), 0)
Please sign in to comment.
Something went wrong with that request. Please try again.