Logo Search packages:      
Sourcecode: aap version File versions


# Part of the A-A-P recipe executive: remember the work specified in the recipe

# Copyright (C) 2002-2003 Stichting NLnet Labs
# Permission to copy and use this file is specified in the file COPYING.
# If this file is missing you can find it here: http://www.a-a-p.org/COPYING

# Currently a Work object contains these items:
#  recdict      -  Dictionary of global variables from the toplevel recipe.
#                  The "_work" variable points back to the Work object.
#  dependencies -  list of dependencies
#  rules        -  list of rules
#  nodes        -  dictionary of nodes from the recipes; index is node.name;
#                  (gone through fname_fold()). normally used to lookup virtual
#                  nodes 
#  absnodes     -  same contents as nodes, but key is the absolute name:
#                  node.absname (gone through fname_fold()).
#  aliasnodes   -  same as absnodes, but uses the alias name of nodes.
#  top_recipe   -  name of toplevel recipe used (None when not reading a recipe)

import os
import os.path
import string

from Node import Node
from RecPos import rpcopy
import Global
from Util import *
from Message import *
from Remote import is_url
from AapVersion import *
import Scope

def set_defaults(rd):
    Set the default values for variables in dictionary "rd".
    This also adds dependencies and rules to the Work object associated with
    from Dictlist import listitem2str, dictlist2str
    from DoRead import read_recipe, read_recipe_dir

    rd["VERSIONSTR"] = version_string

    # $HOME
    home = home_dir()
    if home:
        rd["HOME"] = listitem2str(os.path.dirname(home))
        rd["HOME"] = ''
    cache = []
    if os.path.exists("/var/aap/cache"):
        cache.append({"name" : "/var/aap/cache"})
    if home:
        cache.append({"name" : os.path.join(home, "cache")})
    cache.append({"name" : in_aap_dir("cache")})
    rd["CACHEPATH"] = dictlist2str(cache)

    # $BDIR
    if os.name == "posix":
        def fixname(n):
            """Change all non-letters, non-digits in "n" to underscores and
            return the result."""
            s = ''
            for c in n:
                if c in string.letters + string.digits:
                    s = s + c
                    s = s + '_'
            return s

        sysname, n, release, v, m = os.uname()
      rd["OSNAME"] = fixname(sysname) + fixname(release)
        rd["OSNAME"] = os.name
    rd["BDIR"] = "build-" + rd["OSNAME"]

    # $OSTYPE
    n = os.name
    if os.name == "dos":
        n = "msdos"
    elif os.name == "nt":
        n = "mswin"
    rd["OSTYPE"] = n

    # $MESSAGE

    # $SRCPATH (to be evaluated when used)
    rd["SRCPATH"] = ExpandVar(". $BDIR")

    # Standard variables.
    rd["bar"] = '|'
    rd["br"] = '\n'
    rd["BR"] = '\n'
    rd["empty"] = ''
    rd["gt"] = '>'
    rd["lt"] = '<'
    rd["pipe"] = '|'

    # Variables for a port recipe.
    rd["DISTDIR"] = "distfiles"
    rd["PATCHDISTDIR"] = "patches"
    rd["WRKDIR"] = "work"

    rd["CACHEUPDATE"] = "12 hour"

    rd["AAPVERSION"] = int(version_number)

    # Directory where our modules are.
    if Global.aap_rootdir:
        # Use rootdir if possible, when started as "../Exec/aap" __file__ will
        # be a relative file name, that doesn't work after chdir().
        adir = Global.aap_rootdir
        # Just in case rootdir wasn't set (never happens?).
        adir, tail = os.path.split(__file__)

    # $AAP: Execute aap, making sure the right version of Python is used.
    #       Use quotes where approriate, the path may have a space.
    rd["AAP"] = (listitem2str(sys.executable) + ' '
                                  + listitem2str(os.path.join(adir, "Main.py")))

    # Read A-A-P default recipes.
    read_recipe([], rd, os.path.join(adir, "default.aap"), 1, optional = 1)

    # Keep a copy of the result for the "_default" scope.
    rd["_default"] = Scope.RecipeDict(rd)

    # Read system and user default recipes.
    # Keep a copy of the result for the "_start" scope.
    for adir in default_dirs(rd):
        read_recipe_dir(rd, os.path.join(adir, "startup"))
    rd["_start"] = Scope.RecipeDict(rd)

class Work:
    def __init__(self, recdict = None):
        if recdict is None:
            self.topscope = Scope.create_topscope("toplevel")
            self.recdict = self.topscope.data
            # The ":execute" command may pass on a scope.
            self.topscope = recdict["_top"]
            self.recdict = recdict
        self.dependencies = []
        self.rules = []
        self.routes = {}
        self.nodes = {}
        self.absnodes = {}
        self.aliasnodes = {}
        self.top_recipe = None
        self.recipe_already_read = {}
        self.module_already_read = {}

        # This makes it possible to find "work" from the global variables.
        setwork(self.recdict, self)

    def add_dependency(self, rpstack, dep):
        """Add a new dependency.  This takes care of creating nodes for the
           sources and targets and setting the dependency for the target nodes
           if the dependency has commands."""

        # For each target let the Node know this Depend uses it.  If there are
        # commands also let it know this Depend builds it.
        for item in dep.targetlist:
            n = item["_node"]
            if dep.commands:
                if (n.get_first_build_dependency()
                                    and not n.name in Global.virtual_targets):
                    from Process import recipe_error
                            _('Multiple build commands for target "%s"')
                                                                % item["name"])

    def dictlist_nodes(self, dictlist):
        """Make sure there is a global node for each item in "dictlist" and
           add a reference to the node in the dictlist item.
           Carry over specific attributes to the node."""
        for item in dictlist:
            n = self.get_node(item["name"], 1)
            item["_node"] = n

    def add_dictlist_nodes(self, dictlist):
        """Add nodes for all items in "dictlist".  Also carry over attributes
        to the Node."""
        for item in dictlist:
            self.get_node(item["name"], 1, item)

    def add_node(self, node):
        """Add a Node to the global list of nodes.  The Node is the target
           and/or source in a dependency."""
        self.nodes[fname_fold(node.name)] = node
        self.absnodes[fname_fold(node.absname)] = node

    def del_node(self, node):
        """Remove a node from the global list of nodes."""
        del self.nodes[fname_fold(node.name)]
        del self.absnodes[fname_fold(node.absname)]

    def add_node_alias(self, node):
        """Add a Node to the global list of alias nodes."""
        self.aliasnodes[fname_fold(node.absalias)] = node

    def find_node(self, name, absname = None, use_alias = 1):
        Find an existing Node by name or absname.
        For a virtual node "absname" should be an empty string (not None!).
        If "absname" is given it must have gone through expanduser() and
        # First try the absolute name, it's more reliable.  Must not be used
        # for virtual nodes though.
        # Then check the short name, only for virtual nodes (may have been used
        # in another recipe).
        if absname is None:
            if is_url(name):
                absname = name
                absname = os.path.abspath(os.path.expanduser(name))

        # Might try again with a folded name if fname_fold() may return a
        # different file name.  This is just to optimize the speed.
        if fname_fold_same():
            r = [0]
            r = [1, 0]

        if absname:
            findname = absname
            for i in r:
                if self.absnodes.has_key(findname):
                    return self.absnodes[findname]
                if i:
                    findname = fname_fold(absname)

        for i in r:
            findname = name
            if self.nodes.has_key(findname):
                n = self.nodes[findname]
                if n.attributes.get("virtual"):
                    return n
            if i:
                findname = fname_fold(name)

        # Now try again to find an alias.  Only use the absolute name, virtual
        # nodes don't have an alias.
        if absname and use_alias:
            findname = absname
            for i in r:
                if self.aliasnodes.has_key(findname):
                    return self.aliasnodes[findname]
                if i:
                    findname = fname_fold(absname)

        return None

    def get_node(self, name, add = 0, dict = {}, use_alias = 1):
        """Find a Node by name, create a new one if necessary.
           A new node is added to the global list if "add" is non-zero.
           When "dict" is given, check for attributes that apply to the
        if is_url(name):
            absname = name
            absname = os.path.abspath(os.path.expanduser(name))
        n = self.find_node(name, absname, use_alias = use_alias)
        if n is None:
            n = Node(name, absname)
            if add:
        elif not n.name_relative and not (
                        name[0] == '~' or os.path.isabs(name) or is_url(name)):
            # Remember the relative name was used, this matters for where
            # signatures are stored.
            n.name_relative = 1

        if dict:

        return n

    def node_set_alias(self, name, alias, dict = {}):
        Define a node "name" with an alias "alias".
        n = self.find_node(alias, use_alias = 0)
        if n:
            # If a node already exists by the alias name it may be that the
            # alias was used before it was defined, e.g.:
            #    all : foo
            #    :program foo : foo.c
            # But avoid adding an alias if it's a real node:
            #    :program foo : foo.c
            #    :lib foo : foo.c   # node = libfoo.a, don't use alias "foo"

            if n.alias:
                # Node already has an alias, thus it was not the situation that
                # the alias was used before it was defined.

            if self.find_node(name, use_alias = 0):
                # New name also exists, don't set an alias.

            # Node for alias name already exists, rename it.
            # Need to remove it from the global node list and add it back with
            # the new name.
            # Create a new node.
            n = self.get_node(name, add = 1, dict = dict, use_alias = 0)

    def add_node_attributes(self, dictlist):
        """Add attributes from existing nodes to the items in "dictlist".
           Used for sources and targets of executed dependencies and rules.
           Existing attributes are not overwritten."""
        for item in dictlist:
            node = self.find_node(item["name"])
            if node:
                for k in node.attributes.keys():
                    if not item.has_key(k):
                        item[k] = node.attributes[k]

    def add_rule(self, rule):

    def del_rule(self, rule):

    def clearrules(self):
        self.rules = []

    def add_route(self, route):
        Add a route to "routes".
        We actually add every possible route, since the input and output can be
        a list of filetype names.
        for in_ftype in route.typelist[0]:
            for out_ftype in route.typelist[-1]:
                if not self.routes.has_key(in_ftype):
                    self.routes[in_ftype] = {}
                self.routes[in_ftype][out_ftype] = route

    def find_route(self, in_ftype, out_ftype, use_actions = 0):
        Find a route for input filetype "in_ftype" to "out_ftype".
        Return the found route object or None.
        l = self.routes.get(in_ftype)
        if l:
            l = l.get(out_ftype)
        if not l and use_actions:
            # No route specified, try using one or more actions.
            from Action import find_action_route
            l = find_action_route(in_ftype, out_ftype)
        return l

    def print_comments(self):
        """Print comments for all dependencies with a comment and for standard
        if not self.dependencies:
            msg_print(_("No dependencies in recipe"))
            # Collect the messages in toprint[].
            toprint = {}
            for d in self.dependencies:
                for t in d.targetlist:
                    comment = ''
                    if t.has_key("comment"):
                        comment = t["comment"]
                        node = self.find_node(t["name"])
                        if node and node.attributes.has_key("comment"):
                            comment = node.attributes["comment"]
                    n = t["name"]
                    if comment:
                        toprint[n] = comment
                    elif n in Global.virtual_targets:
                        toprint[n] = _('standard target, no comment specified')

            # Sort the keys and print the messages.
            keys = toprint.keys()
            for n in keys:
                spaces = 12 - len(n)
                if spaces < 1:
                    spaces = 1
                msg_print(('target %s:' % n)
                                        + "            "[:spaces] + toprint[n])

def assert_attribute(recdict, dict, attrname):
    """Check if dictlist "dict" has an entry for attribute "attrname".
       If not, obtain it from any node that has this attribute."""
    if dict.has_key(attrname):
    for node in getwork(recdict).nodes.values():
        if node.attributes.has_key(attrname):
            msg_extra(recdict, _('Using %s attribute from node "%s"')
                                                       % (attrname, node.name))
            dict[attrname] = node.attributes[attrname]
    raise UserError, (_('Missing %s attribute for "%s"')
                                                    % (attrname, dict["name"]))

def setwork(recdict, work):
    """Set the Work object in 'recdict' to "work"."""
    recdict["_work"] = work

def getwork(recdict):
    """Return the Work object that contains 'recdict'.  Search scopes upto the
    return recdict["_no"].get("_work")

def setrpstack(recdict, rpstack):
    """Set the RecPos stack in 'recdict'.  This is separate for each scope."""
    recdict["_rpstack"] = rpstack

def getrpstack(recdict, line_nr = -1):
    """Return the RecPos stack in 'recdict'.
       When a line number is specified: Make a copy and set the line number for
       the item at the top of the stack."""
    rp = recdict["_rpstack"]
    if line_nr >= 0:
        rp = rpcopy(rp, line_nr)
    return rp

# A Route object contains:
#  recdict      - recdict of where the route was defined
#  rpstack      - rpstack of where the route was defined
#  default      - 1 when default route
#  typelist     - list of list of filetype names, input first
#  targetattr   - attributes for the target (e.g., "buildaction")
#  steplist     - list of steps to be taken, each step as a string.
#  lnumlist     - line number in recipe for each step
# Illustration:
#       :route intype1,intype2 xxtype outtype
#               oneaction $(source).xx
#               lastaction
# Too small to put in a separate file...

class Route:
    def __init__(self, recdict, rpstack, default, typelist,
                                               targetattr, steplist, lnumlist):
        self.recdict = recdict
        self.rpstack = rpstack
        self.default = default
        self.typelist = typelist
        self.targetattr = targetattr
        self.steplist = steplist
        self.lnumlist = lnumlist

# vim: set sw=4 et sts=4 tw=79 fo+=l:

Generated by  Doxygen 1.6.0   Back to index