daemon.py (3090B)
1 #!/usr/bin/env python 2 # encoding: utf-8 3 # Matthias Jahn 2006 4 # rewritten by Thomas Nagy 2009 5 6 """ 7 Start a new build as soon as something changes in the build directory. 8 9 PyInotify, Fam, Gamin or time-threshold are used for the detection 10 11 For now only PyInotify and time threshold are supported 12 Watching for new svn revisions could be added too 13 """ 14 15 import select, errno, os, time 16 from waflib import Utils, Scripting, Logs, Build, Node, Context, Options 17 18 w_pyinotify = w_fam = w_gamin = None 19 def check_support(): 20 global w_pyinotify, w_fam, w_gamin 21 try: 22 import pyinotify as w_pyinotify 23 except ImportError: 24 w_pyinotify = None 25 else: 26 try: 27 wm = w_pyinotify.WatchManager() 28 wm = w_pyinotify.Notifier(wm) 29 wm = None 30 except: 31 raise 32 w_pyinotify = None 33 34 try: 35 import gamin as w_gamin 36 except ImportError: 37 w_gamin = None 38 else: 39 try: 40 test = w_gamin.WatchMonitor() 41 test.disconnect() 42 test = None 43 except: 44 w_gamin = None 45 46 try: 47 import _fam as w_fam 48 except ImportError: 49 w_fam = None 50 else: 51 try: 52 test = w_fam.open() 53 test.close() 54 test = None 55 except: 56 w_fam = None 57 58 def daemon(ctx): 59 """waf command: rebuild as soon as something changes""" 60 bld = None 61 while True: 62 bld = Context.create_context('build') 63 try: 64 bld.options = Options.options 65 bld.cmd = 'build' 66 bld.execute() 67 except ctx.errors.WafError as e: 68 print(e) 69 except KeyboardInterrupt: 70 Utils.pprint('RED', 'interrupted') 71 break 72 73 try: 74 x = ctx.state 75 except AttributeError: 76 setattr(ctx, 'state', DirWatch()) 77 x = ctx.state 78 79 x.wait(bld) 80 81 def options(opt): 82 """So this shows how to add new commands from tools""" 83 Context.g_module.__dict__['daemon'] = daemon 84 85 class DirWatch(object): 86 def __init__(self): 87 check_support() 88 if w_pyinotify: 89 self.sup = 'pyinotify' 90 elif w_gamin: 91 self.sup = 'gamin' 92 elif w_fam: 93 self.sup = 'fam' 94 else: 95 self.sup = 'dumb' 96 #self.sup = 'dumb' 97 98 def wait(self, bld): 99 return getattr(self.__class__, 'wait_' + self.sup)(self, bld) 100 101 def enumerate(self, node): 102 if os.path.exists(node.abspath()): 103 yield node.abspath() 104 try: 105 for x in node.children.values(): 106 for k in self.enumerate(x): 107 yield k 108 except AttributeError: 109 pass 110 111 def wait_pyinotify(self, bld): 112 113 class PE(w_pyinotify.ProcessEvent): 114 def stop(self, event): 115 self.notif.ev = True 116 self.notif.stop() 117 raise ValueError("stop for delete") 118 119 process_IN_DELETE = stop 120 process_IN_CLOSE = stop 121 process_default = stop 122 123 proc = PE() 124 wm = w_pyinotify.WatchManager() 125 notif = w_pyinotify.Notifier(wm, proc) 126 proc.notif = notif 127 128 # well, we should add all the folders to watch here 129 for x in self.enumerate(bld.srcnode): 130 wm.add_watch(x, w_pyinotify.IN_DELETE | w_pyinotify.IN_CLOSE_WRITE) 131 132 try: 133 # pyinotify uses an infinite loop ... not too nice, so we have to use an exception 134 notif.loop() 135 except ValueError: 136 pass 137 if not hasattr(notif, 'ev'): 138 raise KeyboardInterrupt 139 140 def wait_dumb(self, bld): 141 time.sleep(5) 142 143 def wait_gamin(self, bld): 144 time.sleep(5) 145 146 def wait_fam(self, bld): 147 time.sleep(5) 148