blob: 1b54a54124bc3cfca310c1ab7dff415a5473bbed [file] [log] [blame]
akmhoquefa8ee9b2014-03-14 09:06:24 -05001#! /usr/bin/env python
2# encoding: utf-8
3# WARNING! Do not edit! http://waf.googlecode.com/git/docs/wafbook/single.html#_obtaining_the_waf_file
4
5import os,shutil,re,tempfile
6from waflib import Utils,Logs,Errors
7NOT_RUN=0
8MISSING=1
9CRASHED=2
10EXCEPTION=3
11SKIPPED=8
12SUCCESS=9
13ASK_LATER=-1
14SKIP_ME=-2
15RUN_ME=-3
16COMPILE_TEMPLATE_SHELL='''
17def f(tsk):
18 env = tsk.env
19 gen = tsk.generator
20 bld = gen.bld
21 wd = getattr(tsk, 'cwd', None)
22 p = env.get_flat
23 tsk.last_cmd = cmd = \'\'\' %s \'\'\' % s
24 return tsk.exec_command(cmd, cwd=wd, env=env.env or None)
25'''
26COMPILE_TEMPLATE_NOSHELL='''
27def f(tsk):
28 env = tsk.env
29 gen = tsk.generator
30 bld = gen.bld
31 wd = getattr(tsk, 'cwd', None)
32 def to_list(xx):
33 if isinstance(xx, str): return [xx]
34 return xx
35 tsk.last_cmd = lst = []
36 %s
37 lst = [x for x in lst if x]
38 return tsk.exec_command(lst, cwd=wd, env=env.env or None)
39'''
40def cache_outputs(cls):
41 m1=cls.run
42 def run(self):
43 bld=self.generator.bld
44 if bld.cache_global and not bld.nocache:
45 if self.can_retrieve_cache():
46 return 0
47 return m1(self)
48 cls.run=run
49 m2=cls.post_run
50 def post_run(self):
51 bld=self.generator.bld
52 ret=m2(self)
53 if bld.cache_global and not bld.nocache:
54 self.put_files_cache()
55 return ret
56 cls.post_run=post_run
57 return cls
58classes={}
59class store_task_type(type):
60 def __init__(cls,name,bases,dict):
61 super(store_task_type,cls).__init__(name,bases,dict)
62 name=cls.__name__
63 if name.endswith('_task'):
64 name=name.replace('_task','')
65 if name!='evil'and name!='TaskBase':
66 global classes
67 if getattr(cls,'run_str',None):
68 (f,dvars)=compile_fun(cls.run_str,cls.shell)
69 cls.hcode=cls.run_str
70 cls.run_str=None
71 cls.run=f
72 cls.vars=list(set(cls.vars+dvars))
73 cls.vars.sort()
74 elif getattr(cls,'run',None)and not'hcode'in cls.__dict__:
75 cls.hcode=Utils.h_fun(cls.run)
76 if not getattr(cls,'nocache',None):
77 cls=cache_outputs(cls)
78 getattr(cls,'register',classes)[name]=cls
79evil=store_task_type('evil',(object,),{})
80class TaskBase(evil):
81 color='GREEN'
82 ext_in=[]
83 ext_out=[]
84 before=[]
85 after=[]
86 hcode=''
87 def __init__(self,*k,**kw):
88 self.hasrun=NOT_RUN
89 try:
90 self.generator=kw['generator']
91 except KeyError:
92 self.generator=self
93 def __repr__(self):
94 return'\n\t{task %r: %s %s}'%(self.__class__.__name__,id(self),str(getattr(self,'fun','')))
95 def __str__(self):
96 if hasattr(self,'fun'):
97 return'executing: %s\n'%self.fun.__name__
98 return self.__class__.__name__+'\n'
99 def __hash__(self):
100 return id(self)
101 def exec_command(self,cmd,**kw):
102 bld=self.generator.bld
103 try:
104 if not kw.get('cwd',None):
105 kw['cwd']=bld.cwd
106 except AttributeError:
107 bld.cwd=kw['cwd']=bld.variant_dir
108 return bld.exec_command(cmd,**kw)
109 def runnable_status(self):
110 return RUN_ME
111 def process(self):
112 m=self.master
113 if m.stop:
114 m.out.put(self)
115 return
116 try:
117 del self.generator.bld.task_sigs[self.uid()]
118 except KeyError:
119 pass
120 try:
121 self.generator.bld.returned_tasks.append(self)
122 self.log_display(self.generator.bld)
123 ret=self.run()
124 except Exception:
125 self.err_msg=Utils.ex_stack()
126 self.hasrun=EXCEPTION
127 m.error_handler(self)
128 m.out.put(self)
129 return
130 if ret:
131 self.err_code=ret
132 self.hasrun=CRASHED
133 else:
134 try:
135 self.post_run()
136 except Errors.WafError:
137 pass
138 except Exception:
139 self.err_msg=Utils.ex_stack()
140 self.hasrun=EXCEPTION
141 else:
142 self.hasrun=SUCCESS
143 if self.hasrun!=SUCCESS:
144 m.error_handler(self)
145 m.out.put(self)
146 def run(self):
147 if hasattr(self,'fun'):
148 return self.fun(self)
149 return 0
150 def post_run(self):
151 pass
152 def log_display(self,bld):
153 bld.to_log(self.display())
154 def display(self):
155 col1=Logs.colors(self.color)
156 col2=Logs.colors.NORMAL
157 master=self.master
158 def cur():
159 tmp=-1
160 if hasattr(master,'ready'):
161 tmp-=master.ready.qsize()
162 return master.processed+tmp
163 if self.generator.bld.progress_bar==1:
164 return self.generator.bld.progress_line(cur(),master.total,col1,col2)
165 if self.generator.bld.progress_bar==2:
166 ela=str(self.generator.bld.timer)
167 try:
168 ins=','.join([n.name for n in self.inputs])
169 except AttributeError:
170 ins=''
171 try:
172 outs=','.join([n.name for n in self.outputs])
173 except AttributeError:
174 outs=''
175 return'|Total %s|Current %s|Inputs %s|Outputs %s|Time %s|\n'%(master.total,cur(),ins,outs,ela)
176 s=str(self)
177 if not s:
178 return None
179 total=master.total
180 n=len(str(total))
181 fs='[%%%dd/%%%dd] %%s%%s%%s'%(n,n)
182 return fs%(cur(),total,col1,s,col2)
183 def attr(self,att,default=None):
184 ret=getattr(self,att,self)
185 if ret is self:return getattr(self.__class__,att,default)
186 return ret
187 def hash_constraints(self):
188 cls=self.__class__
189 tup=(str(cls.before),str(cls.after),str(cls.ext_in),str(cls.ext_out),cls.__name__,cls.hcode)
190 h=hash(tup)
191 return h
192 def format_error(self):
193 msg=getattr(self,'last_cmd','')
194 name=getattr(self.generator,'name','')
195 if getattr(self,"err_msg",None):
196 return self.err_msg
197 elif not self.hasrun:
198 return'task in %r was not executed for some reason: %r'%(name,self)
199 elif self.hasrun==CRASHED:
200 try:
201 return' -> task in %r failed (exit status %r): %r\n%r'%(name,self.err_code,self,msg)
202 except AttributeError:
203 return' -> task in %r failed: %r\n%r'%(name,self,msg)
204 elif self.hasrun==MISSING:
205 return' -> missing files in %r: %r\n%r'%(name,self,msg)
206 else:
207 return'invalid status for task in %r: %r'%(name,self.hasrun)
208 def colon(self,var1,var2):
209 tmp=self.env[var1]
210 if isinstance(var2,str):
211 it=self.env[var2]
212 else:
213 it=var2
214 if isinstance(tmp,str):
215 return[tmp%x for x in it]
216 else:
217 if Logs.verbose and not tmp and it:
218 Logs.warn('Missing env variable %r for task %r (generator %r)'%(var1,self,self.generator))
219 lst=[]
220 for y in it:
221 lst.extend(tmp)
222 lst.append(y)
223 return lst
224class Task(TaskBase):
225 vars=[]
226 shell=False
227 def __init__(self,*k,**kw):
228 TaskBase.__init__(self,*k,**kw)
229 self.env=kw['env']
230 self.inputs=[]
231 self.outputs=[]
232 self.dep_nodes=[]
233 self.run_after=set([])
234 def __str__(self):
235 env=self.env
236 src_str=' '.join([a.nice_path()for a in self.inputs])
237 tgt_str=' '.join([a.nice_path()for a in self.outputs])
238 if self.outputs:sep=' -> '
239 else:sep=''
240 return'%s: %s%s%s\n'%(self.__class__.__name__.replace('_task',''),src_str,sep,tgt_str)
241 def __repr__(self):
242 try:
243 ins=",".join([x.name for x in self.inputs])
244 outs=",".join([x.name for x in self.outputs])
245 except AttributeError:
246 ins=",".join([str(x)for x in self.inputs])
247 outs=",".join([str(x)for x in self.outputs])
248 return"".join(['\n\t{task %r: '%id(self),self.__class__.__name__," ",ins," -> ",outs,'}'])
249 def uid(self):
250 try:
251 return self.uid_
252 except AttributeError:
253 m=Utils.md5()
254 up=m.update
255 up(self.__class__.__name__)
256 for x in self.inputs+self.outputs:
257 up(x.abspath())
258 self.uid_=m.digest()
259 return self.uid_
260 def set_inputs(self,inp):
261 if isinstance(inp,list):self.inputs+=inp
262 else:self.inputs.append(inp)
263 def set_outputs(self,out):
264 if isinstance(out,list):self.outputs+=out
265 else:self.outputs.append(out)
266 def set_run_after(self,task):
267 assert isinstance(task,TaskBase)
268 self.run_after.add(task)
269 def signature(self):
270 try:return self.cache_sig
271 except AttributeError:pass
272 self.m=Utils.md5()
273 self.m.update(self.hcode)
274 self.sig_explicit_deps()
275 self.sig_vars()
276 if self.scan:
277 try:
278 self.sig_implicit_deps()
279 except Errors.TaskRescan:
280 return self.signature()
281 ret=self.cache_sig=self.m.digest()
282 return ret
283 def runnable_status(self):
284 for t in self.run_after:
285 if not t.hasrun:
286 return ASK_LATER
287 bld=self.generator.bld
288 try:
289 new_sig=self.signature()
290 except Errors.TaskNotReady:
291 return ASK_LATER
292 key=self.uid()
293 try:
294 prev_sig=bld.task_sigs[key]
295 except KeyError:
296 Logs.debug("task: task %r must run as it was never run before or the task code changed"%self)
297 return RUN_ME
298 for node in self.outputs:
299 try:
300 if node.sig!=new_sig:
301 return RUN_ME
302 except AttributeError:
303 Logs.debug("task: task %r must run as the output nodes do not exist"%self)
304 return RUN_ME
305 if new_sig!=prev_sig:
306 return RUN_ME
307 return SKIP_ME
308 def post_run(self):
309 bld=self.generator.bld
310 sig=self.signature()
311 for node in self.outputs:
312 try:
313 os.stat(node.abspath())
314 except OSError:
315 self.hasrun=MISSING
316 self.err_msg='-> missing file: %r'%node.abspath()
317 raise Errors.WafError(self.err_msg)
318 node.sig=sig
319 bld.task_sigs[self.uid()]=self.cache_sig
320 def sig_explicit_deps(self):
321 bld=self.generator.bld
322 upd=self.m.update
323 for x in self.inputs+self.dep_nodes:
324 try:
325 upd(x.get_bld_sig())
326 except(AttributeError,TypeError):
327 raise Errors.WafError('Missing node signature for %r (required by %r)'%(x,self))
328 if bld.deps_man:
329 additional_deps=bld.deps_man
330 for x in self.inputs+self.outputs:
331 try:
332 d=additional_deps[id(x)]
333 except KeyError:
334 continue
335 for v in d:
336 if isinstance(v,bld.root.__class__):
337 try:
338 v=v.get_bld_sig()
339 except AttributeError:
340 raise Errors.WafError('Missing node signature for %r (required by %r)'%(v,self))
341 elif hasattr(v,'__call__'):
342 v=v()
343 upd(v)
344 return self.m.digest()
345 def sig_vars(self):
346 bld=self.generator.bld
347 env=self.env
348 upd=self.m.update
349 act_sig=bld.hash_env_vars(env,self.__class__.vars)
350 upd(act_sig)
351 dep_vars=getattr(self,'dep_vars',None)
352 if dep_vars:
353 upd(bld.hash_env_vars(env,dep_vars))
354 return self.m.digest()
355 scan=None
356 def sig_implicit_deps(self):
357 bld=self.generator.bld
358 key=self.uid()
359 prev=bld.task_sigs.get((key,'imp'),[])
360 if prev:
361 try:
362 if prev==self.compute_sig_implicit_deps():
363 return prev
364 except Exception:
365 for x in bld.node_deps.get(self.uid(),[]):
366 if x.is_child_of(bld.srcnode):
367 try:
368 os.stat(x.abspath())
369 except OSError:
370 try:
371 del x.parent.children[x.name]
372 except KeyError:
373 pass
374 del bld.task_sigs[(key,'imp')]
375 raise Errors.TaskRescan('rescan')
376 (nodes,names)=self.scan()
377 if Logs.verbose:
378 Logs.debug('deps: scanner for %s returned %s %s'%(str(self),str(nodes),str(names)))
379 bld.node_deps[key]=nodes
380 bld.raw_deps[key]=names
381 self.are_implicit_nodes_ready()
382 try:
383 bld.task_sigs[(key,'imp')]=sig=self.compute_sig_implicit_deps()
384 except Exception:
385 if Logs.verbose:
386 for k in bld.node_deps.get(self.uid(),[]):
387 try:
388 k.get_bld_sig()
389 except Exception:
390 Logs.warn('Missing signature for node %r (may cause rebuilds)'%k)
391 else:
392 return sig
393 def compute_sig_implicit_deps(self):
394 upd=self.m.update
395 bld=self.generator.bld
396 self.are_implicit_nodes_ready()
397 for k in bld.node_deps.get(self.uid(),[]):
398 upd(k.get_bld_sig())
399 return self.m.digest()
400 def are_implicit_nodes_ready(self):
401 bld=self.generator.bld
402 try:
403 cache=bld.dct_implicit_nodes
404 except AttributeError:
405 bld.dct_implicit_nodes=cache={}
406 try:
407 dct=cache[bld.cur]
408 except KeyError:
409 dct=cache[bld.cur]={}
410 for tsk in bld.cur_tasks:
411 for x in tsk.outputs:
412 dct[x]=tsk
413 modified=False
414 for x in bld.node_deps.get(self.uid(),[]):
415 if x in dct:
416 self.run_after.add(dct[x])
417 modified=True
418 if modified:
419 for tsk in self.run_after:
420 if not tsk.hasrun:
421 raise Errors.TaskNotReady('not ready')
422 def can_retrieve_cache(self):
423 if not getattr(self,'outputs',None):
424 return None
425 sig=self.signature()
426 ssig=Utils.to_hex(self.uid())+Utils.to_hex(sig)
427 dname=os.path.join(self.generator.bld.cache_global,ssig)
428 try:
429 t1=os.stat(dname).st_mtime
430 except OSError:
431 return None
432 for node in self.outputs:
433 orig=os.path.join(dname,node.name)
434 try:
435 shutil.copy2(orig,node.abspath())
436 os.utime(orig,None)
437 except(OSError,IOError):
438 Logs.debug('task: failed retrieving file')
439 return None
440 try:
441 t2=os.stat(dname).st_mtime
442 except OSError:
443 return None
444 if t1!=t2:
445 return None
446 for node in self.outputs:
447 node.sig=sig
448 if self.generator.bld.progress_bar<1:
449 self.generator.bld.to_log('restoring from cache %r\n'%node.abspath())
450 self.cached=True
451 return True
452 def put_files_cache(self):
453 if getattr(self,'cached',None):
454 return None
455 if not getattr(self,'outputs',None):
456 return None
457 sig=self.signature()
458 ssig=Utils.to_hex(self.uid())+Utils.to_hex(sig)
459 dname=os.path.join(self.generator.bld.cache_global,ssig)
460 tmpdir=tempfile.mkdtemp(prefix=self.generator.bld.cache_global+os.sep+'waf')
461 try:
462 shutil.rmtree(dname)
463 except Exception:
464 pass
465 try:
466 for node in self.outputs:
467 dest=os.path.join(tmpdir,node.name)
468 shutil.copy2(node.abspath(),dest)
469 except(OSError,IOError):
470 try:
471 shutil.rmtree(tmpdir)
472 except Exception:
473 pass
474 else:
475 try:
476 os.rename(tmpdir,dname)
477 except OSError:
478 try:
479 shutil.rmtree(tmpdir)
480 except Exception:
481 pass
482 else:
483 try:
484 os.chmod(dname,Utils.O755)
485 except Exception:
486 pass
487def is_before(t1,t2):
488 to_list=Utils.to_list
489 for k in to_list(t2.ext_in):
490 if k in to_list(t1.ext_out):
491 return 1
492 if t1.__class__.__name__ in to_list(t2.after):
493 return 1
494 if t2.__class__.__name__ in to_list(t1.before):
495 return 1
496 return 0
497def set_file_constraints(tasks):
498 ins=Utils.defaultdict(set)
499 outs=Utils.defaultdict(set)
500 for x in tasks:
501 for a in getattr(x,'inputs',[])+getattr(x,'dep_nodes',[]):
502 ins[id(a)].add(x)
503 for a in getattr(x,'outputs',[]):
504 outs[id(a)].add(x)
505 links=set(ins.keys()).intersection(outs.keys())
506 for k in links:
507 for a in ins[k]:
508 a.run_after.update(outs[k])
509def set_precedence_constraints(tasks):
510 cstr_groups=Utils.defaultdict(list)
511 for x in tasks:
512 h=x.hash_constraints()
513 cstr_groups[h].append(x)
514 keys=list(cstr_groups.keys())
515 maxi=len(keys)
516 for i in range(maxi):
517 t1=cstr_groups[keys[i]][0]
518 for j in range(i+1,maxi):
519 t2=cstr_groups[keys[j]][0]
520 if is_before(t1,t2):
521 a=i
522 b=j
523 elif is_before(t2,t1):
524 a=j
525 b=i
526 else:
527 continue
528 aval=set(cstr_groups[keys[a]])
529 for x in cstr_groups[keys[b]]:
530 x.run_after.update(aval)
531def funex(c):
532 dc={}
533 exec(c,dc)
534 return dc['f']
535reg_act=re.compile(r"(?P<backslash>\\)|(?P<dollar>\$\$)|(?P<subst>\$\{(?P<var>\w+)(?P<code>.*?)\})",re.M)
536def compile_fun_shell(line):
537 extr=[]
538 def repl(match):
539 g=match.group
540 if g('dollar'):return"$"
541 elif g('backslash'):return'\\\\'
542 elif g('subst'):extr.append((g('var'),g('code')));return"%s"
543 return None
544 line=reg_act.sub(repl,line)or line
545 parm=[]
546 dvars=[]
547 app=parm.append
548 for(var,meth)in extr:
549 if var=='SRC':
550 if meth:app('tsk.inputs%s'%meth)
551 else:app('" ".join([a.path_from(bld.bldnode) for a in tsk.inputs])')
552 elif var=='TGT':
553 if meth:app('tsk.outputs%s'%meth)
554 else:app('" ".join([a.path_from(bld.bldnode) for a in tsk.outputs])')
555 elif meth:
556 if meth.startswith(':'):
557 m=meth[1:]
558 if m=='SRC':
559 m='[a.path_from(bld.bldnode) for a in tsk.inputs]'
560 elif m=='TGT':
561 m='[a.path_from(bld.bldnode) for a in tsk.outputs]'
562 elif m[:3]not in('tsk','gen','bld'):
563 dvars.extend([var,meth[1:]])
564 m='%r'%m
565 app('" ".join(tsk.colon(%r, %s))'%(var,m))
566 else:
567 app('%s%s'%(var,meth))
568 else:
569 if not var in dvars:dvars.append(var)
570 app("p('%s')"%var)
571 if parm:parm="%% (%s) "%(',\n\t\t'.join(parm))
572 else:parm=''
573 c=COMPILE_TEMPLATE_SHELL%(line,parm)
574 Logs.debug('action: %s'%c.strip().splitlines())
575 return(funex(c),dvars)
576def compile_fun_noshell(line):
577 extr=[]
578 def repl(match):
579 g=match.group
580 if g('dollar'):return"$"
581 elif g('subst'):extr.append((g('var'),g('code')));return"<<|@|>>"
582 return None
583 line2=reg_act.sub(repl,line)
584 params=line2.split('<<|@|>>')
585 assert(extr)
586 buf=[]
587 dvars=[]
588 app=buf.append
589 for x in range(len(extr)):
590 params[x]=params[x].strip()
591 if params[x]:
592 app("lst.extend(%r)"%params[x].split())
593 (var,meth)=extr[x]
594 if var=='SRC':
595 if meth:app('lst.append(tsk.inputs%s)'%meth)
596 else:app("lst.extend([a.path_from(bld.bldnode) for a in tsk.inputs])")
597 elif var=='TGT':
598 if meth:app('lst.append(tsk.outputs%s)'%meth)
599 else:app("lst.extend([a.path_from(bld.bldnode) for a in tsk.outputs])")
600 elif meth:
601 if meth.startswith(':'):
602 m=meth[1:]
603 if m=='SRC':
604 m='[a.path_from(bld.bldnode) for a in tsk.inputs]'
605 elif m=='TGT':
606 m='[a.path_from(bld.bldnode) for a in tsk.outputs]'
607 elif m[:3]not in('tsk','gen','bld'):
608 dvars.extend([var,m])
609 m='%r'%m
610 app('lst.extend(tsk.colon(%r, %s))'%(var,m))
611 else:
612 app('lst.extend(gen.to_list(%s%s))'%(var,meth))
613 else:
614 app('lst.extend(to_list(env[%r]))'%var)
615 if not var in dvars:dvars.append(var)
616 if extr:
617 if params[-1]:
618 app("lst.extend(%r)"%params[-1].split())
619 fun=COMPILE_TEMPLATE_NOSHELL%"\n\t".join(buf)
620 Logs.debug('action: %s'%fun.strip().splitlines())
621 return(funex(fun),dvars)
622def compile_fun(line,shell=False):
623 if line.find('<')>0 or line.find('>')>0 or line.find('&&')>0:
624 shell=True
625 if shell:
626 return compile_fun_shell(line)
627 else:
628 return compile_fun_noshell(line)
629def task_factory(name,func=None,vars=None,color='GREEN',ext_in=[],ext_out=[],before=[],after=[],shell=False,scan=None):
630 params={'vars':vars or[],'color':color,'name':name,'ext_in':Utils.to_list(ext_in),'ext_out':Utils.to_list(ext_out),'before':Utils.to_list(before),'after':Utils.to_list(after),'shell':shell,'scan':scan,}
631 if isinstance(func,str):
632 params['run_str']=func
633 else:
634 params['run']=func
635 cls=type(Task)(name,(Task,),params)
636 global classes
637 classes[name]=cls
638 return cls
639def always_run(cls):
640 old=cls.runnable_status
641 def always(self):
642 ret=old(self)
643 if ret==SKIP_ME:
644 ret=RUN_ME
645 return ret
646 cls.runnable_status=always
647 return cls
648def update_outputs(cls):
649 old_post_run=cls.post_run
650 def post_run(self):
651 old_post_run(self)
652 for node in self.outputs:
653 node.sig=Utils.h_file(node.abspath())
654 self.generator.bld.task_sigs[node.abspath()]=self.uid()
655 cls.post_run=post_run
656 old_runnable_status=cls.runnable_status
657 def runnable_status(self):
658 status=old_runnable_status(self)
659 if status!=RUN_ME:
660 return status
661 try:
662 bld=self.generator.bld
663 prev_sig=bld.task_sigs[self.uid()]
664 if prev_sig==self.signature():
665 for x in self.outputs:
666 if not x.sig or bld.task_sigs[x.abspath()]!=self.uid():
667 return RUN_ME
668 return SKIP_ME
669 except KeyError:
670 pass
671 except IndexError:
672 pass
673 except AttributeError:
674 pass
675 return RUN_ME
676 cls.runnable_status=runnable_status
677 return cls