mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Get rid of home grown multitail
Use the awesomeness that is kitty instead
This commit is contained in:
parent
1e96510e00
commit
4d9440a94c
@ -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
|
@ -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()
|
@ -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)
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user