#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005-2018 (ita)
"""
Classes related to the build phase (build, clean, install, step, etc)
The inheritance tree is the following:
"""
import os, sys, errno, re, shutil, stat
try:
import cPickle
except ImportError:
import pickle as cPickle
from waflib import Node, Runner, TaskGen, Utils, ConfigSet, Task, Logs, Options, Context, Errors
CACHE_DIR = 'c4che'
"""Name of the cache directory"""
CACHE_SUFFIX = '_cache.py'
"""ConfigSet cache files for variants are written under :py:attr:´waflib.Build.CACHE_DIR´ in the form ´variant_name´_cache.py"""
INSTALL = 1337
"""Positive value '->' install, see :py:attr:`waflib.Build.BuildContext.is_install`"""
UNINSTALL = -1337
"""Negative value '<-' uninstall, see :py:attr:`waflib.Build.BuildContext.is_install`"""
SAVED_ATTRS = 'root node_sigs task_sigs imp_sigs raw_deps node_deps'.split()
"""Build class members to save between the runs; these should be all dicts
except for `root` which represents a :py:class:`waflib.Node.Node` instance
"""
CFG_FILES = 'cfg_files'
"""Files from the build directory to hash before starting the build (``config.h`` written during the configuration)"""
POST_AT_ONCE = 0
"""Post mode: all task generators are posted before any task executed"""
POST_LAZY = 1
"""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"""
PROTOCOL = -1
if sys.platform == 'cli':
PROTOCOL = 0
class BuildContext(Context.Context):
'''executes the build'''
cmd = 'build'
variant = ''
def __init__(self, **kw):
super(BuildContext, self).__init__(**kw)
self.is_install = 0
"""Non-zero value when installing or uninstalling file"""
self.top_dir = kw.get('top_dir', Context.top_dir)
"""See :py:attr:`waflib.Context.top_dir`; prefer :py:attr:`waflib.Build.BuildContext.srcnode`"""
self.out_dir = kw.get('out_dir', Context.out_dir)
"""See :py:attr:`waflib.Context.out_dir`; prefer :py:attr:`waflib.Build.BuildContext.bldnode`"""
self.run_dir = kw.get('run_dir', Context.run_dir)
"""See :py:attr:`waflib.Context.run_dir`"""
self.launch_dir = Context.launch_dir
"""See :py:attr:`waflib.Context.out_dir`; prefer :py:meth:`waflib.Build.BuildContext.launch_node`"""
self.post_mode = POST_LAZY
"""Whether to post the task generators at once or group-by-group (default is group-by-group)"""
self.cache_dir = kw.get('cache_dir')
if not self.cache_dir:
self.cache_dir = os.path.join(self.out_dir, CACHE_DIR)
self.all_envs = {}
"""Map names to :py:class:`waflib.ConfigSet.ConfigSet`, the empty string must map to the default environment"""
# ======================================= #
# cache variables
self.node_sigs = {}
"""Dict mapping build nodes to task identifier (uid), it indicates whether a task created a particular file (persists across builds)"""
self.task_sigs = {}
"""Dict mapping task identifiers (uid) to task signatures (persists across builds)"""
self.imp_sigs = {}
"""Dict mapping task identifiers (uid) to implicit task dependencies used for scanning targets (persists across builds)"""
self.node_deps = {}
"""Dict mapping task identifiers (uid) to node dependencies found by :py:meth:`waflib.Task.Task.scan` (persists across builds)"""
self.raw_deps = {}
"""Dict mapping task identifiers (uid) to custom data returned by :py:meth:`waflib.Task.Task.scan` (persists across builds)"""
self.task_gen_cache_names = {}
self.jobs = Options.options.jobs
"""Amount of jobs to run in parallel"""
self.targets = Options.options.targets
"""List of targets to build (default: \\*)"""
self.keep = Options.options.keep
"""Whether the build should continue past errors"""
self.progress_bar = getattr(Options.options, 'progress_bar', 0)
"""
Level of progress status:
0. normal output
1. progress bar
2. IDE output
3. No output at all
"""
# Manual dependencies.
self.deps_man = Utils.defaultdict(list)
"""Manual dependencies set by :py:meth:`waflib.Build.BuildContext.add_manual_dependency`"""
# just the structure here
self.current_group = 0
"""
Current build group
"""
self.groups = []
"""
List containing lists of task generators
"""
self.group_names = {}
"""
Map group names to the group lists. See :py:meth:`waflib.Build.BuildContext.add_group`
"""
for v in SAVED_ATTRS:
if not hasattr(self, v):
setattr(self, v, {})
def get_variant_dir(self):
"""Getter for the variant_dir attribute"""
if not self.variant:
return self.out_dir
return os.path.join(self.out_dir, os.path.normpath(self.variant))
variant_dir = property(get_variant_dir, None)
def __call__(self, *k, **kw):
"""
Create a task generator and add it to the current build group. The following forms are equivalent::
def build(bld):
tg = bld(a=1, b=2)
def build(bld):
tg = bld()
tg.a = 1
tg.b = 2
def build(bld):
tg = TaskGen.task_gen(a=1, b=2)
bld.add_to_group(tg, None)
:param group: group name to add the task generator to
:type group: string
"""
kw['bld'] = self
ret = TaskGen.task_gen(*k, **kw)
self.task_gen_cache_names = {} # reset the cache, each time
self.add_to_group(ret, group=kw.get('group'))
return ret
def __copy__(self):
"""
Build contexts cannot be copied
:raises: :py:class:`waflib.Errors.WafError`
"""
raise Errors.WafError('build contexts cannot be copied')
def load_envs(self):
"""
The configuration command creates files of the form ``build/c4che/NAMEcache.py``. This method
creates a :py:class:`waflib.ConfigSet.ConfigSet` instance for each ``NAME`` by reading those
files and stores them in :py:attr:`waflib.Build.BuildContext.allenvs`.
"""
node = self.root.find_node(self.cache_dir)
if not node:
raise Errors.WafError('The project was not configured: run "waf configure" first!')
lst = node.ant_glob('**/*%s' % CACHE_SUFFIX, quiet=True)
if not lst:
raise Errors.WafError('The cache directory is empty: reconfigure the project')
for x in lst:
name = x.path_from(node).replace(CACHE_SUFFIX, '').replace('\\', '/')
env = ConfigSet.ConfigSet(x.abspath())
self.all_envs[name] = env
for f in env[CFG_FILES]:
newnode = self.root.find_resource(f)
if not newnode or not newnode.exists():
raise Errors.WafError('Missing configuration file %r, reconfigure the project!' % f)
def init_dirs(self):
"""
Initialize the project directory and the build directory by creating the nodes
:py:attr:`waflib.Build.BuildContext.srcnode` and :py:attr:`waflib.Build.BuildContext.bldnode`
corresponding to ``top_dir`` and ``variant_dir`` respectively. The ``bldnode`` directory is
created if necessary.
"""
if not (os.path.isabs(self.top_dir) and os.path.isabs(self.out_dir)):
raise Errors.WafError('The project was not configured: run "waf configure" first!')
sel
|