Blame lib/dbtexmf/dblatex/grubber/maker.py

Packit Service 76cb02
# This file is part of Rubber and thus covered by the GPL
Packit Service 76cb02
# (c) Emmanuel Beffara, 2002--2006
Packit Service 76cb02
"""
Packit Service 76cb02
This module contains all the classes used to manage the building
Packit Service 76cb02
dependencies.
Packit Service 76cb02
"""
Packit Service cd7d79
from __future__ import print_function
Packit Service 76cb02
import os
Packit Service 76cb02
import time
Packit Service 76cb02
import subprocess
Packit Service 76cb02
Packit Service cd7d79
from dbtexmf.dblatex.grubber.msg import _, msg
Packit Service 76cb02
Packit Service 76cb02
class Depend (object): #{{{2
Packit Service 76cb02
    """
Packit Service 76cb02
    This is a base class to represent file dependencies. It provides the base
Packit Service 76cb02
    functionality of date checking and recursive making, supposing the
Packit Service 76cb02
    existence of a method `run()' in the object. This method is supposed to
Packit Service 76cb02
    rebuild the files of this node, returning zero on success and something
Packit Service 76cb02
    else on failure.
Packit Service 76cb02
    """
Packit Service 76cb02
    def __init__ (self, env, prods=None, sources={}, loc={}):
Packit Service 76cb02
        """
Packit Service 76cb02
        Initialize the object for a given set of output files and a given set
Packit Service 76cb02
        of sources. The argument `prods' is a list of file names, and the
Packit Service 76cb02
        argument `sources' is a dictionary that associates file names with
Packit Service 76cb02
        dependency nodes. The optional argument `loc' is a dictionary that
Packit Service 76cb02
        describes where in the sources this dependency was created.
Packit Service 76cb02
        """
Packit Service 76cb02
        self.env = env
Packit Service 76cb02
        if prods:
Packit Service 76cb02
            self.prods = prods
Packit Service 76cb02
        else:
Packit Service 76cb02
            self.prods = []
Packit Service 76cb02
        self.set_date()
Packit Service 76cb02
        self.sources = sources
Packit Service 76cb02
        self.making = 0
Packit Service 76cb02
        self.failed_dep = None
Packit Service 76cb02
        self.loc = loc
Packit Service 76cb02
Packit Service 76cb02
    def set_date (self):
Packit Service 76cb02
        """
Packit Service 76cb02
        Define the date of the last build of this node as that of the most
Packit Service 76cb02
        recent file among the products. If some product does not exist or
Packit Service 76cb02
        there are ne products, the date is set to None.
Packit Service 76cb02
        """
Packit Service 76cb02
        if self.prods == []:
Packit Service 76cb02
            # This is a special case used in rubber.Environment
Packit Service 76cb02
            self.date = None
Packit Service 76cb02
        else:
Packit Service 76cb02
            try:
Packit Service 76cb02
                # We set the node's date to that of the most recently modified
Packit Service 76cb02
                # product file, assuming all other files were up to date then
Packit Service 76cb02
                # (though not necessarily modified).
Packit Service 76cb02
                self.date = max(map(os.path.getmtime, self.prods))
Packit Service 76cb02
            except OSError:
Packit Service 76cb02
                # If some product file does not exist, set the last
Packit Service 76cb02
                # modification date to None.
Packit Service 76cb02
                self.date = None
Packit Service 76cb02
Packit Service 76cb02
    def should_make (self):
Packit Service 76cb02
        """
Packit Service 76cb02
        Check the dependencies. Return true if this node has to be recompiled,
Packit Service 76cb02
        i.e. if some dependency is modified. Nothing recursive is done here.
Packit Service 76cb02
        """
Packit Service 76cb02
        if not self.date:
Packit Service 76cb02
            return 1
Packit Service 76cb02
        for src in self.sources.values():
Packit Service 76cb02
            if src.date > self.date:
Packit Service 76cb02
                return 1
Packit Service 76cb02
        return 0
Packit Service 76cb02
Packit Service 76cb02
    def make (self, force=0):
Packit Service 76cb02
        """
Packit Service 76cb02
        Make the destination file. This recursively makes all dependencies,
Packit Service 76cb02
        then compiles the target if dependencies were modified. The semantics
Packit Service 76cb02
        of the return value is the following:
Packit Service 76cb02
        - 0 means that the process failed somewhere (in this node or in one of
Packit Service 76cb02
          its dependencies)
Packit Service 76cb02
        - 1 means that nothing had to be done
Packit Service 76cb02
        - 2 means that something was recompiled (therefore nodes that depend
Packit Service 76cb02
          on this one have to be remade)
Packit Service 76cb02
        """
Packit Service 76cb02
        if self.making:
Packit Service cd7d79
            print("FIXME: cyclic make")
Packit Service 76cb02
            return 1
Packit Service 76cb02
        self.making = 1
Packit Service 76cb02
Packit Service 76cb02
        # Make the sources
Packit Service 76cb02
        self.failed_dep = None
Packit Service 76cb02
        must_make = force
Packit Service 76cb02
        for src in self.sources.values():
Packit Service 76cb02
            ret = src.make()
Packit Service 76cb02
            if ret == 0:
Packit Service 76cb02
                self.making = 0
Packit Service 76cb02
                self.failed_dep = src.failed_dep
Packit Service 76cb02
                return 0
Packit Service 76cb02
            if ret == 2:
Packit Service 76cb02
                must_make = 1
Packit Service 76cb02
        
Packit Service 76cb02
        # Make this node if necessary
Packit Service 76cb02
Packit Service 76cb02
        if must_make or self.should_make():
Packit Service 76cb02
            if force:
Packit Service 76cb02
                ret = self.force_run()
Packit Service 76cb02
            else:
Packit Service 76cb02
                ret = self.run()
Packit Service 76cb02
            if ret:
Packit Service 76cb02
                self.making = 0
Packit Service 76cb02
                self.failed_dep = self
Packit Service 76cb02
                return 0
Packit Service 76cb02
Packit Service 76cb02
            # Here we must take the integer part of the value returned by
Packit Service 76cb02
            # time.time() because the modification times for files, returned
Packit Service 76cb02
            # by os.path.getmtime(), is an integer. Keeping the fractional
Packit Service 76cb02
            # part could lead to errors in time comparison with the main log
Packit Service 76cb02
            # file when the compilation of the document is shorter than one
Packit Service 76cb02
            # second...
Packit Service 76cb02
Packit Service 76cb02
            self.date = int(time.time())
Packit Service 76cb02
            self.making = 0
Packit Service 76cb02
            return 2
Packit Service 76cb02
        self.making = 0
Packit Service 76cb02
        return 1
Packit Service 76cb02
Packit Service 76cb02
    def force_run (self):
Packit Service 76cb02
        """
Packit Service 76cb02
        This method is called instead of 'run' when rebuilding this node was
Packit Service 76cb02
        forced. By default it is equivalent to 'run'.
Packit Service 76cb02
        """
Packit Service 76cb02
        return self.run()
Packit Service 76cb02
Packit Service 76cb02
    def failed (self):
Packit Service 76cb02
        """
Packit Service 76cb02
        Return a reference to the node that caused the failure of the last
Packit Service 76cb02
        call to "make". If there was no failure, return None.
Packit Service 76cb02
        """
Packit Service 76cb02
        return self.failed_dep
Packit Service 76cb02
Packit Service 76cb02
    def get_errors (self):
Packit Service 76cb02
        """
Packit Service 76cb02
        Report the errors that caused the failure of the last call to run.
Packit Service 76cb02
        """
Packit Service 76cb02
        if None:
Packit Service 76cb02
            yield None
Packit Service 76cb02
Packit Service 76cb02
    def clean (self):
Packit Service 76cb02
        """
Packit Service 76cb02
        Remove the files produced by this rule and recursively clean all
Packit Service 76cb02
        dependencies.
Packit Service 76cb02
        """
Packit Service 76cb02
        for file in self.prods:
Packit Service 76cb02
            if os.path.exists(file):
Packit Service 76cb02
                msg.log(_("removing %s") % file)
Packit Service 76cb02
                os.unlink(file)
Packit Service 76cb02
        for src in self.sources.values():
Packit Service 76cb02
            src.clean()
Packit Service 76cb02
        self.date = None
Packit Service 76cb02
Packit Service 76cb02
    def reinit (self):
Packit Service 76cb02
        """
Packit Service 76cb02
        Reinitializing depends on actual dependency leaf
Packit Service 76cb02
        """
Packit Service 76cb02
        pass
Packit Service 76cb02
Packit Service 76cb02
    def leaves (self):
Packit Service 76cb02
        """
Packit Service 76cb02
        Return a list of all source files that are required by this node and
Packit Service 76cb02
        cannot be built, i.e. the leaves of the dependency tree.
Packit Service 76cb02
        """
Packit Service 76cb02
        if self.sources == {}:
Packit Service 76cb02
            return self.prods
Packit Service 76cb02
        ret = []
Packit Service 76cb02
        for dep in self.sources.values():
Packit Service 76cb02
            ret.extend(dep.leaves())
Packit Service 76cb02
        return ret
Packit Service 76cb02
Packit Service 76cb02
Packit Service 76cb02
class DependLeaf (Depend): #{{{2
Packit Service 76cb02
    """
Packit Service 76cb02
    This class specializes Depend for leaf nodes, i.e. source files with no
Packit Service 76cb02
    dependencies.
Packit Service 76cb02
    """
Packit Service 76cb02
    def __init__ (self, env, *dest, **args):
Packit Service 76cb02
        """
Packit Service 76cb02
        Initialize the node. The arguments of this method are the file
Packit Service 76cb02
        names, since one single node may contain several files.
Packit Service 76cb02
        """
Packit Service 76cb02
        Depend.__init__(self, env, prods=list(dest), **args)
Packit Service 76cb02
Packit Service 76cb02
    def run (self):
Packit Service 76cb02
        # FIXME
Packit Service 76cb02
        if len(self.prods) == 1:
Packit Service 76cb02
            msg.error(_("%r does not exist") % self.prods[0], **self.loc)
Packit Service 76cb02
        else:
Packit Service 76cb02
            msg.error(_("one of %r does not exist") % self.prods, **self.loc)
Packit Service 76cb02
        return 1
Packit Service 76cb02
Packit Service 76cb02
    def clean (self):
Packit Service 76cb02
        pass
Packit Service 76cb02
Packit Service 76cb02
Packit Service 76cb02
class DependShell (Depend): #{{{2
Packit Service 76cb02
    """
Packit Service 76cb02
    This class specializes Depend for generating files using shell commands.
Packit Service 76cb02
    """
Packit Service 76cb02
    def __init__ (self, env, cmd, **args):
Packit Service 76cb02
        Depend.__init__(self, env, **args)
Packit Service 76cb02
        self.cmd = cmd
Packit Service 76cb02
Packit Service 76cb02
    def run (self):
Packit Service 76cb02
        msg.progress(_("running %s") % self.cmd[0])
Packit Service 76cb02
        rc = subprocess.call(self.cmd, stdout=msg.stdout)
Packit Service 76cb02
        if rc != 0:
Packit Service 76cb02
            msg.error(_("execution of %s failed") % self.cmd[0])
Packit Service 76cb02
            return 1
Packit Service 76cb02
        return 0
Packit Service 76cb02
Packit Service 76cb02
Packit Service 76cb02
class Maker:
Packit Service 76cb02
    """
Packit Service 76cb02
    Very simple builder environment. Much simpler than the original rubber
Packit Service 76cb02
    Environment.
Packit Service 76cb02
    """
Packit Service 76cb02
    def __init__(self):
Packit Service 76cb02
        self.dep_nodes = []
Packit Service 76cb02
Packit Service 76cb02
    def dep_last(self):
Packit Service 76cb02
        if not(self.dep_nodes):
Packit Service 76cb02
            return None
Packit Service 76cb02
        else:
Packit Service 76cb02
            return self.dep_nodes[-1]
Packit Service 76cb02
Packit Service 76cb02
    def dep_append(self, dep):
Packit Service 76cb02
        self.dep_nodes.append(dep)
Packit Service 76cb02
Packit Service 76cb02
    def make(self, force=0):
Packit Service 76cb02
        if not(self.dep_nodes):
Packit Service 76cb02
            return 0
Packit Service 76cb02
        # Just ask the last one to compile
Packit Service 76cb02
        rc = self.dep_nodes[-1].make(force=force)
Packit Service 76cb02
        if (rc == 0):
Packit Service 76cb02
            return -1
Packit Service 76cb02
        else:
Packit Service 76cb02
            return 0
Packit Service 76cb02
Packit Service 76cb02
    def reinit(self):
Packit Service 76cb02
        # Forget the old dependency nodes
Packit Service 76cb02
        self.__init__()
Packit Service 76cb02