ref: fa6f6aa65a49d8660c0ca874c7af375932fa6bae
dir: /waflib/Build.py/
#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! http://waf.googlecode.com/git/docs/wafbook/single.html#_obtaining_the_waf_file import os,sys,errno,re,shutil try: import cPickle except ImportError: import pickle as cPickle from waflib import Runner,TaskGen,Utils,ConfigSet,Task,Logs,Options,Context,Errors import waflib.Node CACHE_DIR='c4che' CACHE_SUFFIX='_cache.py' INSTALL=1337 UNINSTALL=-1337 SAVED_ATTRS='root node_deps raw_deps task_sigs'.split() CFG_FILES='cfg_files' POST_AT_ONCE=0 POST_LAZY=1 POST_BOTH=2 class BuildContext(Context.Context): '''executes the build''' cmd='build' variant='' def __init__(self,**kw): super(BuildContext,self).__init__(**kw) self.is_install=0 self.top_dir=kw.get('top_dir',Context.top_dir) self.run_dir=kw.get('run_dir',Context.run_dir) self.post_mode=POST_AT_ONCE self.out_dir=kw.get('out_dir',Context.out_dir) self.cache_dir=kw.get('cache_dir',None) if not self.cache_dir: self.cache_dir=self.out_dir+os.sep+CACHE_DIR self.all_envs={} self.task_sigs={} self.node_deps={} self.raw_deps={} self.cache_dir_contents={} self.task_gen_cache_names={} self.launch_dir=Context.launch_dir self.jobs=Options.options.jobs self.targets=Options.options.targets self.keep=Options.options.keep self.cache_global=Options.cache_global self.nocache=Options.options.nocache self.progress_bar=Options.options.progress_bar self.deps_man=Utils.defaultdict(list) self.current_group=0 self.groups=[] self.group_names={} def get_variant_dir(self): if not self.variant: return self.out_dir return os.path.join(self.out_dir,self.variant) variant_dir=property(get_variant_dir,None) def __call__(self,*k,**kw): kw['bld']=self ret=TaskGen.task_gen(*k,**kw) self.task_gen_cache_names={} self.add_to_group(ret,group=kw.get('group',None)) return ret def rule(self,*k,**kw): def f(rule): ret=self(*k,**kw) ret.rule=rule return ret return f def __copy__(self): raise Errors.WafError('build contexts are not supposed to be copied') def install_files(self,*k,**kw): pass def install_as(self,*k,**kw): pass def symlink_as(self,*k,**kw): pass def load_envs(self): node=self.root.find_node(self.cache_dir) if not node: raise Errors.WafError('The project was not configured: run "waf configure" first!') lst=node.ant_glob('**/*%s'%CACHE_SUFFIX,quiet=True) if not lst: raise Errors.WafError('The cache directory is empty: reconfigure the project') for x in lst: name=x.path_from(node).replace(CACHE_SUFFIX,'').replace('\\','/') env=ConfigSet.ConfigSet(x.abspath()) self.all_envs[name]=env for f in env[CFG_FILES]: newnode=self.root.find_resource(f) try: h=Utils.h_file(newnode.abspath()) except(IOError,AttributeError): Logs.error('cannot find %r'%f) h=Utils.SIG_NIL newnode.sig=h def init_dirs(self): if not(os.path.isabs(self.top_dir)and os.path.isabs(self.out_dir)): raise Errors.WafError('The project was not configured: run "waf configure" first!') self.path=self.srcnode=self.root.find_dir(self.top_dir) self.bldnode=self.root.make_node(self.variant_dir) self.bldnode.mkdir() def execute(self): self.restore() if not self.all_envs: self.load_envs() self.execute_build() def execute_build(self): Logs.info("Waf: Entering directory `%s'"%self.variant_dir) self.recurse([self.run_dir]) self.pre_build() self.timer=Utils.Timer() if self.progress_bar: sys.stderr.write(Logs.colors.cursor_off) try: self.compile() finally: if self.progress_bar==1: c=len(self.returned_tasks)or 1 self.to_log(self.progress_line(c,c,Logs.colors.BLUE,Logs.colors.NORMAL)) print('') sys.stdout.flush() sys.stderr.write(Logs.colors.cursor_on) Logs.info("Waf: Leaving directory `%s'"%self.variant_dir) self.post_build() def restore(self): try: env=ConfigSet.ConfigSet(os.path.join(self.cache_dir,'build.config.py')) except(IOError,OSError): pass else: if env['version']<Context.HEXVERSION: raise Errors.WafError('Version mismatch! reconfigure the project') for t in env['tools']: self.setup(**t) dbfn=os.path.join(self.variant_dir,Context.DBFILE) try: data=Utils.readf(dbfn,'rb') except(IOError,EOFError): Logs.debug('build: Could not load the build cache %s (missing)'%dbfn) else: try: waflib.Node.pickle_lock.acquire() waflib.Node.Nod3=self.node_class try: data=cPickle.loads(data) except Exception ,e: Logs.debug('build: Could not pickle the build cache %s: %r'%(dbfn,e)) else: for x in SAVED_ATTRS: setattr(self,x,data[x]) finally: waflib.Node.pickle_lock.release() self.init_dirs() def store(self): data={} for x in SAVED_ATTRS: data[x]=getattr(self,x) db=os.path.join(self.variant_dir,Context.DBFILE) try: waflib.Node.pickle_lock.acquire() waflib.Node.Nod3=self.node_class x=cPickle.dumps(data,-1) finally: waflib.Node.pickle_lock.release() Utils.writef(db+'.tmp',x,m='wb') try: st=os.stat(db) os.remove(db) if not Utils.is_win32: os.chown(db+'.tmp',st.st_uid,st.st_gid) except(AttributeError,OSError): pass os.rename(db+'.tmp',db) def compile(self): Logs.debug('build: compile()') self.producer=Runner.Parallel(self,self.jobs) self.producer.biter=self.get_build_iterator() self.returned_tasks=[] try: self.producer.start() except KeyboardInterrupt: self.store() raise else: if self.producer.dirty: self.store() if self.producer.error: raise Errors.BuildError(self.producer.error) def setup(self,tool,tooldir=None,funs=None): if isinstance(tool,list): for i in tool:self.setup(i,tooldir) return module=Context.load_tool(tool,tooldir) if hasattr(module,"setup"):module.setup(self) def get_env(self): try: return self.all_envs[self.variant] except KeyError: return self.all_envs[''] def set_env(self,val): self.all_envs[self.variant]=val env=property(get_env,set_env) def add_manual_dependency(self,path,value): if path is None: raise ValueError('Invalid input') if isinstance(path,waflib.Node.Node): node=path elif os.path.isabs(path): node=self.root.find_resource(path) else: node=self.path.find_resource(path) if isinstance(value,list): self.deps_man[id(node)].extend(value) else: self.deps_man[id(node)].append(value) def launch_node(self): try: return self.p_ln except AttributeError: self.p_ln=self.root.find_dir(self.launch_dir) return self.p_ln def hash_env_vars(self,env,vars_lst): if not env.table: env=env.parent if not env: return Utils.SIG_NIL idx=str(id(env))+str(vars_lst) try: cache=self.cache_env except AttributeError: cache=self.cache_env={} else: try: return self.cache_env[idx] except KeyError: pass lst=[env[a]for a in vars_lst] ret=Utils.h_list(lst) Logs.debug('envhash: %s %r',Utils.to_hex(ret),lst) cache[idx]=ret return ret def get_tgen_by_name(self,name): cache=self.task_gen_cache_names if not cache: for g in self.groups: for tg in g: try: cache[tg.name]=tg except AttributeError: pass try: return cache[name] except KeyError: raise Errors.WafError('Could not find a task generator for the name %r'%name) def progress_line(self,state,total,col1,col2): n=len(str(total)) Utils.rot_idx+=1 ind=Utils.rot_chr[Utils.rot_idx%4] pc=(100.*state)/total eta=str(self.timer) 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=Logs.get_term_cols()-len(left)-len(right)+2*len(col1)+2*len(col2) if cols<7:cols=7 ratio=((cols*state)//total)-1 bar=('='*ratio+'>').ljust(cols) msg=Utils.indicator%(left,bar,right) return msg def declare_chain(self,*k,**kw): return TaskGen.declare_chain(*k,**kw) def pre_build(self): for m in getattr(self,'pre_funs',[]): m(self) def post_build(self): for m in getattr(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 get_group(self,x): if not self.groups: self.add_group() if x is None: return self.groups[self.current_group] if x in self.group_names: return self.group_names[x] return self.groups[x] def add_to_group(self,tgen,group=None): assert(isinstance(tgen,TaskGen.task_gen)or isinstance(tgen,Task.TaskBase)) tgen.bld=self self.get_group(group).append(tgen) def get_group_name(self,g): if not isinstance(g,list): g=self.groups[g] for x in self.group_names: if id(self.group_names[x])==id(g): return x return'' def get_group_idx(self,tg): se=id(tg) for i in range(len(self.groups)): for t in self.groups[i]: if id(t)==se: return i return None def add_group(self,name=None,move=True): if name and name in self.group_names: Logs.error('add_group: name %s already present'%name) g=[] self.group_names[name]=g self.groups.append(g) if move: self.current_group=len(self.groups)-1 def set_group(self,idx): if isinstance(idx,str): g=self.group_names[idx] for i in range(len(self.groups)): if id(g)==id(self.groups[i]): self.current_group=i else: self.current_group=idx def total(self): total=0 for group in self.groups: for tg in group: try: total+=len(tg.tasks) except AttributeError: total+=1 return total def get_targets(self): to_post=[] min_grp=0 for name in self.targets.split(','): tg=self.get_tgen_by_name(name) if not tg: raise Errors.WafError('target %r does not exist'%name) m=self.get_group_idx(tg) if m>min_grp: min_grp=m to_post=[tg] elif m==min_grp: to_post.append(tg) return(min_grp,to_post) def get_all_task_gen(self): lst=[] for g in self.groups: lst.extend(g) return lst def post_group(self): if self.targets=='*': for tg in self.groups[self.cur]: try: f=tg.post except AttributeError: pass else: f() elif self.targets: if self.cur<self._min_grp: for tg in self.groups[self.cur]: try: f=tg.post except AttributeError: pass else: f() else: for tg in self._exact_tg: tg.post() else: ln=self.launch_node() if ln.is_child_of(self.bldnode): Logs.warn('Building from the build directory, forcing --targets=*') ln=self.srcnode elif not ln.is_child_of(self.srcnode): Logs.warn('CWD %s is not under %s, forcing --targets=* (run distclean?)'%(ln.abspath(),self.srcnode.abspath())) ln=self.srcnode for tg in self.groups[self.cur]: try: f=tg.post except AttributeError: pass else: if tg.path.is_child_of(ln): f() def get_tasks_group(self,idx): tasks=[] for tg in self.groups[idx]: try: tasks.extend(tg.tasks) except AttributeError: tasks.append(tg) return tasks def get_build_iterator(self): self.cur=0 if self.targets and self.targets!='*': (self._min_grp,self._exact_tg)=self.get_targets() global lazy_post if self.post_mode!=POST_LAZY: while self.cur<len(self.groups): self.post_group() self.cur+=1 self.cur=0 while self.cur<len(self.groups): if self.post_mode!=POST_AT_ONCE: self.post_group() tasks=self.get_tasks_group(self.cur) Task.set_file_constraints(tasks) Task.set_precedence_constraints(tasks) self.cur_tasks=tasks self.cur+=1 if not tasks: continue yield tasks while 1: yield[] class inst(Task.Task): color='CYAN' def uid(self): lst=[self.dest,self.path]+self.source return Utils.h_list(repr(lst)) def post(self): buf=[] for x in self.source: if isinstance(x,waflib.Node.Node): y=x else: y=self.path.find_resource(x) if not y: if Logs.verbose: Logs.warn('Could not find %s immediately (may cause broken builds)'%x) idx=self.generator.bld.get_group_idx(self) for tg in self.generator.bld.groups[idx]: if not isinstance(tg,inst)and id(tg)!=id(self): tg.post() y=self.path.find_resource(x) if y: break else: raise Errors.WafError('Could not find %r in %r'%(x,self.path)) buf.append(y) self.inputs=buf def runnable_status(self): ret=super(inst,self).runnable_status() if ret==Task.SKIP_ME: return Task.RUN_ME return ret def __str__(self): return'' def run(self): return self.generator.exec_task() def get_install_path(self,destdir=True): dest=Utils.subst_vars(self.dest,self.env) dest=dest.replace('/',os.sep) if destdir and Options.options.destdir: dest=os.path.join(Options.options.destdir,os.path.splitdrive(dest)[1].lstrip(os.sep)) return dest def exec_install_files(self): destpath=self.get_install_path() if not destpath: raise Errors.WafError('unknown installation path %r'%self.generator) for x,y in zip(self.source,self.inputs): if self.relative_trick: destfile=os.path.join(destpath,y.path_from(self.path)) else: destfile=os.path.join(destpath,y.name) self.generator.bld.do_install(y.abspath(),destfile,self.chmod) def exec_install_as(self): destfile=self.get_install_path() self.generator.bld.do_install(self.inputs[0].abspath(),destfile,self.chmod) def exec_symlink_as(self): destfile=self.get_install_path() src=self.link if self.relative_trick: src=os.path.relpath(src,os.path.dirname(destfile)) self.generator.bld.do_link(src,destfile) class InstallContext(BuildContext): '''installs the targets on the system''' cmd='install' def __init__(self,**kw): super(InstallContext,self).__init__(**kw) self.uninstall=[] self.is_install=INSTALL def do_install(self,src,tgt,chmod=Utils.O644): d,_=os.path.split(tgt) if not d: raise Errors.WafError('Invalid installation given %r->%r'%(src,tgt)) Utils.check_dir(d) srclbl=src.replace(self.srcnode.abspath()+os.sep,'') if not Options.options.force: try: st1=os.stat(tgt) st2=os.stat(src) except OSError: pass else: if st1.st_mtime+2>=st2.st_mtime and st1.st_size==st2.st_size: if not self.progress_bar: Logs.info('- install %s (from %s)'%(tgt,srclbl)) return False if not self.progress_bar: Logs.info('+ install %s (from %s)'%(tgt,srclbl)) 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): Logs.error('File %r does not exist'%src) raise Errors.WafError('Could not install the file %r'%tgt) def do_link(self,src,tgt): d,_=os.path.split(tgt) Utils.check_dir(d) 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 if not self.progress_bar: Logs.info('+ symlink %s (to %s)'%(tgt,src)) os.symlink(src,tgt) else: if not self.progress_bar: Logs.info('- symlink %s (to %s)'%(tgt,src)) def run_task_now(self,tsk,postpone): tsk.post() if not postpone: if tsk.runnable_status()==Task.ASK_LATER: raise self.WafError('cannot post the task %r'%tsk) tsk.run() def install_files(self,dest,files,env=None,chmod=Utils.O644,relative_trick=False,cwd=None,add=True,postpone=True): tsk=inst(env=env or self.env) tsk.bld=self tsk.path=cwd or self.path tsk.chmod=chmod if isinstance(files,waflib.Node.Node): tsk.source=[files] else: tsk.source=Utils.to_list(files) tsk.dest=dest tsk.exec_task=tsk.exec_install_files tsk.relative_trick=relative_trick if add:self.add_to_group(tsk) self.run_task_now(tsk,postpone) return tsk def install_as(self,dest,srcfile,env=None,chmod=Utils.O644,cwd=None,add=True,postpone=True): tsk=inst(env=env or self.env) tsk.bld=self tsk.path=cwd or self.path tsk.chmod=chmod tsk.source=[srcfile] tsk.dest=dest tsk.exec_task=tsk.exec_install_as if add:self.add_to_group(tsk) self.run_task_now(tsk,postpone) return tsk def symlink_as(self,dest,src,env=None,cwd=None,add=True,postpone=True,relative_trick=False): if Utils.is_win32: return tsk=inst(env=env or self.env) tsk.bld=self tsk.dest=dest tsk.path=cwd or self.path tsk.source=[] tsk.link=src tsk.relative_trick=relative_trick tsk.exec_task=tsk.exec_symlink_as if add:self.add_to_group(tsk) self.run_task_now(tsk,postpone) return tsk class UninstallContext(InstallContext): '''removes the targets installed''' cmd='uninstall' def __init__(self,**kw): super(UninstallContext,self).__init__(**kw) self.is_install=UNINSTALL def do_install(self,src,tgt,chmod=Utils.O644): if not self.progress_bar: Logs.info('- remove %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)) while tgt: tgt=os.path.dirname(tgt) try: os.rmdir(tgt) except OSError: break def do_link(self,src,tgt): try: if not self.progress_bar: Logs.info('- remove %s'%tgt) os.remove(tgt) except OSError: pass while tgt: tgt=os.path.dirname(tgt) try: os.rmdir(tgt) except OSError: break def execute(self): try: def runnable_status(self): return Task.SKIP_ME setattr(Task.Task,'runnable_status_back',Task.Task.runnable_status) setattr(Task.Task,'runnable_status',runnable_status) super(UninstallContext,self).execute() finally: setattr(Task.Task,'runnable_status',Task.Task.runnable_status_back) class CleanContext(BuildContext): '''cleans the project''' cmd='clean' def execute(self): self.restore() if not self.all_envs: self.load_envs() self.recurse([self.run_dir]) try: self.clean() finally: self.store() def clean(self): Logs.debug('build: clean called') if self.bldnode!=self.srcnode: lst=[] for e in self.all_envs.values(): lst.extend(self.root.find_or_declare(f)for f in e[CFG_FILES]) for n in self.bldnode.ant_glob('**/*',excl='.lock* *conf_check_*/** config.log c4che/*',quiet=True): if n in lst: continue n.delete() self.root.children={} for v in'node_deps task_sigs raw_deps'.split(): setattr(self,v,{}) class ListContext(BuildContext): '''lists the targets to execute''' cmd='list' def execute(self): self.restore() if not self.all_envs: self.load_envs() self.recurse([self.run_dir]) self.pre_build() self.timer=Utils.Timer() for g in self.groups: for tg in g: try: f=tg.post except AttributeError: pass else: f() try: self.get_tgen_by_name('') except Exception: pass lst=list(self.task_gen_cache_names.keys()) lst.sort() for k in lst: Logs.pprint('GREEN',k) class StepContext(BuildContext): '''executes tasks in a step-by-step fashion, for debugging''' cmd='step' def __init__(self,**kw): super(StepContext,self).__init__(**kw) self.files=Options.options.files def compile(self): if not self.files: Logs.warn('Add a pattern for the debug build, for example "waf step --files=main.c,app"') BuildContext.compile(self) return targets=None if self.targets and self.targets!='*': targets=self.targets.split(',') for g in self.groups: for tg in g: if targets and tg.name not in targets: continue try: f=tg.post except AttributeError: pass else: f() for pat in self.files.split(','): matcher=self.get_matcher(pat) for tg in g: if isinstance(tg,Task.TaskBase): lst=[tg] else: lst=tg.tasks for tsk in lst: do_exec=False for node in getattr(tsk,'inputs',[]): if matcher(node,output=False): do_exec=True break for node in getattr(tsk,'outputs',[]): if matcher(node,output=True): do_exec=True break if do_exec: ret=tsk.run() Logs.info('%s -> exit %r'%(str(tsk),ret)) def get_matcher(self,pat): inn=True out=True if pat.startswith('in:'): out=False pat=pat.replace('in:','') elif pat.startswith('out:'): inn=False pat=pat.replace('out:','') anode=self.root.find_node(pat) pattern=None if not anode: if not pat.startswith('^'): pat='^.+?%s'%pat if not pat.endswith('$'): pat='%s$'%pat pattern=re.compile(pat) def match(node,output): if output==True and not out: return False if output==False and not inn: return False if anode: return anode==node else: return pattern.match(node.abspath()) return match BuildContext.store=Utils.nogc(BuildContext.store) BuildContext.restore=Utils.nogc(BuildContext.restore)