-
Notifications
You must be signed in to change notification settings - Fork 0
/
AppGameKit.py
175 lines (156 loc) · 10.3 KB
/
AppGameKit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import sublime
import sublime_plugin
import subprocess
import threading
import os
class AgkBuildCommand(sublime_plugin.WindowCommand):
###########################################################################################################################################################
###########################################################################################################################################################
BuildCancelled = False
OutputPanel = None
OutputPanelLock = threading.Lock()
BuildProcess = None
Encoding = 'utf-8'
###########################################################################################################################################################
###########################################################################################################################################################
def is_enabled(self, CancelBuild=False):
if CancelBuild:
# The CancelBuild option should only be available when the process is still running.
return self.BuildProcess is not None and self.BuildProcess.poll() is None
return True
###########################################################################################################################################################
###########################################################################################################################################################
def run(self, CompileAndBroadcast=False, CompileAndDebug=False, CompileAndRun=False, CancelBuild=False):
#==================================================================================================================================================
if CancelBuild:
if self.BuildProcess:
self.BuildProcess.terminate()
self.BuildCancelled = True
return
#==================================================================================================================================================
BuildSetupErrorStr = "AppGameKit.py ERROR: "
BuildSetupError = False
BuildVariables = self.window.extract_variables()
BuildSettings = sublime.load_settings("AppGameKit.sublime-settings")
#==================================================================================================================================================
# Find "AGKCompiler.exe".
Use64bitInterpreter = BuildSettings.get("Use64bitInterpreter", False)
#----------------------------------------------------------------------------------------------------------------------------------------------
if CompileAndRun:
RunMe = BuildSettings.get("AGKCompiler", "")
if not os.path.exists(RunMe):
self.queue_write(BuildSetupErrorStr+"'"+RunMe+"' does not exist.\n"+" "*len(BuildSetupErrorStr)+"Also, try restarting SublimeText.\n")
BuildSetupError = True
else:
if Use64bitInterpreter: RunMe = RunMe + " -run -64 main.agc"
else : RunMe = RunMe + " -run main.agc"
#----------------------------------------------------------------------------------------------------------------------------------------------
elif CompileAndBroadcast:
RunMe = BuildSettings.get("AGKBroadcaster", "")
if not os.path.exists(RunMe):
self.queue_write(BuildSetupErrorStr+"'"+RunMe+"' does not exist.\n"+" "*len(BuildSetupErrorStr)+"Also, try restarting SublimeText.\n")
BuildSetupError = True
else:
if Use64bitInterpreter: RunMe = RunMe + " ??? -64 todo.agc"
else : RunMe = RunMe + " ??? todo.agc"
#----------------------------------------------------------------------------------------------------------------------------------------------
elif CompileAndDebug:
RunMe = BuildSettings.get("AGKBroadcaster", "")
if not os.path.exists(RunMe):
self.queue_write(BuildSetupErrorStr+"'"+RunMe+"' does not exist.\n"+" "*len(BuildSetupErrorStr)+"Also, try restarting SublimeText.\n")
BuildSetupError = True
else:
if Use64bitInterpreter: RunMe = RunMe + " ??? -64 todo.agc"
else : RunMe = RunMe + " ??? todo.agc"
#----------------------------------------------------------------------------------------------------------------------------------------------
else:
RunMe = BuildSettings.get("AGKCompiler", "")
if not os.path.exists(RunMe):
self.queue_write(BuildSetupErrorStr+"'"+RunMe+"' does not exist.\n"+" "*len(BuildSetupErrorStr)+"Also, try restarting SublimeText.\n")
BuildSetupError = True
else:
if Use64bitInterpreter: RunMe = RunMe + " -agk -64 main.agc"
else : RunMe = RunMe + " -agk main.agc"
#----------------------------------------------------------------------------------------------------------------------------------------------
#==================================================================================================================================================
# Find "main.agc".
WorkingDir = BuildVariables["file_path"]
BackSlashCount = WorkingDir.count("\\")-1
SearchDepth = BuildSettings.get("FindMainAgcDepth", 0)
SearchDepth = max(SearchDepth, 0) # Not less-than 0.
SearchDepth = min(SearchDepth, BackSlashCount) # Not greater-than number of parent dirs.
MainFound = False
for iLook in range(0, SearchDepth+1):
if os.path.isfile(WorkingDir+"\\main.agc"):
MainFound = True
break
elif iLook == SearchDepth:
self.queue_write(BuildSetupErrorStr+"Unable to find 'main.agc'. MainAgcLookDepth = "+str(SearchDepth)+"\n")
BuildSetupError = True
elif MainFound == False:
WorkingDir = WorkingDir[0:WorkingDir.rfind("\\", 0, len(WorkingDir)-1)]
#==================================================================================================================================================
# A lock is used to ensure only one thread is touching the output panel at a time.
with self.OutputPanelLock:
# Creating the panel implicitly clears any previous contents.
self.OutputPanel = self.window.create_output_panel('exec')
# OutputPanel result navigation. Double-click message to goto error File & LineNumber.
OutputPanelSettings = self.OutputPanel.settings()
OutputPanelSettings.set('result_file_regex', r'^\s{2}\K(.+\.agc):(\d+):' )
OutputPanelSettings.set('result_line_regex', r':(\d+):' )
OutputPanelSettings.set('result_base_dir', WorkingDir )
#==================================================================================================================================================
# Run build.
if self.BuildProcess is not None:
self.BuildProcess.terminate()
self.BuildProcess = None
if BuildSetupError:
self.BuildCancelled = True
else:
if CompileAndRun : self.queue_write("AppGameKit Compile & Run: " +RunMe+"\n")
elif CompileAndBroadcast: self.queue_write("AppGameKit Compile & Broadcast: "+RunMe+"\n")
elif CompileAndDebug : self.queue_write("AppGameKit Compile & Debug: " +RunMe+"\n")
else : self.queue_write("AppGameKit Compile: " +RunMe+"\n")
self.BuildProcess = subprocess.Popen( RunMe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=WorkingDir )
self.BuildCancelled = False
threading.Thread( target=self.read_handle, args=(self.BuildProcess.stdout,) ).start()
#==================================================================================================================================================
###########################################################################################################################################################
###########################################################################################################################################################
def read_handle(self, handle):
chunk_size = 2 ** 13
out = b''
while True:
try:
data = os.read(handle.fileno(), chunk_size)
# If exactly the requested number of bytes was
# read, there may be more data, and the current
# data may contain part of a multibyte char
out += data
if len(data) == chunk_size:
continue
if data == b'' and out == b'':
raise IOError('EOF')
# We pass out to a function to ensure the
# timeout gets the value of out right now,
# rather than a future (mutated) version
self.queue_write(" " + out.decode(self.Encoding).replace("\r\n", "\n ").replace("\r", "\n "))
if data == b'':
raise IOError('EOF')
out = b''
except (UnicodeDecodeError) as e:
msg = 'Error decoding output using %s - %s'
self.queue_write(msg % (self.Encoding, str(e)))
break
except (IOError):
if self.BuildCancelled: msg = 'Cancelled'
else : msg = 'Finished'
self.queue_write('\n[%s]' % msg)
break
###########################################################################################################################################################
###########################################################################################################################################################
def queue_write(self, text):
sublime.set_timeout(lambda: self.do_write(text), 1)
def do_write(self, text):
with self.OutputPanelLock:
self.OutputPanel.run_command('append', {'characters': text})