1#!/usr/bin/env python
2# encoding: utf-8
3# Thomas Nagy, 2005-2018 (ita)
4
5"""
6Classes related to the build phase (build, clean, install, step, etc)
7
8The inheritance tree is the following:
9
10"""
11
12import os, sys, errno, re, shutil, stat
13try:
14	import cPickle
15except ImportError:
16	import pickle as cPickle
17from waflib import Node, Runner, TaskGen, Utils, ConfigSet, Task, Logs, Options, Context, Errors
18
19CACHE_DIR = 'c4che'
20"""Name of the cache directory"""
21
22CACHE_SUFFIX = '_cache.py'
23"""ConfigSet cache files for variants are written under :py:attr:´waflib.Build.CACHE_DIR´ in the form ´variant_name´_cache.py"""
24
25INSTALL = 1337
26"""Positive value '->' install, see :py:attr:`waflib.Build.BuildContext.is_install`"""
27
28UNINSTALL = -1337
29"""Negative value '<-' uninstall, see :py:attr:`waflib.Build.BuildContext.is_install`"""
30
31SAVED_ATTRS = 'root node_sigs task_sigs imp_sigs raw_deps node_deps'.split()
32"""Build class members to save between the runs; these should be all dicts
33except for `root` which represents a :py:class:`waflib.Node.Node` instance
34"""
35
36CFG_FILES = 'cfg_files'
37"""Files from the build directory to hash before starting the build (``config.h`` written during the configuration)"""
38
39POST_AT_ONCE = 0
40"""Post mode: all task generators are posted before any task executed"""
41
42POST_LAZY = 1
43"""Post mode: post the task generators group after group, the tasks in the next group are created when the tasks in the previous groups are done"""
44
45PROTOCOL = -1
46if sys.platform == 'cli':
47	PROTOCOL = 0
48
49class BuildContext(Context.Context):
50	'''executes the build'''
51
52	cmd = 'build'
53	variant = ''
54
55	def __init__(self, **kw):
56		super(BuildContext, self).__init__(**kw)
57
58		self.is_install = 0
59		"""Non-zero value when installing or uninstalling file"""
60
61		self.top_dir = kw.get('top_dir', Context.top_dir)
62		"""See :py:attr:`waflib.Context.top_dir`; prefer :py:attr:`waflib.Build.BuildContext.srcnode`"""
63
64		self.out_dir = kw.get('out_dir', Context.out_dir)
65		"""See :py:attr:`waflib.Context.out_dir`; prefer :py:attr:`waflib.Build.BuildContext.bldnode`"""
66
67		self.run_dir = kw.get('run_dir', Context.run_dir)
68		"""See :py:attr:`waflib.Context.run_dir`"""
69
70		self.launch_dir = Context.launch_dir
71		"""See :py:attr:`waflib.Context.out_dir`; prefer :py:meth:`waflib.Build.BuildContext.launch_node`"""
72
73		self.post_mode = POST_LAZY
74		"""Whether to post the task generators at once or group-by-group (default is group-by-group)"""
75
76		self.cache_dir = kw.get('cache_dir')
77		if not self.cache_dir:
78			self.cache_dir = os.path.join(self.out_dir, CACHE_DIR)
79
80		self.all_envs = {}
81		"""Map names to :py:class:`waflib.ConfigSet.ConfigSet`, the empty string must map to the default environment"""
82
83		# ======================================= #
84		# cache variables
85
86		self.node_sigs = {}
87		"""Dict mapping build nodes to task identifier (uid), it indicates whether a task created a particular file (persists across builds)"""
88
89		self.task_sigs = {}
90		"""Dict mapping task identifiers (uid) to task signatures (persists across builds)"""
91
92		self.imp_sigs = {}
93		"""Dict mapping task identifiers (uid) to implicit task dependencies used for scanning targets (persists across builds)"""
94
95		self.node_deps = {}
96		"""Dict mapping task identifiers (uid) to node dependencies found by :py:meth:`waflib.Task.Task.scan` (persists across builds)"""
97
98		self.raw_deps = {}
99		"""Dict mapping task identifiers (uid) to custom data returned by :py:meth:`waflib.Task.Task.scan` (persists across builds)"""
100
101		self.task_gen_cache_names = {}
102
103		self.jobs = Options.options.jobs
104		"""Amount of jobs to run in parallel"""
105
106		self.targets = Options.options.targets
107		"""List of targets to build (default: \\*)"""
108
109		self.keep = Options.options.keep
110		"""Whether the build should continue past errors"""
111
112		self.progress_bar = Options.options.progress_bar
113		"""
114		Level of progress status:
115
116		0. normal output
117		1. progress bar
118		2. IDE output
119		3. No output at all
120		"""
121
122		# Manual dependencies.
123		self.deps_man = Utils.defaultdict(list)
124		"""Manual dependencies set by :py:meth:`waflib.Build.BuildContext.add_manual_dependency`"""
125
126		# just the structure here
127		self.current_group = 0
128		"""
129		Current build group
130		"""
131
132		self.groups = []
133		"""
134		List containing lists of task generators
135		"""
136
137		self.group_names = {}
138		"""
139		Map group names to the group lists. See :py:meth:`waflib.Build.BuildContext.add_group`
140		"""
141
142		for v in SAVED_ATTRS:
143			if not hasattr(self, v):
144				setattr(self, v, {})
145
146	def get_variant_dir(self):
147		"""Getter for the variant_dir attribute"""
148		if not self.variant:
149			return self.out_dir
150		return os.path.join(self.out_dir, os.path.normpath(self.variant))
151	variant_dir = property(get_variant_dir, None)
152
153	def __call__(self, *k, **kw):
154		"""
155		Create a task generator and add it to the current build group. The following forms are equivalent::
156
157			def build(bld):
158				tg = bld(a=1, b=2)
159
160			def build(bld):
161				tg = bld()
162				tg.a = 1
163				tg.b = 2
164
165			def build(bld):
166				tg = TaskGen.task_gen(a=1, b=2)
167				bld.add_to_group(tg, None)
168
169		:param group: group name to add the task generator to
170		:type group: string
171		"""
172		kw['bld'] = self
173		ret = TaskGen.task_gen(*k, **kw)
174		self.task_gen_cache_names = {} # reset the cache, each time
175		self.add_to_group(ret, group=kw.get('group'))
176		return ret
177
178	def __copy__(self):
179		"""
180		Build contexts cannot be copied
181
182		:raises: :py:class:`waflib.Errors.WafError`
183		"""
184		raise Errors.WafError('build contexts cannot be copied')
185
186	def load_envs(self):
187		"""
188		The configuration command creates files of the form ``build/c4che/NAMEcache.py``. This method
189		creates a :py:class:`waflib.ConfigSet.ConfigSet` instance for each ``NAME`` by reading those
190		files and stores them in :py:attr:`waflib.Build.BuildContext.allenvs`.
191		"""
192		node = self.root.find_node(self.cache_dir)
193		if not node:
194			raise Errors.WafError('The project was not configured: run "waf configure" first!')
195		lst = node.ant_glob('**/*%s' % CACHE_SUFFIX, quiet=True)
196
197		if not lst:
198			raise Errors.WafError('The cache directory is empty: reconfigure the project')
199
200		for x in lst:
201			name = x.path_from(node).replace(CACHE_SUFFIX, '').replace('\\', '/')
202			env = ConfigSet.ConfigSet(x.abspath())
203			self.all_envs[name] = env
204			for f in env[CFG_FILES]:
205				newnode = self.root.find_resource(f)
206				if not newnode or not newnode.exists():
207					raise Errors.WafError('Missing configuration file %r, reconfigure the project!' % f)
208
209	def init_dirs(self):
210		"""
211		Initialize the project directory and the build directory by creating the nodes
212		:py:attr:`waflib.Build.BuildContext.srcnode` and :py:attr:`waflib.Build.BuildContext.bldnode`
213		corresponding to ``top_dir`` and ``variant_dir`` respectively. The ``bldnode`` directory is
214		created if necessary.
215		"""
216		if not (os.path.isabs(self.top_dir) and os.path.isabs(self.out_dir)):
217			raise Errors.WafError('The project was not configured: run "waf configure" first!')
218
219		self.path = self.srcnode = self.root.find_dir(self.top_dir)
220		self.bldnode = self.root.make_node(self.variant_dir)
221		self.bldnode.mkdir()
222
223	def execute(self):
224		"""
225		Restore data from previous builds and call :py:meth:`waflib.Build.BuildContext.execute_build`.
226		Overrides from :py:func:`waflib.Context.Context.execute`
227		"""
228		self.restore()
229		if not self.all_envs:
230			self.load_envs()
231		self.execute_build()
232
233	def execute_build(self):
234		"""
235		Execute the build by:
236
237		* reading the scripts (see :py:meth:`waflib.Context.Context.recurse`)
238		* calling :py:meth:`waflib.Build.BuildContext.pre_build` to call user build functions
239		* calling :py:meth:`waflib.Build.BuildContext.compile` to process the tasks
240		* calling :py:meth:`waflib.Build.BuildContext.post_build` to call user build functions
241		"""
242
243		Logs.info("Waf: Entering directory `%s'", self.variant_dir)
244		self.recurse([self.run_dir])
245		self.pre_build()
246
247		# display the time elapsed in the progress bar
248		self.timer = Utils.Timer()
249
250		try:
251			self.compile()
252		finally:
253			if self.progress_bar == 1 and sys.stderr.isatty():
254				c = self.producer.processed or 1
255				m = self.progress_line(c, c, Logs.colors.BLUE, Logs.colors.NORMAL)
256				Logs.info(m, extra={'stream': sys.stderr, 'c1': Logs.colors.cursor_off, 'c2' : Logs.colors.cursor_on})
257			Logs.info("Waf: Leaving directory `%s'", self.variant_dir)
258		try:
259			self.producer.bld = None
260			del self.producer
261		except AttributeError:
262			pass
263		self.post_build()
264
265	def restore(self):
266		"""
267		Load data from a previous run, sets the attributes listed in :py:const:`waflib.Build.SAVED_ATTRS`
268		"""
269		try:
270			env = ConfigSet.ConfigSet(os.path.join(self.cache_dir, 'build.config.py'))
271		except EnvironmentError:
272			pass
273		else:
274			if env.version < Context.HEXVERSION:
275				raise Errors.WafError('Project was configured with a different version of Waf, please reconfigure it')
276
277			for t in env.tools:
278				self.setup(**t)
279
280		dbfn = os.path.join(self.variant_dir, Context.DBFILE)
281		try:
282			data = Utils.readf(dbfn, 'rb')
283		except (EnvironmentError, EOFError):
284			# handle missing file/empty file
285			Logs.debug('build: Could not load the build cache %s (missing)', dbfn)
286		else:
287			try:
288				Node.pickle_lock.acquire()
289				Node.Nod3 = self.node_class
290				try:
291					data = cPickle.loads(data)
292				except Exception as e:
293					Logs.debug('build: Could not pickle the build cache %s: %r', dbfn, e)
294				else:
295					for x in SAVED_ATTRS:
296						setattr(self, x, data.get(x, {}))
297			finally:
298				Node.pickle_lock.release()
299
300		self.init_dirs()
301
302	def store(self):
303		"""
304		Store data for next runs, set the attributes listed in :py:const:`waflib.Build.SAVED_ATTRS`. Uses a temporary
305		file to avoid problems on ctrl+c.
306		"""
307		data = {}
308		for x in SAVED_ATTRS:
309			data[x] = getattr(self, x)
310		db = os.path.join(self.variant_dir, Context.DBFILE)
311
312		try:
313			Node.pickle_lock.acquire()
314			Node.Nod3 = self.node_class
315			x = cPickle.dumps(data, PROTOCOL)
316		finally:
317			Node.pickle_lock.release()
318
319		Utils.writef(db + '.tmp', x, m='wb')
320
321		try:
322			st = os.stat(db)
323			os.remove(db)
324			if not Utils.is_win32: # win32 has no chown but we're paranoid
325				os.chown(db + '.tmp', st.st_uid, st.st_gid)
326		except (AttributeError, OSError):
327			pass
328
329		# do not use shutil.move (copy is not thread-safe)
330		os.rename(db + '.tmp', db)
331
332	def compile(self):
333		"""
334		Run the build by creating an instance of :py:class:`waflib.Runner.Parallel`
335		The cache file is written when at least a task was executed.
336
337		:raises: :py:class:`waflib.Errors.BuildError` in case the build fails
338		"""
339		Logs.debug('build: compile()')
340
341		# delegate the producer-consumer logic to another object to reduce the complexity
342		self.producer = Runner.Parallel(self, self.jobs)
343		self.producer.biter = self.get_build_iterator()
344		try:
345			self.producer.start()
346		except KeyboardInterrupt:
347			if self.is_dirty():
348				self.store()
349			raise
350		else:
351			if self.is_dirty():
352				self.store()
353
354		if self.producer.error:
355			raise Errors.BuildError(self.producer.error)
356
357	def is_dirty(self):
358		return self.producer.dirty
359
360	def setup(self, tool, tooldir=None, funs=None):
361		"""
362		Import waf tools defined during the configuration::
363
364			def configure(conf):
365				conf.load('glib2')
366
367			def build(bld):
368				pass # glib2 is imported implicitly
369
370		:param tool: tool list
371		:type tool: list
372		:param tooldir: optional tool directory (sys.path)
373		:type tooldir: list of string
374		:param funs: unused variable
375		"""
376		if isinstance(tool, list):
377			for i in tool:
378				self.setup(i, tooldir)
379			return
380
381		module = Context.load_tool(tool, tooldir)
382		if hasattr(module, "setup"):
383			module.setup(self)
384
385	def get_env(self):
386		"""Getter for the env property"""
387		try:
388			return self.all_envs[self.variant]
389		except KeyError:
390			return self.all_envs['']
391	def set_env(self, val):
392		"""Setter for the env property"""
393		self.all_envs[self.variant] = val
394
395	env = property(get_env, set_env)
396
397	def add_manual_dependency(self, path, value):
398		"""
399		Adds a dependency from a node object to a value::
400
401			def build(bld):
402				bld.add_manual_dependency(
403					bld.path.find_resource('wscript'),
404					bld.root.find_resource('/etc/fstab'))
405
406		:param path: file path
407		:type path: string or :py:class:`waflib.Node.Node`
408		:param value: value to depend
409		:type value: :py:class:`waflib.Node.Node`, byte object, or function returning a byte object
410		"""
411		if not path:
412			raise ValueError('Invalid input path %r' % path)
413
414		if isinstance(path, Node.Node):
415			node = path
416		elif os.path.isabs(path):
417			node = self.root.find_resource(path)
418		else:
419			node = self.path.find_resource(path)
420		if not node:
421			raise ValueError('Could not find the path %r' % path)
422
423		if isinstance(value, list):
424			self.deps_man[node].extend(value)
425		else:
426			self.deps_man[node].append(value)
427
428	def launch_node(self):
429		"""Returns the launch directory as a :py:class:`waflib.Node.Node` object (cached)"""
430		try:
431			# private cache
432			return self.p_ln
433		except AttributeError:
434			self.p_ln = self.root.find_dir(self.launch_dir)
435			return self.p_ln
436
437	def hash_env_vars(self, env, vars_lst):
438		"""
439		Hashes configuration set variables::
440
441			def build(bld):
442				bld.hash_env_vars(bld.env, ['CXX', 'CC'])
443
444		This method uses an internal cache.
445
446		:param env: Configuration Set
447		:type env: :py:class:`waflib.ConfigSet.ConfigSet`
448		:param vars_lst: list of variables
449		:type vars_list: list of string
450		"""
451
452		if not env.table:
453			env = env.parent
454			if not env:
455				return Utils.SIG_NIL
456
457		idx = str(id(env)) + str(vars_lst)
458		try:
459			cache = self.cache_env
460		except AttributeError:
461			cache = self.cache_env = {}
462		else:
463			try:
464				return self.cache_env[idx]
465			except KeyError:
466				pass
467
468		lst = [env[a] for a in vars_lst]
469		cache[idx] = ret = Utils.h_list(lst)
470		Logs.debug('envhash: %s %r', Utils.to_hex(ret), lst)
471		return ret
472
473	def get_tgen_by_name(self, name):
474		"""
475		Fetches a task generator by its name or its target attribute;
476		the name must be unique in a build::
477
478			def build(bld):
479				tg = bld(name='foo')
480				tg == bld.get_tgen_by_name('foo')
481
482		This method use a private internal cache.
483
484		:param name: Task generator name
485		:raises: :py:class:`waflib.Errors.WafError` in case there is no task genenerator by that name
486		"""
487		cache = self.task_gen_cache_names
488		if not cache:
489			# create the index lazily
490			for g in self.groups:
491				for tg in g:
492					try:
493						cache[tg.name] = tg
494					except AttributeError:
495						# raised if not a task generator, which should be uncommon
496						pass
497		try:
498			return cache[name]
499		except KeyError:
500			raise Errors.WafError('Could not find a task generator for the name %r' % name)
501
502	def progress_line(self, idx, total, col1, col2):
503		"""
504		Computes a progress bar line displayed when running ``waf -p``
505
506		:returns: progress bar line
507		:rtype: string
508		"""
509		if not sys.stderr.isatty():
510			return ''
511
512		n = len(str(total))
513
514		Utils.rot_idx += 1
515		ind = Utils.rot_chr[Utils.rot_idx % 4]
516
517		pc = (100. * idx)/total
518		fs = "[%%%dd/%%d][%%s%%2d%%%%%%s][%s][" % (n, ind)
519		left = fs % (idx, total, col1, pc, col2)
520		right = '][%s%s%s]' % (col1, self.timer, col2)
521
522		cols = Logs.get_term_cols() - len(left) - len(right) + 2*len(col1) + 2*len(col2)
523		if cols < 7:
524			cols = 7
525
526		ratio = ((cols * idx)//total) - 1
527
528		bar = ('='*ratio+'>').ljust(cols)
529		msg = Logs.indicator % (left, bar, right)
530
531		return msg
532
533	def declare_chain(self, *k, **kw):
534		"""
535		Wraps :py:func:`waflib.TaskGen.declare_chain` for convenience
536		"""
537		return TaskGen.declare_chain(*k, **kw)
538
539	def pre_build(self):
540		"""Executes user-defined methods before the build starts, see :py:meth:`waflib.Build.BuildContext.add_pre_fun`"""
541		for m in getattr(self, 'pre_funs', []):
542			m(self)
543
544	def post_build(self):
545		"""Executes user-defined methods after the build is successful, see :py:meth:`waflib.Build.BuildContext.add_post_fun`"""
546		for m in getattr(self, 'post_funs', []):
547			m(self)
548
549	def add_pre_fun(self, meth):
550		"""
551		Binds a callback method to execute after the scripts are read and before the build starts::
552
553			def mycallback(bld):
554				print("Hello, world!")
555
556			def build(bld):
557				bld.add_pre_fun(mycallback)
558		"""
559		try:
560			self.pre_funs.append(meth)
561		except AttributeError:
562			self.pre_funs = [meth]
563
564	def add_post_fun(self, meth):
565		"""
566		Binds a callback method to execute immediately after the build is successful::
567
568			def call_ldconfig(bld):
569				bld.exec_command('/sbin/ldconfig')
570
571			def build(bld):
572				if bld.cmd == 'install':
573					bld.add_pre_fun(call_ldconfig)
574		"""
575		try:
576			self.post_funs.append(meth)
577		except AttributeError:
578			self.post_funs = [meth]
579
580	def get_group(self, x):
581		"""
582		Returns the build group named `x`, or the current group if `x` is None
583
584		:param x: name or number or None
585		:type x: string, int or None
586		"""
587		if not self.groups:
588			self.add_group()
589		if x is None:
590			return self.groups[self.current_group]
591		if x in self.group_names:
592			return self.group_names[x]
593		return self.groups[x]
594
595	def add_to_group(self, tgen, group=None):
596		"""Adds a task or a task generator to the build; there is no attempt to remove it if it was already added."""
597		assert(isinstance(tgen, TaskGen.task_gen) or isinstance(tgen, Task.Task))
598		tgen.bld = self
599		self.get_group(group).append(tgen)
600
601	def get_group_name(self, g):
602		"""
603		Returns the name of the input build group
604
605		:param g: build group object or build group index
606		:type g: integer or list
607		:return: name
608		:rtype: string
609		"""
610		if not isinstance(g, list):
611			g = self.groups[g]
612		for x in self.group_names:
613			if id(self.group_names[x]) == id(g):
614				return x
615		return ''
616
617	def get_group_idx(self, tg):
618		"""
619		Returns the index of the group containing the task generator given as argument::
620
621			def build(bld):
622				tg = bld(name='nada')
623				0 == bld.get_group_idx(tg)
624
625		:param tg: Task generator object
626		:type tg: :py:class:`waflib.TaskGen.task_gen`
627		:rtype: int
628		"""
629		se = id(tg)
630		for i, tmp in enumerate(self.groups):
631			for t in tmp:
632				if id(t) == se:
633					return i
634		return None
635
636	def add_group(self, name=None, move=True):
637		"""
638		Adds a new group of tasks/task generators. By default the new group becomes
639		the default group for new task generators (make sure to create build groups in order).
640
641		:param name: name for this group
642		:type name: string
643		:param move: set this new group as default group (True by default)
644		:type move: bool
645		:raises: :py:class:`waflib.Errors.WafError` if a group by the name given already exists
646		"""
647		if name and name in self.group_names:
648			raise Errors.WafError('add_group: name %s already present', name)
649		g = []
650		self.group_names[name] = g
651		self.groups.append(g)
652		if move:
653			self.current_group = len(self.groups) - 1
654
655	def set_group(self, idx):
656		"""
657		Sets the build group at position idx as current so that newly added
658		task generators are added to this one by default::
659
660			def build(bld):
661				bld(rule='touch ${TGT}', target='foo.txt')
662				bld.add_group() # now the current group is 1
663				bld(rule='touch ${TGT}', target='bar.txt')
664				bld.set_group(0) # now the current group is 0
665				bld(rule='touch ${TGT}', target='truc.txt') # build truc.txt before bar.txt
666
667		:param idx: group name or group index
668		:type idx: string or int
669		"""
670		if isinstance(idx, str):
671			g = self.group_names[idx]
672			for i, tmp in enumerate(self.groups):
673				if id(g) == id(tmp):
674					self.current_group = i
675					break
676		else:
677			self.current_group = idx
678
679	def total(self):
680		"""
681		Approximate task count: this value may be inaccurate if task generators
682		are posted lazily (see :py:attr:`waflib.Build.BuildContext.post_mode`).
683		The value :py:attr:`waflib.Runner.Parallel.total` is updated during the task execution.
684
685		:rtype: int
686		"""
687		total = 0
688		for group in self.groups:
689			for tg in group:
690				try:
691					total += len(tg.tasks)
692				except AttributeError:
693					total += 1
694		return total
695
696	def get_targets(self):
697		"""
698		This method returns a pair containing the index of the last build group to post,
699		and the list of task generator objects corresponding to the target names.
700
701		This is used internally by :py:meth:`waflib.Build.BuildContext.get_build_iterator`
702		to perform partial builds::
703
704			$ waf --targets=myprogram,myshlib
705
706		:return: the minimum build group index, and list of task generators
707		:rtype: tuple
708		"""
709		to_post = []
710		min_grp = 0
711		for name in self.targets.split(','):
712			tg = self.get_tgen_by_name(name)
713			m = self.get_group_idx(tg)
714			if m > min_grp:
715				min_grp = m
716				to_post = [tg]
717			elif m == min_grp:
718				to_post.append(tg)
719		return (min_grp, to_post)
720
721	def get_all_task_gen(self):
722		"""
723		Returns a list of all task generators for troubleshooting purposes.
724		"""
725		lst = []
726		for g in self.groups:
727			lst.extend(g)
728		return lst
729
730	def post_group(self):
731		"""
732		Post task generators from the group indexed by self.current_group; used internally
733		by :py:meth:`waflib.Build.BuildContext.get_build_iterator`
734		"""
735		def tgpost(tg):
736			try:
737				f = tg.post
738			except AttributeError:
739				pass
740			else:
741				f()
742
743		if self.targets == '*':
744			for tg in self.groups[self.current_group]:
745				tgpost(tg)
746		elif self.targets:
747			if self.current_group < self._min_grp:
748				for tg in self.groups[self.current_group]:
749					tgpost(tg)
750			else:
751				for tg in self._exact_tg:
752					tg.post()
753		else:
754			ln = self.launch_node()
755			if ln.is_child_of(self.bldnode):
756				if Logs.verbose > 1:
757					Logs.warn('Building from the build directory, forcing --targets=*')
758				ln = self.srcnode
759			elif not ln.is_child_of(self.srcnode):
760				if Logs.verbose > 1:
761					Logs.warn('CWD %s is not under %s, forcing --targets=* (run distclean?)', ln.abspath(), self.srcnode.abspath())
762				ln = self.srcnode
763
764			def is_post(tg, ln):
765				try:
766					p = tg.path
767				except AttributeError:
768					pass
769				else:
770					if p.is_child_of(ln):
771						return True
772
773			def is_post_group():
774				for i, g in enumerate(self.groups):
775					if i > self.current_group:
776						for tg in g:
777							if is_post(tg, ln):
778								return True
779
780			if self.post_mode == POST_LAZY and ln != self.srcnode:
781				# partial folder builds require all targets from a previous build group
782				if is_post_group():
783					ln = self.srcnode
784
785			for tg in self.groups[self.current_group]:
786				if is_post(tg, ln):
787					tgpost(tg)
788
789	def get_tasks_group(self, idx):
790		"""
791		Returns all task instances for the build group at position idx,
792		used internally by :py:meth:`waflib.Build.BuildContext.get_build_iterator`
793
794		:rtype: list of :py:class:`waflib.Task.Task`
795		"""
796		tasks = []
797		for tg in self.groups[idx]:
798			try:
799				tasks.extend(tg.tasks)
800			except AttributeError: # not a task generator
801				tasks.append(tg)
802		return tasks
803
804	def get_build_iterator(self):
805		"""
806		Creates a Python generator object that returns lists of tasks that may be processed in parallel.
807
808		:return: tasks which can be executed immediately
809		:rtype: generator returning lists of :py:class:`waflib.Task.Task`
810		"""
811		if self.targets and self.targets != '*':
812			(self._min_grp, self._exact_tg) = self.get_targets()
813
814		if self.post_mode != POST_LAZY:
815			for self.current_group, _ in enumerate(self.groups):
816				self.post_group()
817
818		for self.current_group, _ in enumerate(self.groups):
819			# first post the task generators for the group
820			if self.post_mode != POST_AT_ONCE:
821				self.post_group()
822
823			# then extract the tasks
824			tasks = self.get_tasks_group(self.current_group)
825
826			# if the constraints are set properly (ext_in/ext_out, before/after)
827			# the call to set_file_constraints may be removed (can be a 15% penalty on no-op rebuilds)
828			# (but leave set_file_constraints for the installation step)
829			#
830			# if the tasks have only files, set_file_constraints is required but set_precedence_constraints is not necessary
831			#
832			Task.set_file_constraints(tasks)
833			Task.set_precedence_constraints(tasks)
834
835			self.cur_tasks = tasks
836			if tasks:
837				yield tasks
838
839		while 1:
840			# the build stops once there are no tasks to process
841			yield []
842
843	def install_files(self, dest, files, **kw):
844		"""
845		Creates a task generator to install files on the system::
846
847			def build(bld):
848				bld.install_files('${DATADIR}', self.path.find_resource('wscript'))
849
850		:param dest: path representing the destination directory
851		:type dest: :py:class:`waflib.Node.Node` or string (absolute path)
852		:param files: input files
853		:type files: list of strings or list of :py:class:`waflib.Node.Node`
854		:param env: configuration set to expand *dest*
855		:type env: :py:class:`waflib.ConfigSet.ConfigSet`
856		:param relative_trick: preserve the folder hierarchy when installing whole folders
857		:type relative_trick: bool
858		:param cwd: parent node for searching srcfile, when srcfile is not an instance of :py:class:`waflib.Node.Node`
859		:type cwd: :py:class:`waflib.Node.Node`
860		:param postpone: execute the task immediately to perform the installation (False by default)
861		:type postpone: bool
862		"""
863		assert(dest)
864		tg = self(features='install_task', install_to=dest, install_from=files, **kw)
865		tg.dest = tg.install_to
866		tg.type = 'install_files'
867		if not kw.get('postpone', True):
868			tg.post()
869		return tg
870
871	def install_as(self, dest, srcfile, **kw):
872		"""
873		Creates a task generator to install a file on the system with a different name::
874
875			def build(bld):
876				bld.install_as('${PREFIX}/bin', 'myapp', chmod=Utils.O755)
877
878		:param dest: destination file
879		:type dest: :py:class:`waflib.Node.Node` or string (absolute path)
880		:param srcfile: input file
881		:type srcfile: string or :py:class:`waflib.Node.Node`
882		:param cwd: parent node for searching srcfile, when srcfile is not an instance of :py:class:`waflib.Node.Node`
883		:type cwd: :py:class:`waflib.Node.Node`
884		:param env: configuration set for performing substitutions in dest
885		:type env: :py:class:`waflib.ConfigSet.ConfigSet`
886		:param postpone: execute the task immediately to perform the installation (False by default)
887		:type postpone: bool
888		"""
889		assert(dest)
890		tg = self(features='install_task', install_to=dest, install_from=srcfile, **kw)
891		tg.dest = tg.install_to
892		tg.type = 'install_as'
893		if not kw.get('postpone', True):
894			tg.post()
895		return tg
896
897	def symlink_as(self, dest, src, **kw):
898		"""
899		Creates a task generator to install a symlink::
900
901			def build(bld):
902				bld.symlink_as('${PREFIX}/lib/libfoo.so', 'libfoo.so.1.2.3')
903
904		:param dest: absolute path of the symlink
905		:type dest: :py:class:`waflib.Node.Node` or string (absolute path)
906		:param src: link contents, which is a relative or absolute path which may exist or not
907		:type src: string
908		:param env: configuration set for performing substitutions in dest
909		:type env: :py:class:`waflib.ConfigSet.ConfigSet`
910		:param add: add the task created to a build group - set ``False`` only if the installation task is created after the build has started
911		:type add: bool
912		:param postpone: execute the task immediately to perform the installation
913		:type postpone: bool
914		:param relative_trick: make the symlink relative (default: ``False``)
915		:type relative_trick: bool
916		"""
917		assert(dest)
918		tg = self(features='install_task', install_to=dest, install_from=src, **kw)
919		tg.dest = tg.install_to
920		tg.type = 'symlink_as'
921		tg.link = src
922		# TODO if add: self.add_to_group(tsk)
923		if not kw.get('postpone', True):
924			tg.post()
925		return tg
926
927@TaskGen.feature('install_task')
928@TaskGen.before_method('process_rule', 'process_source')
929def process_install_task(self):
930	"""Creates the installation task for the current task generator; uses :py:func:`waflib.Build.add_install_task` internally."""
931	self.add_install_task(**self.__dict__)
932
933@TaskGen.taskgen_method
934def add_install_task(self, **kw):
935	"""
936	Creates the installation task for the current task generator, and executes it immediately if necessary
937
938	:returns: An installation task
939	:rtype: :py:class:`waflib.Build.inst`
940	"""
941	if not self.bld.is_install:
942		return
943	if not kw['install_to']:
944		return
945
946	if kw['type'] == 'symlink_as' and Utils.is_win32:
947		if kw.get('win32_install'):
948			kw['type'] = 'install_as'
949		else:
950			# just exit
951			return
952
953	tsk = self.install_task = self.create_task('inst')
954	tsk.chmod = kw.get('chmod', Utils.O644)
955	tsk.link = kw.get('link', '') or kw.get('install_from', '')
956	tsk.relative_trick = kw.get('relative_trick', False)
957	tsk.type = kw['type']
958	tsk.install_to = tsk.dest = kw['install_to']
959	tsk.install_from = kw['install_from']
960	tsk.relative_base = kw.get('cwd') or kw.get('relative_base', self.path)
961	tsk.install_user = kw.get('install_user')
962	tsk.install_group = kw.get('install_group')
963	tsk.init_files()
964	if not kw.get('postpone', True):
965		tsk.run_now()
966	return tsk
967
968@TaskGen.taskgen_method
969def add_install_files(self, **kw):
970	"""
971	Creates an installation task for files
972
973	:returns: An installation task
974	:rtype: :py:class:`waflib.Build.inst`
975	"""
976	kw['type'] = 'install_files'
977	return self.add_install_task(**kw)
978
979@TaskGen.taskgen_method
980def add_install_as(self, **kw):
981	"""
982	Creates an installation task for a single file
983
984	:returns: An installation task
985	:rtype: :py:class:`waflib.Build.inst`
986	"""
987	kw['type'] = 'install_as'
988	return self.add_install_task(**kw)
989
990@TaskGen.taskgen_method
991def add_symlink_as(self, **kw):
992	"""
993	Creates an installation task for a symbolic link
994
995	:returns: An installation task
996	:rtype: :py:class:`waflib.Build.inst`
997	"""
998	kw['type'] = 'symlink_as'
999	return self.add_install_task(**kw)
1000
1001class inst(Task.Task):
1002	"""Task that installs files or symlinks; it is typically executed by :py:class:`waflib.Build.InstallContext` and :py:class:`waflib.Build.UnInstallContext`"""
1003	def __str__(self):
1004		"""Returns an empty string to disable the standard task display"""
1005		return ''
1006
1007	def uid(self):
1008		"""Returns a unique identifier for the task"""
1009		lst = self.inputs + self.outputs + [self.link, self.generator.path.abspath()]
1010		return Utils.h_list(lst)
1011
1012	def init_files(self):
1013		"""
1014		Initializes the task input and output nodes
1015		"""
1016		if self.type == 'symlink_as':
1017			inputs = []
1018		else:
1019			inputs = self.generator.to_nodes(self.install_from)
1020			if self.type == 'install_as':
1021				assert len(inputs) == 1
1022		self.set_inputs(inputs)
1023
1024		dest = self.get_install_path()
1025		outputs = []
1026		if self.type == 'symlink_as':
1027			if self.relative_trick:
1028				self.link = os.path.relpath(self.link, os.path.dirname(dest))
1029			outputs.append(self.generator.bld.root.make_node(dest))
1030		elif self.type == 'install_as':
1031			outputs.append(self.generator.bld.root.make_node(dest))
1032		else:
1033			for y in inputs:
1034				if self.relative_trick:
1035					destfile = os.path.join(dest, y.path_from(self.relative_base))
1036				else:
1037					destfile = os.path.join(dest, y.name)
1038				outputs.append(self.generator.bld.root.make_node(destfile))
1039		self.set_outputs(outputs)
1040
1041	def runnable_status(self):
1042		"""
1043		Installation tasks are always executed, so this method returns either :py:const:`waflib.Task.ASK_LATER` or :py:const:`waflib.Task.RUN_ME`.
1044		"""
1045		ret = super(inst, self).runnable_status()
1046		if ret == Task.SKIP_ME and self.generator.bld.is_install:
1047			return Task.RUN_ME
1048		return ret
1049
1050	def post_run(self):
1051		"""
1052		Disables any post-run operations
1053		"""
1054		pass
1055
1056	def get_install_path(self, destdir=True):
1057		"""
1058		Returns the destination path where files will be installed, pre-pending `destdir`.
1059
1060		Relative paths will be interpreted relative to `PREFIX` if no `destdir` is given.
1061
1062		:rtype: string
1063		"""
1064		if isinstance(self.install_to, Node.Node):
1065			dest = self.install_to.abspath()
1066		else:
1067			dest = os.path.normpath(Utils.subst_vars(self.install_to, self.env))
1068		if not os.path.isabs(dest):
1069		    dest = os.path.join(self.env.PREFIX, dest)
1070		if destdir and Options.options.destdir:
1071			dest = os.path.join(Options.options.destdir, os.path.splitdrive(dest)[1].lstrip(os.sep))
1072		return dest
1073
1074	def copy_fun(self, src, tgt):
1075		"""
1076		Copies a file from src to tgt, preserving permissions and trying to work
1077		around path limitations on Windows platforms. On Unix-like platforms,
1078		the owner/group of the target file may be set through install_user/install_group
1079
1080		:param src: absolute path
1081		:type src: string
1082		:param tgt: absolute path
1083		:type tgt: string
1084		"""
1085		# override this if you want to strip executables
1086		# kw['tsk'].source is the task that created the files in the build
1087		if Utils.is_win32 and len(tgt) > 259 and not tgt.startswith('\\\\?\\'):
1088			tgt = '\\\\?\\' + tgt
1089		shutil.copy2(src, tgt)
1090		self.fix_perms(tgt)
1091
1092	def rm_empty_dirs(self, tgt):
1093		"""
1094		Removes empty folders recursively when uninstalling.
1095
1096		:param tgt: absolute path
1097		:type tgt: string
1098		"""
1099		while tgt:
1100			tgt = os.path.dirname(tgt)
1101			try:
1102				os.rmdir(tgt)
1103			except OSError:
1104				break
1105
1106	def run(self):
1107		"""
1108		Performs file or symlink installation
1109		"""
1110		is_install = self.generator.bld.is_install
1111		if not is_install: # unnecessary?
1112			return
1113
1114		for x in self.outputs:
1115			if is_install == INSTALL:
1116				x.parent.mkdir()
1117		if self.type == 'symlink_as':
1118			fun = is_install == INSTALL and self.do_link or self.do_unlink
1119			fun(self.link, self.outputs[0].abspath())
1120		else:
1121			fun = is_install == INSTALL and self.do_install or self.do_uninstall
1122			launch_node = self.generator.bld.launch_node()
1123			for x, y in zip(self.inputs, self.outputs):
1124				fun(x.abspath(), y.abspath(), x.path_from(launch_node))
1125
1126	def run_now(self):
1127		"""
1128		Try executing the installation task right now
1129
1130		:raises: :py:class:`waflib.Errors.TaskNotReady`
1131		"""
1132		status = self.runnable_status()
1133		if status not in (Task.RUN_ME, Task.SKIP_ME):
1134			raise Errors.TaskNotReady('Could not process %r: status %r' % (self, status))
1135		self.run()
1136		self.hasrun = Task.SUCCESS
1137
1138	def do_install(self, src, tgt, lbl, **kw):
1139		"""
1140		Copies a file from src to tgt with given file permissions. The actual copy is only performed
1141		if the source and target file sizes or timestamps differ. When the copy occurs,
1142		the file is always first removed and then copied so as to prevent stale inodes.
1143
1144		:param src: file name as absolute path
1145		:type src: string
1146		:param tgt: file destination, as absolute path
1147		:type tgt: string
1148		:param lbl: file source description
1149		:type lbl: string
1150		:param chmod: installation mode
1151		:type chmod: int
1152		:raises: :py:class:`waflib.Errors.WafError` if the file cannot be written
1153		"""
1154		if not Options.options.force:
1155			# check if the file is already there to avoid a copy
1156			try:
1157				st1 = os.stat(tgt)
1158				st2 = os.stat(src)
1159			except OSError:
1160				pass
1161			else:
1162				# same size and identical timestamps -> make no copy
1163				if st1.st_mtime + 2 >= st2.st_mtime and st1.st_size == st2.st_size:
1164					if not self.generator.bld.progress_bar:
1165
1166						c1 = Logs.colors.NORMAL
1167						c2 = Logs.colors.BLUE
1168
1169						Logs.info('%s- install %s%s%s (from %s)', c1, c2, tgt, c1, lbl)
1170					return False
1171
1172		if not self.generator.bld.progress_bar:
1173
1174			c1 = Logs.colors.NORMAL
1175			c2 = Logs.colors.BLUE
1176
1177			Logs.info('%s+ install %s%s%s (from %s)', c1, c2, tgt, c1, lbl)
1178
1179		# Give best attempt at making destination overwritable,
1180		# like the 'install' utility used by 'make install' does.
1181		try:
1182			os.chmod(tgt, Utils.O644 | stat.S_IMODE(os.stat(tgt).st_mode))
1183		except EnvironmentError:
1184			pass
1185
1186		# following is for shared libs and stale inodes (-_-)
1187		try:
1188			os.remove(tgt)
1189		except OSError:
1190			pass
1191
1192		try:
1193			self.copy_fun(src, tgt)
1194		except EnvironmentError as e:
1195			if not os.path.exists(src):
1196				Logs.error('File %r does not exist', src)
1197			elif not os.path.isfile(src):
1198				Logs.error('Input %r is not a file', src)
1199			raise Errors.WafError('Could not install the file %r' % tgt, e)
1200
1201	def fix_perms(self, tgt):
1202		"""
1203		Change the ownership of the file/folder/link pointed by the given path
1204		This looks up for `install_user` or `install_group` attributes
1205		on the task or on the task generator::
1206
1207			def build(bld):
1208				bld.install_as('${PREFIX}/wscript',
1209					'wscript',
1210					install_user='nobody', install_group='nogroup')
1211				bld.symlink_as('${PREFIX}/wscript_link',
1212					Utils.subst_vars('${PREFIX}/wscript', bld.env),
1213					install_user='nobody', install_group='nogroup')
1214		"""
1215		if not Utils.is_win32:
1216			user = getattr(self, 'install_user', None) or getattr(self.generator, 'install_user', None)
1217			group = getattr(self, 'install_group', None) or getattr(self.generator, 'install_group', None)
1218			if user or group:
1219				Utils.lchown(tgt, user or -1, group or -1)
1220		if not os.path.islink(tgt):
1221			os.chmod(tgt, self.chmod)
1222
1223	def do_link(self, src, tgt, **kw):
1224		"""
1225		Creates a symlink from tgt to src.
1226
1227		:param src: file name as absolute path
1228		:type src: string
1229		:param tgt: file destination, as absolute path
1230		:type tgt: string
1231		"""
1232		if os.path.islink(tgt) and os.readlink(tgt) == src:
1233			if not self.generator.bld.progress_bar:
1234				c1 = Logs.colors.NORMAL
1235				c2 = Logs.colors.BLUE
1236				Logs.info('%s- symlink %s%s%s (to %s)', c1, c2, tgt, c1, src)
1237		else:
1238			try:
1239				os.remove(tgt)
1240			except OSError:
1241				pass
1242			if not self.generator.bld.progress_bar:
1243				c1 = Logs.colors.NORMAL
1244				c2 = Logs.colors.BLUE
1245				Logs.info('%s+ symlink %s%s%s (to %s)', c1, c2, tgt, c1, src)
1246			os.symlink(src, tgt)
1247			self.fix_perms(tgt)
1248
1249	def do_uninstall(self, src, tgt, lbl, **kw):
1250		"""
1251		See :py:meth:`waflib.Build.inst.do_install`
1252		"""
1253		if not self.generator.bld.progress_bar:
1254			c1 = Logs.colors.NORMAL
1255			c2 = Logs.colors.BLUE
1256			Logs.info('%s- remove %s%s%s', c1, c2, tgt, c1)
1257
1258		#self.uninstall.append(tgt)
1259		try:
1260			os.remove(tgt)
1261		except OSError as e:
1262			if e.errno != errno.ENOENT:
1263				if not getattr(self, 'uninstall_error', None):
1264					self.uninstall_error = True
1265					Logs.warn('build: some files could not be uninstalled (retry with -vv to list them)')
1266				if Logs.verbose > 1:
1267					Logs.warn('Could not remove %s (error code %r)', e.filename, e.errno)
1268		self.rm_empty_dirs(tgt)
1269
1270	def do_unlink(self, src, tgt, **kw):
1271		"""
1272		See :py:meth:`waflib.Build.inst.do_link`
1273		"""
1274		try:
1275			if not self.generator.bld.progress_bar:
1276				c1 = Logs.colors.NORMAL
1277				c2 = Logs.colors.BLUE
1278				Logs.info('%s- remove %s%s%s', c1, c2, tgt, c1)
1279			os.remove(tgt)
1280		except OSError:
1281			pass
1282		self.rm_empty_dirs(tgt)
1283
1284class InstallContext(BuildContext):
1285	'''installs the targets on the system'''
1286	cmd = 'install'
1287
1288	def __init__(self, **kw):
1289		super(InstallContext, self).__init__(**kw)
1290		self.is_install = INSTALL
1291
1292class UninstallContext(InstallContext):
1293	'''removes the targets installed'''
1294	cmd = 'uninstall'
1295
1296	def __init__(self, **kw):
1297		super(UninstallContext, self).__init__(**kw)
1298		self.is_install = UNINSTALL
1299
1300class CleanContext(BuildContext):
1301	'''cleans the project'''
1302	cmd = 'clean'
1303	def execute(self):
1304		"""
1305		See :py:func:`waflib.Build.BuildContext.execute`.
1306		"""
1307		self.restore()
1308		if not self.all_envs:
1309			self.load_envs()
1310
1311		self.recurse([self.run_dir])
1312		try:
1313			self.clean()
1314		finally:
1315			self.store()
1316
1317	def clean(self):
1318		"""
1319		Remove most files from the build directory, and reset all caches.
1320
1321		Custom lists of files to clean can be declared as `bld.clean_files`.
1322		For example, exclude `build/program/myprogram` from getting removed::
1323
1324			def build(bld):
1325				bld.clean_files = bld.bldnode.ant_glob('**',
1326					excl='.lock* config.log c4che/* config.h program/myprogram',
1327					quiet=True, generator=True)
1328		"""
1329		Logs.debug('build: clean called')
1330
1331		if hasattr(self, 'clean_files'):
1332			for n in self.clean_files:
1333				n.delete()
1334		elif self.bldnode != self.srcnode:
1335			# would lead to a disaster if top == out
1336			lst = []
1337			for env in self.all_envs.values():
1338				lst.extend(self.root.find_or_declare(f) for f in env[CFG_FILES])
1339			excluded_dirs = '.lock* *conf_check_*/** config.log %s/*' % CACHE_DIR
1340			for n in self.bldnode.ant_glob('**/*', excl=excluded_dirs, quiet=True):
1341				if n in lst:
1342					continue
1343				n.delete()
1344		self.root.children = {}
1345
1346		for v in SAVED_ATTRS:
1347			if v == 'root':
1348				continue
1349			setattr(self, v, {})
1350
1351class ListContext(BuildContext):
1352	'''lists the targets to execute'''
1353	cmd = 'list'
1354
1355	def execute(self):
1356		"""
1357		In addition to printing the name of each build target,
1358		a description column will include text for each task
1359		generator which has a "description" field set.
1360
1361		See :py:func:`waflib.Build.BuildContext.execute`.
1362		"""
1363		self.restore()
1364		if not self.all_envs:
1365			self.load_envs()
1366
1367		self.recurse([self.run_dir])
1368		self.pre_build()
1369
1370		# display the time elapsed in the progress bar
1371		self.timer = Utils.Timer()
1372
1373		for g in self.groups:
1374			for tg in g:
1375				try:
1376					f = tg.post
1377				except AttributeError:
1378					pass
1379				else:
1380					f()
1381
1382		try:
1383			# force the cache initialization
1384			self.get_tgen_by_name('')
1385		except Errors.WafError:
1386			pass
1387
1388		targets = sorted(self.task_gen_cache_names)
1389
1390		# figure out how much to left-justify, for largest target name
1391		line_just = max(len(t) for t in targets) if targets else 0
1392
1393		for target in targets:
1394			tgen = self.task_gen_cache_names[target]
1395
1396			# Support displaying the description for the target
1397			# if it was set on the tgen
1398			descript = getattr(tgen, 'description', '')
1399			if descript:
1400				target = target.ljust(line_just)
1401				descript = ': %s' % descript
1402
1403			Logs.pprint('GREEN', target, label=descript)
1404
1405class StepContext(BuildContext):
1406	'''executes tasks in a step-by-step fashion, for debugging'''
1407	cmd = 'step'
1408
1409	def __init__(self, **kw):
1410		super(StepContext, self).__init__(**kw)
1411		self.files = Options.options.files
1412
1413	def compile(self):
1414		"""
1415		Overrides :py:meth:`waflib.Build.BuildContext.compile` to perform a partial build
1416		on tasks matching the input/output pattern given (regular expression matching)::
1417
1418			$ waf step --files=foo.c,bar.c,in:truc.c,out:bar.o
1419			$ waf step --files=in:foo.cpp.1.o # link task only
1420
1421		"""
1422		if not self.files:
1423			Logs.warn('Add a pattern for the debug build, for example "waf step --files=main.c,app"')
1424			BuildContext.compile(self)
1425			return
1426
1427		targets = []
1428		if self.targets and self.targets != '*':
1429			targets = self.targets.split(',')
1430
1431		for g in self.groups:
1432			for tg in g:
1433				if targets and tg.name not in targets:
1434					continue
1435
1436				try:
1437					f = tg.post
1438				except AttributeError:
1439					pass
1440				else:
1441					f()
1442
1443			for pat in self.files.split(','):
1444				matcher = self.get_matcher(pat)
1445				for tg in g:
1446					if isinstance(tg, Task.Task):
1447						lst = [tg]
1448					else:
1449						lst = tg.tasks
1450					for tsk in lst:
1451						do_exec = False
1452						for node in tsk.inputs:
1453							if matcher(node, output=False):
1454								do_exec = True
1455								break
1456						for node in tsk.outputs:
1457							if matcher(node, output=True):
1458								do_exec = True
1459								break
1460						if do_exec:
1461							ret = tsk.run()
1462							Logs.info('%s -> exit %r', tsk, ret)
1463
1464	def get_matcher(self, pat):
1465		"""
1466		Converts a step pattern into a function
1467
1468		:param: pat: pattern of the form in:truc.c,out:bar.o
1469		:returns: Python function that uses Node objects as inputs and returns matches
1470		:rtype: function
1471		"""
1472		# this returns a function
1473		inn = True
1474		out = True
1475		if pat.startswith('in:'):
1476			out = False
1477			pat = pat.replace('in:', '')
1478		elif pat.startswith('out:'):
1479			inn = False
1480			pat = pat.replace('out:', '')
1481
1482		anode = self.root.find_node(pat)
1483		pattern = None
1484		if not anode:
1485			if not pat.startswith('^'):
1486				pat = '^.+?%s' % pat
1487			if not pat.endswith('$'):
1488				pat = '%s$' % pat
1489			pattern = re.compile(pat)
1490
1491		def match(node, output):
1492			if output and not out:
1493				return False
1494			if not output and not inn:
1495				return False
1496
1497			if anode:
1498				return anode == node
1499			else:
1500				return pattern.match(node.abspath())
1501		return match
1502
1503class EnvContext(BuildContext):
1504	"""Subclass EnvContext to create commands that require configuration data in 'env'"""
1505	fun = cmd = None
1506	def execute(self):
1507		"""
1508		See :py:func:`waflib.Build.BuildContext.execute`.
1509		"""
1510		self.restore()
1511		if not self.all_envs:
1512			self.load_envs()
1513		self.recurse([self.run_dir])
1514
1515