Node.py :  » Build » A-A-P » aap-1.091 » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Build » A A P 
A A P » aap 1.091 » Node.py
# Part of the A-A-P recipe executive: Node used in a 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

import os

from Util import *
import Filetype

#
# A Node object is the source and/or target of a dependency.
# main items:
# name              name as used in the recipe (at first use, it's not changed
#                   when also used in a recipe in another directory)
# recipe_dir        directory in which "name" is valid
# absname           name with absolute, normalized path (meaningless for
#                   virtual targets, use get_name())
# attributes        dictionary for attributes, such as "virtual"
# dependencies      a list of references to the dependencies in which the node
#                   is a target.  It can be empty.
# build_dependencies  subset of "dependencies" for the ones that have build
#                   commands.  Only nodes like "finally", "clean" and "fetch"
#                   may have multiple entries.

class Node:

    # values used for "status"
    new = 1         # newly created
    busy = 2        # busy updating as a target
    updated = 3     # successfully updated
    builderror = 4  # attempted to update and failed.

    def __init__(self, name, absname = None):
        """Create a node for "name".  Caller must have already made sure "name"
        is normalized (but not made absolute)."""

        self.attributes = {}            # dictionary of attributes; relevant
                                        # ones are "directory", "virtual" and
                                        # "cache_update"
        self.set_name(name, absname)
        self.alias = None               # optional alias name
        self.absalias = None            # optional alias absolute name

        self._set_sign_dir(4)

        self.dependencies = []          # dependencies for which this node is a
                                        # target
        self.build_dependencies = []    # idem, with build commands

        self.status = Node.new          # current status

        self.scope_recdict = None       # Scope in which the node was used as a
                                        # target.  Used for finding rules that
                                        # may apply.

        # When status is "busy", either current_rule or current_dep indicates
        # which rule or dependency is being used, so that a clear error message
        # can be given for cyclic dependencies.
        self.current_rule = None
        self.current_dep = None

        self.autodep_dictlist = None    # dictlist for items this node depends
                                        # on, from automatic dependencies
        self.autodep_recursive = 0      # autodep_dictlist generated recursively
        self.autodep_busy = 0           # used in dictlist_update()
        self.did_add_clean = 0          # used by add_clean()
        self.recursive_level = 0        # depth of building recursively

    def copy(self):
        """Make a copy of a Node object.  This is a shallow copy of most
           things, but the attributes and dependencies are done an extra
           level."""
        import copy

        r = copy.copy(self)
        r.attributes = copy.copy(self.attributes)
        r.dependencies = copy.copy(self.dependencies)
        r.build_dependencies = copy.copy(self.build_dependencies)
        return r

    def rpstack(self):
        """
        Get a useful rpstack for when this node is used as a target (for error
        messages).
        """
        if self.current_rule:
            return self.current_rule.rpstack
        if self.current_dep:
            return self.current_dep.rpstack
        return []

    def set_name(self, name, absname = None):
        """Set the name of the node.  Used when creating a new node and when an
           alias is going to be used."""
        import Global
        from Remote import is_url

        self.name = name

        # Set "virtual" when it's a know virtual target.
        if name in Global.virtual_targets:
            self.attributes["virtual"] = 1

        # Remember the directory of the recipe where the node name was set.
        # "name" is relative to this directory unless it's virtual.
        self.recipe_dir = os.getcwd()

        # Remember the absolute path for the Node.  When it's virtual absname
        # should not be used!  Use get_name() instead.
        # A URL, "~/" and "~user/" are also absolute.
        if os.path.isabs(name):
            self.name_relative = 0
            if absname is None:
                absname = os.path.normpath(name)
        elif is_url(name):
            self.name_relative = 0
            if absname is None:
                absname = name
        elif name[0] == '~':
            self.name_relative = 0
            if absname is None:
                absname = os.path.abspath(os.path.expanduser(name))
        else:
            self.name_relative = 1
            if absname is None:
                absname = os.path.abspath(name)
        self.absname = absname

    def set_alias(self, name, absname = None):
        """Set the alias name for this node."""
        self.alias = name
        if absname:
            self.absalias = absname
        else:
            self.absalias = os.path.abspath(os.path.expanduser(name))

    def get_name(self):
        """Get the name to be used for this Node.  When the "virtual" attribute
           is given it's the unexpanded name, otherwise the absolute name."""
        if self.attributes.get("virtual"):
            return self.name
        return self.absname

    def short_name(self):
        """Get the shortest name that still makes clear what the name of the
           node is.  Used for messages."""
        if self.attributes.get("virtual"):
            return self.name
        return shorten_name(self.absname)

    def get_ftype(self, recdict):
        """Return the detected file type for this node."""
        ft = self.attributes.get("filetype")
        if ft:
            return ft
        # A ":program" command adds a lower priority filetype attribute.
        ft = self.attributes.get("filetypehint")
        if ft:
            return ft
        return Filetype.ft_detect(self.get_name(), recdict = recdict)


    # When the Node is used as a target, we must decide where the
    # signatures are stored.  The priority order is:
    # 1. When used with a relative path name, but no "virtual" attribute, use
    #    the directory of the target.
    # 2. When a dependency with build commands is defined with this Node as
    #    a target, use the directory of that recipe.
    # 3. When any dependency is defined with this node as a target, use the
    #    directory of that recipe.
    # 4. Use the directory of the recipe where this Node was first used.
    # This can be overruled with the "signdirectory" attribute.
    # CAREFUL: the "virtual" and "signdirectory" attributes may be changed!
    # When adding the "virtual" attribute level 1 is skipped, thus the choice
    # between level 2, 3 or 4 must be remembered separately.
    def _set_sign_dir(self, level):
        """Set the directory for the signatures to the directory of the target
        (for level 1) or the current directory (where the recipe is)."""
        self.sign_dir = os.getcwd()
        self.sign_level = level

    def get_sign_fname(self):
        """Get the file name to use for the signatures of this node.
           When using a directory, append "sign_fname"."""
        if self.attributes.has_key("signfile"):
            return os.path.abspath(os.path.expanduser(
                                                  self.attributes["signfile"]))
        if self.attributes.has_key("signdirectory"):
            adir = os.path.abspath(os.path.expanduser(
                                             self.attributes["signdirectory"]))
        elif self.name_relative and not self.attributes.get("virtual"):
            adir = os.path.dirname(self.absname)
        else:
            adir = self.sign_dir

        from Sign import sign_normal_fname
        return os.path.join(adir, sign_normal_fname)

    def relative_name(self):
        """This node has been used with a relative file name, which means the
           target directory is to be used for signatures, unless the "virtual"
           attribute is used (may be added later)."""
        self.name_relative = 1

    def add_dependency(self, dependency):
        self.dependencies.append(dependency)
        if self.sign_level > 3:
            self._set_sign_dir(3)

    def get_dependencies(self):
        return self.dependencies

    def add_build_dependency(self, dependency):
        self.build_dependencies.append(dependency)
        if self.sign_level > 2:
            self._set_sign_dir(2)

    def get_first_build_dependency(self):
        """Return the first build dependency, None if there isn't one."""
        if self.build_dependencies:
            return self.build_dependencies[0]
        return None

    def get_recipe_build_dependency(self, recdict):
        """Return the first build dependency for recipe with "recdict", None if
           there isn't one."""
        for dep in self.build_dependencies:
            if dep.buildrecdict is recdict:
                return dep
        return None

    def get_build_dependencies(self):
        return self.build_dependencies

    def set_attributes(self, dictlist):
        """Set attributes for a node from "dictlist".  Skip "name" and items
        that start with an underscore."""
        for k in dictlist.keys():
            if k == "virtual" and self.attributes.has_key(k):
                # The "virtual" attribute is never reset
                self.attributes[k] = (self.attributes[k] or dictlist[k])
            elif k != "name" and k[0] != '_':
                self.attributes[k] = dictlist[k]

    def set_sticky_attributes(self, dictlist):
        """Set only those attributes for the node from "dictlist" that can be
           carried over from a dependency to everywhere else the node is
           used."""
        for attr in ["virtual", "remember", "directory", "filetype", "force",
                       "constant", "fetch", "commit", "publish", "signfile",
                       "depdir"]:
            if dictlist.has_key(attr) and dictlist[attr]:
                self.attributes[attr] = dictlist[attr]

    def get_cache_update(self):
        """Get the cache_update attribute.  Return None if it's not set."""
        if self.attributes.has_key("cache_update"):
            return self.attributes["cache_update"]
        return None

    def isdir(self):
        """Return non-zero when we know this Node is a directory.  When
           specified with set_attributes() return the value used (mode value
           for creation)."""
        # A specified attribute overrules everything
        if self.attributes.has_key("directory"):
            return self.attributes["directory"]
        # A virtual target can't be a directory
        if self.attributes.get("virtual"):
            return 0
        # Check if the node exists and is a directory
        import os.path
        if os.path.isdir(self.get_name()):
            return 1
        # Must be a file then
        return 0

    def may_fetch(self):
        """Return non-zero if this node should be fetched when using the
           "fetch" target or ":fetch"."""
        # Never fetch a virtual node.
        # Fetching is skipped when the node has a "constant" attribute with
        # a non-empty non-zero value and the file exists.
        return (not self.attributes.get("virtual")
                and (not self.attributes.get("constant")
                    or not os.path.exists(self.get_name())))

    def __str__(self):
        return "Node " + self.get_name()



# vim: set sw=4 et sts=4 tw=79 fo+=l:
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.