midori/waf-modules/wafadmin/Node.py
2013-10-23 20:26:27 -07:00

496 lines
13 KiB
Python

#! /usr/bin/env python
# encoding: utf-8
import sys
if sys.hexversion < 0x020400f0: from sets import Set as set
import os,sys,fnmatch,re,stat
import Utils,Constants
UNDEFINED=0
DIR=1
FILE=2
BUILD=3
type_to_string={UNDEFINED:"unk",DIR:"dir",FILE:"src",BUILD:"bld"}
prune_pats='.git .bzr .hg .svn _MTN _darcs CVS SCCS'.split()
exclude_pats=prune_pats+'*~ #*# .#* %*% ._* .gitignore .cvsignore vssver.scc .DS_Store'.split()
exclude_regs='''
**/*~
**/#*#
**/.#*
**/%*%
**/._*
**/CVS
**/CVS/**
**/.cvsignore
**/SCCS
**/SCCS/**
**/vssver.scc
**/.svn
**/.svn/**
**/.git
**/.git/**
**/.gitignore
**/.bzr
**/.bzr/**
**/.hg
**/.hg/**
**/_MTN
**/_MTN/**
**/_darcs
**/_darcs/**
**/.DS_Store'''
class Node(object):
__slots__=("name","parent","id","childs")
def __init__(self,name,parent,node_type=UNDEFINED):
self.name=name
self.parent=parent
self.__class__.bld.id_nodes+=4
self.id=self.__class__.bld.id_nodes+node_type
if node_type==DIR:self.childs={}
if parent and name in parent.childs:
raise Utils.WafError('node %s exists in the parent files %r already'%(name,parent))
if parent:parent.childs[name]=self
def __setstate__(self,data):
if len(data)==4:
(self.parent,self.name,self.id,self.childs)=data
else:
(self.parent,self.name,self.id)=data
def __getstate__(self):
if getattr(self,'childs',None)is None:
return(self.parent,self.name,self.id)
else:
return(self.parent,self.name,self.id,self.childs)
def __str__(self):
if not self.parent:return''
return"%s://%s"%(type_to_string[self.id&3],self.abspath())
def __repr__(self):
return self.__str__()
def __hash__(self):
raise Utils.WafError('nodes, you are doing it wrong')
def __copy__(self):
raise Utils.WafError('nodes are not supposed to be cloned')
def get_type(self):
return self.id&3
def set_type(self,t):
self.id=self.id+t-self.id&3
def dirs(self):
return[x for x in self.childs.values()if x.id&3==DIR]
def files(self):
return[x for x in self.childs.values()if x.id&3==FILE]
def get_dir(self,name,default=None):
node=self.childs.get(name,None)
if not node or node.id&3!=DIR:return default
return node
def get_file(self,name,default=None):
node=self.childs.get(name,None)
if not node or node.id&3!=FILE:return default
return node
def get_build(self,name,default=None):
node=self.childs.get(name,None)
if not node or node.id&3!=BUILD:return default
return node
def find_resource(self,lst):
if isinstance(lst,str):
lst=Utils.split_path(lst)
if len(lst)==1:
parent=self
else:
parent=self.find_dir(lst[:-1])
if not parent:return None
self.__class__.bld.rescan(parent)
name=lst[-1]
node=parent.childs.get(name,None)
if node:
tp=node.id&3
if tp==FILE or tp==BUILD:
return node
else:
return None
tree=self.__class__.bld
if not name in tree.cache_dir_contents[parent.id]:
return None
path=parent.abspath()+os.sep+name
try:
st=Utils.h_file(path)
except IOError:
return None
child=self.__class__(name,parent,FILE)
tree.node_sigs[0][child.id]=st
return child
def find_or_declare(self,lst):
if isinstance(lst,str):
lst=Utils.split_path(lst)
if len(lst)==1:
parent=self
else:
parent=self.find_dir(lst[:-1])
if not parent:return None
self.__class__.bld.rescan(parent)
name=lst[-1]
node=parent.childs.get(name,None)
if node:
tp=node.id&3
if tp!=BUILD:
raise Utils.WafError('find_or_declare found a source file where a build file was expected %r'%'/'.join(lst))
return node
node=self.__class__(name,parent,BUILD)
return node
def find_dir(self,lst):
if isinstance(lst,str):
lst=Utils.split_path(lst)
current=self
for name in lst:
self.__class__.bld.rescan(current)
prev=current
if not current.parent and name==current.name:
continue
elif not name:
continue
elif name=='.':
continue
elif name=='..':
current=current.parent or current
else:
current=prev.childs.get(name,None)
if current is None:
dir_cont=self.__class__.bld.cache_dir_contents
if prev.id in dir_cont and name in dir_cont[prev.id]:
if not prev.name:
if os.sep=='/':
dirname=os.sep+name
else:
dirname=name
else:
dirname=prev.abspath()+os.sep+name
if not os.path.isdir(dirname):
return None
current=self.__class__(name,prev,DIR)
elif(not prev.name and len(name)==2 and name[1]==':')or name.startswith('\\\\'):
current=self.__class__(name,prev,DIR)
else:
return None
else:
if current.id&3!=DIR:
return None
return current
def ensure_dir_node_from_path(self,lst):
if isinstance(lst,str):
lst=Utils.split_path(lst)
current=self
for name in lst:
if not name:
continue
elif name=='.':
continue
elif name=='..':
current=current.parent or current
else:
prev=current
current=prev.childs.get(name,None)
if current is None:
current=self.__class__(name,prev,DIR)
return current
def exclusive_build_node(self,path):
lst=Utils.split_path(path)
name=lst[-1]
if len(lst)>1:
parent=None
try:
parent=self.find_dir(lst[:-1])
except OSError:
pass
if not parent:
parent=self.ensure_dir_node_from_path(lst[:-1])
self.__class__.bld.rescan(parent)
else:
try:
self.__class__.bld.rescan(parent)
except OSError:
pass
else:
parent=self
node=parent.childs.get(name,None)
if not node:
node=self.__class__(name,parent,BUILD)
return node
def path_to_parent(self,parent):
lst=[]
p=self
h1=parent.height()
h2=p.height()
while h2>h1:
h2-=1
lst.append(p.name)
p=p.parent
if lst:
lst.reverse()
ret=os.path.join(*lst)
else:
ret=''
return ret
def find_ancestor(self,node):
dist=self.height()-node.height()
if dist<0:return node.find_ancestor(self)
cand=self
while dist>0:
cand=cand.parent
dist-=1
if cand==node:return cand
cursor=node
while cand.parent:
cand=cand.parent
cursor=cursor.parent
if cand==cursor:return cand
def relpath_gen(self,from_node):
if self==from_node:return'.'
if from_node.parent==self:return'..'
ancestor=self.find_ancestor(from_node)
lst=[]
cand=self
while not cand.id==ancestor.id:
lst.append(cand.name)
cand=cand.parent
cand=from_node
while not cand.id==ancestor.id:
lst.append('..')
cand=cand.parent
lst.reverse()
return os.sep.join(lst)
def nice_path(self,env=None):
tree=self.__class__.bld
ln=tree.launch_node()
if self.id&3==FILE:return self.relpath_gen(ln)
else:return os.path.join(tree.bldnode.relpath_gen(ln),env.variant(),self.relpath_gen(tree.srcnode))
def is_child_of(self,node):
p=self
diff=self.height()-node.height()
while diff>0:
diff-=1
p=p.parent
return p.id==node.id
def variant(self,env):
if not env:return 0
elif self.id&3==FILE:return 0
else:return env.variant()
def height(self):
d=self
val=-1
while d:
d=d.parent
val+=1
return val
def abspath(self,env=None):
variant=(env and(self.id&3!=FILE)and env.variant())or 0
ret=self.__class__.bld.cache_node_abspath[variant].get(self.id,None)
if ret:return ret
if not variant:
if not self.parent:
val=os.sep=='/'and os.sep or''
elif not self.parent.name:
val=(os.sep=='/'and os.sep or'')+self.name
else:
val=self.parent.abspath()+os.sep+self.name
else:
val=os.sep.join((self.__class__.bld.bldnode.abspath(),variant,self.path_to_parent(self.__class__.bld.srcnode)))
self.__class__.bld.cache_node_abspath[variant][self.id]=val
return val
def change_ext(self,ext):
name=self.name
k=name.rfind('.')
if k>=0:
name=name[:k]+ext
else:
name=name+ext
return self.parent.find_or_declare([name])
def src_dir(self,env):
return self.parent.srcpath(env)
def bld_dir(self,env):
return self.parent.bldpath(env)
def bld_base(self,env):
s=os.path.splitext(self.name)[0]
return os.path.join(self.bld_dir(env),s)
def bldpath(self,env=None):
if self.id&3==FILE:
return self.relpath_gen(self.__class__.bld.bldnode)
p=self.path_to_parent(self.__class__.bld.srcnode)
if p is not'':
return env.variant()+os.sep+p
return env.variant()
def srcpath(self,env=None):
if self.id&3==BUILD:
return self.bldpath(env)
return self.relpath_gen(self.__class__.bld.bldnode)
def read(self,env):
return Utils.readf(self.abspath(env))
def dir(self,env):
return self.parent.abspath(env)
def file(self):
return self.name
def file_base(self):
return os.path.splitext(self.name)[0]
def suffix(self):
k=max(0,self.name.rfind('.'))
return self.name[k:]
def find_iter_impl(self,src=True,bld=True,dir=True,accept_name=None,is_prune=None,maxdepth=25):
bld_ctx=self.__class__.bld
bld_ctx.rescan(self)
for name in bld_ctx.cache_dir_contents[self.id]:
if accept_name(self,name):
node=self.find_resource(name)
if node:
if src and node.id&3==FILE:
yield node
else:
node=self.find_dir(name)
if node and node.id!=bld_ctx.bldnode.id:
if dir:
yield node
if not is_prune(self,name):
if maxdepth:
for k in node.find_iter_impl(src,bld,dir,accept_name,is_prune,maxdepth=maxdepth-1):
yield k
else:
if not is_prune(self,name):
node=self.find_resource(name)
if not node:
node=self.find_dir(name)
if node and node.id!=bld_ctx.bldnode.id:
if maxdepth:
for k in node.find_iter_impl(src,bld,dir,accept_name,is_prune,maxdepth=maxdepth-1):
yield k
if bld:
for node in self.childs.values():
if node.id==bld_ctx.bldnode.id:
continue
if node.id&3==BUILD:
if accept_name(self,node.name):
yield node
raise StopIteration
def find_iter(self,in_pat=['*'],ex_pat=exclude_pats,prune_pat=prune_pats,src=True,bld=True,dir=False,maxdepth=25,flat=False):
if not(src or bld or dir):
raise StopIteration
if self.id&3!=DIR:
raise StopIteration
in_pat=Utils.to_list(in_pat)
ex_pat=Utils.to_list(ex_pat)
prune_pat=Utils.to_list(prune_pat)
def accept_name(node,name):
for pat in ex_pat:
if fnmatch.fnmatchcase(name,pat):
return False
for pat in in_pat:
if fnmatch.fnmatchcase(name,pat):
return True
return False
def is_prune(node,name):
for pat in prune_pat:
if fnmatch.fnmatchcase(name,pat):
return True
return False
ret=self.find_iter_impl(src,bld,dir,accept_name,is_prune,maxdepth=maxdepth)
if flat:
return" ".join([x.relpath_gen(self)for x in ret])
return ret
def ant_glob(self,*k,**kw):
src=kw.get('src',1)
bld=kw.get('bld',0)
dir=kw.get('dir',0)
excl=kw.get('excl',exclude_regs)
incl=k and k[0]or kw.get('incl','**')
def to_pat(s):
lst=Utils.to_list(s)
ret=[]
for x in lst:
x=x.replace('//','/')
if x.endswith('/'):
x+='**'
lst2=x.split('/')
accu=[]
for k in lst2:
if k=='**':
accu.append(k)
else:
k=k.replace('.','[.]').replace('*','.*').replace('?','.')
k='^%s$'%k
accu.append(re.compile(k))
ret.append(accu)
return ret
def filtre(name,nn):
ret=[]
for lst in nn:
if not lst:
pass
elif lst[0]=='**':
ret.append(lst)
if len(lst)>1:
if lst[1].match(name):
ret.append(lst[2:])
else:
ret.append([])
elif lst[0].match(name):
ret.append(lst[1:])
return ret
def accept(name,pats):
nacc=filtre(name,pats[0])
nrej=filtre(name,pats[1])
if[]in nrej:
nacc=[]
return[nacc,nrej]
def ant_iter(nodi,maxdepth=25,pats=[]):
nodi.__class__.bld.rescan(nodi)
tmp=list(nodi.__class__.bld.cache_dir_contents[nodi.id])
tmp.sort()
for name in tmp:
npats=accept(name,pats)
if npats and npats[0]:
accepted=[]in npats[0]
node=nodi.find_resource(name)
if node and accepted:
if src and node.id&3==FILE:
yield node
else:
node=nodi.find_dir(name)
if node and node.id!=nodi.__class__.bld.bldnode.id:
if accepted and dir:
yield node
if maxdepth:
for k in ant_iter(node,maxdepth=maxdepth-1,pats=npats):
yield k
if bld:
for node in nodi.childs.values():
if node.id==nodi.__class__.bld.bldnode.id:
continue
if node.id&3==BUILD:
npats=accept(node.name,pats)
if npats and npats[0]and[]in npats[0]:
yield node
raise StopIteration
ret=[x for x in ant_iter(self,pats=[to_pat(incl),to_pat(excl)])]
if kw.get('flat',True):
return" ".join([x.relpath_gen(self)for x in ret])
return ret
def update_build_dir(self,env=None):
if not env:
for env in bld.all_envs:
self.update_build_dir(env)
return
path=self.abspath(env)
lst=Utils.listdir(path)
try:
self.__class__.bld.cache_dir_contents[self.id].update(lst)
except KeyError:
self.__class__.bld.cache_dir_contents[self.id]=set(lst)
self.__class__.bld.cache_scanned_folders[self.id]=True
for k in lst:
npath=path+os.sep+k
st=os.stat(npath)
if stat.S_ISREG(st[stat.ST_MODE]):
ick=self.find_or_declare(k)
if not(ick.id in self.__class__.bld.node_sigs[env.variant()]):
self.__class__.bld.node_sigs[env.variant()][ick.id]=Constants.SIG_NIL
elif stat.S_ISDIR(st[stat.ST_MODE]):
child=self.find_dir(k)
if not child:
child=self.ensure_dir_node_from_path(k)
child.update_build_dir(env)
class Nodu(Node):
pass