I can't build with Sublime Text 2 [SOLVED]

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
Sheepolution
Party member
Posts: 264
Joined: Mon Mar 04, 2013 9:31 am
Location: The Netherlands
Contact:

I can't build with Sublime Text 2 [SOLVED]

Post by Sheepolution »

My buildsystem looks like this:

Code: Select all

{
    "selector": "source.lua",
    "cmd": ["C:\\Program Files (x86)\\LOVE\\love.exe", "$file_path"]
}
And when I build it says Building for a few seconds, and then it's done. But nothing happened and no window opened.

It gives the following console error:

Code: Select all

Traceback (most recent call last):
  File ".\sublime_plugin.py", line 337, in run_
  File ".\exec.py", line 154, in run
  File ".\exec.py", line 45, in __init__
UnicodeDecodeError: 'ascii' codec can't decode byte 0xeb in position 13: ordinal not in range(128)
My exec.py file looks like this:

Code: Select all

import sublime, sublime_plugin
import os, sys
import thread
import subprocess
import functools
import time

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

        self.start_time = time.time()

        # 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(v).encode("utf8") 

        proc_env = os.environ.copy()
        proc_env.update(env)
        for k, v in proc_env.iteritems():
            proc_env[k] = os.path.expandvars(v).encode("utf8") 

        self.proc = subprocess.Popen(arg_list, stdout=subprocess.PIPE,
            stderr=subprocess.PIPE, startupinfo=startupinfo, env=proc_env, shell=shell)

        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
            self.proc.terminate()
            self.listener = None

    def poll(self):
        return self.proc.poll() == None

    def exit_code(self):
        return self.proc.poll()

    def read_stdout(self):
        while True:
            data = os.read(self.proc.stdout.fileno(), 2**15)

            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(), 2**15)

            if data != "":
                if self.listener:
                    self.listener.on_data(self, data)
            else:
                self.proc.stderr.close()
                break

class ExecCommand(sublime_plugin.WindowCommand, ProcessListener):
    def run(self, cmd = [], file_regex = "", line_regex = "", working_dir = "",
            encoding = "utf-8", env = {}, quiet = False, kill = False,
            # Catches "path" and "shell"
            **kwargs):

        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())

        self.output_view.settings().set("result_file_regex", file_regex)
        self.output_view.settings().set("result_line_regex", line_regex)
        self.output_view.settings().set("result_base_dir", working_dir)

        # 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 "Running " + " ".join(cmd)
            sublime.status_message("Building")

        show_panel_on_build = sublime.load_settings("Preferences.sublime-settings").get("show_panel_on_build", True)
        if show_panel_on_build:
            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

        try:
            # Forward kwargs to AsyncProcess
            self.proc = AsyncProcess(cmd, merged_env, self, **kwargs)
        except err_type as e:
            self.append_data(None, str(e) + "\n")
            self.append_data(None, "[cmd:  " + str(cmd) + "]\n")
            self.append_data(None, "[dir:  " + str(os.getcwdu()) + "]\n")
            if "PATH" in merged_env:
                self.append_data(None, "[path: " + str(merged_env["PATH"]) + "]\n")
            else:
                self.append_data(None, "[path: " + str(os.environ["PATH"]) + "]\n")
            if not self.quiet:
                self.append_data(None, "[Finished]")

    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 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 + "]\n"
            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:
            elapsed = time.time() - proc.start_time
            exit_code = proc.exit_code()
            if exit_code == 0 or exit_code == None:
                self.append_data(proc, ("[Finished in %.1fs]") % (elapsed))
            else:
                self.append_data(proc, ("[Finished in %.1fs with exit code %d]") % (elapsed, exit_code))

        if proc != self.proc:
            return

        errs = self.output_view.find_all_results()
        if len(errs) == 0:
            sublime.status_message("Build finished")
        else:
            sublime.status_message(("Build finished with %d errors") % len(errs))

        # 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)
Or in a pastebin: http://pastebin.com/B8qrbdCa

I'm pressing F7 when I'm on main.lua. My directory link is C:\Users\Daniël\Documents\Mijn Documenten\Programmeren\Love2D\Test. The chat said it could be the ë of my name. I've tried it from out usb or another folder and with no changes.

It's like this kind of stuff always happens to me :x
Last edited by Sheepolution on Tue Jun 25, 2013 5:22 pm, edited 1 time in total.
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: I can't build with Sublime Text 2

Post by scutheotaku »

I can't give a lot of help here, except that this is what my build system looks like (I'm on Windows 7):

Code: Select all

 
        {
            "cmd": [
                "love2d", 
                "--console", 
                "$project_path"
            ], 
            "name": "LOVE", 
            "target": "run_love2d"
        }
Of course, LOVE's directory has to be setup in your Windows environment variables for that to work.
User avatar
Sheepolution
Party member
Posts: 264
Joined: Mon Mar 04, 2013 9:31 am
Location: The Netherlands
Contact:

Re: I can't build with Sublime Text 2

Post by Sheepolution »

So I changed some stuff on exec.py, and used your system build, now I get this:

Build result:

Code: Select all

depth not expected on this moment
[Finished in 0.1s with exit code 1]
Console:

Code: Select all

Running for depth in {0..5}; do if [ -f main.lua ]; then love .; fi; cd ..; done; ls

I have no idea..
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: I can't build with Sublime Text 2

Post by scutheotaku »

Sheepolution wrote:So I changed some stuff on exec.py, and used your system build, now I get this:

Build result:

Code: Select all

depth not expected on this moment
[Finished in 0.1s with exit code 1]
Console:

Code: Select all

Running for depth in {0..5}; do if [ -f main.lua ]; then love .; fi; cd ..; done; ls

I have no idea..
Try this:
Setup a Sublime Text project file for your project, making sure to your project's folder to it, and make sure that the project is open in Sublime Text.
Edit the ".sublime-project" file, and replace whatever is there with this:

Code: Select all

{
    "folders": [
        {
            "path": "./"
        }
    ], 
    "build_systems": [
        {
            "cmd": [
                "love2d", 
                "--console", 
                "$project_path"
            ], 
            "name": "LOVE", 
            "target": "run_love2d"
        }, 
        {
            "cmd": [
                "love2d_jit_dll", 
                "$project_path"
            ], 
            "name": "LOVE JIT Dll", 
            "target": "run_love2d"
        }
    ]
}
Save it.
In the top menu, go to Tools>Build System, and make sure that "LOVE" is selected (if you have other LOVE build systems setup, make sure you are selecting the one defined by this project file; it should be right by the "LOVE JIT DLL" build system in the list).
No try both "F7" and "Ctrl - B" - did either work?
If not, please confirm that the LOVE directory is setup in your Windows environment variables.
User avatar
Sheepolution
Party member
Posts: 264
Joined: Mon Mar 04, 2013 9:31 am
Location: The Netherlands
Contact:

Re: I can't build with Sublime Text 2

Post by Sheepolution »

I tried it with PHP, and I think this is more of a general Sublime problem than löve.
Since that also returns

Code: Select all

Traceback (most recent call last):
  File ".\sublime_plugin.py", line 337, in run_
    return self.run(**args)
  File ".\exec.py", line 154, in run
  File ".\exec.py", line 45, in __init__
UnicodeDecodeError: 'ascii' codec can't decode byte 0xeb in position 13: ordinal not in range(128)

Anyways..

Build Result:

Code: Select all

Love2D settings not found!
Please add settings to Preferences -> Settings - User
Like these, but with your own correct paths:
"love2dexe": "c:/Program Files (x86)/love/love.exe",
"love2djitdll": "c:/dev/love2d/dynjit/love.exe" 

Windows environment variables.
5gKM4ae.png
5gKM4ae.png (46.14 KiB) Viewed 403 times
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: I can't build with Sublime Text 2

Post by scutheotaku »

In your Environment Variables, please append LOVE's directory to the PATH variable.

E.g.,
Image

After that, try running LOVE from the command prompt by simply typing in "love" (if you had the command prompt open already, you may have to close it and reopen it). The "pig demo" should pop up. If it does, try building LOVE with the build system I provided again. Note: you shouldn't need any Python or PHP script to execute LOVE.

EDIT:
If LOVE runs from the command prompt but you're still having trouble getting it to run from Sublime Text, try this:
1. Download this project and unzip it: https://dl.dropboxusercontent.com/u/172 ... roject.zip
2. Open the project file
3. Hit "F7" on your keyboard
Did it run?
User avatar
Sheepolution
Party member
Posts: 264
Joined: Mon Mar 04, 2013 9:31 am
Location: The Netherlands
Contact:

Re: I can't build with Sublime Text 2

Post by Sheepolution »

After that, try running LOVE from the command prompt by simply typing in "love"
I did, it works.

Code: Select all

If it does, try building LOVE with the build system I provided again.
Nope, getting the next build result with your build settings:

Code: Select all

Love2D settings not found!
Please add settings to Preferences -> Settings - User
Like these, but with your own correct paths:
"love2dexe": "c:/Program Files (x86)/love/love.exe",
"love2djitdll": "c:/dev/love2d/dynjit/love.exe"
If I use the original SublimeLove builder it returns

Code: Select all

Running for depth in {0..5}; do if [ -f main.lua ]; then love .; fi; cd ..; done; ls
Traceback (most recent call last):
  File ".\sublime_plugin.py", line 337, in run_
    return self.run(**args)
  File ".\exec.py", line 154, in run
  File ".\exec.py", line 45, in __init__
UnicodeDecodeError: 'ascii' codec can't decode byte 0xeb in position 13: ordinal not in range(128)
Note: you shouldn't need any Python or PHP script to execute LOVE.
I mean that I tried a PHP script with a PHP-builder. And it resulted in the same error.
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: I can't build with Sublime Text 2

Post by scutheotaku »

Ok, sorry that I didn't think of this before...
In Sublime Text, in the top menu, go to Preferences > Settings - User.
In the file that opens up, add a comma right after the last entry (before the last curly brace), and then add this between that comma and the curly brace (obviously, you must change the path here so that it is correct for your install):

Code: Select all

"love2dexe": "c:/love/love.exe"
Here's what mine looks like:
Image

After you've done that, save the preferences file, open your project, then try again.

If that doesn't work...
Sheepolution wrote:
Note: you shouldn't need any Python or PHP script to execute LOVE.
I mean that I tried a PHP script with a PHP-builder. And it resulted in the same error.
Sorry if I'm a bit slow on the uptake, but by this do you mean that you haven't been able to build Python or PHP with Sublime Text (completely unconnected with LOVE)? If that is the case, I'd look into user permissions (make sure there's nothing there preventing you from building things/accessing files? seems a stretch, but...), and also consider reinstalling Sublime Text.

Also, for PHP and Python, have you tried extremely simple programs (just to confirm that it's not a syntax error causing those problems), e.g., for Python:

Code: Select all

print "Hello world!\n"
#obviously haha
User avatar
Sheepolution
Party member
Posts: 264
Joined: Mon Mar 04, 2013 9:31 am
Location: The Netherlands
Contact:

Re: I can't build with Sublime Text 2 [SOLVED]

Post by Sheepolution »

Ah yes, that solved the build result error, but it's still giving the console error and not working.

It has to be my exec.py that is fucking it up. I'm looking online for solutions and they say to replace certain lines with

Code: Select all

os.path.expandvars(path).encode(sys.getfilesystemencoding())
Which doesn't work either.

Sigh..

EDIT:
Okay so I played a bit with the exec.py file, and it finally works.

A correct exec.py (for me) should be

Code: Select all

import sublime, sublime_plugin
import os, sys
import thread
import subprocess
import functools
import time

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

        self.start_time = time.time()

        # 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 index, arg in enumerate(arg_list[:]):
            arg_list[index] = arg.encode(sys.getfilesystemencoding())

        for k, v in proc_env.iteritems():
            proc_env[k] = os.path.expandvars(v.decode(sys.getfilesystemencoding())).encode(sys.getfilesystemencoding())

        self.proc = subprocess.Popen(arg_list, stdout=subprocess.PIPE,
            stderr=subprocess.PIPE, startupinfo=startupinfo, env=proc_env, shell=shell)

        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
            self.proc.terminate()
            self.listener = None

    def poll(self):
        return self.proc.poll() == None

    def exit_code(self):
        return self.proc.poll()

    def read_stdout(self):
        while True:
            data = os.read(self.proc.stdout.fileno(), 2**15)

            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(), 2**15)

            if data != "":
                if self.listener:
                    self.listener.on_data(self, data)
            else:
                self.proc.stderr.close()
                break

class ExecCommand(sublime_plugin.WindowCommand, ProcessListener):
    def run(self, cmd = [], file_regex = "", line_regex = "", working_dir = "",
            encoding = "utf-8", env = {}, quiet = False, kill = False,
            # Catches "path" and "shell"
            **kwargs):

        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())

        self.output_view.settings().set("result_file_regex", file_regex)
        self.output_view.settings().set("result_line_regex", line_regex)
        self.output_view.settings().set("result_base_dir", working_dir)

        # 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 "Running " + " ".join(cmd)
            sublime.status_message("Building")

        show_panel_on_build = sublime.load_settings("Preferences.sublime-settings").get("show_panel_on_build", True)
        if show_panel_on_build:
            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

        try:
            # Forward kwargs to AsyncProcess
            self.proc = AsyncProcess(cmd, merged_env, self, **kwargs)
        except err_type as e:
            self.append_data(None, str(e) + "\n")
            self.append_data(None, "[cmd:  " + str(cmd) + "]\n")
            self.append_data(None, "[dir:  " + str(os.getcwdu()) + "]\n")
            if "PATH" in merged_env:
                self.append_data(None, "[path: " + str(merged_env["PATH"]) + "]\n")
            else:
                self.append_data(None, "[path: " + str(os.environ["PATH"]) + "]\n")
            if not self.quiet:
                self.append_data(None, "[Finished]")

    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 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 + "]\n"
            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:
            elapsed = time.time() - proc.start_time
            exit_code = proc.exit_code()
            if exit_code == 0 or exit_code == None:
                self.append_data(proc, ("[Finished in %.1fs]") % (elapsed))
            else:
                self.append_data(proc, ("[Finished in %.1fs with exit code %d]") % (elapsed, exit_code))

        if proc != self.proc:
            return

        errs = self.output_view.find_all_results()
        if len(errs) == 0:
            sublime.status_message("Build finished")
        else:
            sublime.status_message(("Build finished with %d errors") % len(errs))

        # 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)
Anyways, thanks so much for all the help! This will be so helpful (YES! No more dragging and dropping!).
scutheotaku
Party member
Posts: 235
Joined: Sat Dec 15, 2012 6:54 am

Re: I can't build with Sublime Text 2 [SOLVED]

Post by scutheotaku »

Sheepolution wrote:Ah yes, that solved the build result error, but it's still giving the console error and not working.

It has to be my exec.py that is fucking it up. I'm looking online for solutions and they say to replace certain lines with

Code: Select all

os.path.expandvars(path).encode(sys.getfilesystemencoding())
Which doesn't work either.

Sigh..

EDIT:
Okay so I played a bit with the exec.py file, and it finally works.

A correct exec.py (for me) should be

Code: Select all

import sublime, sublime_plugin
import os, sys
import thread
import subprocess
import functools
import time

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

        self.start_time = time.time()

        # 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 index, arg in enumerate(arg_list[:]):
            arg_list[index] = arg.encode(sys.getfilesystemencoding())

        for k, v in proc_env.iteritems():
            proc_env[k] = os.path.expandvars(v.decode(sys.getfilesystemencoding())).encode(sys.getfilesystemencoding())

        self.proc = subprocess.Popen(arg_list, stdout=subprocess.PIPE,
            stderr=subprocess.PIPE, startupinfo=startupinfo, env=proc_env, shell=shell)

        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
            self.proc.terminate()
            self.listener = None

    def poll(self):
        return self.proc.poll() == None

    def exit_code(self):
        return self.proc.poll()

    def read_stdout(self):
        while True:
            data = os.read(self.proc.stdout.fileno(), 2**15)

            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(), 2**15)

            if data != "":
                if self.listener:
                    self.listener.on_data(self, data)
            else:
                self.proc.stderr.close()
                break

class ExecCommand(sublime_plugin.WindowCommand, ProcessListener):
    def run(self, cmd = [], file_regex = "", line_regex = "", working_dir = "",
            encoding = "utf-8", env = {}, quiet = False, kill = False,
            # Catches "path" and "shell"
            **kwargs):

        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())

        self.output_view.settings().set("result_file_regex", file_regex)
        self.output_view.settings().set("result_line_regex", line_regex)
        self.output_view.settings().set("result_base_dir", working_dir)

        # 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 "Running " + " ".join(cmd)
            sublime.status_message("Building")

        show_panel_on_build = sublime.load_settings("Preferences.sublime-settings").get("show_panel_on_build", True)
        if show_panel_on_build:
            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

        try:
            # Forward kwargs to AsyncProcess
            self.proc = AsyncProcess(cmd, merged_env, self, **kwargs)
        except err_type as e:
            self.append_data(None, str(e) + "\n")
            self.append_data(None, "[cmd:  " + str(cmd) + "]\n")
            self.append_data(None, "[dir:  " + str(os.getcwdu()) + "]\n")
            if "PATH" in merged_env:
                self.append_data(None, "[path: " + str(merged_env["PATH"]) + "]\n")
            else:
                self.append_data(None, "[path: " + str(os.environ["PATH"]) + "]\n")
            if not self.quiet:
                self.append_data(None, "[Finished]")

    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 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 + "]\n"
            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:
            elapsed = time.time() - proc.start_time
            exit_code = proc.exit_code()
            if exit_code == 0 or exit_code == None:
                self.append_data(proc, ("[Finished in %.1fs]") % (elapsed))
            else:
                self.append_data(proc, ("[Finished in %.1fs with exit code %d]") % (elapsed, exit_code))

        if proc != self.proc:
            return

        errs = self.output_view.find_all_results()
        if len(errs) == 0:
            sublime.status_message("Build finished")
        else:
            sublime.status_message(("Build finished with %d errors") % len(errs))

        # 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)
Anyways, thanks so much for all the help! This will be so helpful (YES! No more dragging and dropping!).
Great! Glad you got it working, and glad I could help!

And yes - being able to just hit "F7" instead of dragging and dropping or running a batch file is SO helpful.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 4 guests