#! /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