Commits (3)
......@@ -16,3 +16,12 @@ Install
To do
* (websockets-based) view to monitor output of make command
* add --js option to mixin custom javascript plugins (a la greasemonkey)
* add --saveable option to mixin saveable javascript
makewiki (aka cookbook)
Curated set of deployable makefiles
For instance to convert AVI to webm
avi=$(shell ls *.AVI)
all: $(aviwebm)
%.webm: %.AVI
ffmpeg -i $< -s 360x270 -c:a libvorbis -c:v libvpx $@
# find all .md files in the directory
# mdsrc=$(shell ls *.md)
mdsrc=$(shell find . -iname "*.md")
# mdsrc=$(wildcard *.md */*.md **/*.md )
# map *.mp => *.html for mdsrc
all: $(html_from_md)
rename "s/ /_/g" *
touch `date +""`
mkdir `date +"%Y-%m-%d-%H%M%S"`
# Implicit rule to know how to make .html from .md
%.html: $< | \
pandoc --from markdown \
--to html \
--standalone \
--smart \
--section-divs \
--css styles.css | \
html5tidy \
--alternate /$<?edit "Edit source" "text/html" \
--script /__makeserver__/links.js > $@
# special rule for debugging variables
@echo '$*=$($*)'
......@@ -3,7 +3,8 @@ import sys, os, asyncio, json
from asyncio import create_subprocess_exec
from urllib.parse import urlparse, unquote as urlunquote, quote as urlquote
import argparse
from asyncio.subprocess import DEVNULL
from asyncio.subprocess import DEVNULL, STDOUT
import aiohttp
Todo: use proper http error responses for errors, see aiohttp/
......@@ -14,14 +15,19 @@ async def is_makeable (path, cwd=".", makefile="makefile"):
p = await create_subprocess_exec("make", "--question", "-f", makefile, rpath, cwd = cwd, stdout=DEVNULL, stderr=DEVNULL)
# retcode 0=file is up to date, 1=file needs remaking, 2=file is not makeable
ret = await p.wait() == 1
# print (f"is_makeable {path}: {ret}", file=sys.stderr)
print (f"is_makeable {path}: {ret}", file=sys.stderr)
await log(f"is_makeable {path}: {ret}")
return ret
async def make (path, cwd=".", makefile="makefile"):
rpath = os.path.relpath(path, cwd)
p = await create_subprocess_exec("make", "-f", makefile, rpath, cwd = cwd)
p = await create_subprocess_exec("make", "-f", makefile, rpath, stderr = STDOUT, cwd = cwd)
while True:
line = await p.stdout.readline()
ret = await p.wait() == 0
# print (f"make {path}: {ret}", file=sys.stderr)
print (f"make {path}: {ret}", file=sys.stderr)
await log(f"make {path}: {ret}")
return ret
textchars = bytearray({7,8,9,10,12,13,27} | set(range(0x20, 0x100)) - {0x7f})
......@@ -38,7 +44,7 @@ def editable (path):
_, ext = os.path.splitext(path)
ext = ext.lower()[1:]
# print (f"editable? ext {ext}", file=sys.stderr)
return ((ext not in ("html", "htm")) and not is_binary_file(path))
return ((ext not in ("html", "htm")) and not os.path.isdir(path) and not is_binary_file(path))
# From aiohttp/
def directory_as_html(filepath, directory, prefix, editor_url=None):
......@@ -84,7 +90,7 @@ def directory_as_html(filepath, directory, prefix, editor_url=None):
ul = "<ul>\n{}\n</ul>".format('\n'.join(index_list))
body = "<body>\n{}\n{}\n</body>".format(h1, ul)
head_str = "<head>\n<title>{}</title>\n</head>".format(index_of)
head_str = "<head>\n<title>{}</title>\n<link rel=\"stylesheet\" href=\"/include/index.css\"></script>\n<script src=\"/include/index.js\"></script>\n</head>".format(index_of)
html = "<html>\n{}\n{}\n</html>".format(head_str, body)
return html
......@@ -111,17 +117,56 @@ async def route_post (request):
print ("POST", path, file=sys.stderr)
if os.path.exists(path) and os.path.isfile(path):
data = await
resp = {}
if 'text' in data:
text = data['text']
# doing file io inline here is not strictly speaking very async ;)
with open(path, "w") as f:
return web.Response(text=json.dumps({"response": "ok"}), content_type="application/json")
return web.Response(text="no text")
resp['text'] = 'ok'
if 'name' in data:
newpath = os.path.join(os.path.split(path)[0], data['name'])
if path != newpath:
os.rename(path, newpath)
resp['name'] = 'ok'
if 'delete' in data:
resp['delete'] = 'ok'
return web.Response(text=json.dumps(resp), content_type="application/json")
return web.Response(text="post not allowed on {0}".format(path))
active_sockets = []
async def log (msg):
for ws in active_sockets:
await ws.send_str(msg)
async def websocket_handler(request):
print('Websocket connection starting')
ws = web.WebSocketResponse()
await ws.prepare(request)
print('Websocket connection ready')
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
if == 'close':
await ws.close()
await ws.send_str( + '/answer')
print('Websocket connection closed')
return ws
def main ():
ap = argparse.ArgumentParser("make & serve")
ap.add_argument("--makefile", default="makefile")
......@@ -136,6 +181,7 @@ def main ():
app = web.Application()
app['makefile'] = args.makefile
app['editor'] = args.editor
app.router.add_route('GET', '/ws', websocket_handler)
app.add_routes([web.get('/{make:.*}', route_get)])
app.add_routes(['/{make:.*}', route_post)])