#! /usr/bin/env python # encoding: utf-8 import sys if sys.hexversion < 0x020400f0: from sets import Set as set import os,sys,errno,re,glob,gc,datetime,shutil try:import cPickle except:import pickle as cPickle import Runner,TaskGen,Node,Scripting,Utils,Environment,Task,Logs,Options from Logs import debug,error,info from Constants import* SAVED_ATTRS='root srcnode bldnode node_sigs node_deps raw_deps task_sigs id_nodes'.split() bld=None class BuildError(Utils.WafError): def __init__(self,b=None,t=[]): self.bld=b self.tasks=t self.ret=1 Utils.WafError.__init__(self,self.format_error()) def format_error(self): lst=['Build failed:'] for tsk in self.tasks: txt=tsk.format_error() if txt:lst.append(txt) sep=' ' if len(lst)>2: sep='\n' return sep.join(lst) def group_method(fun): def f(*k,**kw): if not k[0].is_install: return False postpone=True if'postpone'in kw: postpone=kw['postpone'] del kw['postpone'] if postpone: m=k[0].task_manager if not m.groups:m.add_group() m.groups[m.current_group].post_funs.append((fun,k,kw)) if not'cwd'in kw: kw['cwd']=k[0].path else: fun(*k,**kw) return f class BuildContext(Utils.Context): def __init__(self): global bld bld=self self.task_manager=Task.TaskManager() self.id_nodes=0 self.idx={} self.all_envs={} self.bdir='' self.path=None self.deps_man=Utils.DefaultDict(list) self.cache_node_abspath={} self.cache_scanned_folders={} self.uninstall=[] for v in'cache_node_abspath task_sigs node_deps raw_deps node_sigs'.split(): var={} setattr(self,v,var) self.cache_dir_contents={} self.all_task_gen=[] self.task_gen_cache_names={} self.cache_sig_vars={} self.log=None self.root=None self.srcnode=None self.bldnode=None class node_class(Node.Node): pass self.node_class=node_class self.node_class.__module__="Node" self.node_class.__name__="Nodu" self.node_class.bld=self self.is_install=None def __copy__(self): raise Utils.WafError('build contexts are not supposed to be cloned') def load(self): try: env=Environment.Environment(os.path.join(self.cachedir,'build.config.py')) except(IOError,OSError): pass else: if env['version']1:raise if data: for x in SAVED_ATTRS:setattr(self,x,data[x]) else: debug('build: Build cache loading failed') finally: if f:f.close() gc.enable() def save(self): gc.disable() self.root.__class__.bld=None Node.Nodu=self.node_class db=os.path.join(self.bdir,DBFILE) file=open(db+'.tmp','wb') data={} for x in SAVED_ATTRS:data[x]=getattr(self,x) cPickle.dump(data,file,-1) file.close() try:os.unlink(db) except OSError:pass os.rename(db+'.tmp',db) self.root.__class__.bld=self gc.enable() def clean(self): debug('build: clean called') precious=set([]) for env in self.all_envs.values(): for x in env[CFG_FILES]: node=self.srcnode.find_resource(x) if node: precious.add(node.id) def clean_rec(node): for x in list(node.childs.keys()): nd=node.childs[x] tp=nd.id&3 if tp==Node.DIR: clean_rec(nd) elif tp==Node.BUILD: if nd.id in precious:continue for env in self.all_envs.values(): try:os.remove(nd.abspath(env)) except OSError:pass node.childs.__delitem__(x) clean_rec(self.srcnode) for v in'node_sigs node_deps task_sigs raw_deps cache_node_abspath'.split(): setattr(self,v,{}) def compile(self): debug('build: compile called') self.flush() self.generator=Runner.Parallel(self,Options.options.jobs) def dw(on=True): if Options.options.progress_bar: if on:sys.stderr.write(Logs.colors.cursor_on) else:sys.stderr.write(Logs.colors.cursor_off) debug('build: executor starting') back=os.getcwd() os.chdir(self.bldnode.abspath()) try: try: dw(on=False) self.generator.start() except KeyboardInterrupt: dw() self.save() raise except Exception: dw() raise else: dw() self.save() if self.generator.error: raise BuildError(self,self.task_manager.tasks_done) finally: os.chdir(back) def install(self): debug('build: install called') self.flush() if self.is_install<0: lst=[] for x in self.uninstall: dir=os.path.dirname(x) if not dir in lst:lst.append(dir) lst.sort() lst.reverse() nlst=[] for y in lst: x=y while len(x)>4: if not x in nlst:nlst.append(x) x=os.path.dirname(x) nlst.sort() nlst.reverse() for x in nlst: try:os.rmdir(x) except OSError:pass def new_task_gen(self,*k,**kw): if self.task_gen_cache_names: self.task_gen_cache_names={} kw['bld']=self if len(k)==0: ret=TaskGen.task_gen(*k,**kw) else: cls_name=k[0] try:cls=TaskGen.task_gen.classes[cls_name] except KeyError:raise Utils.WscriptError('%s is not a valid task generator -> %s'%(cls_name,[x for x in TaskGen.task_gen.classes])) ret=cls(*k,**kw) return ret def __call__(self,*k,**kw): if self.task_gen_cache_names: self.task_gen_cache_names={} kw['bld']=self return TaskGen.task_gen(*k,**kw) def load_envs(self): try: lst=Utils.listdir(self.cachedir) except OSError,e: if e.errno==errno.ENOENT: raise Utils.WafError('The project was not configured: run "waf configure" first!') else: raise if not lst: raise Utils.WafError('The cache directory is empty: reconfigure the project') for file in lst: if file.endswith(CACHE_SUFFIX): env=Environment.Environment(os.path.join(self.cachedir,file)) name=file[:-len(CACHE_SUFFIX)] self.all_envs[name]=env self.init_variants() for env in self.all_envs.values(): for f in env[CFG_FILES]: newnode=self.path.find_or_declare(f) try: hash=Utils.h_file(newnode.abspath(env)) except(IOError,AttributeError): error("cannot find "+f) hash=SIG_NIL self.node_sigs[env.variant()][newnode.id]=hash self.bldnode=self.root.find_dir(self.bldnode.abspath()) self.path=self.srcnode=self.root.find_dir(self.srcnode.abspath()) self.cwd=self.bldnode.abspath() def setup(self,tool,tooldir=None,funs=None): if isinstance(tool,list): for i in tool:self.setup(i,tooldir) return if not tooldir:tooldir=Options.tooldir module=Utils.load_tool(tool,tooldir) if hasattr(module,"setup"):module.setup(self) def init_variants(self): debug('build: init variants') lstvariants=[] for env in self.all_envs.values(): if not env.variant()in lstvariants: lstvariants.append(env.variant()) self.lst_variants=lstvariants debug('build: list of variants is %r',lstvariants) for name in lstvariants+[0]: for v in'node_sigs cache_node_abspath'.split(): var=getattr(self,v) if not name in var: var[name]={} def load_dirs(self,srcdir,blddir,load_cache=1): assert(os.path.isabs(srcdir)) assert(os.path.isabs(blddir)) self.cachedir=os.path.join(blddir,CACHE_DIR) if srcdir==blddir: raise Utils.WafError("build dir must be different from srcdir: %s <-> %s "%(srcdir,blddir)) self.bdir=blddir self.load() if not self.root: Node.Nodu=self.node_class self.root=Node.Nodu('',None,Node.DIR) if not self.srcnode: self.srcnode=self.root.ensure_dir_node_from_path(srcdir) debug('build: srcnode is %s and srcdir %s',self.srcnode.name,srcdir) self.path=self.srcnode try:os.makedirs(blddir) except OSError:pass if not self.bldnode: self.bldnode=self.root.ensure_dir_node_from_path(blddir) self.init_variants() def rescan(self,src_dir_node): if self.cache_scanned_folders.get(src_dir_node.id,None):return self.cache_scanned_folders[src_dir_node.id]=True if hasattr(self,'repository'):self.repository(src_dir_node) if not src_dir_node.name and sys.platform=='win32': return parent_path=src_dir_node.abspath() try: lst=set(Utils.listdir(parent_path)) except OSError: lst=set([]) self.cache_dir_contents[src_dir_node.id]=lst cache=self.node_sigs[0] for x in src_dir_node.childs.values(): if x.id&3!=Node.FILE:continue if x.name in lst: try: cache[x.id]=Utils.h_file(x.abspath()) except IOError: raise Utils.WafError('The file %s is not readable or has become a dir'%x.abspath()) else: try:del cache[x.id] except KeyError:pass del src_dir_node.childs[x.name] h1=self.srcnode.height() h2=src_dir_node.height() lst=[] child=src_dir_node while h2>h1: lst.append(child.name) child=child.parent h2-=1 lst.reverse() try: for variant in self.lst_variants: sub_path=os.path.join(self.bldnode.abspath(),variant,*lst) self.listdir_bld(src_dir_node,sub_path,variant) except OSError: for node in src_dir_node.childs.values(): if node.id&3!=Node.BUILD: continue for dct in self.node_sigs.values(): if node.id in dct: dct.__delitem__(node.id) src_dir_node.childs.__delitem__(node.name) for variant in self.lst_variants: sub_path=os.path.join(self.bldnode.abspath(),variant,*lst) try: os.makedirs(sub_path) except OSError: pass def listdir_src(self,parent_node): pass def remove_node(self,node): pass def listdir_bld(self,parent_node,path,variant): i_existing_nodes=[x for x in parent_node.childs.values()if x.id&3==Node.BUILD] lst=set(Utils.listdir(path)) node_names=set([x.name for x in i_existing_nodes]) remove_names=node_names-lst ids_to_remove=[x.id for x in i_existing_nodes if x.name in remove_names] cache=self.node_sigs[variant] for nid in ids_to_remove: if nid in cache: cache.__delitem__(nid) def get_env(self): return self.env_of_name('default') def set_env(self,name,val): self.all_envs[name]=val env=property(get_env,set_env) def add_manual_dependency(self,path,value): if isinstance(path,Node.Node): node=path elif os.path.isabs(path): node=self.root.find_resource(path) else: node=self.path.find_resource(path) self.deps_man[node.id].append(value) def launch_node(self): try: return self.p_ln except AttributeError: self.p_ln=self.root.find_dir(Options.launch_dir) return self.p_ln def glob(self,pattern,relative=True): path=self.path.abspath() files=[self.root.find_resource(x)for x in glob.glob(path+os.sep+pattern)] if relative: files=[x.path_to_parent(self.path)for x in files if x] else: files=[x.abspath()for x in files if x] return files def add_group(self,*k): self.task_manager.add_group(*k) def set_group(self,*k,**kw): self.task_manager.set_group(*k,**kw) def hash_env_vars(self,env,vars_lst): idx=str(id(env))+str(vars_lst) try:return self.cache_sig_vars[idx] except KeyError:pass lst=[str(env[a])for a in vars_lst] ret=Utils.h_list(lst) debug('envhash: %r %r',ret,lst) self.cache_sig_vars[idx]=ret return ret def name_to_obj(self,name,env): cache=self.task_gen_cache_names if not cache: for x in self.all_task_gen: vt=x.env.variant()+'_' if x.name: cache[vt+x.name]=x else: if isinstance(x.target,str): target=x.target else: target=' '.join(x.target) v=vt+target if not cache.get(v,None): cache[v]=x return cache.get(env.variant()+'_'+name,None) def flush(self,all=1): self.ini=datetime.datetime.now() self.task_gen_cache_names={} self.name_to_obj('',self.env) debug('build: delayed operation TaskGen.flush() called') if Options.options.compile_targets: debug('task_gen: posting objects %r listed in compile_targets',Options.options.compile_targets) mana=self.task_manager to_post=[] min_grp=0 target_objects=Utils.DefaultDict(list) for target_name in Options.options.compile_targets.split(','): target_name=target_name.strip() for env in self.all_envs.values(): tg=self.name_to_obj(target_name,env) if tg: target_objects[target_name].append(tg) m=mana.group_idx(tg) if m>min_grp: min_grp=m to_post=[tg] elif m==min_grp: to_post.append(tg) if not target_name in target_objects and all: raise Utils.WafError("target '%s' does not exist"%target_name) debug('group: Forcing up to group %s for target %s',mana.group_name(min_grp),Options.options.compile_targets) for i in xrange(len(mana.groups)): mana.current_group=i if i==min_grp: break g=mana.groups[i] debug('group: Forcing group %s',mana.group_name(g)) for t in g.tasks_gen: debug('group: Posting %s',t.name or t.target) t.post() for t in to_post: t.post() else: debug('task_gen: posting objects (normal)') ln=self.launch_node() if ln.is_child_of(self.bldnode)or not ln.is_child_of(self.srcnode): ln=self.srcnode proj_node=self.root.find_dir(os.path.split(Utils.g_module.root_path)[0]) if proj_node.id!=self.srcnode.id: ln=self.srcnode for i in xrange(len(self.task_manager.groups)): g=self.task_manager.groups[i] self.task_manager.current_group=i if Logs.verbose: groups=[x for x in self.task_manager.groups_names if id(self.task_manager.groups_names[x])==id(g)] name=groups and groups[0]or'unnamed' Logs.debug('group: group',name) for tg in g.tasks_gen: if not tg.path.is_child_of(ln): continue if Logs.verbose: Logs.debug('group: %s'%tg) tg.post() def env_of_name(self,name): try: return self.all_envs[name] except KeyError: error('no such environment: '+name) return None def progress_line(self,state,total,col1,col2): n=len(str(total)) Utils.rot_idx+=1 ind=Utils.rot_chr[Utils.rot_idx%4] ini=self.ini pc=(100.*state)/total eta=Utils.get_elapsed_time(ini) fs="[%%%dd/%%%dd][%%s%%2d%%%%%%s][%s]["%(n,n,ind) left=fs%(state,total,col1,pc,col2) right='][%s%s%s]'%(col1,eta,col2) cols=Utils.get_term_cols()-len(left)-len(right)+2*len(col1)+2*len(col2) if cols<7:cols=7 ratio=int((cols*state)/total)-1 bar=('='*ratio+'>').ljust(cols) msg=Utils.indicator%(left,bar,right) return msg def do_install(self,src,tgt,chmod=O644): if self.is_install>0: if not Options.options.force: try: st1=os.stat(tgt) st2=os.stat(src) except OSError: pass else: if st1.st_mtime>=st2.st_mtime and st1.st_size==st2.st_size: return False srclbl=src.replace(self.srcnode.abspath(None)+os.sep,'') info("* installing %s as %s"%(srclbl,tgt)) try:os.remove(tgt) except OSError:pass try: shutil.copy2(src,tgt) os.chmod(tgt,chmod) except IOError: try: os.stat(src) except(OSError,IOError): error('File %r does not exist'%src) raise Utils.WafError('Could not install the file %r'%tgt) return True elif self.is_install<0: info("* uninstalling %s"%tgt) self.uninstall.append(tgt) try: os.remove(tgt) except OSError,e: if e.errno!=errno.ENOENT: if not getattr(self,'uninstall_error',None): self.uninstall_error=True Logs.warn('build: some files could not be uninstalled (retry with -vv to list them)') if Logs.verbose>1: Logs.warn('could not remove %s (error code %r)'%(e.filename,e.errno)) return True red=re.compile(r"^([A-Za-z]:)?[/\\\\]*") def get_install_path(self,path,env=None): if not env:env=self.env destdir=env.get_destdir() path=path.replace('/',os.sep) destpath=Utils.subst_vars(path,env) if destdir: destpath=os.path.join(destdir,self.red.sub('',destpath)) return destpath def install_dir(self,path,env=None): if env: assert isinstance(env,Environment.Environment),"invalid parameter" else: env=self.env if not path: return[] destpath=self.get_install_path(path,env) if self.is_install>0: info('* creating %s'%destpath) Utils.check_dir(destpath) elif self.is_install<0: info('* removing %s'%destpath) self.uninstall.append(destpath+'/xxx') def install_files(self,path,files,env=None,chmod=O644,relative_trick=False,cwd=None): if env: assert isinstance(env,Environment.Environment),"invalid parameter" else: env=self.env if not path:return[] if not cwd: cwd=self.path if isinstance(files,str)and'*'in files: gl=cwd.abspath()+os.sep+files lst=glob.glob(gl) else: lst=Utils.to_list(files) if not getattr(lst,'__iter__',False): lst=[lst] destpath=self.get_install_path(path,env) Utils.check_dir(destpath) installed_files=[] for filename in lst: if isinstance(filename,str)and os.path.isabs(filename): alst=Utils.split_path(filename) destfile=os.path.join(destpath,alst[-1]) else: if isinstance(filename,Node.Node): nd=filename else: nd=cwd.find_resource(filename) if not nd: raise Utils.WafError("Unable to install the file %r (not found in %s)"%(filename,cwd)) if relative_trick: destfile=os.path.join(destpath,filename) Utils.check_dir(os.path.dirname(destfile)) else: destfile=os.path.join(destpath,nd.name) filename=nd.abspath(env) if self.do_install(filename,destfile,chmod): installed_files.append(destfile) return installed_files def install_as(self,path,srcfile,env=None,chmod=O644,cwd=None): if env: assert isinstance(env,Environment.Environment),"invalid parameter" else: env=self.env if not path: raise Utils.WafError("where do you want to install %r? (%r?)"%(srcfile,path)) if not cwd: cwd=self.path destpath=self.get_install_path(path,env) dir,name=os.path.split(destpath) Utils.check_dir(dir) if isinstance(srcfile,Node.Node): src=srcfile.abspath(env) else: src=srcfile if not os.path.isabs(srcfile): node=cwd.find_resource(srcfile) if not node: raise Utils.WafError("Unable to install the file %r (not found in %s)"%(srcfile,cwd)) src=node.abspath(env) return self.do_install(src,destpath,chmod) def symlink_as(self,path,src,env=None,cwd=None): if sys.platform=='win32': return if not path: raise Utils.WafError("where do you want to install %r? (%r?)"%(src,path)) tgt=self.get_install_path(path,env) dir,name=os.path.split(tgt) Utils.check_dir(dir) if self.is_install>0: link=False if not os.path.islink(tgt): link=True elif os.readlink(tgt)!=src: link=True if link: try:os.remove(tgt) except OSError:pass info('* symlink %s (-> %s)'%(tgt,src)) os.symlink(src,tgt) return 0 else: try: info('* removing %s'%(tgt)) os.remove(tgt) return 0 except OSError: return 1 def exec_command(self,cmd,**kw): debug('runner: system command -> %s',cmd) if self.log: self.log.write('%s\n'%cmd) kw['log']=self.log try: if not kw.get('cwd',None): kw['cwd']=self.cwd except AttributeError: self.cwd=kw['cwd']=self.bldnode.abspath() return Utils.exec_command(cmd,**kw) def printout(self,s): f=self.log or sys.stderr f.write(s) f.flush() def add_subdirs(self,dirs): self.recurse(dirs,'build') def pre_recurse(self,name_or_mod,path,nexdir): if not hasattr(self,'oldpath'): self.oldpath=[] self.oldpath.append(self.path) self.path=self.root.find_dir(nexdir) return{'bld':self,'ctx':self} def post_recurse(self,name_or_mod,path,nexdir): self.path=self.oldpath.pop() def pre_build(self): if hasattr(self,'pre_funs'): for m in self.pre_funs: m(self) def post_build(self): if hasattr(self,'post_funs'): for m in self.post_funs: m(self) def add_pre_fun(self,meth): try:self.pre_funs.append(meth) except AttributeError:self.pre_funs=[meth] def add_post_fun(self,meth): try:self.post_funs.append(meth) except AttributeError:self.post_funs=[meth] def use_the_magic(self): Task.algotype=Task.MAXPARALLEL Task.file_deps=Task.extract_deps self.magic=True install_as=group_method(install_as) install_files=group_method(install_files) symlink_as=group_method(symlink_as)