# 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
"rd".
"""
from Dictlist import listitem2str,dictlist2str
from DoRead import read_recipe,read_recipe_dir
# $VERSIONSTR
rd["VERSIONSTR"] = version_string
# $HOME
home = home_dir()
if home:
rd["HOME"] = listitem2str(os.path.dirname(home))
else:
rd["HOME"] = ''
# $CACHEPATH
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
else:
s = s + '_'
return s
sysname, n, release, v, m = os.uname()
rd["OSNAME"] = fixname(sysname) + fixname(release)
else:
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
msg_init(rd)
# $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"
# $CACHEUPDATE
rd["CACHEUPDATE"] = "12 hour"
# $AAPVERSION
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
else:
# Just in case rootdir wasn't set (never happens?).
adir, tail = os.path.split(__file__)
Global.set_aap_rootdir(adir)
# $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
else:
# 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."""
self.dependencies.append(dep);
# 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"]
n.add_dependency(dep)
if dep.commands:
if (n.get_first_build_dependency()
and not n.name in Global.virtual_targets):
from Process import recipe_error
recipe_error(rpstack,
_('Multiple build commands for target "%s"')
% item["name"])
n.add_build_dependency(dep)
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)
n.set_sticky_attributes(item)
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
abspath().
"""
# 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
else:
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]
else:
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
Node."""
if is_url(name):
absname = name
else:
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:
self.add_node(n)
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:
n.set_attributes(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.
return
if self.find_node(name, use_alias = 0):
# New name also exists, don't set an alias.
return
# 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.
self.del_node(n)
n.set_name(name)
n.set_attributes(dict)
self.add_node(n)
else:
# Create a new node.
n = self.get_node(name, add = 1, dict = dict, use_alias = 0)
n.set_alias(alias)
self.add_node_alias(n)
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):
self.rules.append(rule);
def del_rule(self, rule):
self.rules.remove(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
targets."""
if not self.dependencies:
msg_print(_("No dependencies in recipe"))
else:
# 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"]
else:
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()
keys.sort()
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):
return
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]
return
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
toplevel."""
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:
|