4
if sys.hexversion < 0x020400f0: from sets import Set as set
5
import os,sys,cPickle,types,imp,errno,re,glob,gc,time,shutil
6
import Runner,TaskGen,Node,Scripting,Utils,Environment,Task,Logs,Options
7
from Logs import debug,error,info
9
SAVED_ATTRS='root srcnode bldnode node_sigs node_deps raw_deps task_sigs id_nodes'.split()
12
class BuildError(Utils.WafError):
13
def __init__(self,b=None,t=[]):
17
Utils.WafError.__init__(self,self.format_error())
18
def format_error(self):
20
for tsk in self.tasks:
21
txt=tsk.format_error()
22
if txt:lst.append(txt)
24
class BuildContext(object):
28
self.task_manager=Task.TaskManager()
33
self.cache_node_abspath={}
34
self.cache_scanned_folders={}
36
for v in'cache_node_abspath task_sigs node_deps raw_deps node_sigs'.split():
39
self.cache_dir_contents={}
41
self.task_gen_cache_names={}
42
self.cache_sig_vars={}
50
file=open(os.path.join(self.cachedir,'build.config.py'),'r')
53
except(IOError,OSError):
56
re_imp=re.compile('^(#)*?([^#=]*?)\ =\ (.*?)$',re.M)
57
for m in re_imp.finditer(code):
60
if eval(g(3))<HEXVERSION:
61
raise Utils.WafError('Version mismatch! reconfigure the project')
68
file=open(os.path.join(self.bdir,DBFILE),'rb')
69
data=cPickle.load(file)
71
except(IOError,EOFError):
72
debug('build: Build cache loading failed')
74
for x in SAVED_ATTRS:setattr(self,x,data[x])
78
file=open(os.path.join(self.bdir,DBFILE),'wb')
80
for x in SAVED_ATTRS:data[x]=getattr(self,x)
81
cPickle.dump(data,file,-1)
85
debug('build: clean called')
87
for x in node.childs.keys():
93
for env in self.all_envs.values():
95
if pt in env['waf_config_files']:continue
98
node.childs.__delitem__(x)
99
node=self.launch_node()or self.srcnode
101
if node==self.srcnode:
102
for v in'node_sigs node_deps task_sigs raw_deps cache_node_abspath'.split():
106
debug('build: compile called')
109
self.generator=Runner.Parallel(self,Options.options.jobs)
111
if Options.options.progress_bar:
112
if on:sys.stdout.write(Logs.colors.cursor_on)
113
else:sys.stdout.write(Logs.colors.cursor_off)
114
debug('build: executor starting')
117
self.generator.start()
118
except KeyboardInterrupt:
120
os.chdir(self.srcnode.abspath())
122
Utils.pprint('RED','Build interrupted')
123
if Logs.verbose>1:raise
131
if self.generator.error:
132
os.chdir(self.srcnode.abspath())
133
raise BuildError(self,self.task_manager.tasks_done)
134
os.chdir(self.srcnode.abspath())
136
debug('build: install called')
138
if Options.commands['uninstall']:
140
for x in self.uninstall:
141
dir=os.path.dirname(x)
142
if not dir in lst:lst.append(dir)
149
if not x in nlst:nlst.append(x)
156
def add_subdirs(self,dirs):
157
for dir in Utils.to_list(dirs):
158
if dir:Scripting.add_subdir(dir,self)
159
def new_task_gen(self,*k,**kw):
160
if len(k)==0:return TaskGen.task_gen()
162
try:cls=TaskGen.task_gen.classes[cls_name]
163
except KeyError:raise Utils.WscriptError('%s is not a valid task generator -> %s'%(cls_name,[x for x in TaskGen.task_gen.classes]))
164
else:return cls(*k,**kw)
167
lst=Utils.listdir(self.cachedir)
169
if e.errno==errno.ENOENT:
170
raise Utils.WafError('The project was not configured: run "waf configure" first!')
174
raise Utils.WafError('The cache directory is empty: reconfigure the project')
176
if file.endswith(CACHE_SUFFIX):
177
env=Environment.Environment()
178
env.load(os.path.join(self.cachedir,file))
179
name=file.split('.')[0]
180
self.all_envs[name]=env
182
for env in self.all_envs.values():
183
for f in env['dep_files']:
184
newnode=self.srcnode.find_or_declare(f)
186
hash=Utils.h_file(newnode.abspath(env))
187
except(IOError,AttributeError):
188
error("cannot find "+f)
190
self.node_sigs[env.variant()][newnode.id]=hash
191
def setup(self,tool,tooldir=None,funs=None):
192
if type(tool)is types.ListType:
193
for i in tool:self.setup(i,tooldir)
195
if not tooldir:tooldir=Options.tooldir
197
key=str((tool,tooldir))
198
module=g_modcache.get(key,None)
200
file,name,desc=imp.find_module(tool,tooldir)
201
module=imp.load_module(tool,file,name,desc)
202
g_modcache[key]=module
203
if hasattr(module,"setup"):module.setup(self)
205
def init_variants(self):
206
debug('build: init variants')
208
for env in self.all_envs.values():
209
if not env.variant()in lstvariants:
210
lstvariants.append(env.variant())
211
self._variants=lstvariants
212
debug('build: list of variants is %s'%str(lstvariants))
213
for name in lstvariants+[0]:
214
for v in'node_sigs cache_node_abspath'.split():
218
def load_dirs(self,srcdir,blddir):
219
assert(os.path.isabs(srcdir))
220
assert(os.path.isabs(blddir))
221
self.cachedir=os.path.join(blddir,CACHE_DIR)
223
raise Utils.WafError("build dir must be different from srcdir: %s <-> %s "%(srcdir,blddir))
227
self.root=Node.Node('',None,Node.DIR)
229
self.srcnode=self.root.ensure_dir_node_from_path(srcdir)
230
debug('build: srcnode is %s and srcdir %s'%(str(self.srcnode.name),srcdir))
231
self.path=self.srcnode
232
try:os.makedirs(blddir)
235
self.bldnode=self.root.ensure_dir_node_from_path(blddir)
237
def rescan(self,src_dir_node):
238
if self.cache_scanned_folders.get(src_dir_node.id,None):return
239
self.cache_scanned_folders[src_dir_node.id]=1
240
if hasattr(self,'repository'):self.repository(src_dir_node)
241
if sys.platform=="win32"and not src_dir_node.name:
243
self.listdir_src(src_dir_node,src_dir_node.abspath())
244
h1=self.srcnode.height()
245
h2=src_dir_node.height()
249
lst.append(child.name)
253
for variant in self._variants:
254
sub_path=os.path.join(self.bldnode.abspath(),variant,*lst)
256
self.listdir_bld(src_dir_node,sub_path,variant)
258
dict=self.node_sigs[variant]
259
for node in src_dir_node.childs.values():
261
dict.__delitem__(node.id)
262
if node.id!=self.bldnode.id:
263
src_dir_node.childs.__delitem__(node.name)
264
os.makedirs(sub_path)
265
def listdir_src(self,parent_node,path):
266
listed_files=set(Utils.listdir(path))
267
self.cache_dir_contents[parent_node.id]=listed_files
268
debug('build: folder contents '+str(listed_files))
269
node_names=set([x.name for x in parent_node.childs.values()if x.id&3==Node.FILE])
270
cache=self.node_sigs[0]
271
to_keep=listed_files&node_names
273
node=parent_node.childs[x]
275
cache[node.id]=Utils.h_file(path+os.sep+node.name)
277
raise Utils.WafError("The file %s is not readable or has become a dir"%node.abspath())
278
to_remove=node_names-listed_files
280
cache=self.node_sigs[0]
281
for name in to_remove:
282
nd=parent_node.childs[name]
284
cache.__delitem__(nd.id)
285
parent_node.childs.__delitem__(name)
286
def listdir_bld(self,parent_node,path,variant):
287
i_existing_nodes=[x for x in parent_node.childs.values()if x.id&3==Node.BUILD]
288
listed_files=set(Utils.listdir(path))
289
node_names=set([x.name for x in i_existing_nodes])
290
remove_names=node_names-listed_files
291
ids_to_remove=[x.id for x in i_existing_nodes if x.name in remove_names]
292
cache=self.node_sigs[variant]
293
for nid in ids_to_remove:
295
cache.__delitem__(nid)
297
return self.env_of_name('default')
298
def set_env(self,name,val):
299
self.all_envs[name]=val
300
env=property(get_env,set_env)
301
def add_manual_dependency(self,path,value):
302
h=getattr(self,'deps_man',{})
303
if os.path.isabs(path):
304
node=self.root.find_resource(path)
306
node=self.path.find_resource(path)
309
def launch_node(self):
312
except AttributeError:
313
self.p_ln=self.root.find_dir(Options.launch_dir)
315
def glob(self,pattern,relative=True):
316
path=self.path.abspath()
317
files=[self.root.find_resource(x)for x in glob.glob(path+os.sep+pattern)]
319
files=[x.path_to_parent(self.path)for x in files if x]
321
files=[x.abspath()for x in files if x]
325
self.task_manager.add_group()
326
def hash_env_vars(self,env,vars_lst):
327
idx=str(id(env))+str(vars_lst)
328
try:return self.cache_sig_vars[idx]
330
lst=[env.get_flat(a)for a in vars_lst]
331
ret=Utils.h_list(lst)
332
debug("envhash: %s %s"%(ret.encode('hex'),str(lst)))
333
self.cache_sig_vars[idx]=ret
335
def name_to_obj(self,name):
336
cache=self.task_gen_cache_names
338
for x in self.all_task_gen:
341
elif not self.task_gen_cache_names.get(x,''):
343
return cache.get(name,None)
344
def flush(self,all=1):
346
self.task_gen_cache_names={}
348
debug('build: delayed operation TaskGen.flush() called')
349
if Options.options.compile_targets:
350
debug('task_gen: posting objects listed in compile_targets')
352
for target_name in Options.options.compile_targets.split(','):
353
target_name=target_name.strip()
354
targets_objects[target_name]=self.name_to_obj(target_name)
355
if all and not targets_objects[target_name]:raise Utils.WafError("target '%s' does not exist"%target_name)
356
for target_obj in targets_objects.values():
360
debug('task_gen: posting objects (normal)')
361
ln=self.launch_node()
362
if ln.is_child_of(self.bldnode)or not ln.is_child_of(self.srcnode):
364
for obj in self.all_task_gen:
365
if not obj.path.is_child_of(ln):continue
367
def env_of_name(self,name):
369
error('env_of_name called with no name!')
372
return self.all_envs[name]
374
error('no such environment: '+name)
376
def progress_line(self,state,total,col1,col2):
379
ind=Utils.rot_chr[Utils.rot_idx%4]
381
pc=(100.*state)/total
382
eta=time.strftime('%H:%M:%S',time.gmtime(time.time()-ini))
383
fs="[%%%dd/%%%dd][%%s%%2d%%%%%%s][%s]["%(n,n,ind)
384
left=fs%(state,total,col1,pc,col2)
385
right='][%s%s%s]'%(col1,eta,col2)
386
cols=Utils.get_term_cols()-len(left)-len(right)+2*len(col1)+2*len(col2)
388
ratio=int((cols*state)/total)-1
389
bar=('='*ratio+'>').ljust(cols)
390
msg=Utils.indicator%(left,bar,right)
392
def do_install(self,src,tgt,chmod=0644):
393
if Options.commands['install']:
394
if not Options.options.force:
396
t1=os.stat(tgt).st_mtime
397
t2=os.stat(src).st_mtime
403
srclbl=src.replace(self.srcnode.abspath(None)+os.sep,'')
404
info("* installing %s as %s"%(srclbl,tgt))
408
shutil.copy2(src,tgt)
414
error('File %r does not exist'%src)
415
raise Utils.WafError('Could not install the file %r'%tgt)
417
elif Options.commands['uninstall']:
418
info("* uninstalling %s"%tgt)
419
self.uninstall.append(tgt)
423
def get_install_path(self,path,env=None):
424
if not env:env=self.env
425
destdir=env.get_destdir()
426
destpath=Utils.subst_vars(path,env)
428
destpath=os.path.join(destdir,destpath.lstrip(os.sep))
430
def install_files(self,path,files,env=None,chmod=0644,relative_trick=False):
431
if not Options.is_install:return[]
434
if type(files)is types.StringType and'*'in files:
435
gl=node.abspath()+os.sep+files
438
lst=Utils.to_list(files)
440
destpath=self.get_install_path(path,env)
441
Utils.check_dir(destpath)
444
if not os.path.isabs(filename):
445
nd=node.find_resource(filename)
447
raise Utils.WafError("Unable to install the file `%s': not found in %s"%(filename,node))
449
destfile=os.path.join(destpath,filename)
450
Utils.check_dir(os.path.dirname(destfile))
452
destfile=os.path.join(destpath,nd.name)
453
filename=nd.abspath(env)
455
alst=Utils.split_path(filename)
456
destfile=os.path.join(destpath,alst[-1])
457
if self.do_install(filename,destfile,chmod):
458
installed_files.append(destfile)
459
return installed_files
460
def install_as(self,path,srcfile,env=None,chmod=0644):
461
if not Options.is_install:return False
462
if not path:return False
463
if not env:env=self.env
465
destpath=self.get_install_path(path,env)
466
dir,name=os.path.split(destpath)
468
if not os.path.isabs(srcfile):
469
filenode=node.find_resource(srcfile)
470
src=filenode.abspath(env)
473
return self.do_install(src,destpath,chmod)
474
def symlink_as(self,path,src,env=None):
475
if not Options.is_install:return
477
tgt=self.get_install_path(path,env)
478
dir,name=os.path.split(tgt)
480
if Options.commands['install']:
482
if not os.path.islink(tgt)or os.readlink(tgt)!=src:
483
info("* symlink %s (-> %s)"%(tgt,src))
488
elif Options.commands['uninstall']:
490
info("* removing %s"%(tgt))