346 lines
10 KiB
Python
346 lines
10 KiB
Python
#! /usr/bin/env python
|
|
# encoding: utf-8
|
|
import sys
|
|
if sys.hexversion < 0x020400f0: from sets import Set as set
|
|
import os,traceback,copy
|
|
import Build,Task,Utils,Logs,Options
|
|
from Logs import debug,error,warn
|
|
from Constants import*
|
|
typos={'sources':'source','targets':'target','include':'includes','define':'defines','importpath':'importpaths','install_var':'install_path','install_subdir':'install_path','inst_var':'install_path','inst_dir':'install_path','feature':'features',}
|
|
class register_obj(type):
|
|
def __init__(cls,name,bases,dict):
|
|
super(register_obj,cls).__init__(name,bases,dict)
|
|
name=cls.__name__
|
|
suffix='_taskgen'
|
|
if name.endswith(suffix):
|
|
task_gen.classes[name.replace(suffix,'')]=cls
|
|
class task_gen(object):
|
|
__metaclass__=register_obj
|
|
mappings={}
|
|
mapped={}
|
|
prec=Utils.DefaultDict(list)
|
|
traits=Utils.DefaultDict(set)
|
|
classes={}
|
|
def __init__(self,*kw,**kwargs):
|
|
self.prec=Utils.DefaultDict(list)
|
|
self.source=''
|
|
self.target=''
|
|
self.meths=[]
|
|
self.mappings={}
|
|
self.features=list(kw)
|
|
self.tasks=[]
|
|
self.default_chmod=O644
|
|
self.default_install_path=None
|
|
self.allnodes=[]
|
|
self.bld=kwargs.get('bld',Build.bld)
|
|
self.env=self.bld.env.copy()
|
|
self.path=self.bld.path
|
|
self.name=''
|
|
self.idx=self.bld.idx[self.path.id]=self.bld.idx.get(self.path.id,0)+1
|
|
for key,val in kwargs.iteritems():
|
|
setattr(self,key,val)
|
|
self.bld.task_manager.add_task_gen(self)
|
|
self.bld.all_task_gen.append(self)
|
|
def __str__(self):
|
|
return("<task_gen '%s' of type %s defined in %s>"%(self.name or self.target,self.__class__.__name__,str(self.path)))
|
|
def __setattr__(self,name,attr):
|
|
real=typos.get(name,name)
|
|
if real!=name:
|
|
warn('typo %s -> %s'%(name,real))
|
|
if Logs.verbose>0:
|
|
traceback.print_stack()
|
|
object.__setattr__(self,real,attr)
|
|
def to_list(self,value):
|
|
if isinstance(value,str):return value.split()
|
|
else:return value
|
|
def apply(self):
|
|
keys=set(self.meths)
|
|
self.features=Utils.to_list(self.features)
|
|
for x in self.features+['*']:
|
|
st=task_gen.traits[x]
|
|
if not st:
|
|
warn('feature %r does not exist - bind at least one method to it'%x)
|
|
keys.update(st)
|
|
prec={}
|
|
prec_tbl=self.prec or task_gen.prec
|
|
for x in prec_tbl:
|
|
if x in keys:
|
|
prec[x]=prec_tbl[x]
|
|
tmp=[]
|
|
for a in keys:
|
|
for x in prec.values():
|
|
if a in x:break
|
|
else:
|
|
tmp.append(a)
|
|
out=[]
|
|
while tmp:
|
|
e=tmp.pop()
|
|
if e in keys:out.append(e)
|
|
try:
|
|
nlst=prec[e]
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
del prec[e]
|
|
for x in nlst:
|
|
for y in prec:
|
|
if x in prec[y]:
|
|
break
|
|
else:
|
|
tmp.append(x)
|
|
if prec:raise Utils.WafError("graph has a cycle %s"%str(prec))
|
|
out.reverse()
|
|
self.meths=out
|
|
debug('task_gen: posting %s %d',self,id(self))
|
|
for x in out:
|
|
try:
|
|
v=getattr(self,x)
|
|
except AttributeError:
|
|
raise Utils.WafError("tried to retrieve %s which is not a valid method"%x)
|
|
debug('task_gen: -> %s (%d)',x,id(self))
|
|
v()
|
|
def post(self):
|
|
if not self.name:
|
|
if isinstance(self.target,list):
|
|
self.name=' '.join(self.target)
|
|
else:
|
|
self.name=self.target
|
|
if getattr(self,'posted',None):
|
|
return
|
|
self.apply()
|
|
self.posted=True
|
|
debug('task_gen: posted %s',self.name)
|
|
def get_hook(self,ext):
|
|
try:return self.mappings[ext]
|
|
except KeyError:
|
|
try:return task_gen.mappings[ext]
|
|
except KeyError:return None
|
|
def create_task(self,name,src=None,tgt=None,env=None):
|
|
env=env or self.env
|
|
task=Task.TaskBase.classes[name](env.copy(),generator=self)
|
|
if src:
|
|
task.set_inputs(src)
|
|
if tgt:
|
|
task.set_outputs(tgt)
|
|
self.tasks.append(task)
|
|
return task
|
|
def name_to_obj(self,name):
|
|
return self.bld.name_to_obj(name,self.env)
|
|
def find_sources_in_dirs(self,dirnames,excludes=[],exts=[]):
|
|
err_msg="'%s' attribute must be a list"
|
|
if not isinstance(excludes,list):
|
|
raise Utils.WscriptError(err_msg%'excludes')
|
|
if not isinstance(exts,list):
|
|
raise Utils.WscriptError(err_msg%'exts')
|
|
lst=[]
|
|
dirnames=self.to_list(dirnames)
|
|
ext_lst=exts or list(self.mappings.keys())+list(task_gen.mappings.keys())
|
|
for name in dirnames:
|
|
anode=self.path.find_dir(name)
|
|
if not anode or not anode.is_child_of(self.bld.srcnode):
|
|
raise Utils.WscriptError("Unable to use '%s' - either because it's not a relative path"", or it's not child of '%s'."%(name,self.bld.srcnode))
|
|
self.bld.rescan(anode)
|
|
for name in self.bld.cache_dir_contents[anode.id]:
|
|
if name.startswith('.'):
|
|
continue
|
|
(base,ext)=os.path.splitext(name)
|
|
if ext in ext_lst and not name in lst and not name in excludes:
|
|
lst.append((anode.relpath_gen(self.path)or'.')+os.path.sep+name)
|
|
lst.sort()
|
|
self.source=self.to_list(self.source)
|
|
if not self.source:self.source=lst
|
|
else:self.source+=lst
|
|
def clone(self,env):
|
|
newobj=task_gen(bld=self.bld)
|
|
for x in self.__dict__:
|
|
if x in['env','bld']:
|
|
continue
|
|
elif x in["path","features"]:
|
|
setattr(newobj,x,getattr(self,x))
|
|
else:
|
|
setattr(newobj,x,copy.copy(getattr(self,x)))
|
|
newobj.__class__=self.__class__
|
|
if isinstance(env,str):
|
|
newobj.env=self.bld.all_envs[env].copy()
|
|
else:
|
|
newobj.env=env.copy()
|
|
return newobj
|
|
def get_inst_path(self):
|
|
return getattr(self,'_install_path',getattr(self,'default_install_path',''))
|
|
def set_inst_path(self,val):
|
|
self._install_path=val
|
|
install_path=property(get_inst_path,set_inst_path)
|
|
def get_chmod(self):
|
|
return getattr(self,'_chmod',getattr(self,'default_chmod',O644))
|
|
def set_chmod(self,val):
|
|
self._chmod=val
|
|
chmod=property(get_chmod,set_chmod)
|
|
def declare_extension(var,func):
|
|
try:
|
|
for x in Utils.to_list(var):
|
|
task_gen.mappings[x]=func
|
|
except:
|
|
raise Utils.WscriptError('declare_extension takes either a list or a string %r'%var)
|
|
task_gen.mapped[func.__name__]=func
|
|
def declare_order(*k):
|
|
assert(len(k)>1)
|
|
n=len(k)-1
|
|
for i in xrange(n):
|
|
f1=k[i]
|
|
f2=k[i+1]
|
|
if not f1 in task_gen.prec[f2]:
|
|
task_gen.prec[f2].append(f1)
|
|
def declare_chain(name='',action='',ext_in='',ext_out='',reentrant=True,color='BLUE',install=0,before=[],after=[],decider=None,rule=None,scan=None):
|
|
action=action or rule
|
|
if isinstance(action,str):
|
|
act=Task.simple_task_type(name,action,color=color)
|
|
else:
|
|
act=Task.task_type_from_func(name,action,color=color)
|
|
act.ext_in=tuple(Utils.to_list(ext_in))
|
|
act.ext_out=tuple(Utils.to_list(ext_out))
|
|
act.before=Utils.to_list(before)
|
|
act.after=Utils.to_list(after)
|
|
act.scan=scan
|
|
def x_file(self,node):
|
|
if decider:
|
|
ext=decider(self,node)
|
|
else:
|
|
ext=ext_out
|
|
if isinstance(ext,str):
|
|
out_source=node.change_ext(ext)
|
|
if reentrant:
|
|
self.allnodes.append(out_source)
|
|
elif isinstance(ext,list):
|
|
out_source=[node.change_ext(x)for x in ext]
|
|
if reentrant:
|
|
for i in xrange((reentrant is True)and len(out_source)or reentrant):
|
|
self.allnodes.append(out_source[i])
|
|
else:
|
|
raise Utils.WafError("do not know how to process %s"%str(ext))
|
|
tsk=self.create_task(name,node,out_source)
|
|
if node.__class__.bld.is_install:
|
|
tsk.install=install
|
|
declare_extension(act.ext_in,x_file)
|
|
return x_file
|
|
def bind_feature(name,methods):
|
|
lst=Utils.to_list(methods)
|
|
task_gen.traits[name].update(lst)
|
|
def taskgen(func):
|
|
setattr(task_gen,func.__name__,func)
|
|
return func
|
|
def feature(*k):
|
|
def deco(func):
|
|
setattr(task_gen,func.__name__,func)
|
|
for name in k:
|
|
task_gen.traits[name].update([func.__name__])
|
|
return func
|
|
return deco
|
|
def before(*k):
|
|
def deco(func):
|
|
setattr(task_gen,func.__name__,func)
|
|
for fun_name in k:
|
|
if not func.__name__ in task_gen.prec[fun_name]:
|
|
task_gen.prec[fun_name].append(func.__name__)
|
|
return func
|
|
return deco
|
|
def after(*k):
|
|
def deco(func):
|
|
setattr(task_gen,func.__name__,func)
|
|
for fun_name in k:
|
|
if not fun_name in task_gen.prec[func.__name__]:
|
|
task_gen.prec[func.__name__].append(fun_name)
|
|
return func
|
|
return deco
|
|
def extension(var):
|
|
def deco(func):
|
|
setattr(task_gen,func.__name__,func)
|
|
try:
|
|
for x in Utils.to_list(var):
|
|
task_gen.mappings[x]=func
|
|
except:
|
|
raise Utils.WafError('extension takes either a list or a string %r'%var)
|
|
task_gen.mapped[func.__name__]=func
|
|
return func
|
|
return deco
|
|
def apply_core(self):
|
|
find_resource=self.path.find_resource
|
|
for filename in self.to_list(self.source):
|
|
x=self.get_hook(filename)
|
|
if x:
|
|
x(self,filename)
|
|
else:
|
|
node=find_resource(filename)
|
|
if not node:raise Utils.WafError("source not found: '%s' in '%s'"%(filename,str(self.path)))
|
|
self.allnodes.append(node)
|
|
for node in self.allnodes:
|
|
x=self.get_hook(node.suffix())
|
|
if not x:
|
|
raise Utils.WafError("Cannot guess how to process %s (got mappings %r in %r) -> try conf.check_tool(..)?"%(str(node),self.__class__.mappings.keys(),self.__class__))
|
|
x(self,node)
|
|
feature('*')(apply_core)
|
|
def exec_rule(self):
|
|
if not getattr(self,'rule',None):
|
|
return
|
|
try:
|
|
self.meths.remove('apply_core')
|
|
except ValueError:
|
|
pass
|
|
func=self.rule
|
|
vars2=[]
|
|
if isinstance(func,str):
|
|
(func,vars2)=Task.compile_fun('',self.rule,shell=getattr(self,'shell',True))
|
|
func.code=self.rule
|
|
name=getattr(self,'name',None)or self.target or self.rule
|
|
if not isinstance(name,str):
|
|
name=str(self.idx)
|
|
cls=Task.task_type_from_func(name,func,getattr(self,'vars',vars2))
|
|
cls.color=getattr(self,'color','BLUE')
|
|
tsk=self.create_task(name)
|
|
dep_vars=getattr(self,'dep_vars',['ruledeps'])
|
|
if dep_vars:
|
|
tsk.dep_vars=dep_vars
|
|
if isinstance(self.rule,str):
|
|
tsk.env.ruledeps=self.rule
|
|
else:
|
|
tsk.env.ruledeps=Utils.h_fun(self.rule)
|
|
if getattr(self,'target',None):
|
|
cls.quiet=True
|
|
tsk.outputs=[self.path.find_or_declare(x)for x in self.to_list(self.target)]
|
|
if getattr(self,'source',None):
|
|
cls.quiet=True
|
|
tsk.inputs=[]
|
|
for x in self.to_list(self.source):
|
|
y=self.path.find_resource(x)
|
|
if not y:
|
|
raise Utils.WafError('input file %r could not be found (%r)'%(x,self.path.abspath()))
|
|
tsk.inputs.append(y)
|
|
if self.allnodes:
|
|
tsk.inputs.extend(self.allnodes)
|
|
if getattr(self,'scan',None):
|
|
cls.scan=self.scan
|
|
if getattr(self,'install_path',None):
|
|
tsk.install_path=self.install_path
|
|
if getattr(self,'cwd',None):
|
|
tsk.cwd=self.cwd
|
|
if getattr(self,'on_results',None):
|
|
Task.update_outputs(cls)
|
|
if getattr(self,'always',None):
|
|
Task.always_run(cls)
|
|
for x in['after','before','ext_in','ext_out']:
|
|
setattr(cls,x,getattr(self,x,[]))
|
|
feature('*')(exec_rule)
|
|
before('apply_core')(exec_rule)
|
|
def sequence_order(self):
|
|
if self.meths and self.meths[-1]!='sequence_order':
|
|
self.meths.append('sequence_order')
|
|
return
|
|
if getattr(self,'seq_start',None):
|
|
return
|
|
if getattr(self.bld,'prev',None):
|
|
self.bld.prev.post()
|
|
for x in self.bld.prev.tasks:
|
|
for y in self.tasks:
|
|
y.set_run_after(x)
|
|
self.bld.prev=self
|
|
feature('seq')(sequence_order)
|
|
|