Get rid of home grown multitail

Use the awesomeness that is kitty instead
This commit is contained in:
Kovid Goyal 2019-09-08 12:47:38 +05:30
parent 1e96510e00
commit 4d9440a94c
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 25 additions and 261 deletions

View File

@ -1,5 +0,0 @@
Update coretext_fontdatabase.mm
Just import the one from Qt 5.12 wholesale. There have been lots of
improvements to it and hopefully it will just continue to work.
The original file was imported wholesale from Qt 5.6

View File

@ -1,188 +0,0 @@
#!/usr/bin/env python2
# vim:fileencoding=utf-8
from __future__ import absolute_import, division, print_function, unicode_literals
__license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
import curses, os, select, fcntl, errno, re
from io import BlockingIOError
from polyglot.builtins import map
from threading import Thread
clean_pat = re.compile(b'[\n\r\f\v]')
def debug(*args):
print (*args, file=open('/tmp/log', 'a'))
def show_buf(window, fname, buf, keep_trailing=True):
while buf:
n = buf.find(b'\n')
if n == -1:
if not keep_trailing:
show_line(window, bytes(buf), fname)
del buf[:]
break
show_line(window, bytes(buf[:n]), fname)
del buf[:n + 1]
def nonblocking_readlines(window, fileobj, buf, name, copy_to=None):
while True:
try:
byts = fileobj.read()
except BlockingIOError:
break
except EnvironmentError as err:
if err.errno == errno.EAGAIN:
break
raise
if not byts:
break
if copy_to is not None:
copy_to.write(byts)
buf.extend(byts)
show_buf(window, name, buf)
def show_line(window, line, fname):
line = clean_pat.sub(b'', line)
max_lines, max_chars = window.getmaxyx()
title = str(b" %s " % fname)
if line:
continue_prompt = b'> '
max_line_len = max_chars - 2
if len(line) > max_line_len:
first_portion = line[0:max_line_len - 1]
trailing_len = max_line_len - (len(continue_prompt) + 1)
remaining = [line[i:i + trailing_len]
for i in range(max_line_len - 1, len(line), trailing_len)]
line_portions = [first_portion] + remaining
else:
line_portions = [line]
def addstr(i, text):
try:
if i > 0:
window.addstr(continue_prompt, curses.color_pair(1))
window.addstr(text + b'\n')
except curses.error:
pass
for i, line_portion in enumerate(line_portions):
y, x = window.getyx()
y = max(1, y)
if y >= max_lines - 1:
window.move(1, 1)
window.deleteln()
window.move(y - 1, 1)
window.deleteln()
addstr(i, line_portion)
else:
window.move(y, x + 1)
addstr(i, line_portion)
window.border()
y, x = window.getyx()
window.addstr(0, max_chars // 2 - len(title) // 2, title, curses.A_BOLD)
window.move(y, x)
window.refresh()
def mainloop(scr, files, control_file, copy_to, name_map):
curses.use_default_colors()
curses.init_pair(1, curses.COLOR_GREEN, -1)
rows, columns = scr.getmaxyx()
half_columns = columns // 2
windows = []
if len(files) == 1:
windows.append(curses.newwin(rows, columns, 0, 0))
elif len(files) == 2:
windows.append(curses.newwin(rows, half_columns, 0, 0))
windows.append(curses.newwin(rows, half_columns, 0, half_columns))
elif len(files) == 3:
windows.append(curses.newwin(rows // 2, half_columns, 0, 0))
windows.append(curses.newwin(rows // 2, half_columns, 0, half_columns))
windows.append(curses.newwin(rows // 2, half_columns, rows // 2, 0))
elif len(files) == 4:
windows.append(curses.newwin(rows // 2, half_columns, 0, 0))
windows.append(curses.newwin(rows // 2, half_columns, 0, half_columns))
windows.append(curses.newwin(rows // 2, half_columns, rows // 2, 0))
windows.append(curses.newwin(rows // 2, half_columns, rows // 2, half_columns))
window_map = dict(zip(files, windows))
buffer_map = {f:bytearray() for f in files}
handles = set([control_file] + list(files))
if copy_to is not None:
copy_to = {h:dest for h, dest in zip(files, copy_to)}
else:
copy_to = {}
name_map = {h:name_map.get(h, h.name) for h in files}
def flush_buffer(h):
show_buf(window_map[h], name_map[h], buffer_map[h], keep_trailing=False)
run = True
while run:
readable, writable, error = select.select(list(handles), [], list(handles))
for h in error:
if h is control_file:
run = False
break
else:
flush_buffer(h)
handles.discard(h)
for h in readable:
if h is control_file:
run = False
break
nonblocking_readlines(window_map[h], h, buffer_map[h], name_map[h], copy_to.get(h))
tuple(map(flush_buffer, files))
def watch(pipes, control_file, copy_to, name_map):
try:
curses.wrapper(mainloop, pipes, control_file, copy_to, name_map)
except KeyboardInterrupt:
pass
def multitail(pipes, name_map=None, copy_to=None):
if not 1 <= len(pipes) <= 4:
raise ValueError('Can only watch 1-4 files at a time')
r, w = pipe()
t = Thread(target=watch, args=(pipes, r, copy_to, name_map or {}))
t.daemon = True
t.start()
def stop():
try:
w.write(b'0'), w.flush(), w.close()
except IOError:
pass
t.join()
return stop, t.is_alive
def pipe():
r, w = os.pipe()
r, w = os.fdopen(r, 'r'), os.fdopen(w, 'w')
fl = fcntl.fcntl(r, fcntl.F_GETFL)
fcntl.fcntl(r, fcntl.F_SETFL, fl | os.O_NONBLOCK)
return r, w
def test():
import random, time
r1, w1 = pipe()
r2, w2 = pipe()
r3, w3 = pipe()
with w1, w2, w3:
files = (w1, w2, w3)
stop, is_alive = multitail((r1, r2, r3))
try:
num = 0
while is_alive():
num += 1
print (((' %dabc\r' % num) * random.randint(9, 100)), file=random.choice(files))
[f.flush() for f in files]
time.sleep(1)
except KeyboardInterrupt:
stop()
if __name__ == '__main__':
test()

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, shutil, subprocess, glob, tempfile, json, time, filecmp, atexit, sys
import os, shutil, subprocess, tempfile, json, time, filecmp, sys
from setup import Command, __version__, require_clean_git, require_git_master
from setup.upload import installers
@ -37,79 +37,36 @@ class Stage2(Command):
description = 'Stage 2 of the publish process, builds the binaries'
def run(self, opts):
from setup.multitail import pipe, multitail
for x in glob.glob(os.path.join(self.d(self.SRC), 'dist', '*')):
os.remove(x)
build = os.path.join(self.d(self.SRC), 'build')
if os.path.exists(build):
shutil.rmtree(build)
processes = []
tdir = tempfile.mkdtemp('_build_logs')
atexit.register(shutil.rmtree, tdir)
base = os.path.join(self.d(self.SRC))
for x in ('dist', 'build'):
x = os.path.join(base, x)
if os.path.exists(x):
shutil.rmtree(x)
os.mkdir(x)
self.info('Starting builds for all platforms, this will take a while...')
def kill_child_on_parent_death():
import ctypes, signal
libc = ctypes.CDLL("libc.so.6")
libc.prctl(1, signal.SIGTERM)
session = ['layout vertical']
platforms = 'linux', 'osx', 'win'
for x in platforms:
cmd = (
'''{exe} -c "import subprocess; subprocess.Popen(['{exe}', './setup.py', '{x}']).wait() != 0 and'''
''' input('Build of {x} failed, press Enter to exit');"'''
).format(exe=sys.executable, x=x)
session.append('title ' + x)
session.append('launch ' + cmd)
for x in ('linux', 'osx', 'win'):
r, w = pipe()
p = subprocess.Popen([sys.executable, 'setup.py', x],
stdout=w,
stderr=subprocess.STDOUT,
cwd=self.d(self.SRC),
preexec_fn=kill_child_on_parent_death)
p.log, p.start_time, p.bname = r, time.time(), x
p.save = open(os.path.join(tdir, x), 'w+b')
p.duration = None
processes.append(p)
p = subprocess.Popen([
'kitty', '-o', 'enabled_layouts=vertical,stack', '-o', 'scrollback_lines=20000',
'-o', 'close_on_child_death=y', '--session=-'
], stdin=subprocess.PIPE)
def workers_running():
running = False
for p in processes:
rc = p.poll()
if rc is not None:
if p.duration is None:
p.duration = int(time.time() - p.start_time)
else:
running = True
return running
stop_multitail = multitail([proc.log for proc in processes],
name_map={
proc.log: proc.bname
for proc in processes
},
copy_to=[proc.save for proc in processes])[0]
while workers_running():
os.waitpid(-1, 0)
stop_multitail()
failed = False
for p in processes:
if p.poll() != 0:
failed = True
log = p.save
log.flush()
log.seek(0)
raw = log.read()
self.info('Building of %s failed' % p.bname)
sys.stderr.write(raw)
sys.stderr.write(b'\n\n')
if failed:
raise SystemExit('Building of installers failed!')
for p in sorted(processes, key=lambda p: p.duration):
self.info(
'Built %s in %d minutes and %d seconds' %
(p.bname, p.duration // 60, p.duration % 60)
)
p.communicate('\n'.join(session).encode('utf-8'))
p.wait()
for installer in installers(include_source=False):
if not os.path.exists(self.j(self.d(self.SRC), installer)):
installer = self.j(self.d(self.SRC), installer)
if not os.path.exists(installer) or os.path.getsize(installer) < 10000:
raise SystemExit(
'The installer %s does not exist' % os.path.basename(installer)
)