3
# WARNING! All changes made to this file will be lost!
6
if sys.hexversion < 0x020400f0: from sets import Set as set
7
import os,shutil,re,tempfile
8
from waflib import Utils,Logs,Errors
18
COMPILE_TEMPLATE_SHELL='''
23
wd = getattr(tsk, 'cwd', None)
25
tsk.last_cmd = cmd = \'\'\' %s \'\'\' % s
26
return tsk.exec_command(cmd, cwd=wd, env=env.env or None)
28
COMPILE_TEMPLATE_NOSHELL='''
33
wd = getattr(tsk, 'cwd', None)
35
if isinstance(xx, str): return [xx]
37
tsk.last_cmd = lst = []
39
lst = [x for x in lst if x]
40
return tsk.exec_command(lst, cwd=wd, env=env.env or None)
42
def cache_outputs(cls):
45
bld=self.generator.bld
46
if bld.cache_global and not bld.nocache:
47
if self.can_retrieve_cache():
53
bld=self.generator.bld
55
if bld.cache_global and not bld.nocache:
56
self.put_files_cache()
61
class store_task_type(type):
62
def __init__(cls,name,bases,dict):
63
super(store_task_type,cls).__init__(name,bases,dict)
65
if name.endswith('_task'):
66
name=name.replace('_task','')
67
if name!='evil'and name!='TaskBase':
69
if getattr(cls,'run_str',None):
70
(f,dvars)=compile_fun(cls.run_str,cls.shell)
74
cls.vars.extend(dvars)
75
elif getattr(cls,'run',None)and not'hcode'in cls.__dict__:
76
cls.hcode=Utils.h_fun(cls.run)
77
if not getattr(cls,'nocache',None):
78
cls=cache_outputs(cls)
80
evil=store_task_type('evil',(object,),{})
88
def __init__(self,*k,**kw):
91
self.generator=kw['generator']
95
return'\n\t{task %r: %s %s}'%(self.__class__.__name__,id(self),str(getattr(self,'fun','')))
97
if hasattr(self,'fun'):
98
return'executing: %s\n'%self.fun.__name__
99
return self.__class__.__name__+'\n'
102
def exec_command(self,cmd,**kw):
103
bld=self.generator.bld
105
if not kw.get('cwd',None):
107
except AttributeError:
108
bld.cwd=kw['cwd']=bld.variant_dir
109
return bld.exec_command(cmd,**kw)
110
def runnable_status(self):
118
del self.generator.bld.task_sigs[self.uid()]
121
self.generator.bld.returned_tasks.append(self)
122
self.log_display(self.generator.bld)
126
self.err_msg=Utils.ex_stack()
127
self.hasrun=EXCEPTION
128
m.error_handler(self)
137
except Errors.WafError:
140
self.err_msg=Utils.ex_stack()
141
self.hasrun=EXCEPTION
144
if self.hasrun!=SUCCESS:
145
m.error_handler(self)
148
if hasattr(self,'fun'):
149
return self.fun(self)
153
def log_display(self,bld):
154
bld.to_log(self.display())
156
col1=Logs.colors(self.color)
157
col2=Logs.colors.NORMAL
158
if self.generator.bld.progress_bar==1:
159
return self.generator.bld.progress_line(len(self.generator.bld.returned_tasks),self.position[1],col1,col2)
160
if self.generator.bld.progress_bar==2:
161
ela=str(self.generator.bld.timer)
163
ins=','.join([n.name for n in self.inputs])
164
except AttributeError:
167
outs=','.join([n.name for n in self.outputs])
168
except AttributeError:
170
return'|Total %s|Current %s|Inputs %s|Outputs %s|Time %s|\n'%(self.position[1],len(self.generator.bld.returned_tasks),ins,outs,ela)
174
total=self.position[1]
176
fs='[%%%dd/%%%dd] %%s%%s%%s'%(n,n)
177
return fs%(len(self.generator.bld.returned_tasks),self.position[1],col1,s,col2)
178
def attr(self,att,default=None):
179
ret=getattr(self,att,self)
180
if ret is self:return getattr(self.__class__,att,default)
182
def hash_constraints(self):
184
tup=(str(cls.before),str(cls.after),str(cls.ext_in),str(cls.ext_out),cls.__name__,cls.hcode)
187
def format_error(self):
188
msg=getattr(self,'last_cmd','')
189
if getattr(self,"err_msg",None):
191
elif self.hasrun==CRASHED:
193
return' -> task failed (exit status %r): %r\n%r'%(self.err_code,self,msg)
194
except AttributeError:
195
return' -> task failed: %r\n%r'%(self,msg)
196
elif self.hasrun==MISSING:
197
return' -> missing files: %r\n%r'%(self,msg)
200
class Task(TaskBase):
203
def __init__(self,*k,**kw):
204
TaskBase.__init__(self,*k,**kw)
209
self.run_after=set([])
212
src_str=' '.join([a.nice_path(env)for a in self.inputs])
213
tgt_str=' '.join([a.nice_path(env)for a in self.outputs])
214
if self.outputs:sep=' -> '
216
return'%s: %s%s%s\n'%(self.__class__.__name__.replace('_task',''),src_str,sep,tgt_str)
218
return"".join(['\n\t{task %r: '%id(self),self.__class__.__name__," ",",".join([x.name for x in self.inputs])," -> ",",".join([x.name for x in self.outputs]),'}'])
222
except AttributeError:
225
up(self.__class__.__name__)
226
for x in self.inputs+self.outputs:
230
def set_inputs(self,inp):
231
if isinstance(inp,list):self.inputs+=inp
232
else:self.inputs.append(inp)
233
def set_outputs(self,out):
234
if isinstance(out,list):self.outputs+=out
235
else:self.outputs.append(out)
236
def set_run_after(self,task):
237
assert isinstance(task,TaskBase)
238
self.run_after.add(task)
240
try:return self.cache_sig
241
except AttributeError:pass
243
self.m.update(self.hcode)
244
self.sig_explicit_deps()
248
imp_sig=self.sig_implicit_deps()
249
except Errors.TaskRescan:
250
return self.signature()
251
ret=self.cache_sig=self.m.digest()
253
def runnable_status(self):
254
for t in self.run_after:
258
bld=self.generator.bld
260
new_sig=self.signature()
261
except Errors.TaskNotReady:
265
prev_sig=bld.task_sigs[key]
267
Logs.debug("task: task %r must run as it was never run before or the task code changed"%self)
269
for node in self.outputs:
271
if node.sig!=new_sig:
273
except AttributeError:
274
Logs.debug("task: task %r must run as the output nodes do not exist"%self)
276
if new_sig!=prev_sig:
280
bld=self.generator.bld
283
for node in self.outputs:
285
os.stat(node.abspath())
288
self.err_msg='-> missing file: %r'%node.abspath()
289
raise Errors.WafError(self.err_msg)
291
bld.task_sigs[self.uid()]=self.cache_sig
292
def sig_explicit_deps(self):
293
bld=self.generator.bld
295
for x in self.inputs+self.dep_nodes:
298
except(AttributeError,TypeError):
299
raise Errors.WafError('Missing node signature for %r (required by %r)'%(x,self))
301
additional_deps=bld.deps_man
302
for x in self.inputs+self.outputs:
304
d=additional_deps[id(x)]
308
if isinstance(v,bld.root.__class__):
311
except AttributeError:
312
raise Errors.WafError('Missing node signature for %r (required by %r)'%(v,self))
313
elif hasattr(v,'__call__'):
316
return self.m.digest()
318
bld=self.generator.bld
321
act_sig=bld.hash_env_vars(env,self.__class__.vars)
323
dep_vars=getattr(self,'dep_vars',None)
325
upd(bld.hash_env_vars(env,dep_vars))
326
return self.m.digest()
328
def sig_implicit_deps(self):
329
bld=self.generator.bld
331
prev=bld.task_sigs.get((key,'imp'),[])
334
if prev==self.compute_sig_implicit_deps():
338
del bld.task_sigs[(key,'imp')]
339
raise Errors.TaskRescan('rescan')
340
(nodes,names)=self.scan()
342
Logs.debug('deps: scanner for %s returned %s %s'%(str(self),str(nodes),str(names)))
343
bld.node_deps[key]=nodes
344
bld.raw_deps[key]=names
345
self.are_implicit_nodes_ready()
346
bld.task_sigs[(key,'imp')]=sig=self.compute_sig_implicit_deps()
348
def compute_sig_implicit_deps(self):
350
bld=self.generator.bld
352
self.are_implicit_nodes_ready()
354
for k in bld.node_deps.get(self.uid(),[]):
356
except AttributeError:
358
for k in bld.node_deps.get(self.uid(),[]):
361
except AttributeError:
363
raise Errors.WafError('Missing node signature for %r (for implicit dependencies %r)'%(nodes,self))
364
return self.m.digest()
365
def are_implicit_nodes_ready(self):
366
bld=self.generator.bld
368
cache=bld.dct_implicit_nodes
370
bld.dct_implicit_nodes=cache={}
374
dct=cache[bld.cur]={}
375
for tsk in bld.cur_tasks:
376
for x in tsk.outputs:
379
for x in bld.node_deps.get(self.uid(),[]):
381
self.run_after.add(dct[x])
384
for tsk in self.run_after:
386
raise Errors.TaskNotReady('not ready')
387
def can_retrieve_cache(self):
388
if not getattr(self,'outputs',None):
392
ssig=Utils.to_hex(sig)
393
dname=os.path.join(self.generator.bld.cache_global,ssig)
395
t1=os.stat(dname).st_mtime
398
for node in self.outputs:
399
orig=os.path.join(dname,node.name)
401
shutil.copy2(orig,node.abspath())
403
except(OSError,IOError):
404
Logs.debug('task: failed retrieving file')
407
t2=os.stat(dname).st_mtime
412
for node in self.outputs:
414
if self.generator.bld.progress_bar<1:
415
self.generator.bld.to_log('restoring from cache %r\n'%node.abspath())
418
def put_files_cache(self):
419
if getattr(self,'cached',None):
422
ssig=Utils.to_hex(sig)
423
dname=os.path.join(self.generator.bld.cache_global,ssig)
424
tmpdir=tempfile.mkdtemp(prefix=self.generator.bld.cache_global+os.sep+'waf')
430
for node in self.outputs:
431
dest=os.path.join(tmpdir,node.name)
432
shutil.copy2(node.abspath(),dest)
433
except(OSError,IOError):
435
shutil.rmtree(tmpdir)
440
os.rename(tmpdir,dname)
443
shutil.rmtree(tmpdir)
448
os.chmod(dname,Utils.O755)
451
def is_before(t1,t2):
452
to_list=Utils.to_list
453
for k in to_list(t2.ext_in):
454
if k in to_list(t1.ext_out):
456
if t1.__class__.__name__ in to_list(t2.after):
458
if t2.__class__.__name__ in to_list(t1.before):
461
def set_file_constraints(tasks):
462
ins=Utils.defaultdict(set)
463
outs=Utils.defaultdict(set)
465
for a in getattr(x,'inputs',[])+getattr(x,'dep_nodes',[]):
467
for a in getattr(x,'outputs',[]):
469
links=set(ins.keys()).intersection(outs.keys())
472
a.run_after.update(outs[k])
473
def set_precedence_constraints(tasks):
474
cstr_groups=Utils.defaultdict(list)
476
h=x.hash_constraints()
477
cstr_groups[h].append(x)
478
keys=list(cstr_groups.keys())
480
for i in range(maxi):
481
t1=cstr_groups[keys[i]][0]
482
for j in range(i+1,maxi):
483
t2=cstr_groups[keys[j]][0]
487
elif is_before(t2,t1):
492
for x in cstr_groups[keys[b]]:
493
x.run_after.update(cstr_groups[keys[a]])
498
reg_act=re.compile(r"(?P<backslash>\\)|(?P<dollar>\$\$)|(?P<subst>\$\{(?P<var>\w+)(?P<code>.*?)\})",re.M)
499
def compile_fun_shell(line):
503
if g('dollar'):return"$"
504
elif g('backslash'):return'\\\\'
505
elif g('subst'):extr.append((g('var'),g('code')));return"%s"
507
line=reg_act.sub(repl,line)or line
511
for(var,meth)in extr:
513
if meth:app('tsk.inputs%s'%meth)
514
else:app('" ".join([a.path_from(bld.bldnode) for a in tsk.inputs])')
516
if meth:app('tsk.outputs%s'%meth)
517
else:app('" ".join([a.path_from(bld.bldnode) for a in tsk.outputs])')
519
if meth.startswith(':'):
520
app('" ".join([env[%r] %% x for x in env[%r]])'%(var,meth[1:]))
521
dvars.extend([var,meth[1:]])
523
app('%s%s'%(var,meth))
525
if not var in dvars:dvars.append(var)
527
if parm:parm="%% (%s) "%(',\n\t\t'.join(parm))
529
c=COMPILE_TEMPLATE_SHELL%(line,parm)
530
Logs.debug('action: %s'%c)
531
return(funex(c),dvars)
532
def compile_fun_noshell(line):
536
if g('dollar'):return"$"
537
elif g('subst'):extr.append((g('var'),g('code')));return"<<|@|>>"
539
line2=reg_act.sub(repl,line)
540
params=line2.split('<<|@|>>')
545
for x in range(len(extr)):
546
params[x]=params[x].strip()
548
app("lst.extend(%r)"%params[x].split())
551
if meth:app('lst.append(tsk.inputs%s)'%meth)
552
else:app("lst.extend([a.path_from(bld.bldnode) for a in tsk.inputs])")
554
if meth:app('lst.append(tsk.outputs%s)'%meth)
555
else:app("lst.extend([a.path_from(bld.bldnode) for a in tsk.outputs])")
557
if meth.startswith(':'):
558
app('lst.extend([env[%r] %% x for x in env[%r]])'%(var,meth[1:]))
559
dvars.extend([var,meth[1:]])
561
app('lst.extend(gen.to_list(%s%s))'%(var,meth))
563
app('lst.extend(to_list(env[%r]))'%var)
564
if not var in dvars:dvars.append(var)
567
app("lst.extend(%r)"%params[-1].split())
568
fun=COMPILE_TEMPLATE_NOSHELL%"\n\t".join(buf)
569
Logs.debug('action: %s'%fun)
570
return(funex(fun),dvars)
571
def compile_fun(line,shell=False):
572
if line.find('<')>0 or line.find('>')>0 or line.find('&&')>0:
575
return compile_fun_shell(line)
577
return compile_fun_noshell(line)
578
def task_factory(name,func=None,vars=[],color='GREEN',ext_in=[],ext_out=[],before=[],after=[],shell=False,scan=None):
579
params={'vars':vars,'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,}
580
if isinstance(func,str):
581
params['run_str']=func
584
cls=type(Task)(name,(Task,),params)
589
old=cls.runnable_status
595
cls.runnable_status=always
597
def update_outputs(cls):
598
old_post_run=cls.post_run
601
for node in self.outputs:
602
node.sig=Utils.h_file(node.abspath())
603
cls.post_run=post_run
604
old_runnable_status=cls.runnable_status
605
def runnable_status(self):
606
status=old_runnable_status(self)
610
bld=self.generator.bld
611
new_sig=self.signature()
612
prev_sig=bld.task_sigs[self.uid()]
613
if prev_sig==new_sig:
614
for x in self.outputs:
623
cls.runnable_status=runnable_status