|
"""
|
|
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 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
|
|
|
|
def __init__(self,command,config):
|
|
self.command = command
|
|
self.config = config
|
|
roots = self.config['path']
|
|
self.roots = [Path(root) for root in roots]
|
|
super(FileSystemEventHandler,self).__init__()
|
|
|
|
def on_modified(self, event):
|
|
if not event.is_directory:
|
|
t = datetime.now()
|
|
src_path = Path(event.src_path).resolve()
|
|
if self.config['extensions'] is None or src_path.suffix in self.config['extensions']:
|
|
for root in self.roots:
|
|
if src_path.is_relative_to(root.resolve()):
|
|
print('{} modified at {}'.format(root / src_path.relative_to(root.resolve()),t))
|
|
if self.last_time is None or (t-self.last_time).seconds>self.gap:
|
|
self.run()
|
|
self.last_time = t
|
|
|
|
def run(self):
|
|
subprocess.call(self.command)
|
|
|
|
class WatchMaker(object):
|
|
|
|
def __init__(self,rootpath):
|
|
self.rootpath = rootpath
|
|
config = {
|
|
'path': '.',
|
|
'default_make': [],
|
|
'extensions': ['.js'],
|
|
}
|
|
|
|
try:
|
|
with open(rootpath / '.watchmakerc') as f:
|
|
config.update(yaml.load(f.read(),Loader=yaml.SafeLoader))
|
|
except IOError:
|
|
pass
|
|
|
|
self.config = config
|
|
|
|
def run(self,targets=None):
|
|
paths = self.config.get('path','.')
|
|
if isinstance(paths,str):
|
|
paths = [paths]
|
|
paths = [self.rootpath / p for p in paths]
|
|
paths = [p for p in paths if os.path.exists(p)]
|
|
|
|
print("Watching {}".format(', '.join(str(p) for p in paths)))
|
|
|
|
command = ['make']+(targets if targets else self.config['default_make'])
|
|
|
|
event_handler = MakeHandler(command,self.config)
|
|
event_handler.run()
|
|
|
|
observer = Observer()
|
|
for path in paths:
|
|
observer.schedule(event_handler,str(path),recursive=True)
|
|
observer.start()
|
|
|
|
try:
|
|
while True:
|
|
time.sleep(1)
|
|
except KeyboardInterrupt:
|
|
observer.stop()
|
|
|
|
observer.join()
|