-
Notifications
You must be signed in to change notification settings - Fork 0
/
matter_build.py
205 lines (159 loc) · 6.81 KB
/
matter_build.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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import sublime
import sublime_plugin
import subprocess
import threading
import os
import os.path
import re
FILE_REGEX = r"^INFO *(.*[^:]):(\d+):(\d+): (?:fatal )?((?:error|warning): .+)$"
CHECKOUT_ROOT_PATH="/home/andrei/devel/connectedhomeip/"
DOCKER_MAPPED_PATH="/workspace/"
# DOCKER_EXEC_BASH_STR= "docker exec -w /workspace bld_vscode /bin/bash -c"
DOCKER_EXEC_BASH_STR= "podman exec -w /workspace bld_vscode /bin/bash -c"
RE_COMPUTE_TARGET = "RE-COMPUTE"
TARGETS_PATH = os.path.join(sublime.cache_path(), 'matter_build_targets.list')
class MatterDockerBuild(sublime_plugin.WindowCommand):
encoding = 'utf-8'
killed = False
proc = None
panel = None
panel_lock = threading.Lock()
build_targets = None
last_selected_index = -1
def targets(self):
if self.build_targets is None:
self.compute_build_targets()
# Allow re-computation of all targets
self.build_targets.append([RE_COMPUTE_TARGET, "Re-compute build targets"])
return self.build_targets
def compute_build_targets(self):
self.build_targets = []
if not os.path.exists(TARGETS_PATH):
self.recompute_build_targets()
with open(TARGETS_PATH, 'rt') as f:
for item in f.readlines():
if item.strip():
self.build_targets.append([item.strip(), ])
def recompute_build_targets(self):
items = subprocess.check_output(
DOCKER_EXEC_BASH_STR.split() +
['source ./scripts/activate.sh >/dev/null 2>&1 && ./scripts/build/build_examples.py --log-level fatal targets --expand']).split(b'\n')
with open(TARGETS_PATH, 'wt') as f:
for item in items:
f.write('%s\n' % item.decode('utf8'))
def is_enabled(self, kill=False):
# The Cancel build option should only be available
# when the process is still running
if kill:
return self.proc is not None and self.proc.poll() is None
return True
def target_input_done(self, target_index):
if target_index < 0:
return
self.last_selected_index = target_index
target = self.targets()[target_index][0]
if target == RE_COMPUTE_TARGET:
os.remove(TARGETS_PATH)
self.build_targets = None
self.targets() # forces a recompute
else:
self.run_build(target)
def run(self, kill=False):
if kill:
if self.proc:
self.killed = True
self.proc.terminate()
return
with self.panel_lock:
# Creating the panel implicitly clears any previous contents
# self.panel = self.window.create_output_panel('matter_build')
self.panel = self.window.create_output_panel('exec')
self.panel.set_read_only(True)
self.window.show_quick_panel(
self.targets(),
self.target_input_done,
selected_index=self.last_selected_index,
placeholder='Target',
)
def run_build(self, target):
vars = self.window.extract_variables()
# A lock is used to ensure only one thread is
# touching the output panel at a time
with self.panel_lock:
# Enable result navigation. The result_file_regex does
# the primary matching, but result_line_regex is used
# when build output includes some entries that only
# contain line/column info beneath a previous line
# listing the file info. The result_base_dir sets the
# path to resolve relative file names against.
settings = self.panel.settings()
settings.set('result_file_regex', FILE_REGEX)
settings.set('result_line_regex', "")
# TODO: this should be dynamic by project directory or given via build
# configuration.
settings.set('result_base_dir', '%s/out/fake' % CHECKOUT_ROOT_PATH)
# do not attempt to interpret syntax
self.panel.assign_syntax(
sublime.Syntax('Packages/Text/Plain text.tmLanguage', 'Plain Text', False, 'text.plain'))
self.window.run_command('show_panel', {'panel': 'output.exec'})
if self.proc is not None:
self.proc.terminate()
self.proc = None
self.queue_write("Starting build for %s\n" % target)
args = DOCKER_EXEC_BASH_STR.split()
target_str = '--target "%s"' % target
args.append('source ./scripts/activate.sh && ./scripts/build/build_examples.py --enable-flashbundle --no-log-timestamps %s build' % target_str)
self.proc = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
self.killed = False
threading.Thread(
target=self.read_handle,
args=(self.proc.stdout,)
).start()
def read_handle(self, handle):
print("Matter build run loop starting")
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))
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.killed:
msg = 'Cancelled'
else:
msg = 'Finished'
self.queue_write('\n[%s]' % msg)
break
print("Matter build run loop completed")
with self.panel_lock:
regions = self.panel.find_all(FILE_REGEX)
self.panel.add_regions("docker.build.errors", regions=regions, scope="region.redish")
def queue_write(self, text):
sublime.set_timeout(lambda: self.do_write(text), 1)
def do_write(self, text):
with self.panel_lock:
# This makes errors clickable
text = text.replace(DOCKER_MAPPED_PATH, CHECKOUT_ROOT_PATH)
self.panel.run_command('append', {'characters': text, 'force': True, 'scroll_to_end': True})