136 lines
3.6 KiB
Python
136 lines
3.6 KiB
Python
"""
|
|
Requires the ``watchdog`` package.
|
|
|
|
Watch the current directory and its children for changes to files, and run ``make`` when certain files are changed.
|
|
|
|
Can be configured by a .watchmakerc file, containing settings in YAML format:
|
|
|
|
path = <the path to watch for changes> (default: .)
|
|
default_make: <a list of `make` targets to run> (default: empty list, so the first target in the Makefile)
|
|
extensions: <a list of file extensions that should trigger a ``make`` run> (default: '.js')
|
|
"""
|
|
|
|
from copy import deepcopy
|
|
from django.core.management.base import BaseCommand, CommandError
|
|
from django.conf import settings
|
|
from datetime import datetime
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from watchdog.observers import Observer
|
|
from watchdog.events import FileSystemEventHandler
|
|
import yaml
|
|
from pathlib import Path
|
|
|
|
class MakeHandler(FileSystemEventHandler):
|
|
|
|
last_time = None
|
|
gap = 2
|
|
|
|
extensions = None
|
|
|
|
default_config = {
|
|
'path': '.',
|
|
'default_make': [],
|
|
'extensions': ['.js'],
|
|
}
|
|
|
|
def __init__(self, root):
|
|
super().__init__()
|
|
|
|
self.configs = {}
|
|
|
|
self.root = root.resolve()
|
|
|
|
def get_think_root(self, d):
|
|
while d.parent != self.root:
|
|
d = d.parent
|
|
|
|
return d
|
|
|
|
def get_config(self, p):
|
|
d = self.get_think_root(p)
|
|
|
|
if d not in self.configs:
|
|
self.load_config(d)
|
|
|
|
return self.configs[d]
|
|
|
|
def load_config(self, d):
|
|
print(f"Fetch config for {d}")
|
|
|
|
config_path = d / '.watchmakerc'
|
|
|
|
config = deepcopy(self.default_config)
|
|
if config_path.exists():
|
|
with open(config_path) as f:
|
|
config.update(yaml.load(f.read(),Loader=yaml.SafeLoader))
|
|
|
|
self.configs[d] = config
|
|
|
|
def on_modified(self, event):
|
|
root = self.root
|
|
|
|
if event.is_directory:
|
|
return
|
|
|
|
t = datetime.now()
|
|
|
|
src_path = Path(event.src_path).resolve()
|
|
|
|
if self.extensions is None or src_path.suffix in self.extensions:
|
|
if not src_path.is_relative_to(root.resolve()):
|
|
return
|
|
|
|
print('{} modified at {}'.format(root / src_path.relative_to(root.resolve()),t))
|
|
|
|
if src_path.name == '.watchmakerc' or (self.last_time is None or (t-self.last_time).seconds > self.gap):
|
|
self.run(src_path)
|
|
|
|
self.last_time = t
|
|
|
|
def run(self, p):
|
|
d = self.get_think_root(p)
|
|
print(f"Run for {d}")
|
|
print(p.name)
|
|
if p.name == '.watchmakerc':
|
|
self.load_config(d)
|
|
config = self.get_config(p)
|
|
print(config)
|
|
|
|
if (d / 'Makefile').exists():
|
|
print("Making")
|
|
command = ['make'] + config.get('default_make', [])
|
|
res = subprocess.run(command, cwd=d, capture_output=True, encoding='utf-8')
|
|
with open(d / '.make.log', 'w') as f:
|
|
f.write(f"{datetime.now()}\n")
|
|
f.write(res.stdout)
|
|
f.write(res.stderr)
|
|
else:
|
|
print("No make")
|
|
|
|
class Command(BaseCommand):
|
|
|
|
def handle(self, *args, **options):
|
|
self.rootpath = settings.THINKS_DIR
|
|
|
|
self.run()
|
|
|
|
def run(self,targets=None):
|
|
print(f"Watching {self.rootpath}")
|
|
|
|
event_handler = MakeHandler(self.rootpath)
|
|
|
|
observer = Observer()
|
|
observer.schedule(event_handler,str(self.rootpath),recursive=True)
|
|
observer.start()
|
|
|
|
try:
|
|
while True:
|
|
time.sleep(1)
|
|
except KeyboardInterrupt:
|
|
observer.stop()
|
|
|
|
observer.join()
|
|
|