From 406f89271452fdb573c7e28113b1ed08ff2b4eda Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
Date: Sun, 17 Mar 2019 17:31:05 +0100
Subject: Squashed 'waflib/' changes from 915dcb1..e7a29b6

e7a29b6 Upgrade to waf 2.0.15
8280f9d Add command for running executables from the build directory
8073c1a Make make_simple_dox() safe in case of exception
70d03b8 Avoid use of global counter hacks for configuration display
b7d689a Rewrite test framework
94deadf Automatically add options and move add_flags() to options context
f4259ee Reduce system include path noise
927b608 Automatically display configuration header
c44b8f3 Set line justification from a constant in the wscript
a48e26f Automatically detect if wscript has a test hook
ef66724 Save runtime variables in the environment
63bcbcd Clean up TestContext
b1d9505 Add ExecutionContext for setting runtime environment
387c1df Add show_diff() and test_file_equals() utilities
29d4d29 Fix in-tree library paths
9fde01f Add custom configuration context
6d3612f Add lib_path_name constant

git-subtree-dir: waflib
git-subtree-split: e7a29b6b9b2f842314244c23c14d8f8f560904e1
---
 waflib/TaskGen.py | 917 ------------------------------------------------------
 1 file changed, 917 deletions(-)
 delete mode 100644 waflib/TaskGen.py

(limited to 'waflib/TaskGen.py')

diff --git a/waflib/TaskGen.py b/waflib/TaskGen.py
deleted file mode 100644
index a74e643..0000000
--- a/waflib/TaskGen.py
+++ /dev/null
@@ -1,917 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-# Thomas Nagy, 2005-2018 (ita)
-
-"""
-Task generators
-
-The class :py:class:`waflib.TaskGen.task_gen` encapsulates the creation of task objects (low-level code)
-The instances can have various parameters, but the creation of task nodes (Task.py)
-is deferred. To achieve this, various methods are called from the method "apply"
-"""
-
-import copy, re, os, functools
-from waflib import Task, Utils, Logs, Errors, ConfigSet, Node
-
-feats = Utils.defaultdict(set)
-"""remember the methods declaring features"""
-
-HEADER_EXTS = ['.h', '.hpp', '.hxx', '.hh']
-
-class task_gen(object):
-	"""
-	Instances of this class create :py:class:`waflib.Task.Task` when
-	calling the method :py:meth:`waflib.TaskGen.task_gen.post` from the main thread.
-	A few notes:
-
-	* The methods to call (*self.meths*) can be specified dynamically (removing, adding, ..)
-	* The 'features' are used to add methods to self.meths and then execute them
-	* The attribute 'path' is a node representing the location of the task generator
-	* The tasks created are added to the attribute *tasks*
-	* The attribute 'idx' is a counter of task generators in the same path
-	"""
-
-	mappings = Utils.ordered_iter_dict()
-	"""Mappings are global file extension mappings that are retrieved in the order of definition"""
-
-	prec = Utils.defaultdict(set)
-	"""Dict that holds the precedence execution rules for task generator methods"""
-
-	def __init__(self, *k, **kw):
-		"""
-		Task generator objects predefine various attributes (source, target) for possible
-		processing by process_rule (make-like rules) or process_source (extensions, misc methods)
-
-		Tasks are stored on the attribute 'tasks'. They are created by calling methods
-		listed in ``self.meths`` or referenced in the attribute ``features``
-		A topological sort is performed to execute the methods in correct order.
-
-		The extra key/value elements passed in ``kw`` are set as attributes
-		"""
-		self.source = []
-		self.target = ''
-
-		self.meths = []
-		"""
-		List of method names to execute (internal)
-		"""
-
-		self.features = []
-		"""
-		List of feature names for bringing new methods in
-		"""
-
-		self.tasks = []
-		"""
-		Tasks created are added to this list
-		"""
-
-		if not 'bld' in kw:
-			# task generators without a build context :-/
-			self.env = ConfigSet.ConfigSet()
-			self.idx = 0
-			self.path = None
-		else:
-			self.bld = kw['bld']
-			self.env = self.bld.env.derive()
-			self.path = self.bld.path # emulate chdir when reading scripts
-
-			# Provide a unique index per folder
-			# This is part of a measure to prevent output file name collisions
-			path = self.path.abspath()
-			try:
-				self.idx = self.bld.idx[path] = self.bld.idx.get(path, 0) + 1
-			except AttributeError:
-				self.bld.idx = {}
-				self.idx = self.bld.idx[path] = 1
-
-			# Record the global task generator count
-			try:
-				self.tg_idx_count = self.bld.tg_idx_count = self.bld.tg_idx_count + 1
-			except AttributeError:
-				self.tg_idx_count = self.bld.tg_idx_count = 1
-
-		for key, val in kw.items():
-			setattr(self, key, val)
-
-	def __str__(self):
-		"""Debugging helper"""
-		return "<task_gen %r declared in %s>" % (self.name, self.path.abspath())
-
-	def __repr__(self):
-		"""Debugging helper"""
-		lst = []
-		for x in self.__dict__:
-			if x not in ('env', 'bld', 'compiled_tasks', 'tasks'):
-				lst.append("%s=%s" % (x, repr(getattr(self, x))))
-		return "bld(%s) in %s" % (", ".join(lst), self.path.abspath())
-
-	def get_cwd(self):
-		"""
-		Current working directory for the task generator, defaults to the build directory.
-		This is still used in a few places but it should disappear at some point as the classes
-		define their own working directory.
-
-		:rtype: :py:class:`waflib.Node.Node`
-		"""
-		return self.bld.bldnode
-
-	def get_name(self):
-		"""
-		If the attribute ``name`` is not set on the instance,
-		the name is computed from the target name::
-
-			def build(bld):
-				x = bld(name='foo')
-				x.get_name() # foo
-				y = bld(target='bar')
-				y.get_name() # bar
-
-		:rtype: string
-		:return: name of this task generator
-		"""
-		try:
-			return self._name
-		except AttributeError:
-			if isinstance(self.target, list):
-				lst = [str(x) for x in self.target]
-				name = self._name = ','.join(lst)
-			else:
-				name = self._name = str(self.target)
-			return name
-	def set_name(self, name):
-		self._name = name
-
-	name = property(get_name, set_name)
-
-	def to_list(self, val):
-		"""
-		Ensures that a parameter is a list, see :py:func:`waflib.Utils.to_list`
-
-		:type val: string or list of string
-		:param val: input to return as a list
-		:rtype: list
-		"""
-		if isinstance(val, str):
-			return val.split()
-		else:
-			return val
-
-	def post(self):
-		"""
-		Creates tasks for this task generators. The following operations are performed:
-
-		#. The body of this method is called only once and sets the attribute ``posted``
-		#. The attribute ``features`` is used to add more methods in ``self.meths``
-		#. The methods are sorted by the precedence table ``self.prec`` or `:waflib:attr:waflib.TaskGen.task_gen.prec`
-		#. The methods are then executed in order
-		#. The tasks created are added to :py:attr:`waflib.TaskGen.task_gen.tasks`
-		"""
-		if getattr(self, 'posted', None):
-			return False
-		self.posted = True
-
-		keys = set(self.meths)
-		keys.update(feats['*'])
-
-		# add the methods listed in the features
-		self.features = Utils.to_list(self.features)
-		for x in self.features:
-			st = feats[x]
-			if st:
-				keys.update(st)
-			elif not x in Task.classes:
-				Logs.warn('feature %r does not exist - bind at least one method to it?', x)
-
-		# copy the precedence table
-		prec = {}
-		prec_tbl = self.prec
-		for x in prec_tbl:
-			if x in keys:
-				prec[x] = prec_tbl[x]
-
-		# elements disconnected
-		tmp = []
-		for a in keys:
-			for x in prec.values():
-				if a in x:
-					break
-			else:
-				tmp.append(a)
-
-		tmp.sort(reverse=True)
-
-		# topological sort
-		out = []
-		while tmp:
-			e = tmp.pop()
-			if e in keys:
-				out.append(e)
-			try:
-				nlst = prec[e]
-			except KeyError:
-				pass
-			else:
-				del prec[e]
-				for x in nlst:
-					for y in prec:
-						if x in prec[y]:
-							break
-					else:
-						tmp.append(x)
-						tmp.sort(reverse=True)
-
-		if prec:
-			buf = ['Cycle detected in the method execution:']
-			for k, v in prec.items():
-				buf.append('- %s after %s' % (k, [x for x in v if x in prec]))
-			raise Errors.WafError('\n'.join(buf))
-		self.meths = out
-
-		# then we run the methods in order
-		Logs.debug('task_gen: posting %s %d', self, id(self))
-		for x in out:
-			try:
-				v = getattr(self, x)
-			except AttributeError:
-				raise Errors.WafError('%r is not a valid task generator method' % x)
-			Logs.debug('task_gen: -> %s (%d)', x, id(self))
-			v()
-
-		Logs.debug('task_gen: posted %s', self.name)
-		return True
-
-	def get_hook(self, node):
-		"""
-		Returns the ``@extension`` method to call for a Node of a particular extension.
-
-		:param node: Input file to process
-		:type node: :py:class:`waflib.Tools.Node.Node`
-		:return: A method able to process the input node by looking at the extension
-		:rtype: function
-		"""
-		name = node.name
-		for k in self.mappings:
-			try:
-				if name.endswith(k):
-					return self.mappings[k]
-			except TypeError:
-				# regexps objects
-				if k.match(name):
-					return self.mappings[k]
-		keys = list(self.mappings.keys())
-		raise Errors.WafError("File %r has no mapping in %r (load a waf tool?)" % (node, keys))
-
-	def create_task(self, name, src=None, tgt=None, **kw):
-		"""
-		Creates task instances.
-
-		:param name: task class name
-		:type name: string
-		:param src: input nodes
-		:type src: list of :py:class:`waflib.Tools.Node.Node`
-		:param tgt: output nodes
-		:type tgt: list of :py:class:`waflib.Tools.Node.Node`
-		:return: A task object
-		:rtype: :py:class:`waflib.Task.Task`
-		"""
-		task = Task.classes[name](env=self.env.derive(), generator=self)
-		if src:
-			task.set_inputs(src)
-		if tgt:
-			task.set_outputs(tgt)
-		task.__dict__.update(kw)
-		self.tasks.append(task)
-		return task
-
-	def clone(self, env):
-		"""
-		Makes a copy of a task generator. Once the copy is made, it is necessary to ensure that the
-		it does not create the same output files as the original, or the same files may
-		be compiled several times.
-
-		:param env: A configuration set
-		:type env: :py:class:`waflib.ConfigSet.ConfigSet`
-		:return: A copy
-		:rtype: :py:class:`waflib.TaskGen.task_gen`
-		"""
-		newobj = self.bld()
-		for x in self.__dict__:
-			if x in ('env', 'bld'):
-				continue
-			elif x in ('path', 'features'):
-				setattr(newobj, x, getattr(self, x))
-			else:
-				setattr(newobj, x, copy.copy(getattr(self, x)))
-
-		newobj.posted = False
-		if isinstance(env, str):
-			newobj.env = self.bld.all_envs[env].derive()
-		else:
-			newobj.env = env.derive()
-
-		return newobj
-
-def declare_chain(name='', rule=None, reentrant=None, color='BLUE',
-	ext_in=[], ext_out=[], before=[], after=[], decider=None, scan=None, install_path=None, shell=False):
-	"""
-	Creates a new mapping and a task class for processing files by extension.
-	See Tools/flex.py for an example.
-
-	:param name: name for the task class
-	:type name: string
-	:param rule: function to execute or string to be compiled in a function
-	:type rule: string or function
-	:param reentrant: re-inject the output file in the process (done automatically, set to 0 to disable)
-	:type reentrant: int
-	:param color: color for the task output
-	:type color: string
-	:param ext_in: execute the task only after the files of such extensions are created
-	:type ext_in: list of string
-	:param ext_out: execute the task only before files of such extensions are processed
-	:type ext_out: list of string
-	:param before: execute instances of this task before classes of the given names
-	:type before: list of string
-	:param after: execute instances of this task after classes of the given names
-	:type after: list of string
-	:param decider: if present, function that returns a list of output file extensions (overrides ext_out for output files, but not for the build order)
-	:type decider: function
-	:param scan: scanner function for the task
-	:type scan: function
-	:param install_path: installation path for the output nodes
-	:type install_path: string
-	"""
-	ext_in = Utils.to_list(ext_in)
-	ext_out = Utils.to_list(ext_out)
-	if not name:
-		name = rule
-	cls = Task.task_factory(name, rule, color=color, ext_in=ext_in, ext_out=ext_out, before=before, after=after, scan=scan, shell=shell)
-
-	def x_file(self, node):
-		if ext_in:
-			_ext_in = ext_in[0]
-
-		tsk = self.create_task(name, node)
-		cnt = 0
-
-		ext = decider(self, node) if decider else cls.ext_out
-		for x in ext:
-			k = node.change_ext(x, ext_in=_ext_in)
-			tsk.outputs.append(k)
-
-			if reentrant != None:
-				if cnt < int(reentrant):
-					self.source.append(k)
-			else:
-				# reinject downstream files into the build
-				for y in self.mappings: # ~ nfile * nextensions :-/
-					if k.name.endswith(y):
-						self.source.append(k)
-						break
-			cnt += 1
-
-		if install_path:
-			self.install_task = self.add_install_files(install_to=install_path, install_from=tsk.outputs)
-		return tsk
-
-	for x in cls.ext_in:
-		task_gen.mappings[x] = x_file
-	return x_file
-
-def taskgen_method(func):
-	"""
-	Decorator that registers method as a task generator method.
-	The function must accept a task generator as first parameter::
-
-		from waflib.TaskGen import taskgen_method
-		@taskgen_method
-		def mymethod(self):
-			pass
-
-	:param func: task generator method to add
-	:type func: function
-	:rtype: function
-	"""
-	setattr(task_gen, func.__name__, func)
-	return func
-
-def feature(*k):
-	"""
-	Decorator that registers a task generator method that will be executed when the
-	object attribute ``feature`` contains the corresponding key(s)::
-
-		from waflib.Task import feature
-		@feature('myfeature')
-		def myfunction(self):
-			print('that is my feature!')
-		def build(bld):
-			bld(features='myfeature')
-
-	:param k: feature names
-	:type k: list of string
-	"""
-	def deco(func):
-		setattr(task_gen, func.__name__, func)
-		for name in k:
-			feats[name].update([func.__name__])
-		return func
-	return deco
-
-def before_method(*k):
-	"""
-	Decorator that registera task generator method which will be executed
-	before the functions of given name(s)::
-
-		from waflib.TaskGen import feature, before
-		@feature('myfeature')
-		@before_method('fun2')
-		def fun1(self):
-			print('feature 1!')
-		@feature('myfeature')
-		def fun2(self):
-			print('feature 2!')
-		def build(bld):
-			bld(features='myfeature')
-
-	:param k: method names
-	:type k: list of string
-	"""
-	def deco(func):
-		setattr(task_gen, func.__name__, func)
-		for fun_name in k:
-			task_gen.prec[func.__name__].add(fun_name)
-		return func
-	return deco
-before = before_method
-
-def after_method(*k):
-	"""
-	Decorator that registers a task generator method which will be executed
-	after the functions of given name(s)::
-
-		from waflib.TaskGen import feature, after
-		@feature('myfeature')
-		@after_method('fun2')
-		def fun1(self):
-			print('feature 1!')
-		@feature('myfeature')
-		def fun2(self):
-			print('feature 2!')
-		def build(bld):
-			bld(features='myfeature')
-
-	:param k: method names
-	:type k: list of string
-	"""
-	def deco(func):
-		setattr(task_gen, func.__name__, func)
-		for fun_name in k:
-			task_gen.prec[fun_name].add(func.__name__)
-		return func
-	return deco
-after = after_method
-
-def extension(*k):
-	"""
-	Decorator that registers a task generator method which will be invoked during
-	the processing of source files for the extension given::
-
-		from waflib import Task
-		class mytask(Task):
-			run_str = 'cp ${SRC} ${TGT}'
-		@extension('.moo')
-		def create_maa_file(self, node):
-			self.create_task('mytask', node, node.change_ext('.maa'))
-		def build(bld):
-			bld(source='foo.moo')
-	"""
-	def deco(func):
-		setattr(task_gen, func.__name__, func)
-		for x in k:
-			task_gen.mappings[x] = func
-		return func
-	return deco
-
-@taskgen_method
-def to_nodes(self, lst, path=None):
-	"""
-	Flatten the input list of string/nodes/lists into a list of nodes.
-
-	It is used by :py:func:`waflib.TaskGen.process_source` and :py:func:`waflib.TaskGen.process_rule`.
-	It is designed for source files, for folders, see :py:func:`waflib.Tools.ccroot.to_incnodes`:
-
-	:param lst: input list
-	:type lst: list of string and nodes
-	:param path: path from which to search the nodes (by default, :py:attr:`waflib.TaskGen.task_gen.path`)
-	:type path: :py:class:`waflib.Tools.Node.Node`
-	:rtype: list of :py:class:`waflib.Tools.Node.Node`
-	"""
-	tmp = []
-	path = path or self.path
-	find = path.find_resource
-
-	if isinstance(lst, Node.Node):
-		lst = [lst]
-
-	for x in Utils.to_list(lst):
-		if isinstance(x, str):
-			node = find(x)
-		elif hasattr(x, 'name'):
-			node = x
-		else:
-			tmp.extend(self.to_nodes(x))
-			continue
-		if not node:
-			raise Errors.WafError('source not found: %r in %r' % (x, self))
-		tmp.append(node)
-	return tmp
-
-@feature('*')
-def process_source(self):
-	"""
-	Processes each element in the attribute ``source`` by extension.
-
-	#. The *source* list is converted through :py:meth:`waflib.TaskGen.to_nodes` to a list of :py:class:`waflib.Node.Node` first.
-	#. File extensions are mapped to methods having the signature: ``def meth(self, node)`` by :py:meth:`waflib.TaskGen.extension`
-	#. The method is retrieved through :py:meth:`waflib.TaskGen.task_gen.get_hook`
-	#. When called, the methods may modify self.source to append more source to process
-	#. The mappings can map an extension or a filename (see the code below)
-	"""
-	self.source = self.to_nodes(getattr(self, 'source', []))
-	for node in self.source:
-		self.get_hook(node)(self, node)
-
-@feature('*')
-@before_method('process_source')
-def process_rule(self):
-	"""
-	Processes the attribute ``rule``. When present, :py:meth:`waflib.TaskGen.process_source` is disabled::
-
-		def build(bld):
-			bld(rule='cp ${SRC} ${TGT}', source='wscript', target='bar.txt')
-
-	Main attributes processed:
-
-	* rule: command to execute, it can be a tuple of strings for multiple commands
-	* chmod: permissions for the resulting files (integer value such as Utils.O755)
-	* shell: set to False to execute the command directly (default is True to use a shell)
-	* scan: scanner function
-	* vars: list of variables to trigger rebuilds, such as CFLAGS
-	* cls_str: string to display when executing the task
-	* cls_keyword: label to display when executing the task
-	* cache_rule: by default, try to re-use similar classes, set to False to disable
-	* source: list of Node or string objects representing the source files required by this task
-	* target: list of Node or string objects representing the files that this task creates
-	* cwd: current working directory (Node or string)
-	* stdout: standard output, set to None to prevent waf from capturing the text
-	* stderr: standard error, set to None to prevent waf from capturing the text
-	* timeout: timeout for command execution (Python 3)
-	* always: whether to always run the command (False by default)
-	* deep_inputs: whether the task must depend on the input file tasks too (False by default)
-	"""
-	if not getattr(self, 'rule', None):
-		return
-
-	# create the task class
-	name = str(getattr(self, 'name', None) or self.target or getattr(self.rule, '__name__', self.rule))
-
-	# or we can put the class in a cache for performance reasons
-	try:
-		cache = self.bld.cache_rule_attr
-	except AttributeError:
-		cache = self.bld.cache_rule_attr = {}
-
-	chmod = getattr(self, 'chmod', None)
-	shell = getattr(self, 'shell', True)
-	color = getattr(self, 'color', 'BLUE')
-	scan = getattr(self, 'scan', None)
-	_vars = getattr(self, 'vars', [])
-	cls_str = getattr(self, 'cls_str', None)
-	cls_keyword = getattr(self, 'cls_keyword', None)
-	use_cache = getattr(self, 'cache_rule', 'True')
-	deep_inputs = getattr(self, 'deep_inputs', False)
-
-	scan_val = has_deps = hasattr(self, 'deps')
-	if scan:
-		scan_val = id(scan)
-
-	key = Utils.h_list((name, self.rule, chmod, shell, color, cls_str, cls_keyword, scan_val, _vars, deep_inputs))
-
-	cls = None
-	if use_cache:
-		try:
-			cls = cache[key]
-		except KeyError:
-			pass
-	if not cls:
-		rule = self.rule
-		if chmod is not None:
-			def chmod_fun(tsk):
-				for x in tsk.outputs:
-					os.chmod(x.abspath(), tsk.generator.chmod)
-			if isinstance(rule, tuple):
-				rule = list(rule)
-				rule.append(chmod_fun)
-				rule = tuple(rule)
-			else:
-				rule = (rule, chmod_fun)
-
-		cls = Task.task_factory(name, rule, _vars, shell=shell, color=color)
-
-		if cls_str:
-			setattr(cls, '__str__', self.cls_str)
-
-		if cls_keyword:
-			setattr(cls, 'keyword', self.cls_keyword)
-
-		if deep_inputs:
-			Task.deep_inputs(cls)
-
-		if scan:
-			cls.scan = self.scan
-		elif has_deps:
-			def scan(self):
-				nodes = []
-				for x in self.generator.to_list(getattr(self.generator, 'deps', None)):
-					node = self.generator.path.find_resource(x)
-					if not node:
-						self.generator.bld.fatal('Could not find %r (was it declared?)' % x)
-					nodes.append(node)
-				return [nodes, []]
-			cls.scan = scan
-
-		if use_cache:
-			cache[key] = cls
-
-	# now create one instance
-	tsk = self.create_task(name)
-
-	for x in ('after', 'before', 'ext_in', 'ext_out'):
-		setattr(tsk, x, getattr(self, x, []))
-
-	if hasattr(self, 'stdout'):
-		tsk.stdout = self.stdout
-
-	if hasattr(self, 'stderr'):
-		tsk.stderr = self.stderr
-
-	if getattr(self, 'timeout', None):
-		tsk.timeout = self.timeout
-
-	if getattr(self, 'always', None):
-		tsk.always_run = True
-
-	if getattr(self, 'target', None):
-		if isinstance(self.target, str):
-			self.target = self.target.split()
-		if not isinstance(self.target, list):
-			self.target = [self.target]
-		for x in self.target:
-			if isinstance(x, str):
-				tsk.outputs.append(self.path.find_or_declare(x))
-			else:
-				x.parent.mkdir() # if a node was given, create the required folders
-				tsk.outputs.append(x)
-		if getattr(self, 'install_path', None):
-			self.install_task = self.add_install_files(install_to=self.install_path,
-				install_from=tsk.outputs, chmod=getattr(self, 'chmod', Utils.O644))
-
-	if getattr(self, 'source', None):
-		tsk.inputs = self.to_nodes(self.source)
-		# bypass the execution of process_source by setting the source to an empty list
-		self.source = []
-
-	if getattr(self, 'cwd', None):
-		tsk.cwd = self.cwd
-
-	if isinstance(tsk.run, functools.partial):
-		# Python documentation says: "partial objects defined in classes
-		# behave like static methods and do not transform into bound
-		# methods during instance attribute look-up."
-		tsk.run = functools.partial(tsk.run, tsk)
-
-@feature('seq')
-def sequence_order(self):
-	"""
-	Adds a strict sequential constraint between the tasks generated by task generators.
-	It works because task generators are posted in order.
-	It will not post objects which belong to other folders.
-
-	Example::
-
-		bld(features='javac seq')
-		bld(features='jar seq')
-
-	To start a new sequence, set the attribute seq_start, for example::
-
-		obj = bld(features='seq')
-		obj.seq_start = True
-
-	Note that the method is executed in last position. This is more an
-	example than a widely-used solution.
-	"""
-	if self.meths and self.meths[-1] != 'sequence_order':
-		self.meths.append('sequence_order')
-		return
-
-	if getattr(self, 'seq_start', None):
-		return
-
-	# all the tasks previously declared must be run before these
-	if getattr(self.bld, 'prev', None):
-		self.bld.prev.post()
-		for x in self.bld.prev.tasks:
-			for y in self.tasks:
-				y.set_run_after(x)
-
-	self.bld.prev = self
-
-
-re_m4 = re.compile('@(\w+)@', re.M)
-
-class subst_pc(Task.Task):
-	"""
-	Creates *.pc* files from *.pc.in*. The task is executed whenever an input variable used
-	in the substitution changes.
-	"""
-
-	def force_permissions(self):
-		"Private for the time being, we will probably refactor this into run_str=[run1,chmod]"
-		if getattr(self.generator, 'chmod', None):
-			for x in self.outputs:
-				os.chmod(x.abspath(), self.generator.chmod)
-
-	def run(self):
-		"Substitutes variables in a .in file"
-
-		if getattr(self.generator, 'is_copy', None):
-			for i, x in enumerate(self.outputs):
-				x.write(self.inputs[i].read('rb'), 'wb')
-				stat = os.stat(self.inputs[i].abspath()) # Preserve mtime of the copy
-				os.utime(self.outputs[i].abspath(), (stat.st_atime, stat.st_mtime))
-			self.force_permissions()
-			return None
-
-		if getattr(self.generator, 'fun', None):
-			ret = self.generator.fun(self)
-			if not ret:
-				self.force_permissions()
-			return ret
-
-		code = self.inputs[0].read(encoding=getattr(self.generator, 'encoding', 'latin-1'))
-		if getattr(self.generator, 'subst_fun', None):
-			code = self.generator.subst_fun(self, code)
-			if code is not None:
-				self.outputs[0].write(code, encoding=getattr(self.generator, 'encoding', 'latin-1'))
-			self.force_permissions()
-			return None
-
-		# replace all % by %% to prevent errors by % signs
-		code = code.replace('%', '%%')
-
-		# extract the vars foo into lst and replace @foo@ by %(foo)s
-		lst = []
-		def repl(match):
-			g = match.group
-			if g(1):
-				lst.append(g(1))
-				return "%%(%s)s" % g(1)
-			return ''
-		code = getattr(self.generator, 're_m4', re_m4).sub(repl, code)
-
-		try:
-			d = self.generator.dct
-		except AttributeError:
-			d = {}
-			for x in lst:
-				tmp = getattr(self.generator, x, '') or self.env[x] or self.env[x.upper()]
-				try:
-					tmp = ''.join(tmp)
-				except TypeError:
-					tmp = str(tmp)
-				d[x] = tmp
-
-		code = code % d
-		self.outputs[0].write(code, encoding=getattr(self.generator, 'encoding', 'latin-1'))
-		self.generator.bld.raw_deps[self.uid()] = lst
-
-		# make sure the signature is updated
-		try:
-			delattr(self, 'cache_sig')
-		except AttributeError:
-			pass
-
-		self.force_permissions()
-
-	def sig_vars(self):
-		"""
-		Compute a hash (signature) of the variables used in the substitution
-		"""
-		bld = self.generator.bld
-		env = self.env
-		upd = self.m.update
-
-		if getattr(self.generator, 'fun', None):
-			upd(Utils.h_fun(self.generator.fun).encode())
-		if getattr(self.generator, 'subst_fun', None):
-			upd(Utils.h_fun(self.generator.subst_fun).encode())
-
-		# raw_deps: persistent custom values returned by the scanner
-		vars = self.generator.bld.raw_deps.get(self.uid(), [])
-
-		# hash both env vars and task generator attributes
-		act_sig = bld.hash_env_vars(env, vars)
-		upd(act_sig)
-
-		lst = [getattr(self.generator, x, '') for x in vars]
-		upd(Utils.h_list(lst))
-
-		return self.m.digest()
-
-@extension('.pc.in')
-def add_pcfile(self, node):
-	"""
-	Processes *.pc.in* files to *.pc*. Installs the results to ``${PREFIX}/lib/pkgconfig/`` by default
-
-		def build(bld):
-			bld(source='foo.pc.in', install_path='${LIBDIR}/pkgconfig/')
-	"""
-	tsk = self.create_task('subst_pc', node, node.change_ext('.pc', '.pc.in'))
-	self.install_task = self.add_install_files(
-		install_to=getattr(self, 'install_path', '${LIBDIR}/pkgconfig/'), install_from=tsk.outputs)
-
-class subst(subst_pc):
-	pass
-
-@feature('subst')
-@before_method('process_source', 'process_rule')
-def process_subst(self):
-	"""
-	Defines a transformation that substitutes the contents of *source* files to *target* files::
-
-		def build(bld):
-			bld(
-				features='subst',
-				source='foo.c.in',
-				target='foo.c',
-				install_path='${LIBDIR}/pkgconfig',
-				VAR = 'val'
-			)
-
-	The input files are supposed to contain macros of the form *@VAR@*, where *VAR* is an argument
-	of the task generator object.
-
-	This method overrides the processing by :py:meth:`waflib.TaskGen.process_source`.
-	"""
-
-	src = Utils.to_list(getattr(self, 'source', []))
-	if isinstance(src, Node.Node):
-		src = [src]
-	tgt = Utils.to_list(getattr(self, 'target', []))
-	if isinstance(tgt, Node.Node):
-		tgt = [tgt]
-	if len(src) != len(tgt):
-		raise Errors.WafError('invalid number of source/target for %r' % self)
-
-	for x, y in zip(src, tgt):
-		if not x or not y:
-			raise Errors.WafError('null source or target for %r' % self)
-		a, b = None, None
-
-		if isinstance(x, str) and isinstance(y, str) and x == y:
-			a = self.path.find_node(x)
-			b = self.path.get_bld().make_node(y)
-			if not os.path.isfile(b.abspath()):
-				b.parent.mkdir()
-		else:
-			if isinstance(x, str):
-				a = self.path.find_resource(x)
-			elif isinstance(x, Node.Node):
-				a = x
-			if isinstance(y, str):
-				b = self.path.find_or_declare(y)
-			elif isinstance(y, Node.Node):
-				b = y
-
-		if not a:
-			raise Errors.WafError('could not find %r for %r' % (x, self))
-
-		tsk = self.create_task('subst', a, b)
-		for k in ('after', 'before', 'ext_in', 'ext_out'):
-			val = getattr(self, k, None)
-			if val:
-				setattr(tsk, k, val)
-
-		# paranoid safety measure for the general case foo.in->foo.h with ambiguous dependencies
-		for xt in HEADER_EXTS:
-			if b.name.endswith(xt):
-				tsk.ext_in = tsk.ext_in + ['.h']
-				break
-
-		inst_to = getattr(self, 'install_path', None)
-		if inst_to:
-			self.install_task = self.add_install_files(install_to=inst_to,
-				install_from=b, chmod=getattr(self, 'chmod', Utils.O644))
-
-	self.source = []
-
-- 
cgit v1.2.1