3 # WARNING! Do not edit! http://waf.googlecode.com/git/docs/wafbook/single.html#_obtaining_the_waf_file
5 import os,sys,errno,re,shutil
9 import pickle as cPickle
10 from waflib import Runner,TaskGen,Utils,ConfigSet,Task,Logs,Options,Context,Errors
13 CACHE_SUFFIX='_cache.py'
16 SAVED_ATTRS='root node_deps raw_deps task_sigs'.split()
21 class BuildContext(Context.Context):
22 '''executes the build'''
25 def __init__(self,**kw):
26 super(BuildContext,self).__init__(**kw)
28 self.top_dir=kw.get('top_dir',Context.top_dir)
29 self.run_dir=kw.get('run_dir',Context.run_dir)
30 self.post_mode=POST_AT_ONCE
31 self.out_dir=kw.get('out_dir',Context.out_dir)
32 self.cache_dir=kw.get('cache_dir',None)
33 if not self.cache_dir:
34 self.cache_dir=self.out_dir+os.sep+CACHE_DIR
39 self.cache_dir_contents={}
40 self.task_gen_cache_names={}
41 self.launch_dir=Context.launch_dir
42 self.jobs=Options.options.jobs
43 self.targets=Options.options.targets
44 self.keep=Options.options.keep
45 self.cache_global=Options.cache_global
46 self.nocache=Options.options.nocache
47 self.progress_bar=Options.options.progress_bar
48 self.deps_man=Utils.defaultdict(list)
52 def get_variant_dir(self):
55 return os.path.join(self.out_dir,self.variant)
56 variant_dir=property(get_variant_dir,None)
57 def __call__(self,*k,**kw):
59 ret=TaskGen.task_gen(*k,**kw)
60 self.task_gen_cache_names={}
61 self.add_to_group(ret,group=kw.get('group',None))
63 def rule(self,*k,**kw):
70 raise Errors.WafError('build contexts are not supposed to be copied')
71 def install_files(self,*k,**kw):
73 def install_as(self,*k,**kw):
75 def symlink_as(self,*k,**kw):
78 node=self.root.find_node(self.cache_dir)
80 raise Errors.WafError('The project was not configured: run "waf configure" first!')
81 lst=node.ant_glob('**/*%s'%CACHE_SUFFIX,quiet=True)
83 raise Errors.WafError('The cache directory is empty: reconfigure the project')
85 name=x.path_from(node).replace(CACHE_SUFFIX,'').replace('\\','/')
86 env=ConfigSet.ConfigSet(x.abspath())
87 self.all_envs[name]=env
88 for f in env[CFG_FILES]:
89 newnode=self.root.find_resource(f)
91 h=Utils.h_file(newnode.abspath())
92 except(IOError,AttributeError):
93 Logs.error('cannot find %r'%f)
97 if not(os.path.isabs(self.top_dir)and os.path.isabs(self.out_dir)):
98 raise Errors.WafError('The project was not configured: run "waf configure" first!')
99 self.path=self.srcnode=self.root.find_dir(self.top_dir)
100 self.bldnode=self.root.make_node(self.variant_dir)
104 if not self.all_envs:
107 def execute_build(self):
108 Logs.info("Waf: Entering directory `%s'"%self.variant_dir)
109 self.recurse([self.run_dir])
111 self.timer=Utils.Timer()
112 if self.progress_bar:
113 sys.stderr.write(Logs.colors.cursor_off)
117 if self.progress_bar==1:
118 c=len(self.returned_tasks)or 1
119 self.to_log(self.progress_line(c,c,Logs.colors.BLUE,Logs.colors.NORMAL))
122 sys.stderr.write(Logs.colors.cursor_on)
123 Logs.info("Waf: Leaving directory `%s'"%self.variant_dir)
127 env=ConfigSet.ConfigSet(os.path.join(self.cache_dir,'build.config.py'))
128 except(IOError,OSError):
131 if env['version']<Context.HEXVERSION:
132 raise Errors.WafError('Version mismatch! reconfigure the project')
133 for t in env['tools']:
135 dbfn=os.path.join(self.variant_dir,Context.DBFILE)
137 data=Utils.readf(dbfn,'rb')
138 except(IOError,EOFError):
139 Logs.debug('build: Could not load the build cache %s (missing)'%dbfn)
142 waflib.Node.pickle_lock.acquire()
143 waflib.Node.Nod3=self.node_class
145 data=cPickle.loads(data)
147 Logs.debug('build: Could not pickle the build cache %s: %r'%(dbfn,e))
149 for x in SAVED_ATTRS:
150 setattr(self,x,data[x])
152 waflib.Node.pickle_lock.release()
156 for x in SAVED_ATTRS:
157 data[x]=getattr(self,x)
158 db=os.path.join(self.variant_dir,Context.DBFILE)
160 waflib.Node.pickle_lock.acquire()
161 waflib.Node.Nod3=self.node_class
162 x=cPickle.dumps(data,-1)
164 waflib.Node.pickle_lock.release()
165 Utils.writef(db+'.tmp',x,m='wb')
169 if not Utils.is_win32:
170 os.chown(db+'.tmp',st.st_uid,st.st_gid)
171 except(AttributeError,OSError):
173 os.rename(db+'.tmp',db)
175 Logs.debug('build: compile()')
176 self.producer=Runner.Parallel(self,self.jobs)
177 self.producer.biter=self.get_build_iterator()
178 self.returned_tasks=[]
180 self.producer.start()
181 except KeyboardInterrupt:
185 if self.producer.dirty:
187 if self.producer.error:
188 raise Errors.BuildError(self.producer.error)
189 def setup(self,tool,tooldir=None,funs=None):
190 if isinstance(tool,list):
191 for i in tool:self.setup(i,tooldir)
193 module=Context.load_tool(tool,tooldir)
194 if hasattr(module,"setup"):module.setup(self)
197 return self.all_envs[self.variant]
199 return self.all_envs['']
200 def set_env(self,val):
201 self.all_envs[self.variant]=val
202 env=property(get_env,set_env)
203 def add_manual_dependency(self,path,value):
205 raise ValueError('Invalid input')
206 if isinstance(path,waflib.Node.Node):
208 elif os.path.isabs(path):
209 node=self.root.find_resource(path)
211 node=self.path.find_resource(path)
212 if isinstance(value,list):
213 self.deps_man[id(node)].extend(value)
215 self.deps_man[id(node)].append(value)
216 def launch_node(self):
219 except AttributeError:
220 self.p_ln=self.root.find_dir(self.launch_dir)
222 def hash_env_vars(self,env,vars_lst):
227 idx=str(id(env))+str(vars_lst)
230 except AttributeError:
231 cache=self.cache_env={}
234 return self.cache_env[idx]
237 lst=[env[a]for a in vars_lst]
238 ret=Utils.h_list(lst)
239 Logs.debug('envhash: %s %r',Utils.to_hex(ret),lst)
242 def get_tgen_by_name(self,name):
243 cache=self.task_gen_cache_names
245 for g in self.groups:
249 except AttributeError:
254 raise Errors.WafError('Could not find a task generator for the name %r'%name)
255 def progress_line(self,state,total,col1,col2):
258 ind=Utils.rot_chr[Utils.rot_idx%4]
259 pc=(100.*state)/total
261 fs="[%%%dd/%%%dd][%%s%%2d%%%%%%s][%s]["%(n,n,ind)
262 left=fs%(state,total,col1,pc,col2)
263 right='][%s%s%s]'%(col1,eta,col2)
264 cols=Logs.get_term_cols()-len(left)-len(right)+2*len(col1)+2*len(col2)
266 ratio=((cols*state)//total)-1
267 bar=('='*ratio+'>').ljust(cols)
268 msg=Utils.indicator%(left,bar,right)
270 def declare_chain(self,*k,**kw):
271 return TaskGen.declare_chain(*k,**kw)
273 for m in getattr(self,'pre_funs',[]):
275 def post_build(self):
276 for m in getattr(self,'post_funs',[]):
278 def add_pre_fun(self,meth):
280 self.pre_funs.append(meth)
281 except AttributeError:
283 def add_post_fun(self,meth):
285 self.post_funs.append(meth)
286 except AttributeError:
287 self.post_funs=[meth]
288 def get_group(self,x):
292 return self.groups[self.current_group]
293 if x in self.group_names:
294 return self.group_names[x]
295 return self.groups[x]
296 def add_to_group(self,tgen,group=None):
297 assert(isinstance(tgen,TaskGen.task_gen)or isinstance(tgen,Task.TaskBase))
299 self.get_group(group).append(tgen)
300 def get_group_name(self,g):
301 if not isinstance(g,list):
303 for x in self.group_names:
304 if id(self.group_names[x])==id(g):
307 def get_group_idx(self,tg):
309 for i in range(len(self.groups)):
310 for t in self.groups[i]:
314 def add_group(self,name=None,move=True):
315 if name and name in self.group_names:
316 Logs.error('add_group: name %s already present'%name)
318 self.group_names[name]=g
319 self.groups.append(g)
321 self.current_group=len(self.groups)-1
322 def set_group(self,idx):
323 if isinstance(idx,str):
324 g=self.group_names[idx]
325 for i in range(len(self.groups)):
326 if id(g)==id(self.groups[i]):
329 self.current_group=idx
332 for group in self.groups:
336 except AttributeError:
339 def get_targets(self):
342 for name in self.targets.split(','):
343 tg=self.get_tgen_by_name(name)
345 raise Errors.WafError('target %r does not exist'%name)
346 m=self.get_group_idx(tg)
352 return(min_grp,to_post)
353 def get_all_task_gen(self):
355 for g in self.groups:
358 def post_group(self):
359 if self.targets=='*':
360 for tg in self.groups[self.cur]:
363 except AttributeError:
368 if self.cur<self._min_grp:
369 for tg in self.groups[self.cur]:
372 except AttributeError:
377 for tg in self._exact_tg:
380 ln=self.launch_node()
381 if ln.is_child_of(self.bldnode):
382 Logs.warn('Building from the build directory, forcing --targets=*')
384 elif not ln.is_child_of(self.srcnode):
385 Logs.warn('CWD %s is not under %s, forcing --targets=* (run distclean?)'%(ln.abspath(),self.srcnode.abspath()))
387 for tg in self.groups[self.cur]:
390 except AttributeError:
393 if tg.path.is_child_of(ln):
395 def get_tasks_group(self,idx):
397 for tg in self.groups[idx]:
399 tasks.extend(tg.tasks)
400 except AttributeError:
403 def get_build_iterator(self):
405 if self.targets and self.targets!='*':
406 (self._min_grp,self._exact_tg)=self.get_targets()
408 if self.post_mode!=POST_LAZY:
409 while self.cur<len(self.groups):
413 while self.cur<len(self.groups):
414 if self.post_mode!=POST_AT_ONCE:
416 tasks=self.get_tasks_group(self.cur)
417 Task.set_file_constraints(tasks)
418 Task.set_precedence_constraints(tasks)
426 class inst(Task.Task):
429 lst=[self.dest,self.path]+self.source
430 return Utils.h_list(repr(lst))
433 for x in self.source:
434 if isinstance(x,waflib.Node.Node):
437 y=self.path.find_resource(x)
440 Logs.warn('Could not find %s immediately (may cause broken builds)'%x)
441 idx=self.generator.bld.get_group_idx(self)
442 for tg in self.generator.bld.groups[idx]:
443 if not isinstance(tg,inst)and id(tg)!=id(self):
445 y=self.path.find_resource(x)
449 raise Errors.WafError('Could not find %r in %r'%(x,self.path))
452 def runnable_status(self):
453 ret=super(inst,self).runnable_status()
454 if ret==Task.SKIP_ME:
460 return self.generator.exec_task()
461 def get_install_path(self,destdir=True):
462 dest=Utils.subst_vars(self.dest,self.env)
463 dest=dest.replace('/',os.sep)
464 if destdir and Options.options.destdir:
465 dest=os.path.join(Options.options.destdir,os.path.splitdrive(dest)[1].lstrip(os.sep))
467 def exec_install_files(self):
468 destpath=self.get_install_path()
470 raise Errors.WafError('unknown installation path %r'%self.generator)
471 for x,y in zip(self.source,self.inputs):
472 if self.relative_trick:
473 destfile=os.path.join(destpath,y.path_from(self.path))
475 destfile=os.path.join(destpath,y.name)
476 self.generator.bld.do_install(y.abspath(),destfile,self.chmod)
477 def exec_install_as(self):
478 destfile=self.get_install_path()
479 self.generator.bld.do_install(self.inputs[0].abspath(),destfile,self.chmod)
480 def exec_symlink_as(self):
481 destfile=self.get_install_path()
483 if self.relative_trick:
484 src=os.path.relpath(src,os.path.dirname(destfile))
485 self.generator.bld.do_link(src,destfile)
486 class InstallContext(BuildContext):
487 '''installs the targets on the system'''
489 def __init__(self,**kw):
490 super(InstallContext,self).__init__(**kw)
492 self.is_install=INSTALL
493 def do_install(self,src,tgt,chmod=Utils.O644):
494 d,_=os.path.split(tgt)
496 raise Errors.WafError('Invalid installation given %r->%r'%(src,tgt))
498 srclbl=src.replace(self.srcnode.abspath()+os.sep,'')
499 if not Options.options.force:
506 if st1.st_mtime+2>=st2.st_mtime and st1.st_size==st2.st_size:
507 if not self.progress_bar:
508 Logs.info('- install %s (from %s)'%(tgt,srclbl))
510 if not self.progress_bar:
511 Logs.info('+ install %s (from %s)'%(tgt,srclbl))
517 shutil.copy2(src,tgt)
522 except(OSError,IOError):
523 Logs.error('File %r does not exist'%src)
524 raise Errors.WafError('Could not install the file %r'%tgt)
525 def do_link(self,src,tgt):
526 d,_=os.path.split(tgt)
529 if not os.path.islink(tgt):
531 elif os.readlink(tgt)!=src:
536 if not self.progress_bar:
537 Logs.info('+ symlink %s (to %s)'%(tgt,src))
540 if not self.progress_bar:
541 Logs.info('- symlink %s (to %s)'%(tgt,src))
542 def run_task_now(self,tsk,postpone):
545 if tsk.runnable_status()==Task.ASK_LATER:
546 raise self.WafError('cannot post the task %r'%tsk)
548 def install_files(self,dest,files,env=None,chmod=Utils.O644,relative_trick=False,cwd=None,add=True,postpone=True):
549 tsk=inst(env=env or self.env)
551 tsk.path=cwd or self.path
553 if isinstance(files,waflib.Node.Node):
556 tsk.source=Utils.to_list(files)
558 tsk.exec_task=tsk.exec_install_files
559 tsk.relative_trick=relative_trick
560 if add:self.add_to_group(tsk)
561 self.run_task_now(tsk,postpone)
563 def install_as(self,dest,srcfile,env=None,chmod=Utils.O644,cwd=None,add=True,postpone=True):
564 tsk=inst(env=env or self.env)
566 tsk.path=cwd or self.path
570 tsk.exec_task=tsk.exec_install_as
571 if add:self.add_to_group(tsk)
572 self.run_task_now(tsk,postpone)
574 def symlink_as(self,dest,src,env=None,cwd=None,add=True,postpone=True,relative_trick=False):
577 tsk=inst(env=env or self.env)
580 tsk.path=cwd or self.path
583 tsk.relative_trick=relative_trick
584 tsk.exec_task=tsk.exec_symlink_as
585 if add:self.add_to_group(tsk)
586 self.run_task_now(tsk,postpone)
588 class UninstallContext(InstallContext):
589 '''removes the targets installed'''
591 def __init__(self,**kw):
592 super(UninstallContext,self).__init__(**kw)
593 self.is_install=UNINSTALL
594 def do_install(self,src,tgt,chmod=Utils.O644):
595 if not self.progress_bar:
596 Logs.info('- remove %s'%tgt)
597 self.uninstall.append(tgt)
601 if e.errno!=errno.ENOENT:
602 if not getattr(self,'uninstall_error',None):
603 self.uninstall_error=True
604 Logs.warn('build: some files could not be uninstalled (retry with -vv to list them)')
606 Logs.warn('Could not remove %s (error code %r)'%(e.filename,e.errno))
608 tgt=os.path.dirname(tgt)
613 def do_link(self,src,tgt):
615 if not self.progress_bar:
616 Logs.info('- remove %s'%tgt)
621 tgt=os.path.dirname(tgt)
628 def runnable_status(self):
630 setattr(Task.Task,'runnable_status_back',Task.Task.runnable_status)
631 setattr(Task.Task,'runnable_status',runnable_status)
632 super(UninstallContext,self).execute()
634 setattr(Task.Task,'runnable_status',Task.Task.runnable_status_back)
635 class CleanContext(BuildContext):
636 '''cleans the project'''
640 if not self.all_envs:
642 self.recurse([self.run_dir])
648 Logs.debug('build: clean called')
649 if self.bldnode!=self.srcnode:
651 for e in self.all_envs.values():
652 lst.extend(self.root.find_or_declare(f)for f in e[CFG_FILES])
653 for n in self.bldnode.ant_glob('**/*',excl='.lock* *conf_check_*/** config.log c4che/*',quiet=True):
657 self.root.children={}
658 for v in'node_deps task_sigs raw_deps'.split():
660 class ListContext(BuildContext):
661 '''lists the targets to execute'''
665 if not self.all_envs:
667 self.recurse([self.run_dir])
669 self.timer=Utils.Timer()
670 for g in self.groups:
674 except AttributeError:
679 self.get_tgen_by_name('')
682 lst=list(self.task_gen_cache_names.keys())
685 Logs.pprint('GREEN',k)
686 class StepContext(BuildContext):
687 '''executes tasks in a step-by-step fashion, for debugging'''
689 def __init__(self,**kw):
690 super(StepContext,self).__init__(**kw)
691 self.files=Options.options.files
694 Logs.warn('Add a pattern for the debug build, for example "waf step --files=main.c,app"')
695 BuildContext.compile(self)
698 if self.targets and self.targets!='*':
699 targets=self.targets.split(',')
700 for g in self.groups:
702 if targets and tg.name not in targets:
706 except AttributeError:
710 for pat in self.files.split(','):
711 matcher=self.get_matcher(pat)
713 if isinstance(tg,Task.TaskBase):
719 for node in getattr(tsk,'inputs',[]):
720 if matcher(node,output=False):
723 for node in getattr(tsk,'outputs',[]):
724 if matcher(node,output=True):
729 Logs.info('%s -> exit %r'%(str(tsk),ret))
730 def get_matcher(self,pat):
733 if pat.startswith('in:'):
735 pat=pat.replace('in:','')
736 elif pat.startswith('out:'):
738 pat=pat.replace('out:','')
739 anode=self.root.find_node(pat)
742 if not pat.startswith('^'):
744 if not pat.endswith('$'):
746 pattern=re.compile(pat)
747 def match(node,output):
748 if output==True and not out:
750 if output==False and not inn:
755 return pattern.match(node.abspath())
757 BuildContext.store=Utils.nogc(BuildContext.store)
758 BuildContext.restore=Utils.nogc(BuildContext.restore)