summaryrefslogtreecommitdiffstats
path: root/waflib/TaskGen.py
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-11-24 13:44:03 +0100
committerDavid Robillard <d@drobilla.net>2018-11-24 13:44:03 +0100
commita7d83f19b08eb4c6f79a82fe60c2b86db13f4420 (patch)
treed9b620bfba1e7462df4ddb3f6225cc5216c0ca81 /waflib/TaskGen.py
parentd63edc742cebd685f8a05936682210aa5c1e69a9 (diff)
downloadingen-a7d83f19b08eb4c6f79a82fe60c2b86db13f4420.tar.gz
ingen-a7d83f19b08eb4c6f79a82fe60c2b86db13f4420.tar.bz2
ingen-a7d83f19b08eb4c6f79a82fe60c2b86db13f4420.zip
Squashed 'waflib/' changes from 6e726eb1..5ea8f99f
5ea8f99f Improve test output spacing 0e23b29f Raise exception when test suite fails to ensure non-zero exit status d6de073b Show run time of unit tests 5b655541 Add short configure option for ultra-strict flags 4687ba6d Use gtest-like test output 258903d9 Fix failure count in test group summaries da07e738 Fix verbose tests with Python 3 git-subtree-dir: waflib git-subtree-split: 5ea8f99f6e1246079c1fe6bb590c38a53aadd40d
Diffstat (limited to 'waflib/TaskGen.py')
-rw-r--r--waflib/TaskGen.py917
1 files changed, 0 insertions, 917 deletions
diff --git a/waflib/TaskGen.py b/waflib/TaskGen.py
deleted file mode 100644
index a74e6431..00000000
--- 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 = []
-