# Part of the A-A-P recipe executive: Aap commands 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
#
# These are functions used to handle the commands in a recipe.
# Some functions are used for translated items, such as dependencies.
#
# It's OK to do "from Commands import *", these things are supposed to be
# global.
#
import os
import os.path
import sys
import import_re # import the re module in a special way
import string
import copy
import glob
import imp
from Depend import Depend
from Dictlist import str2dictlist,get_attrdict,list2str,dictlist2str
from Dictlist import listitem2str
from Dictlist import dictlist_expanduser,dict_expand,dictlist_expand
from DoRead import read_recipe,recipe_dir,did_read_recipe
from DoArgs import doargs,add_cmdline_settings,copy_global_options
from Error import *
from Filetype import ft_known,ft_declare
import Global
from Process import assert_var_name,assert_scope_name
from Process import recipe_error,option_error
from RecPos import rpdeepcopy,rpcopy
from Rule import Rule
from Util import *
from Work import getwork,setwork,getrpstack,Route,set_defaults
from Work import assert_attribute
from Work import Work
from Message import *
def get_args(line_nr, recdict, arg, options = None, exp_attr = 1):
"""Parse command arguments. Always use A-A-P style expansion.
When "options" is given, it must be a dictionary indexed by accepted
options with the value of the option name.
"exp_attr" can be set to 0 to avoid including attributes.
Return a dictionary with options, a dictionary with leading attributes
and a dictlist with arguments."""
# Get the optional leading attributes.
rpstack = getrpstack(recdict, line_nr)
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
optiondict = {}
# Move options from attrdict to optiondict. Translate aliases.
if options:
for k in attrdict.keys():
if options.has_key(k):
optiondict[options[k]] = attrdict[k]
del attrdict[k]
# Get the list of items. Expand $var things.
varlist = str2dictlist(rpstack,
expand(line_nr, recdict, arg[i:],
Expand(exp_attr, Expand.quote_aap)))
return optiondict, attrdict, varlist
def aap_pass(line_nr, recdict, arg):
"""
Do nothing.
"""
if arg:
rpstack = getrpstack(recdict, line_nr)
recipe_error(rpstack, _(':pass does not take an argument'))
def aap_buildcheck(line_nr, recdict, arg):
"""
Do nothing with an argument. Used to mention variables that should be
included in the buildcheck.
"""
pass
def aap_depend(line_nr, recdict, targets, sources, commands):
"""Add a dependency."""
work = getwork(recdict)
rpstack = getrpstack(recdict, line_nr)
# Expand the targets into dictlists.
targetlist = str2dictlist(rpstack,
expand(line_nr, recdict, targets, Expand(1, Expand.quote_aap)))
targetlist = dictlist_expand(targetlist)
# Parse build attributes {attr = value} zero or more times.
# Variables are not expanded now but when executing the build rules.
build_attr, i = get_attrdict(rpstack, None, sources, 0, 0)
# Expand the sources into dictlists.
sourcelist = str2dictlist(rpstack,
expand(line_nr, recdict, sources[i:], Expand(1, Expand.quote_aap)))
sourcelist = dictlist_expand(sourcelist)
# Add to the global lists of dependencies.
# Make a copy of the RecPos stack, so that we can print the recipe stack
# for an error. The parsing continues, thus it needs to be a copy.
d = Depend(targetlist, build_attr, sourcelist, work,
rpdeepcopy(getrpstack(recdict), line_nr), commands)
# We need to remember the recdict of the recipe where the dependency was
# defined.
d.buildrecdict = recdict
work.add_dependency(rpstack, d)
def aap_clearrules(line_nr, recdict, arg):
"""Generate a recipe for downloading files."""
rpstack = getrpstack(recdict, line_nr)
work = getwork(recdict)
if arg:
recipe_error(rpstack, _(':clearrules does not take an argument'))
work.clearrules()
def aap_delrule(line_nr, recdict, targets, sources):
"""Delete a rule."""
aap_rule(line_nr, recdict, targets, sources, None, cmd = ":delrule")
def aap_rule(line_nr, recdict, targets, sources, commands, cmd = ":rule"):
"""Add a rule."""
work = getwork(recdict)
rpstack = getrpstack(recdict, line_nr)
# Parse command attributes {global} or {local} zero or more times.
scope = "normal"
default = 0
sourceexists = 0
quiet = 0
cmd_attr, i = get_attrdict(rpstack, None, targets, 0, 0)
for k in cmd_attr.keys():
if cmd == ":delrule" and (k == "quiet" or k == "q"):
quiet = 1
elif cmd == ":rule" and (k == "local" or k == "global"):
if scope != "normal":
recipe_error(rpstack, _('extra option for :rule: "%s"') % k)
scope = k
elif cmd == ":rule" and k == "sourceexists":
sourceexists = 1
elif cmd == ":rule" and k == "default":
default = 1
else:
recipe_error(rpstack, _('unknown option for %s: "%s"') % (cmd, k))
# Expand the targets into dictlists.
targetlist = str2dictlist(rpstack,
expand(line_nr, recdict, targets[i:], Expand(1, Expand.quote_aap)))
dictlist_expanduser(targetlist)
# Parse any build attributes {attr = value}.
# Variables in the attributes are not expanded now but when executing the
# build rules.
build_attr, i = get_attrdict(rpstack, None, sources, 0, 0)
# Expand the sources into dictlists.
sourcelist = str2dictlist(rpstack,
expand(line_nr, recdict, sources[i:], Expand(1, Expand.quote_aap)))
dictlist_expanduser(sourcelist)
# Check if there is an identical rule.
did_one = 0
for r in work.rules:
if (dictlist_sameentries(targetlist, r.targetlist)
and dictlist_sameentries(sourcelist, r.sourcelist)):
if cmd == ":rule" and not r.default:
msg_warning(recdict, _("Replacing existing rule"),
rpstack = rpstack)
work.del_rule(r)
did_one = 1
if cmd == ":delrule":
if not did_one and not quiet:
msg_warning(recdict, _("Did not find matching rule to delete"),
rpstack = rpstack)
else:
rule = Rule(targetlist, build_attr, sourcelist,
rpdeepcopy(getrpstack(recdict), line_nr), commands)
rule.default = default
rule.sourceexists = sourceexists
rule.scope = scope
# We need to remember the recdict of the recipe where the rule was
# defined.
rule.buildrecdict = recdict
work.add_rule(rule)
def aap_route(line_nr, recdict, arg, linestring):
"""Add a route."""
work = getwork(recdict)
rpstack = getrpstack(recdict, line_nr)
# Parse command attributes {global} or {local} zero or more times.
default = 0
cmd_attr, i = get_attrdict(rpstack, None, arg, 0, 0)
for k in cmd_attr.keys():
if k == "default":
default = 1
else:
recipe_error(rpstack, _('unknown option for :route: "%s"') % k)
# Expand the list of filetypes into dictlists.
arglist = str2dictlist(rpstack,
expand(line_nr, recdict, arg[i:], Expand(1, Expand.quote_aap)))
typelist = map(lambda x: x["name"], arglist)
if len(typelist) < 2:
recipe_error(rpstack, _(':route: requires at least two filetypes'))
# Process the action lines.
# Skip lines starting with a comment.
# Concatenate lines that have more indent than the first line.
from Process import get_line_marker
rawlines = string.split(linestring, '\n')
first_indent = -1
lines = []
lnums = []
lnum = line_nr + 1
for line in rawlines:
i = skip_white(line, 0)
if i < len(line):
if line[i] == '#':
nr = get_line_marker(line[i:])
if nr:
lnum = nr
else:
ind = get_indent(line)
if first_indent == -1:
first_indent = ind
if ind > first_indent:
lines[-1] = lines[-1] + line # append to previous line
else:
lines.append(line) # append action line
lnums.append(lnum)
if len(typelist) != len(lines) + 1:
recipe_error(rpstack, _(':route: found %d action lines, expected %d')
% (len(lines), len(typelist) - 1))
# Each entry in typelist can be a comma separate list of filetypes. Turn
# it into a list of lists.
typelist = map(lambda x: string.split(x, ","), typelist)
# Check that all the filetypes are known
for il in typelist:
for i in il:
if not ft_known(i):
msg_warning(recdict, _('unknown filetype "%s" in :route command; recipe %s line %d') % (i, rpstack[-1].name, rpstack[-1].line_nr))
# Now add the routes themselves
for in_type in typelist[0]:
for out_type in typelist[-1]:
route = work.find_route(in_type, out_type)
if route and not route.default:
recipe_error(rpstack,
_('redefining existing route from "%s" to "%s"')
% (in_type, out_type))
route = Route(recdict, rpdeepcopy(rpstack, line_nr), default,
typelist, arglist[-1], lines, lnums)
work.add_route(route)
def aap_program(line_nr, recdict, arg, type = "program"):
"""
Add a program, lib, dll, etc. build from sources.
Also for ":totype".
"arg" is the whole argument string, except for ":produce" where it lacks
the first argument (turned into the type).
"""
rpstack = getrpstack(recdict, line_nr)
work = getwork(recdict)
# Parse command attributes.
cmd_attr, i = get_attrdict(rpstack, recdict, arg, 0, 1)
# Expand the arguments into a dictlist.
dictlist = str2dictlist(rpstack,
expand(line_nr, recdict, arg[i:], Expand(1, Expand.quote_aap)))
cmdname = type
# For ":produce" the first argument is the type of the target.
atype = type
if atype == "produce":
if len(dictlist) < 2:
recipe_error(rpstack,
_(":produce requires both a type name and a target name"))
atype = dictlist[0]["name"]
cmd_attr.update(dictlist[0])
dictlist = dictlist[1:]
# Find the ":" item between the target and the sources.
colonidx = -1
for i in range(len(dictlist)):
if dictlist[i]["name"] == ":":
colonidx = i
break
if colonidx == -1:
recipe_error(rpstack, _("Missing ':' after :%s") % cmdname)
targetlist = dictlist[0:colonidx]
if atype != "totype":
targetlist = dictlist_expand(targetlist)
if len(targetlist) != 1:
recipe_error(rpstack, _(":%s requires one target") % cmdname)
# get any build attributes {attr = value} from after the ':'.
build_attr = dictlist[colonidx]
# Expand the sources into dictlists.
sourcelist = dictlist[colonidx + 1:]
sourcelist = dictlist_expand(sourcelist)
if len(sourcelist) < 1:
recipe_error(rpstack, _(":%s requires at least one source") % cmdname)
# If there are build attributes, apply them to all the
# source files.
for ba in build_attr.keys():
# Everything is named already, skip it
if ba == "name":
continue
value = build_attr[ba]
if ba.startswith("add_") or ba.startswith("var_"):
# Build var_ + source var_ => source takes precedence
# Build add_ + source var_ => source takes precedence
# Build var_ + source add_ => append source to build
# Build add_ + source add_ => append source to build ?
# Build var_ + no source => use build var
# Build add_ + no source => use build add
varname = ba[4:]
for source in sourcelist:
# Source-level var_ attributes take precedence
if source.has_key("var_"+varname):
continue
# Prepend build attribute
if source.has_key("add_"+varname):
if ba.startswith("add_"):
source[ba] = value + " " + source[ba]
else:
source["var_"+varname]=value + " " + source["add_"+varname]
del source["add_"+varname]
else:
source[ba] = value
else:
for source in sourcelist:
if not source.has_key(ba):
source[ba] = value
# ":produce abc" needs to declare "abc" as a filetype.
if cmdname == "produce":
ft_declare(atype)
if atype != "totype":
# If the target doesn't have an suffix, add $EXESUF. It's done here so
# that the user can set $EXESUF before/after ":program".
bn = os.path.basename(targetlist[0]["name"])
if string.find(bn, ".") < 0:
# Remember the original name as an alias, it may be used as a
# source in another dependency (e.g., for "all").
alias = targetlist[0]["name"]
if atype == "program":
prevar = None
sufvar = "EXESUF"
elif atype == "lib":
prevar = "LIBPRE"
sufvar = "LIBSUF"
elif atype == "ltlib":
prevar = "LTLIBPRE"
sufvar = "LTLIBSUF"
elif atype == "dll":
prevar = "DLLPRE"
sufvar = "DLLSUF"
else: # ":produce"
prevar = None
sufvar = None
# Get the prefix from a variable or from the "prefix" attribute.
pre = cmd_attr.get("targetprefix")
if not pre and prevar:
pre = get_var_val(line_nr, recdict, "_no", prevar)
if pre:
newname = alias[:-len(bn)] + pre + bn
else:
newname = alias
# Get the suffix from a variable or from the "suffix" attribute.
suf = cmd_attr.get("targetsuffix")
if not suf and sufvar:
suf = get_var_val(line_nr, recdict, "_no", sufvar)
if suf:
newname = newname + suf
targetlist[0]["name"] = newname
# Add the alias to the node. May rename an existing node.
work.node_set_alias(newname, alias, targetlist[0])
# Add a comment attribute if there isn't one.
if not targetlist[0].get("comment"):
s = cmd_attr.get("comment")
if not s:
d = {"program" : "program",
"lib" : "static library",
"ltlib" : "libool archive",
"dll" : "shared library" }
s = d.get(atype)
if not s:
s = atype
targetlist[0]["comment"] = (_('build %s "%s"') % (s, newname))
# Setting the "filetype" attribute on the target would overrule a filetype
# that the user has given to the node. Use "filetypehint" to avoid that.
targetlist[0]["filetypehint"] = atype
# Add the build rule.
from DoAddDef import add_buildrule
add_buildrule(rpstack, work, recdict,
atype, cmd_attr, targetlist, build_attr, sourcelist)
if atype != "totype":
# Add the target to the default targets.
t = targetlist[0]
recdict["_buildrule_targets"].append(work.get_node(t["name"], 0, t))
def aap_lib(line_nr, recdict, arg):
"""Add a static library built from sources."""
aap_program(line_nr, recdict, arg, type = "lib")
def aap_ltlib(line_nr, recdict, arg):
"""Add a libtool library built from sources."""
# We automatically import the libtool module.
if not getwork(recdict).module_already_read.has_key("libtool"):
aap_import(line_nr, recdict, "libtool")
aap_program(line_nr, recdict, arg, type = "ltlib")
def aap_dll(line_nr, recdict, arg):
"""Add a dynamic library built from sources."""
aap_program(line_nr, recdict, arg, type = "dll")
def aap_produce(line_nr, recdict, arg):
"""Produce something user-defined from sources."""
aap_program(line_nr, recdict, arg, type = "produce")
def aap_totype(line_nr, recdict, arg):
"""Add a dynamic library build from sources."""
aap_program(line_nr, recdict, arg, type = "totype")
def aap_tree(line_nr, recdict, arg, cmd_line_nr, commands):
"""":tree"."""
rpstack = getrpstack(recdict, line_nr)
# Expand dirname and options into a dictlist
dictlist = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(1, Expand.quote_aap)))
if not dictlist[0]["name"]:
# Arguments start with an attribute.
recipe_error(rpstack, _('First argument of :tree must be a directory name'))
dictlist_expanduser(dictlist)
if len(dictlist) != 1:
recipe_error(rpstack, _(":tree requires one directory name"))
for k in dictlist[0].keys():
if k not in [ "filename", "dirname", "reject", "skipdir",
"contents", "name", "follow" ]:
recipe_error(rpstack, _('Invalid attribute for :tree: %s') % k)
dirpat = dictlist[0].get("dirname")
filepat = dictlist[0].get("filename")
if not dirpat and not filepat:
# No pattern specified, match all files
filepat = ".*"
# Compile the pattern. Prepend "^(" and add ")$" to match the whole name
# and allow alternatives: "^(" + "foo|bar" + ")$".
# Ignore case on non-posix systems
if os.name == "posix":
flags = 0
else:
flags = re.IGNORECASE
options = {}
if dirpat:
try:
options["dirrex"] = re.compile("^(" + dirpat + ")$", flags)
except Exception, e:
recipe_error(rpstack, _('Invalid pattern "%s": %s')
% (dirpat, str(e)))
else:
options["dirrex"] = None
if filepat:
try:
options["filerex"] = re.compile("^(" + filepat + ")$", flags)
except Exception, e:
recipe_error(rpstack, _('Invalid pattern "%s": %s')
% (filepat, str(e)))
else:
options["filerex"] = None
# Compile the reject pattern if given.
rejectpat = dictlist[0].get("reject")
if rejectpat:
try:
options["rejectrex"] = re.compile("^(" + rejectpat + ")$", flags)
except Exception, e:
recipe_error(rpstack, _('Invalid pattern "%s": %s')
% (rejectpat, str(e)))
else:
options["rejectrex"] = None
# Compile the skipdir pattern if given.
skipdirpat = dictlist[0].get("skipdir")
if skipdirpat:
try:
options["skipdirrex"] = re.compile("^(" + skipdirpat + ")$", flags)
except Exception, e:
recipe_error(rpstack, _('Invalid pattern "%s": %s')
% (skipdirpat, str(e)))
else:
options["skipdirrex"] = None
# Compile the text contents pattern if given.
contentspat = dictlist[0].get("contents")
if contentspat:
try:
options["contentsrex"] = re.compile(contentspat)
except Exception, e:
recipe_error(rpstack, _('Invalid pattern "%s": %s')
% (contentspat, str(e)))
else:
options["contentsrex"] = None
# Take over the "follow" option.
options["follow"] = dictlist[0].get("follow")
aap_tree_recurse(rpstack, recdict, dictlist[0]["name"], options,
cmd_line_nr, commands)
def aap_tree_recurse(rpstack, recdict, dir, options, cmd_line_nr, commands):
"""
Handle one directory level for ":tree" and recurse into subdirectories.
"""
from ParsePos import ParsePos
from Process import Process
# Handle the situation that a directory is not readable.
try:
dirlist = os.listdir(dir)
except:
msg_warning(recdict, _('Cannot read directory "%s"') % dir)
return
for f in dirlist:
f = os.path.join(dir, f)
# Handle recursion, but don't follow links and skip directories that
# match the "skipdir" pattern.
srex = options["skipdirrex"]
if (os.path.isdir(f)
and (options["follow"] or not os.path.islink(f))
and (not srex or not srex.match(os.path.basename(f)))):
aap_tree_recurse(rpstack, recdict, f, options,
cmd_line_nr, commands)
if os.path.isfile(f):
rex = options["filerex"]
elif os.path.isdir(f):
rex = options["dirrex"]
else:
rex = None
rrex = options["rejectrex"]
crex = options["contentsrex"]
n = os.path.basename(f)
if rex and rex.match(n) and (not rrex or not rrex.match(n)):
if crex and os.path.isfile(f):
try:
fd = open(f)
except:
msg_warning(recdict, _('Cannot open "%s"') % f)
continue
match = 0
try:
while 1:
line = fd.readline()
if not line:
break
if crex.match(line):
match = 1
break
except:
fd.close()
msg_warning(recdict, _('Cannot read "%s"') % f)
continue
fd.close()
if not match:
continue
# Use the current scope for the the command block. This makes it
# easier to use, like a "for" loop.
new_rpstack = rpcopy(rpstack, cmd_line_nr)
name_save = recdict.get("name")
recdict["name"] = listitem2str(f)
# Create a ParsePos object to contain the parse position in the
# string.
# Parse and execute the commands.
fp = ParsePos(new_rpstack, string = commands)
Process(fp, recdict, 0)
if name_save is None:
del recdict["name"]
else:
recdict["name"] = name_save
def aap_update(line_nr, recdict, arg):
"""Handle ":update target ...": update target(s) now."""
work = getwork(recdict)
rpstack = getrpstack(recdict, line_nr)
from DoBuild import target_update,updated_OK
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"f": "force", "force" : "force",
"searchpath" : "searchpath" })
if attrdict:
option_error(rpstack, attrdict, ":update")
if len(argdictlist) == 0:
recipe_error(rpstack, _("Missing argument for :update"))
argdictlist = dictlist_expand(argdictlist)
adir = os.getcwd()
# Loop over all arguments.
for t in argdictlist:
msg_depend(recdict, _('Start :update "%s"') % t["name"])
if os.path.isabs(t["name"]):
sp = [ '' ] # do not use "searchpath" for absolute name
else:
sp = optiondict.get("searchpath")
if not sp:
sp = [ '' ] # No {searchpath = ...}, do not use a path.
else:
sp = map(lambda x: x["name"], str2dictlist(rpstack, sp))
# Loop over "searchpath", the first one that works is used.
done = 0
for path in sp:
name = t["name"]
if path:
name = os.path.join(path, name)
target = work.get_node(name)
if len(sp) > 1:
msg_depend(recdict, _('Attempt :update "%s"') % name)
# For finding rules the scope of the current recipe is used.
target.scope_recdict = recdict
if target_update(work, recdict, target, 0,
optiondict.get("force"), level = 1) == updated_OK:
done = 1
if len(sp) > 1:
msg_depend(recdict, _(':update "%s" successful') % name)
break
if len(sp) > 1:
msg_depend(recdict, _(':update "%s" failed') % name)
if not done:
recipe_error(rpstack, _(':update failed for "%s"') % t["name"])
msg_depend(recdict, _('End of :update "%s"') % t["name"])
# Return to the original directory, could be halfway build commands.
goto_dir(recdict, adir)
def aap_error(line_nr, recdict, arg):
"""Handle: ":error foo bar"."""
rpstack = getrpstack(recdict, line_nr)
recipe_error(rpstack, expand(line_nr, recdict, arg,
Expand(1, Expand.quote_aap)))
def aap_unknown(line_nr, recdict, arg):
"""
Handle: ":xxx arg". Postponed until executing the line, so that an "@if
_no.AAPVERSION > nr" can be used.
"""
rpstack = getrpstack(recdict, line_nr)
recipe_error(rpstack, _('Unknown command: "%s"') % arg)
def aap_nothere(line_nr, recdict, arg):
"""
Handle using a toplevel command in build commands. Postponed until
executing the line, so that an "@if _no.AAPVERSION > nr" can be used.
"""
rpstack = getrpstack(recdict, line_nr)
recipe_error(rpstack, _('Command cannot be used here: "%s"') % arg)
#
############## start of commands used in a pipe
#
def _get_redir(line_nr, recdict, raw_arg, xp):
"""Get the redirection and pipe from the argument "raw_arg".
Returns these items:
1. the argument with $VAR expanded and redirection removed
2. the file name for redirection or None
3. the mode for redirection or None ("a" for append, "w" for write).
4. a command following '|' or None
When using ">file" also checks if the file doesn't exist yet.
Use "xp" for expanding $var in the argument."""
rpstack = getrpstack(recdict, line_nr)
mode = None
fname = None
nextcmd = None
# Loop over the argument, getting one token at a time. Each token is
# either non-white (possibly with quoting) or a span of white space.
raw_arg_len = len(raw_arg)
i = 0 # current index in raw_arg
new_arg = '' # argument without redirection so far
while i < raw_arg_len:
t, i = get_token(raw_arg, i)
# Ignore trailing white space.
if i == raw_arg_len and is_white(t[0]):
break
# After (a span of) white space, check for redirection or pipe.
# Also at the start of the argument.
if new_arg == '' or is_white(t[0]):
if new_arg == '':
# Use the token at the start of the argument.
nt = t
t = ''
else:
# Get the token after the white space
nt, i = get_token(raw_arg, i)
if nt[0] == '>':
# Redirection: >, >> or >!
if mode:
recipe_error(rpstack, _('redirection appears twice'))
nt_len = len(nt)
ni = 1 # index after the ">", ">>" or ">!"
mode = 'w'
overwrite = 0
if nt_len > 1:
if nt[1] == '>':
mode = 'a'
ni = 2
elif nt[1] == '!':
overwrite = 1
ni = 2
if ni >= nt_len:
# white space after ">", get next token for fname
redir = nt[:ni]
if i < raw_arg_len:
# Get the separating white space.
nt, i = get_token(raw_arg, i)
if i == raw_arg_len:
recipe_error(rpstack, _('Missing file name after %s')
% redir)
# Get the file name
nt, i = get_token(raw_arg, i)
else:
# fname follows immediately after ">"
nt = nt[ni:]
# Expand $VAR in the file name. No attributes are added.
# Remove quotes from the result, it's used as a filename.
fname = unquote(recdict, expand(line_nr, recdict, nt,
Expand(0, Expand.quote_aap)))
from Remote import url_split3
scheme, machine, path = url_split3(fname)
if not scheme:
# Not an URL: expand wildcards and check for overwriting.
l = glob.glob(os.path.expanduser(fname))
if len(l) == 1:
fname = l[0]
# else: give an error message?
if mode == "w" and not overwrite:
check_exists(rpstack, fname)
else:
# No expanding in a URL yet, at least remove escaped
# wildcards: [*] -> *.
fname = expand_unescape(fname)
# When redirection is at the start, ignore the white space
# after it.
if new_arg == '' and i < raw_arg_len:
t, i = get_token(raw_arg, i)
elif nt[0] == '|':
# Pipe: the rest of the argument is another command
if mode:
recipe_error(rpstack, _("both redirection and '|'"))
if len(nt) > 1:
nextcmd = nt[1:] + raw_arg[i:]
else:
i = skip_white(raw_arg, i)
nextcmd = raw_arg[i:]
if not nextcmd:
# Can't have an empty command.
recipe_error(rpstack, _("Nothing follows '|'"))
if nextcmd[0] != ':':
# Must have an aap command here.
recipe_error(rpstack, _("Missing ':' after '|'"))
break
else:
# No redirection or pipe: add to argument
new_arg = new_arg + t + nt
else:
# Normal token: add to argument
new_arg = new_arg + t
if new_arg and xp:
arg = expand(line_nr, recdict, new_arg, xp)
else:
arg = new_arg
return arg, fname, mode, nextcmd
def _aap_pipe(line_nr, recdict, cmd, pipein):
"""Handle the command that follows a '|'."""
rpstack = getrpstack(recdict, line_nr)
items = string.split(cmd, None, 1)
if len(items) == 1: # command without argument, append empty argument.
items.append('')
if items[0] == ":assign":
_pipe_assign(line_nr, recdict, items[1], pipein)
elif items[0] == ":cat":
aap_cat(line_nr, recdict, items[1], pipein)
elif items[0] == ":syseval":
aap_syseval(line_nr, recdict, items[1], pipein)
elif items[0] == ":eval":
aap_eval(line_nr, recdict, items[1], pipein)
elif items[0] == ":print":
aap_print(line_nr, recdict, items[1], pipein)
elif items[0] == ":tee":
_pipe_tee(line_nr, recdict, items[1], pipein)
else:
recipe_error(rpstack,
(_('Invalid command after \'|\': "%s"') % items[0]))
def _pipe_assign(line_nr, recdict, raw_arg, pipein):
"""Handle: ":assign var". Can only be used in a pipe."""
rpstack = getrpstack(recdict, line_nr)
assert_var_name(rpstack, raw_arg)
# Separate scope and decide which dictionary to use.
rd, scope, varname = get_scope_recdict(rpstack, recdict, raw_arg, 1)
rd[varname] = pipein
def aap_cat(line_nr, recdict, raw_arg, pipein = None):
"""Handle: ":cat >foo $bar"."""
rpstack = getrpstack(recdict, line_nr)
# get the special items out of the argument
arg, fname, mode, nextcmd = _get_redir(line_nr, recdict, raw_arg,
Expand(0, Expand.quote_aap))
# Skip writing file when not actually building.
if mode and skip_commands():
msg_skip(line_nr, recdict, ':cat ' + raw_arg)
return
# get the list of files from the remaining argument
filelist = str2dictlist(rpstack, arg)
if len(filelist) == 0:
if pipein is None:
recipe_error(rpstack,
_(':cat command requires at least one file name argument'))
filelist = [ {"name" : "-"} ]
result = ''
scheme = None
if mode:
# Open the output file for writing
from Remote import url_split3
from RecPython import tempfname
scheme, machine, path = url_split3(fname)
if scheme:
# Remote file, first write to a temp file.
ffname = tempfname()
else:
ffname = fname
try:
wf = open(ffname, mode)
except IOError, e:
recipe_error(rpstack,
(_('Cannot open "%s" for writing') % ffname) + str(e))
try:
# Loop over all arguments
filelist = dictlist_expand(filelist)
for item in filelist:
fn = item["name"]
if fn == '-':
# "-" argument: use pipe input
if pipein is None:
recipe_error(rpstack, _('Using - not after a pipe'))
if nextcmd:
result = result + pipein
else:
# Split into separate lines; can't use split() here, we
# want to keep the line breaks.
lines = []
i = 0
while i < len(pipein):
n = string.find(pipein, "\n", i)
if n < 0:
lines.append(pipein[i:])
break
lines.append(pipein[i:n + 1])
i = n + 1
else:
# file name argument: read the file
try:
rf = open(fn, "r")
except IOError, e:
recipe_error(rpstack,
(_('Cannot open "%s" for reading') % fn) + str(e))
try:
lines = rf.readlines()
rf.close()
except IOError, e:
recipe_error(rpstack,
(_('Cannot read from "%s"') % fn) + str(e))
if nextcmd:
# pipe output: append lines to the result
for l in lines:
result = result + l
if mode:
# file output: write lines to the file
try:
wf.writelines(lines)
except IOError, e:
recipe_error(rpstack,
(_('Cannot write to "%s"') % ffname) + str(e))
elif not nextcmd:
# output to the terminal: print lines
for line in lines:
if line[-1] == '\n':
msg_print(line[:-1])
else:
msg_print(line)
if mode:
# close the output file
try:
wf.close()
except IOError, e:
recipe_error(rpstack, (_('Error closing "%s"')
% ffname) + str(e))
else:
if scheme:
from CopyMove import remote_copy_move
remote_copy_move(rpstack, recdict, 0,
[ {"name" : ffname} ], { "name" : fname },
{}, 0, errmsg = 1)
finally:
if scheme:
# Always delete the temp file.
try_delete(ffname)
if nextcmd:
# pipe output: execute the following command
_aap_pipe(line_nr, recdict, nextcmd, result)
elif mode:
msg_info(recdict, _('Concatenated files into "%s"') % fname)
def aap_eval(line_nr, recdict, raw_arg, pipein = None):
"""Handle: ":eval function ...". Can be used in a pipe."""
rpstack = getrpstack(recdict, line_nr)
arg, fname, mode, nextcmd = _get_redir(line_nr, recdict, raw_arg, None)
if pipein and string.find(arg, "stdin") < 0:
recipe_error(rpstack, _('stdin missing in :eval argument'))
# Evaluate the expression.
if not pipein is None:
recdict["stdin"] = pipein
try:
result = str(eval(arg, recdict, recdict))
except StandardError, e:
recipe_error(rpstack, (_(':eval command "%s" failed: ') % arg)
+ str(e))
if pipein:
del recdict["stdin"]
if mode:
# redirection: write output to a file
_write2file(rpstack, recdict, fname, result, mode)
elif nextcmd:
# pipe output: execute next command
_aap_pipe(line_nr, recdict, nextcmd, result)
else:
# output to terminal: print the result
msg_print(result)
def aap_print(line_nr, recdict, raw_arg, pipein = None):
"""
Handle: ":print foo $bar", print a message
"""
rpstack = getrpstack(recdict, line_nr)
if pipein:
recdict["stdin"] = pipein
arg, fname, mode, nextcmd = _get_redir(line_nr, recdict, raw_arg,
Expand(1, Expand.quote_aap))
if pipein:
del recdict["stdin"]
if not arg:
arg = pipein
if mode:
if len(arg) == 0 or arg[-1] != '\n':
arg = arg + '\n'
_write2file(rpstack, recdict, fname, arg, mode)
elif nextcmd:
if len(arg) == 0 or arg[-1] != '\n':
arg = arg + '\n'
_aap_pipe(line_nr, recdict, nextcmd, arg)
else:
msg_print(arg)
def aap_log(line_nr, recdict, raw_arg, pipein = None):
"""
Handle: ":log foo $bar", write a message in the log file.
"""
rpstack = getrpstack(recdict, line_nr)
if pipein:
recdict["stdin"] = pipein
arg, fname, mode, nextcmd = _get_redir(line_nr, recdict, raw_arg,
Expand(1, Expand.quote_aap))
if pipein:
del recdict["stdin"]
if not arg:
arg = pipein
if mode or nextcmd:
recipe_error(rpstack, _('Cannot redirect or pipe :log output'))
msg_log(recdict, arg)
def aap_syseval(line_nr, recdict, raw_arg, pipein = None):
"""Execute a shell command, redirecting stdout and/or stderr and stdin."""
rpstack = getrpstack(recdict, line_nr)
attrdict, idx = get_attrdict(rpstack, recdict, raw_arg, 0, 1)
for k in attrdict.keys():
if k != "stderr":
recipe_error(rpstack, _('unknown option for :syseval: "%s"') % k)
arg, fname, mode, nextcmd = _get_redir(line_nr, recdict, raw_arg[idx:],
Expand(1, Expand.quote_aap))
try:
tmpin = None
tmpout = None
from RecPython import tempfname
cmd = '(' + arg + ')'
if pipein:
tmpin = tempfname()
try:
f = open(tmpin, "w")
f.write(pipein)
f.close()
except IOError, e:
recipe_error(rpstack, (_('Cannot write stdin to "%s": ')
% tmpin) + str(e))
cmd = cmd + (' < %s' % tmpin)
tmpout = tempfname()
if attrdict.has_key("stderr"):
cmd = cmd + ' 2>&1'
cmd = cmd + (' > %s' % tmpout)
recdict["exit"] = os.system(cmd)
# read the output file
try:
rf = open(tmpout)
out = rf.read()
rf.close()
except IOError:
out = ''
finally:
# Clean up the temp files
if tmpin:
try_delete(tmpin)
if tmpout:
try_delete(tmpout)
# Remove leading and trailing blanks and newlines.
out = re.sub(r'^\s*', '', out)
out = re.sub(r'\s*$', '', out)
if mode:
_write2file(rpstack, recdict, fname, out, mode)
elif nextcmd:
_aap_pipe(line_nr, recdict, nextcmd, out)
else:
msg_print(out)
def _pipe_tee(line_nr, recdict, raw_arg, pipein):
"""Handle: ":tee fname ...". Can only be used in a pipe."""
rpstack = getrpstack(recdict, line_nr)
arg, fname, mode, nextcmd = _get_redir(line_nr, recdict, raw_arg,
Expand(0, Expand.quote_aap))
# get the list of files from the remaining argument
filelist = str2dictlist(rpstack, arg)
if len(filelist) == 0:
recipe_error(rpstack,
_(':tee command requires at least one file name argument'))
for f in filelist:
fn = f["name"]
check_exists(rpstack, fn)
_write2file(rpstack, recdict, fn, pipein, "w")
if mode:
# redirection: write output to a file
_write2file(rpstack, recdict, fname, pipein, mode)
elif nextcmd:
# pipe output: execute next command
_aap_pipe(line_nr, recdict, nextcmd, pipein)
else:
# output to terminal: print the result
msg_print(pipein)
def _write2file(rpstack, recdict, fname, string, mode):
"""Write string "string" to file "fname" opened with mode "mode"."""
# Skip when not actually building.
if skip_commands():
msg_info(recdict, _('skip writing to "%s"') % fname)
return
from Remote import url_split3
from RecPython import tempfname
scheme, machine, path = url_split3(fname)
if scheme:
# Remote file, first write to a temp file.
ffname = tempfname()
else:
ffname = fname
try:
f = open(ffname, mode)
except IOError, e:
recipe_error(rpstack,
(_('Cannot open "%s" for writing') % ffname) + str(e))
try:
try:
f.write(string)
f.close()
except IOError, e:
recipe_error(rpstack, (_('Cannot write to "%s"') % fname) + str(e))
if scheme:
from CopyMove import remote_copy_move
remote_copy_move(rpstack, recdict, 0,
[ {"name" : ffname} ], { "name" : fname },
{}, 0, errmsg = 1)
finally:
if scheme:
# Always delete the temp file.
try_delete(ffname)
#
############## end of commands used in a pipe
#
def aap_child(line_nr, recdict, arg):
"""Handle ":child filename": read a recipe."""
aap_child_and_exe(line_nr, recdict, arg, 1)
def aap_execute(line_nr, recdict, arg):
"""Handle ":execute filename [target] ...": execute a recipe."""
aap_child_and_exe(line_nr, recdict, arg, 0)
def aap_child_and_exe(line_nr, recdict, arg, child):
"""Handle reading and possibly executing a recipe.
"child" is non-zero for ":child", zero for ":execute"."""
rpstack = getrpstack(recdict, line_nr)
work = getwork(recdict)
if child:
cmdname = ":child"
else:
cmdname = ":execute"
# When still starting up we can't create a child scope.
if not recdict.get("_start"):
recipe_error(rpstack,
_("%s cannot be used in a startup recipe") % cmdname)
# Evaluate the options and arguments
optiondict, attrdict, varlist = get_args(line_nr, recdict, arg,
{"p": "pass", "pass" : "pass",
"n": "nopass", "nopass" : "nopass"})
if attrdict:
option_error(rpstack, attrdict, cmdname)
if len(varlist) == 0:
recipe_error(rpstack, _("%s requires an argument") % cmdname)
if child and len(varlist) > 1:
recipe_error(rpstack, _("%s only accepts one argument") % cmdname)
varlist = dictlist_expand(varlist)
name = varlist[0]["name"]
force_fetch = Global.cmd_args.has_option("fetch-recipe")
if ((force_fetch or not os.path.exists(name))
and varlist[0].has_key("fetch")):
# Need to create a node to fetch it.
# Ignore errors, a check for existence is below.
# Use a cached file when no forced fetch.
from VersCont import fetch_nodelist
node = work.get_node(name, 0, varlist[0])
fetch_nodelist(rpstack, recdict, [ node ], not force_fetch)
if not os.path.exists(name):
if varlist[0].has_key("fetch"):
recipe_error(rpstack, _('Cannot download recipe "%s"') % name)
else:
recipe_error(rpstack, _('Recipe "%s" does not exist') % name)
try:
cwd = os.getcwd()
except OSError:
recipe_error(rpstack, _("Cannot obtain current directory"))
# Get the directory name of the child relative to the parent before
# changing directory.
childdir = shorten_name(os.path.dirname(name), cwd)
if not child:
msg_extra(recdict, _('Executing recipe "%s"') % name)
# Change to the directory where the recipe is located.
name = recipe_dir(recdict, os.path.abspath(name))
#
# Create a new scope and/or Work object to execute the recipe with.
# ":child" inherit scope, no new Work
# ":child {nopass}" new toplevel scope, no new Work
# ":execute {pass}" inherit scope, new Work
# ":execute" new toplevel scope, new Work
#
from Scope import get_build_recdict,create_topscope
# Create a new scope for the executed/child recipe.
if ((child and not optiondict.get("nopass"))
or (not child and optiondict.get("pass"))):
# Pass on the variables available in the current scope.
new_recdict = get_build_recdict(recdict, None, recipe_name = name)
else:
# Hide everything from the current scope, create a new toplevel scope.
if child:
# ":child" command without passing the current scope: Create a new
# toplevel scope and set the default values.
new_recdict = create_topscope(name).data
new_recdict["_default"] = recdict["_default"]
new_recdict["_start"] = recdict["_start"]
# Set the startup values from the "_start" scope. Do not read the
# startup recipes again, the dependencies and rules are already
# defined.
fromdict = recdict["_start"].data
for k in fromdict.keys():
if k[0] != '_':
new_recdict[k] = fromdict[k]
# Use the current Work object.
setwork(new_recdict, work)
else:
# ":execute": let Work() create a new toplevel scope below.
new_recdict = None
if child:
newwork = work
else:
# ":execute": Create a new Work object.
# Get the arguments like command line arguments
oldargs = Global.cmd_args
Global.cmd_args = doargs(map(lambda x: x["name"], varlist[1:]))
# Keep global options, e.g., --nobuild.
copy_global_options(oldargs, Global.cmd_args)
# Create a new Work object to execute the recipe in.
oldwork = getwork(recdict)
newwork = Work(new_recdict)
newwork.top_recipe = name
# Remember the top directory.
newwork.top_dir = os.getcwd()
# Need to read the startup recipes to get the default dependencies and
# rules. But when passing on the scope we don't use the variable
# values. Tricky!!!
if new_recdict:
rd = create_topscope(name).data
setwork(rd, newwork)
else:
rd = newwork.recdict
new_recdict = rd
set_defaults(rd)
# Set $TARGETARG.
new_recdict["TARGETARG"] = list2str(Global.cmd_args.targets)
# Add values set in the command line arguments
add_cmdline_settings(new_recdict)
# Always set the parent, also when a new toplevel is created.
absdir = os.path.dirname(os.path.abspath(name))
new_recdict["_parent"] = recdict["_recipe"]
new_recdict["CHILDDIR"] = childdir
new_recdict["PARENTDIR"] = shorten_name(cwd, os.path.dirname(name) or None)
new_recdict["TOPDIR"] = shorten_name(absdir, newwork.top_dir)
new_recdict["BASEDIR"] = shorten_name(newwork.top_dir, absdir)
#
# Read the recipe
#
read_recipe(rpstack, new_recdict, name, not child or Global.at_toplevel)
from DoAddDef import doadddef
if not child:
#
# ":execute" the recipe right now.
#
from DoBuild import dobuild
doadddef(newwork, newwork.recdict, 1)
dobuild(newwork)
# Restore the previous Work object and continue there.
setwork(recdict, oldwork)
Global.cmd_args = oldargs
else:
# For a child recipe add default dependencies.
doadddef(work, new_recdict, 0)
# go back to the previous current directory
try:
goto_dir(recdict, cwd)
except OSError:
recipe_error(rpstack, _('Cannot change to directory "%s"') % cwd)
def aap_attr(line_nr, recdict, arg):
"""Add attributes to nodes."""
aap_attribute(line_nr, recdict, arg)
def aap_attribute(line_nr, recdict, arg):
"""Add attributes to nodes."""
rpstack = getrpstack(recdict, line_nr)
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg)
if not argdictlist:
recipe_error(rpstack, _(":attr command requires a file argument"))
# Make file names relative to the current recipe.
dict_expand(attrdict)
# Expand wildcards.
argdictlist = dictlist_expand(argdictlist)
# Loop over all items, adding attributes to the node.
work = getwork(recdict)
for adict in argdictlist:
if adict["name"] == '':
from Dictlist import dictlistattr2str
recipe_error(rpstack, _('Attribute without a name: %s') % dictlistattr2str(adict))
node = work.get_node(adict["name"], 1, adict)
node.set_attributes(attrdict)
def sep_scope(rpstack, name):
"""Separate the scope and the variable name out of "name".
Returns (scope, name). Scope is None when there is none."""
i = string.find(name, ".")
if i < 0:
return None, name
scope = name[:i]
varname = name[i + 1:]
if i == 0 or string.find(varname, ".") >= 0:
recipe_error(rpstack, _("Invalid dot in variable name %s") % name)
return scope, varname
def get_scope_recdict(rpstack, recdict, name, assign = 0):
"""
Get the recdict, scope and varname to use for "name", which may include a
scope name. The returned scope is "_no" when no scope was specified.
When a variable exists with the name of the scope the returned "rd" is the
variable value.
When "assign" is non-zero, create a new user scope when needed.
"""
scope, varname = sep_scope(rpstack, name)
if not scope:
scope = "_no"
try:
rd = recdict[scope]
except:
from Scope import check_user_scope,create_user_scope
if assign and scope[0] in string.letters:
assert_scope_name(rpstack, scope)
msg = check_user_scope(recdict, scope)
if msg:
recipe_error(rpstack, msg)
create_user_scope(recdict, scope)
rd = recdict[scope]
else:
recipe_error(rpstack, _("Invalid scope name: %s") % name)
return rd, scope, varname
def aap_assign(line_nr, recdict, name, arg, dollar, extra):
"""Assignment command in a recipe.
"name" is the name of the variable.
"arg" is the argument value (Python expression already expanded).
When "dollar" is '$' don't expand $VAR items.
When "extra" is '?' only assign when "name" wasn't set yet.
When "extra" is '+' append to "name"."""
rpstack = getrpstack(recdict, line_nr)
# Separate scope and decide which dictionary to use.
rd, scope, varname = get_scope_recdict(rpstack, recdict, name, 1)
# Skip the whole assignment for "var ?= val" if var was already set.
if extra != '?' or not rd.has_key(varname):
if dollar != '$':
# Expand variables in "arg".
val = expand(line_nr, recdict, arg, Expand(1, Expand.quote_aap))
else:
val = arg
import types
if rd.has_key(varname):
v = rd.get(varname)
from Scope import RecipeDict
if isinstance(v, types.DictType) or isinstance(v, RecipeDict):
if extra == '+':
# Appending to a scope is impossible, this is an error.
recipe_error(rpstack,
_("Cannot append to a scope: %s") % name)
else:
# Overwriting a scope with a variable. Is that an error?
msg_warning(recdict, _("Warning: Variable name already in use as a scope: %s") % name, rpstack = rpstack)
if extra == '+':
# append to old value
oldval = get_var_val(line_nr, recdict, scope, varname)
if oldval:
val = oldval + ' ' + val
if dollar == '$':
# Postpone expanding variables in "arg". Create an ExpandVar
# object to remember it has to be done when using the variable.
val = ExpandVar(val)
# set the value
try:
rd[varname] = val
except WriteAfterRead, e:
recipe_error(rpstack, e)
except TypeError:
if not isinstance(rd, types.DictType):
recipe_error(rpstack,
_("scope name refers to a variable: %s") % name)
recipe_error(rpstack, _("Invalid scope name in %s") % name)
def expand(line_nr, recdict, arg, argexpand, startquote = '', skip_errors = 0):
"""Evaluate $VAR, $(VAR) and ${VAR} in "arg", which is a string.
$VAR is expanded and the resulting string is combined with what comes
before and after $VAR. text$VAR -> "textval1" "textval2".
"argexpand" is an Expand object that specifies the way $VAR is expanded.
When "startquote" isn't empty, work like "arg" was preceded by it.
When "skip_errors" is non-zero, leave items with errors unexpanded,
never fail.
"""
rpstack = getrpstack(recdict, line_nr)
res = '' # resulting string so far
inquote = startquote # ' or " when inside quotes
itemstart = 0 # index where a white separated item starts
arg_len = len(arg)
idx = 0
while idx < arg_len:
if arg[idx] == '$':
sidx = idx
idx = idx + 1
if arg[idx] == '$':
res = res + '$' # reduce $$ to a single $
idx = idx + 1
elif arg[idx] == '#':
res = res + '#' # reduce $# to a single #
idx = idx + 1
elif arg[idx] == '(' and idx + 2 < arg_len and arg[idx + 2] == ')':
res = res + arg[idx + 1] # reduce $(x) to x
idx = idx + 3
else:
# Remember what non-white text came before the $.
before = res[itemstart:]
exp = copy.copy(argexpand) # make a copy so that we can
# change it
while arg[idx] in '?-+*/=\'"\\!':
if arg[idx] == '?': # optional existence
exp.optional = 1
elif arg[idx] == '-': # exclude attributes
exp.attr = 0
elif arg[idx] == '+': # include attributes
exp.attr = 1
elif arg[idx] == '*': # no rc-style expansion
exp.rcstyle = 1
elif arg[idx] == '/': # change slash to backslash
exp.slash = 1
elif arg[idx] == '=': # no quoting
exp.quote = Expand.quote_none
elif arg[idx] == "'": # A-A-P quoting
exp.quote = Expand.quote_aap
elif arg[idx] == '"': # double quotes
exp.quote = Expand.quote_double
elif arg[idx] == '\\': # backslash quoting
exp.quote = Expand.quote_bs
elif arg[idx] == '!': # shell quoting
exp.quote = Expand.quote_shell
else:
print "Ooops!"
idx = idx + 1
# Check for $(VAR) and ${VAR}.
if arg[idx] == '(' or arg[idx] == '{':
s = skip_white(arg, idx + 1)
else:
s = idx
# get the variable name
e = s
while e < arg_len and varchar(arg[e]):
e = e + 1
# Exclude a trailing dot, so that ":print did $target." works.
if e > s and arg[e - 1] == '.':
e = e - 1
if e == s:
if skip_errors:
res = res + arg[sidx:idx]
continue
recipe_error(rpstack, _("Invalid character after $"))
name = arg[s:e]
scope, varname = sep_scope(rpstack, name)
if not scope:
# No scope specified, use "_no".
scope = "_no"
try:
rd = recdict[scope]
scope_found = 1
except:
scope_found = 0
if scope_found:
try:
var_found = rd[varname]
var_found = 1
except:
var_found = 0
if not (scope_found and var_found) and not exp.optional:
if skip_errors:
res = res + arg[sidx:idx]
continue
if not scope_found:
recipe_error(rpstack, _("Invalid scope name in %s") % name)
recipe_error(rpstack, _('Unknown variable: "%s"') % name)
index = -1
if s > idx:
# Inside () or {}
e = skip_white(arg, e)
if e < arg_len and arg[e] == '[':
# get index for $(var[n])
b = e
brak = 0
e = e + 1
# TODO: ignore [] inside quotes?
while e < arg_len and (arg[e] != ']' or brak > 0):
if arg[e] == '[':
brak = brak + 1
elif arg[e] == ']':
brak = brak - 1
e = e + 1
if e == arg_len or arg[e] != ']':
if skip_errors:
res = res + arg[sidx:idx]
continue
recipe_error(rpstack, _("Missing ']'"))
v = expand(line_nr, recdict, arg[b+1:e],
Expand(0, Expand.quote_none), '', skip_errors)
try:
index = int(v)
except:
if skip_errors:
res = res + arg[sidx:idx]
continue
recipe_error(rpstack,
_('index does not evaluate to a number: "%s"')
% v)
if index < 0:
if skip_errors:
res = res + arg[sidx:idx]
continue
recipe_error(rpstack,
_('index evaluates to a negative number: "%d"')
% index)
e = skip_white(arg, e + 1)
# Check for matching () and {}
if (e == arg_len
or (arg[idx] == '(' and arg[e] != ')')
or (arg[idx] == '{' and arg[e] != '}')):
if skip_errors:
res = res + arg[sidx:idx]
continue
recipe_error(rpstack, _('No match for "%s"') % arg[idx])
# Continue after the () or {}
idx = e + 1
else:
# Continue after the varname
idx = e
# Skip over optional variable that is not defined.
if not (scope_found and var_found) and not exp.rcstyle:
continue
# Find what comes after $VAR.
# Need to remember whether it starts inside quotes.
after_inquote = inquote
s = idx
while idx < arg_len:
if inquote:
if arg[idx] == inquote:
inquote = ''
elif arg[idx] == '"' or arg[idx] == "'":
inquote = arg[idx]
elif string.find(string.whitespace + "{", arg[idx]) != -1:
break
idx = idx + 1
after = arg[s:idx]
if exp.attr:
# Obtain any following attributes, advance to after them.
# Also expand $VAR inside the attributes.
attrdict, idx = get_attrdict(rpstack, recdict, arg, idx, 1)
else:
attrdict = {}
if exp.rcstyle and not (scope_found and var_found):
# xxx$?var results in nothing when $var doesn't exists
res = res[0:itemstart]
continue
if not exp.rcstyle or (before == '' and after == ''
and len(attrdict) == 0):
if index < 0:
# No rc-style expansion or index, use the value of
# $VAR as specified with quote-expansion
try:
res = res + get_var_val(line_nr, recdict,
scope, varname, exp)
except (TypeError, KeyError):
# Get a KeyError for using a user scope as variable
# name.
if skip_errors:
res = res + arg[sidx:idx]
continue
from Scope import is_scope_name
if is_scope_name(recdict, varname):
recipe_error(rpstack,
_('Using scope name as variable: "%s"')
% varname)
recipe_error(rpstack,
_('Type of variable "%s" must be a string')
% varname)
else:
# No rc-style expansion but does have an index.
# Get the Dictlist of the referred variable.
varlist = str2dictlist(rpstack,
get_var_val(line_nr, recdict, scope, varname))
if len(varlist) < index + 1:
if skip_errors:
# Note changing $(source[1]) to $(source[2]).
res = res + arg[sidx:idx]
else:
msg_warning(recdict,
_('using %s[%d] but length is %d')
% (name, index, len(varlist)))
else:
res = res + expand_item(varlist[index], exp)
# TODO: Why was this here?
#for k in attrdict.keys():
#res = res + "{%s = %s}" % (k, attrdict[k])
# Continue with what comes after $VAR.
inquote = after_inquote
idx = s
else:
# rc-style expansion of a variable
# Get the Dictlist of the referred variable.
# When an index is specified use that entry of the list.
# When index is out of range or the list is empty, use a
# list with one empty entry.
varlist1 = str2dictlist(rpstack,
get_var_val(line_nr, recdict, scope, varname))
if (len(varlist1) == 0
or (index >= 0 and len(varlist1) < index + 1)):
if index >= 0 and not skip_errors:
msg_warning(recdict,
_('Index "%d" is out of range for variable "%s"')
% (index, name))
varlist1 = [{"name": ""}]
elif index >= 0:
varlist1 = [ varlist1[index] ]
# Evaluate the "after" of $(VAR)after {attr = val}.
varlist2 = str2dictlist(rpstack,
expand(line_nr, recdict, after,
Expand(1, Expand.quote_aap),
startquote = after_inquote),
startquote = after_inquote)
if len(varlist2) == 0:
varlist2 = [{"name": ""}]
# If the referred variable is empty and "after" has only
# one item, the result is empty. Thus "-L$*LIBS" is
# nothing when $LIBS is empty.
if (len(varlist1) == 1 and varlist1[0]["name"] == ""
and len(varlist2) == 1):
res = res[0:itemstart]
else:
# Remove quotes from "before", they are put back when
# needed.
lead = ''
q = ''
for c in before:
if q:
if c == q:
q = ''
else:
lead = lead + c
elif c == '"' or c == "'":
q = c
else:
lead = lead + c
# Combine "before", the list from $VAR, the list from
# "after" and the following attributes.
# Put "startquote" in front, because the terminalting
# quote will have been removed.
rcs = startquote
startquote = ''
for d1 in varlist1:
for d2 in varlist2:
if rcs:
rcs = rcs + ' '
s = lead + d1["name"] + d2["name"]
# If $VAR was in quotes put the result in
# quotes.
if after_inquote:
rcs = rcs + enquote(s,
quote = after_inquote)
else:
rcs = rcs + expand_itemstr(s, exp)
if exp.attr:
for k in d1.keys():
if k != "name":
rcs = rcs + "{%s = %s}" % (k, d1[k])
for k in d2.keys():
if k != "name":
rcs = rcs + "{%s = %s}" % (k, d2[k])
for k in attrdict.keys():
rcs = rcs + "{%s = %s}" % (k, attrdict[k])
res = res[0:itemstart] + rcs
else:
# No '$' at this position, include the character in the result.
# Check if quoting starts or ends and whether white space separates
# an item, this is used for expanding $VAR.
c = arg[idx]
if inquote:
if c == inquote:
inquote = ''
elif c == '"' or c == "'":
inquote = c
elif c == ' ' or c == '\t':
itemstart = len(res) + 1
res = res + c
idx = idx + 1
return res
def shell_cmd_has_force(rpstack, recdict, s):
"""Return non-zero when "s" starts with a "force" attribute."""
attrdict, i = get_attrdict(rpstack, recdict, s, 0, 1)
if attrdict.get("f") or attrdict.get("force"):
return 1
return 0
def aap_shell(line_nr, recdict, cmds, async = -1):
"""Execute shell commands from the recipe.
"cmds" must end in a newline character.
When "async" is positive work asynchronously.
When "async" is negative (the default) work asynchronously when the
$async variable is set."""
# Skip when not actually building.
if skip_commands():
if cmds[-1] == '\n':
s = cmds[:-1]
else:
s = cmds
msg_skip(line_nr, recdict, 'shell commands "%s"' % s)
return
rpstack = getrpstack(recdict, line_nr)
# Need to get the "force" argument here. Leave the attributes in the
# command, they will be used by logged_system() as well.
forced = shell_cmd_has_force(rpstack, recdict, cmds)
cmd = expand(line_nr, recdict, cmds, Expand(0, Expand.quote_shell))
if recdict.has_key("target"):
msg_extra(recdict, _('Shell commands for updating "%s":')
% recdict["target"])
if async < 0:
async = recdict.get("async")
if async and os.name in [ "posix", "nt" ]:
# Run the command asynchronously.
async_system(rpstack, recdict, cmd)
n = 0
else:
n = logged_system(recdict, cmd)
recdict["sysresult"] = n
if n != 0 and not forced:
recipe_error(getrpstack(recdict, line_nr),
_("Shell command returned %d") % n)
def aap_system(line_nr, recdict, cmds):
"""Implementation of ":system cmds". Almost the same as aap_shell()."""
aap_shell(line_nr, recdict, cmds + '\n')
def aap_sys(line_nr, recdict, cmds):
"""Implementation of ":sys cmds". Almost the same as aap_shell()."""
aap_shell(line_nr, recdict, cmds + '\n')
def aap_sysdepend(line_nr, recdict, arg):
"""
Implementation of ":sysdepend {filepat = pattern} shell-command".
"""
rpstack = getrpstack(recdict, line_nr)
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
for k in attrdict.keys():
if k != "filepat" and k != "srcpath":
recipe_error(rpstack, _('Unknown option for :sysdepend: "%s"') % k)
if not attrdict.get("filepat"):
recipe_error(rpstack, _('filepat option missing in :sysdepend'))
cmd = expand(line_nr, recdict, arg[i:], Expand(0, Expand.quote_shell))
from RecPython import redir_system
if attrdict.has_key("srcpath"):
# Use the specified search path.
searchpath = attrdict.get("srcpath")
else:
# Use the value of $INCLUDE, removing "-I".
# Also look in the current directory.
i = get_var_val(line_nr, recdict, "_no", "INCLUDE")
if not i:
searchpath = '.'
else:
searchpath = re.sub('^-I|[ "]-I', " ", i) + " ."
# Also look in the directory of the source file, because most C
# compilers will do this (e.g., compiling "test/foo.c" which contains
# '#include "foo.h"' finds "test/foo.h").
s = get_var_val(line_nr, recdict, "_no", "source")
if s:
s = str2dictlist(rpstack, s)[0]["name"]
d = os.path.dirname(s)
if d:
searchpath = listitem2str(d) + ' ' + searchpath
prev_files = []
while 1:
ok, text = redir_system(cmd)
if ok:
break
# If files are missing try to fetch them and try again.
files = []
for line in string.split(text, '\n'):
try:
m = re.match(attrdict["filepat"], line)
except StandardError, e:
recipe_error(rpstack, _('error in filepat: %s') % str(e))
if m:
try:
f = m.group(1)
except StandardError, e:
recipe_error(rpstack,
_('error using first group from filepat: %s') % str(e))
# Trim white space from the file name.
s = skip_white(f, 0)
e = len(f)
while e > s and is_white(f[e - 1]):
e = e - 1
files.append(f[s:e])
if not files:
recipe_error(rpstack, _("Generating dependencies failed"))
files_str = list2str(files)
if files == prev_files:
if len(files) == 1:
msg_info(recdict, _("Cannot find included file: %s")
% files_str)
else:
msg_info(recdict, _("Cannot find included files: %s")
% files_str)
break
# Attempt fetching and/or updating the missing files.
for afile in files:
if searchpath:
opt = "{searchpath = %s} " % searchpath
else:
opt = ''
aap_update(line_nr, recdict, opt + listitem2str(afile))
prev_files = files
def aap_syspath(line_nr, recdict, arg):
"""Implementation of ":syspath path arg"."""
# Skip when not actually building.
if skip_commands():
msg_skip(line_nr, recdict, ':syspath ' + arg)
return
# Get the arguments into a dictlist
rpstack = getrpstack(recdict, line_nr)
xp = Expand(0, Expand.quote_shell)
# Evaluate the arguments
args = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(0, Expand.quote_aap)))
if len(args) < 2:
recipe_error(rpstack, _(":syspath requires at least two arguments"))
path = args[0]["name"]
path_len = len(path)
i = 0
while i < path_len:
# Isolate one part of the path, until a colon, replacing %s with the
# arguments, %% with % and %: with :.
cmd = ''
had_ps = 0
while i < path_len and path[i] != ':':
if path[i] == '%' and i + 1 < path_len:
i = i + 1
if path[i] == 's':
cmd = cmd + dictlist2str(args[1:], xp)
had_ps = 1
else:
cmd = cmd + path[i]
else:
cmd = cmd + path[i]
i = i + 1
if not had_ps:
cmd = cmd + ' ' + dictlist2str(args[1:], xp)
if recdict.get("async") and os.name in [ "posix", "nt" ]:
# Run the command asynchronously.
# TODO: first check if the command exists.
async_system(rpstack, recdict, cmd)
return
msg_system(recdict, cmd)
if os.system(cmd) == 0:
return
i = i + 1
recipe_error(rpstack, _('No working command found for :syspath "%s"')
% path)
def aap_start(line_nr, recdict, cmds):
"""Implementation of ":start cmd"."""
aap_shell(line_nr, recdict, cmds + '\n', async = 1)
def aap_copy(line_nr, recdict, arg):
"""Implementation of ":copy -x from to"."""
# It's in a separate module, it's quite a bit of stuff.
from CopyMove import copy_move
copy_move(line_nr, recdict, arg, 1)
def aap_move(line_nr, recdict, arg):
"""Implementation of ":move -x from to"."""
# It's in a separate module, it's quite a bit of stuff.
from CopyMove import copy_move
copy_move(line_nr, recdict, arg, 0)
def aap_symlink(line_nr, recdict, arg):
"""Implementation of ":symlink {f}{q} from to"."""
# Skip when not actually building.
if skip_commands():
msg_skip(line_nr, recdict, ':symlink ' + arg)
return
rpstack = getrpstack(recdict, line_nr)
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"f": "force", "force" : "force",
"q": "quiet", "quiet" : "quiet"})
if attrdict:
option_error(rpstack, attrdict, ":symlink")
# Get the remaining arguments, should be at least one.
if len(argdictlist) != 2:
recipe_error(rpstack, _(":symlink command requires two arguments"))
fromarg = argdictlist[0]["name"]
toarg = argdictlist[1]["name"]
if os.path.islink(toarg) or os.path.exists(toarg):
if optiondict.get("force"):
os.unlink(toarg)
else:
msg = _(':symlink: target exists: "%s"') % toarg
if optiondict.get("quiet"):
msg_extra(recdict, msg)
else:
recipe_error(rpstack, msg)
return
if not os.path.exists(fromarg) and not optiondict.get("quiet"):
msg_warning(recdict, _('file linked to does not exist: "%s"')
% fromarg)
try:
os.symlink(fromarg, toarg)
except StandardError, e:
msg = _(':symlink "%s" "%s" failed: %s') % (fromarg, toarg, str(e))
if optiondict.get("quiet"):
msg_extra(recdict, msg)
else:
recipe_error(rpstack, msg)
def aap_delete(line_nr, recdict, arg):
"""Alias for aap_del()."""
aap_del(line_nr, recdict, arg)
def aap_del(line_nr, recdict, arg):
"""Implementation of ":del {r} file1 file2"."""
# Skip when not actually building.
if skip_commands():
msg_skip(line_nr, recdict, ":delete " + arg)
return
rpstack = getrpstack(recdict, line_nr)
work = getwork(recdict)
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"f": "force", "force" : "force",
"q": "quiet", "quiet" : "quiet",
"c" : "continue", "continue" : "continue",
"r": "recursive", "recursive" : "recursive"},
exp_attr = 0)
if attrdict:
option_error(rpstack, attrdict, ":delete")
# Get the remaining arguments, should be at least one.
if not argdictlist:
recipe_error(rpstack, _(":delete command requires a file argument"))
from Remote import url_split3
for a in argdictlist:
fname = a["name"]
scheme, mach, path = url_split3(fname)
if scheme != '':
recipe_error(rpstack, _('Cannot delete remotely yet: "%s"') % fname)
# Expand ~user and wildcards.
fname = os.path.expanduser(fname)
fl = glob.glob(fname)
if len(fl) == 0:
# glob() doesn't include a symbolic link if its destination doesn't
# exist, we want to delete it anyway.
if isalink(fname):
fl = [ fname ]
elif optiondict.get("force"):
# No match, try without expanding.
fl = [ fname ]
elif optiondict.get("continue") and has_wildcard(fname):
# Skip
msg_note(recdict, _("No match for %s") % fname)
continue
else:
recipe_error(rpstack, _('No such file or directory: "%s"')
% fname)
for f in fl:
f_msg = shorten_name(f, work.top_dir)
isdir = os.path.isdir(f)
islink = isalink(f)
try:
if optiondict.get("recursive"):
deltree(f)
else:
os.remove(f)
except EnvironmentError, e:
msg = (_('Could not delete "%s"') % f_msg) + str(e)
if optiondict.get("force"):
msg_note(recdict, msg)
continue
recipe_error(rpstack, msg)
else:
if os.path.exists(f):
msg = _('Could not delete "%s"') % f_msg
if optiondict.get("force"):
msg_note(recdict, msg)
continue
recipe_error(rpstack, msg)
if not optiondict.get("quiet"):
if islink:
msg_info(recdict, _('Deleted link "%s"') % f_msg)
elif isdir:
msg_info(recdict, _('Deleted directory tree "%s"') % f_msg)
else:
msg_info(recdict, _('Deleted "%s"') % f_msg)
def aap_deldir(line_nr, recdict, arg):
"""Implementation of ":deldir dir1 dir2"."""
# Skip when not actually building.
if skip_commands():
msg_skip(line_nr, recdict, ':deldir ' + arg)
return
rpstack = getrpstack(recdict, line_nr)
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"f": "force", "force" : "force",
"q": "quiet", "quiet" : "quiet"})
if attrdict:
option_error(rpstack, attrdict, ":deldir")
# Get the remaining arguments, should be at least one.
if not argdictlist:
recipe_error(rpstack,
_(":deldir command requires a directory argument"))
from Remote import url_split3
# Loop over the arguments
for a in argdictlist:
item = a["name"]
scheme, mach, path = url_split3(item)
if scheme != '':
recipe_error(rpstack, _('Cannot delete remotely yet: "%s"') % item)
# Expand ~user and wildcards.
dirlist = glob.glob(os.path.expanduser(item))
if len(dirlist) == 0 and not optiondict.get("force"):
recipe_error(rpstack, _('No match for "%s"') % item)
# Loop over expanded items.
for adir in dirlist:
if not os.path.exists(adir):
if not optiondict.get("force"):
recipe_error(rpstack, _('"%s" does not exists') % adir)
elif not os.path.isdir(adir):
recipe_error(rpstack, _('"Not a directory: "%s"') % adir)
else:
try:
os.rmdir(adir)
except StandardError, e:
if os.path.exists(adir):
recipe_error(rpstack, (_('Could not delete "%s"') % adir)
+ str(e))
else:
if not optiondict.get("quiet"):
msg_info(recdict, _('Deleted directory "%s"') % adir)
def aap_mkdir(line_nr, recdict, arg):
"""Implementation of ":mkdir dir1 dir2"."""
# Skip when not actually building.
if skip_commands():
msg_skip(line_nr, recdict, ':mkdir ' + arg)
return
rpstack = getrpstack(recdict, line_nr)
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"f": "force", "force" : "force",
"q": "quiet", "quiet" : "quiet",
"r": "recursive", "recursive" : "recursive"})
if attrdict:
option_error(rpstack, attrdict, ":mkdir")
if not argdictlist:
recipe_error(rpstack, _(":mkdir command requires an argument"))
from Remote import url_split3
for a in argdictlist:
name = a["name"]
scheme, mach, path = url_split3(name)
if scheme != '':
recipe_error(rpstack, _('Cannot create remote directory yet: "%s"')
% name)
# Expand ~user, create directory
adir = os.path.expanduser(name)
# Skip creation when it already exists.
if os.path.exists(adir):
if not os.path.isdir(adir):
recipe_error(rpstack, _('"%s" exists but is not a directory')
% adir)
if not optiondict.get("force"):
recipe_error(rpstack, _('"%s" already exists') % adir)
else:
try:
if optiondict.get("recursive"):
if a.get("mode"):
os.makedirs(adir, oct2int(a["mode"]))
else:
os.makedirs(adir)
else:
if a.get("mode"):
os.mkdir(adir, oct2int(a["mode"]))
else:
os.mkdir(adir)
except EnvironmentError, e:
recipe_error(rpstack, (_('Could not create directory "%s"')
% adir) + str(e))
else:
if not optiondict.get("quiet"):
msg_info(recdict, _('Created directory "%s"') % adir)
def aap_changed(line_nr, recdict, arg):
"""Implementation of ":changed File ...".
"line_nr" is used for error messages."""
rpstack = getrpstack(recdict, line_nr)
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"r": "recursive", "recursive" : "recursive"})
if attrdict:
option_error(rpstack, attrdict, ":changed")
if not argdictlist:
recipe_error(rpstack, _(":changed command requires a file argument"))
from Sign import sign_clear_file
for a in argdictlist:
sign_clear_file(a["name"], optiondict.get("recursive"))
def aap_touch(line_nr, recdict, arg):
"""Implementation of ":touch file1 file2"."""
# Skip when not actually building.
if skip_commands():
msg_skip(line_nr, recdict, ':touch ' + arg)
return
rpstack = getrpstack(recdict, line_nr)
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"f": "force", "force" : "force",
"e": "exist", "exist" : "exist", "exists": "exist"})
if attrdict:
option_error(rpstack, attrdict, ":touch")
# Get the remaining arguments, should be at least one.
if not argdictlist:
recipe_error(rpstack, _(":touch command requires a file argument"))
from Remote import url_split3
import time
for a in argdictlist:
name = a["name"]
scheme, mach, path = url_split3(name)
if scheme != '':
recipe_error(rpstack, _('Cannot touch remote file yet: "%s"')
% name)
# Expand ~user, touch file
name = os.path.expanduser(name)
if os.path.exists(name):
if optiondict.get("exist"):
continue
now = time.time()
try:
os.utime(name, (now, now))
except EnvironmentError, e:
recipe_error(rpstack, (_('Could not update time of "%s"')
% name) + str(e))
else:
if not optiondict.get("force") and not optiondict.get("exist"):
recipe_error(rpstack,
_('"%s" does not exist (use :touch {force} to create it)')
% name)
try:
# create an empty file or directory
if a.get("directory"):
if a.get("mode"):
os.makedirs(name, oct2int(a["mode"]))
else:
os.makedirs(name)
else:
if a.get("mode"):
touch_file(name, oct2int(a["mode"]))
else:
touch_file(name, 0644)
except EnvironmentError, e:
recipe_error(rpstack, (_('Could not create "%s"')
% name) + str(e))
def touch_file(name, mode):
"""Unconditionally create empty file "name" with mode "mode"."""
f = os.open(name, os.O_WRONLY + os.O_CREAT + os.O_EXCL, mode)
os.close(f)
def aap_chmod(line_nr, recdict, arg):
"""Implementation of ":chmod mode file1 file2"."""
# Skip when not actually building.
if skip_commands():
msg_skip(line_nr, recdict, ':chmod ' + arg)
return
rpstack = getrpstack(recdict, line_nr)
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"f" : "force", "force" : "force",
"c" : "continue", "continue" : "continue"})
if attrdict:
option_error(rpstack, attrdict, ":chmod")
# Get the remaining arguments, should be at least one.
if len(argdictlist) < 2:
recipe_error(rpstack, _(":chmod command requires two arguments"))
try:
mode = oct2int(argdictlist[0]["name"])
except UserError, e:
recipe_error(rpstack, _("in :chmod command: ") + str(e))
from Remote import url_split3
# Loop over the file name arguments.
for a in argdictlist[1:]:
item = a["name"]
scheme, mach, path = url_split3(item)
if scheme != '':
recipe_error(rpstack, _('Cannot chmod a remote file yet: "%s"')
% item)
# Expand ~user and wildcards.
fname = os.path.expanduser(item)
filelist = glob.glob(fname)
if len(filelist) == 0:
# No matching files.
if optiondict.get("force"):
# Try without expanding
filelist = [ fname ]
elif optiondict.get("continue") and has_wildcard(fname):
# Skip
continue
else:
recipe_error(rpstack, _('No match for "%s"') % item)
# Loop over expanded items.
for fname in filelist:
if not os.path.exists(fname):
if not optiondict.get("force"):
recipe_error(rpstack, _('"%s" does not exists') % fname)
else:
try:
os.chmod(fname, mode)
except StandardError, e:
recipe_error(rpstack, (_('Could not chmod "%s"') % fname)
+ str(e))
# dictionary of recipes that have been fetched (using full path name).
recipe_fetched = {}
def aap_include(line_nr, recdict, arg):
"""Handle ":include filename": read the recipe into the current recdict."""
work = getwork(recdict)
rpstack = getrpstack(recdict, line_nr)
# Evaluate the options and arguments
optiondict, attrdict, args = get_args(line_nr, recdict, arg,
{"q": "quiet", "quiet" : "quiet",
"o": "once", "once" : "once"})
if attrdict:
option_error(rpstack, attrdict, ":include")
if len(args) != 1:
recipe_error(rpstack, _(":include requires one argument"))
args = dictlist_expand(args)
recname = args[0]["name"]
includelist = Global.cmd_args.options.get("include")
if not os.path.isabs(recname) and recname[0] != '.' and includelist:
# Search for the recipe in the list of include directories.
for adir in [ "." ] + includelist:
n = os.path.join(adir, recname)
if os.path.isfile(n):
recname = n
break
if optiondict.get("once") and did_read_recipe(work, recname):
msg_extra(recdict, _('Skipping recipe already read: %s"') % recname)
return
# Fetch the recipe when invoked with the "-R" argument.
if ((Global.cmd_args.has_option("fetch-recipe")
or not os.path.exists(recname))
and args[0].has_key("fetch")):
# Use the original recipe name, without the directory of "-I dir" added.
fetchname = args[0]["name"]
fullname = full_fname(fetchname)
if not recipe_fetched.has_key(fullname):
from VersCont import fetch_nodelist
# Create a node for the recipe and fetch it.
node = work.get_node(fetchname, 0, args[0])
if fetch_nodelist(rpstack, recdict, [ node ], 0):
msg_warning(recdict,
_('Could not update recipe "%s"') % fetchname)
else:
recname = fetchname
# Also mark it as updated when it failed, don't try again.
recipe_fetched[fullname] = 1
read_recipe(rpstack, recdict, recname, Global.at_toplevel,
optional = optiondict.get("quiet"))
def aap_import(line_nr, recdict, arg):
"""
Handle :import commands. Also used automagically for :produce
targets, which specify a new language.
"""
# Boilerplate - find the work object and stack for the current recipe.
work = getwork(recdict)
rpstack = getrpstack(recdict, line_nr)
# Evaluate the options and arguments - there are no options, so complain if
# any are given.
optiondict, attrdict, args = get_args(line_nr, recdict, arg, { } )
if attrdict:
option_error(rpstack, attrdict, ":import")
if optiondict:
option_error(rpstack, optiondict, ":import")
if len(args) != 1:
recipe_error(rpstack, _(":import requires one argument"))
name = args[0]["name"]
# Import only imports a given module once.
if work.module_already_read.has_key(name):
msg_extra(recdict, _('Skipping module already imported: %s"') % name)
return
work.module_already_read[name] = 1
# Check if the module name can be used for a scope name.
from Scope import check_user_scope
scope_name = "m_" + name
assert_scope_name(rpstack, scope_name)
rd = recdict.get(scope_name)
if rd:
from UserDict import UserDict
if isinstance(rd, UserDict):
recipe_error(rpstack, _('module name conflicts existing scope: %s')
% scope_name)
else:
recipe_error(rpstack, _('module name conflicts with variable: %s')
% scope_name)
msg = check_user_scope(recdict, scope_name)
if msg:
recipe_error(rpstack, msg)
# Like a ":child" command, but only passing the _top scope, not the recipe
# tree: Create a new scope.
from Scope import get_build_recdict
new_recdict = get_build_recdict(recdict["_top"], None, recipe_name = name)
# Add the module name as a scope name. Use a RecipeDict to make
# "m_name.var" work in Python commands.
from Scope import add_user_scope,RecipeDict
scope_recdict = RecipeDict()
scope_recdict.data = new_recdict
add_user_scope(new_recdict, scope_name, scope_recdict)
# Use the current Work object.
setwork(new_recdict, work)
dirs = map(lambda x: os.path.join(x, "modules"), get_import_dirs(recdict))
msg_extra(recdict, _('Importing "%s" from %s') % (name, str(dirs)))
done = 0
for adir in dirs:
# Read the imported recipe if it exists in "adir". This will produce an
# error if the recipe exists but cannot be read.
recname = os.path.join(adir, name) + ".aap"
if os.path.exists(recname):
read_recipe(rpstack, new_recdict, recname, 1)
done = 1
break
if not done:
# TODO: download the module (if the user wants this)
recipe_error(rpstack, _('Cannot import "%s"') % name)
# Also read the extra settings from "modules2/".
for d in default_dirs(recdict, homedirfirst = 1):
recname = os.path.join(os.path.join(d, "modules2"), name) + ".aap"
if os.path.exists(recname):
read_recipe(rpstack, new_recdict, recname, 1)
def aap_toolsearch(line_nr, recdict, arg):
"""
:toolsearch tool1 tool2 ...
"""
rpstack = getrpstack(recdict, line_nr)
# Evaluate the options and arguments - there are no options, so complain if
# any are given.
optiondict, attrdict, args = get_args(line_nr, recdict, arg, { } )
if attrdict:
option_error(rpstack, attrdict, ":toolsearch")
if optiondict:
option_error(rpstack, optiondict, ":toolsearch")
# only import "name" from tools directories
tools_path = [ os.path.join(d, "tools")
for d in get_import_dirs(recdict) ]
didone = 0
for a in args:
c = a["name"]
fpd = imp.find_module(c, tools_path)
exec "tools_%s = imp.load_module(c, *fpd)" % c
if eval("tools_%s.exists()" % c):
exec "tools_%s.define_actions()" % c
if not didone:
didone = 1
exec "tools_%s.use_actions(recdict['_top'])" % c
def maydo_recipe_cmd(rpstack):
"""Return non-zero if a ":recipe" command in the current recipe may be
executed."""
# Return right away when not invoked with the "-R" argument.
if not Global.cmd_args.has_option("fetch-recipe"):
return 0
# Skip when this recipe was already updated.
recname = full_fname(rpstack[-1].name)
if recipe_fetched.has_key(recname):
return 0
return 1
def aap_recipe(line_nr, recdict, arg):
"""Handle ":recipe {fetch = name_list}": may download this recipe."""
work = getwork(recdict)
rpstack = getrpstack(recdict, line_nr)
# Return right away when not to be executed.
if not maydo_recipe_cmd(rpstack):
return
# Register the recipe to have been updated. Also when it failed, don't
# want to try again.
recname = full_fname(rpstack[-1].name)
recipe_fetched[recname] = 1
short_name = shorten_name(recname)
msg_info(recdict, _('Updating recipe "%s"') % short_name)
orgdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
if not orgdict.has_key("fetch"):
recipe_error(rpstack, _(":recipe requires a fetch attribute"))
# TODO: warning for trailing characters?
from VersCont import fetch_nodelist
# Create a node for the recipe and fetch it.
node = work.get_node(short_name, 0, orgdict)
if not fetch_nodelist(rpstack, recdict, [ node ], 0):
# TODO: check if the recipe was completely read
# TODO: no need for restart if the recipe didn't change
# Restore the recdict to the values from when starting to read the
# recipe.
start_recdict = recdict["_start_recdict"]
for k in recdict.keys():
if not start_recdict.has_key(k):
del recdict[k]
for k in start_recdict.keys():
recdict[k] = start_recdict[k]
# read the new recipe file
read_recipe(rpstack, recdict, recname, Global.at_toplevel, reread = 1)
# Throw an exception to cancel executing the rest of the script
# generated from the old recipe. This is catched in read_recipe()
raise OriginUpdate
msg_warning(recdict, _('Could not update recipe "%s"') % node.name)
#
# Generic function for getting the arguments of :fetch, :checkout, :commit,
# :checkin, :unlock and :publish
#
def get_verscont_args(line_nr, recdict, arg, cmd):
""""Handle ":cmd {attr = } file ..."."""
rpstack = getrpstack(recdict, line_nr)
# Get the optional attributes that apply to all arguments.
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
# evaluate the arguments into a dictlist
varlist = str2dictlist(rpstack,
expand(line_nr, recdict, arg[i:], Expand(1, Expand.quote_aap)))
if not varlist:
recipe_error(rpstack, _(':%s requires an argument') % cmd)
varlist = dictlist_expand(varlist)
return attrdict, varlist
def do_verscont_cmd(rpstack, recdict, action, attrdict, varlist):
"""Perform "action" on items in "varlist", using attributes in
"attrdict"."""
from VersCont import verscont_nodelist,fetch_nodelist,publish_nodelist
work = getwork(recdict)
# Turn the dictlist into a nodelist.
# Skip files that exist for "fetch" with the "constant" attribute.
nodelist = []
for item in varlist:
node = work.get_node(item["name"], 1, item)
node.set_attributes(attrdict)
if action != "fetch" or node.may_fetch():
nodelist.append(node)
# Perform the action on the nodelist
if nodelist:
if action == "fetch":
failed = fetch_nodelist(rpstack, recdict, nodelist, 0)
elif action == "publish":
failed = publish_nodelist(rpstack, recdict, nodelist, 1)
else:
failed = verscont_nodelist(rpstack, recdict, nodelist, action)
if failed:
recipe_error(rpstack, _('%s failed for "%s"') % (action,
str(map(lambda x: x.short_name(), failed))))
def verscont_cmd(line_nr, recdict, arg, action):
"""Perform "action" for each item "varlist"."""
rpstack = getrpstack(recdict, line_nr)
attrdict, varlist = get_verscont_args(line_nr, recdict, arg, action)
do_verscont_cmd(rpstack, recdict, action, attrdict, varlist)
def aap_fetch(line_nr, recdict, arg):
""""Handle ":fetch {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "fetch")
def aap_checkout(line_nr, recdict, arg):
""""Handle ":checkout {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "checkout")
def aap_commit(line_nr, recdict, arg):
""""Handle ":commit {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "commit")
def aap_checkin(line_nr, recdict, arg):
""""Handle ":checkin {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "checkin")
def aap_unlock(line_nr, recdict, arg):
""""Handle ":unlock {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "unlock")
def aap_publish(line_nr, recdict, arg):
""""Handle ":publish {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "publish")
def aap_add(line_nr, recdict, arg):
""""Handle ":add {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "add")
def aap_remove(line_nr, recdict, arg):
""""Handle ":remove {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "remove")
def aap_tag(line_nr, recdict, arg):
""""Handle ":tag {attr = val} file ..."."""
verscont_cmd(line_nr, recdict, arg, "tag")
def aap_verscont(line_nr, recdict, arg):
""""Handle ":verscont action {attr = val} [file ...]"."""
rpstack = getrpstack(recdict, line_nr)
# evaluate the arguments into a dictlist
varlist = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(1, Expand.quote_aap)))
if not varlist:
recipe_error(rpstack, _(':verscont requires an argument'))
if len(varlist) > 1:
arglist = dictlist_expand(varlist[1:])
else:
arglist = []
do_verscont_cmd(rpstack, recdict, varlist[0]["name"], varlist[0], arglist)
def do_fetch_all(rpstack, recdict, attrdict):
"""Fetch all nodes with a "fetch" or "commit" attribute.
Return non-zero for success."""
work = getwork(recdict)
from VersCont import fetch_nodelist
nodelist = []
for node in work.nodes.values():
# Only need to fetch a node when:
# - it has an "fetch" attribute
# - the node doesn't exist yet
# - it does exist and the "constant" attribute isn't set
if ((node.attributes.has_key("fetch")
or node.attributes.has_key("commit"))
and node.may_fetch()):
node.set_attributes(attrdict)
nodelist.append(node)
if nodelist and fetch_nodelist(rpstack, recdict, nodelist, 0):
ok = 0
else:
ok = 1
return ok
def aap_fetchall(line_nr, recdict, arg):
""""Handle ":fetchall {attr = val}"."""
rpstack = getrpstack(recdict, line_nr)
# Get the optional attributes that apply to all nodes.
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
do_fetch_all(rpstack, recdict, attrdict)
def do_verscont_all(rpstack, recdict, action, attrdict):
"""Do version control action "action" on all nodes with the "commit"
attribute.
Apply items from dictionary 'attrdict" to each node.
Return non-zero for success."""
work = getwork(recdict)
from VersCont import verscont_nodelist
# Loop over all nodes.
nodelist = []
for node in work.nodes.values():
if (node.attributes.has_key("commit")
and (action != "add" or node.attributes.has_key("tag"))):
node.set_attributes(attrdict)
nodelist.append(node)
if nodelist and verscont_nodelist(rpstack, recdict, nodelist, action):
ok = 0
else:
ok = 1
return ok
def aap_commitall(line_nr, recdict, arg):
""""Handle ":commitall {attr = val}"."""
rpstack = getrpstack(recdict, line_nr)
# Get the optional attributes that apply to all nodes.
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
do_verscont_all(rpstack, recdict, "commit", attrdict)
def aap_checkinall(line_nr, recdict, arg):
""""Handle ":checkinall {attr = val}"."""
rpstack = getrpstack(recdict, line_nr)
# Get the optional attributes that apply to all nodes.
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
do_verscont_all(rpstack, recdict, "checkin", attrdict)
def aap_checkoutall(line_nr, recdict, arg):
""""Handle ":checkoutall {attr = val}"."""
rpstack = getrpstack(recdict, line_nr)
# Get the optional attributes that apply to all nodes.
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
do_verscont_all(rpstack, recdict, "checkout", attrdict)
def aap_unlockall(line_nr, recdict, arg):
""""Handle ":unlockall {attr = val}"."""
rpstack = getrpstack(recdict, line_nr)
# Get the optional attributes that apply to all nodes.
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
do_verscont_all(rpstack, recdict, "unlock", attrdict)
def aap_tagall(line_nr, recdict, arg):
""""Handle ":tagall {attr = val}"."""
rpstack = getrpstack(recdict, line_nr)
# Get the optional attributes that apply to all nodes.
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
do_verscont_all(rpstack, recdict, "tag", attrdict)
def do_revise_all(rpstack, recdict, attrdict, local):
res1 = do_verscont_all(rpstack, recdict, "checkin", attrdict)
res2 = do_remove_add(rpstack, recdict, attrdict, local, "remove")
return res1 and res2
def aap_reviseall(line_nr, recdict, arg):
""""Handle ":reviseall {attr = val}"."""
rpstack = getrpstack(recdict, line_nr)
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"l": "local", "local" : "local"})
if argdictlist:
recipe_error(rpstack, _('Too many arguments for :reviseall'))
do_revise_all(rpstack, recdict, attrdict, optiondict.get("local"))
def do_publish_all(rpstack, recdict, attrdict):
"""Publish all noces with a "publish" attribute.
Returns a list of nodes that failed."""
work = getwork(recdict)
from VersCont import publish_nodelist
# Loop over all nodes.
nodelist = []
for node in work.nodes.values():
if node.attributes.has_key("publish"):
node.set_attributes(attrdict)
nodelist.append(node)
if nodelist:
failed = publish_nodelist(rpstack, recdict, nodelist, 0)
else:
msg_extra(recdict, _('nothing to be published'))
failed = []
return failed
def aap_publishall(line_nr, recdict, arg):
""""Handle ":publishall {attr = val}"."""
rpstack = getrpstack(recdict, line_nr)
# Get the optional attributes that apply to all nodes.
attrdict, i = get_attrdict(rpstack, recdict, arg, 0, 1)
failed = do_publish_all(rpstack, recdict, attrdict)
if failed:
recipe_error(rpstack, _('publish failed for "%s"')
% (str(map(lambda x: x.short_name(), failed))))
def do_remove_add(rpstack, recdict, attrdict, local, action):
"""When "action" is "remove": Remove all files from VCS that don't appear
in the recipe or don't have the "commit" attribute.
When "action" is "add": Add files to VCS that appear in the recipe with
the "commit" attribute but don't appear in the VCS.
Returns non-zero for success."""
# Skip when not actually building.
if skip_commands():
msg_info(recdict, _('skip %sall') % action)
return 1
attrdict["name"] = "."
assert_attribute(recdict, attrdict, "commit")
from VersCont import verscont_remove_add
return verscont_remove_add(rpstack, recdict, attrdict, not local, action)
def aap_remove_add(line_nr, recdict, arg, action):
"""Common code for ":removeall" and ":addall"."""
# Skip when not actually building.
if skip_commands():
msg_skip(line_nr, recdict, ':%sall %s' % (action, arg))
return
rpstack = getrpstack(recdict, line_nr)
# Get the arguments and check the options.
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"l": "local", "local" : "local",
"r": "recursive", "recursive" : "recursive"})
from VersCont import verscont_remove_add
if argdictlist:
argdictlist = dictlist_expand(argdictlist)
# Directory name arguments: Do each directory non-recursively
for adir in argdictlist:
for k in attrdict.keys():
adir[k] = attrdict[k]
assert_attribute(recdict, adir, "commit")
verscont_remove_add(rpstack, recdict, adir,
optiondict.get("recursive"), action)
else:
# No arguments: Do current directory recursively
do_remove_add(rpstack, recdict, attrdict,
optiondict.get("local"), action)
def aap_removeall(line_nr, recdict, arg):
""""Handle ":removeall {attr = val} [dir ...]"."""
aap_remove_add(line_nr, recdict, arg, "remove")
def aap_addall(line_nr, recdict, arg):
""""Handle ":addall {attr = val}"."""
aap_remove_add(line_nr, recdict, arg, "add")
def aap_filetype(line_nr, recdict, arg, cmd_line_nr, commands):
"""Add filetype detection from a file or in-line detection rules."""
from Filetype import ft_add_rules,ft_read_file,DetectError
rpstack = getrpstack(recdict, line_nr)
# look through the arguments
args = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(0, Expand.quote_aap)))
if len(args) > 1:
recipe_error(rpstack, _('Too many arguments for :filetype'))
if len(args) == 1 and commands:
recipe_error(rpstack,
_('Cannot have file name and commands for :filetype'))
if len(args) == 0 and not commands:
recipe_error(rpstack,
_('Must have file name or commands for :filetype'))
try:
if commands:
what = "lines"
ft_add_rules(commands, cmd_line_nr, recdict)
else:
fname = args[0]["name"]
what = 'file "%s"' % fname
ft_read_file(fname, recdict)
except DetectError, e:
recipe_error(rpstack, (_('Error in detection %s: ') % what) + str(e))
def aap_action(line_nr, recdict, arg, cmd_line_nr, commands):
"""Add an application for an action-filetype pair."""
from Action import action_add
rpstack = getrpstack(recdict, line_nr)
action_add(rpdeepcopy(rpstack, cmd_line_nr),
recdict,
str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(1, Expand.quote_aap))),
commands)
def aap_usetool(line_nr, recdict, arg):
"""
Set a specific tool to be used in the current scope.
"""
rpstack = getrpstack(recdict, line_nr)
args = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(0, Expand.quote_aap)))
if len(args) != 1:
recipe_error(rpstack, _(':usetool requires one argument'))
toolname = args[0]["name"]
try:
# Only import "tools_name" module from specific directories.
tools_path = [ os.path.join(d, "tools")
for d in get_import_dirs(recdict) ]
fpd = imp.find_module(toolname, tools_path)
exec "tools_%s = imp.load_module(toolname, *fpd)" % toolname
except:
recipe_error(rpstack, _('Tool "%s" is not supported') % toolname)
if eval("tools_%s.exists()" % toolname):
exec "tools_%s.define_actions()" % toolname
exec "tools_%s.use_actions(recdict)" % toolname
else:
recipe_error(rpstack, _('Tool "%s" cannot be found on the system')
% toolname)
def aap_progsearch(line_nr, recdict, arg):
"""Search for programs in $PATH."""
# Get the arguments, first one is the variable name.
rpstack = getrpstack(recdict, line_nr)
args = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(0, Expand.quote_aap)))
if len(args) < 2:
recipe_error(rpstack, _(':progsearch requires at least two arguments'))
assert_var_name(rpstack, args[0]["name"])
# Separate scope and decide which dictionary to use.
rd, scope, varname = get_scope_recdict(rpstack, recdict, args[0]["name"])
from RecPython import program_path
# Search for the programs, quit as soon as one is found.
prog = ''
for arg in args[1:]:
prog = program_path(arg["name"])
if prog:
break
if not prog:
msg_note(recdict, _(':progsearch did not find any of %s')
% map(lambda x: x["name"], args[1:]))
# If the program name includes a space put it in double quotes.
elif " " in prog:
prog = '"%s"' % prog
try:
rd[varname] = prog
except StandardError, e:
recipe_error(rpstack, _(':progsearch assignment error: %s') % str(e))
def aap_do(line_nr, recdict, arg):
"""Execute an action for a type of file: :do action {attr} fname ..."""
rpstack = getrpstack(recdict, line_nr)
args = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(1, Expand.quote_aap)))
if len(args) < 2:
recipe_error(rpstack, _(':do requires at least two arguments'))
args = [args[0]] + dictlist_expand(args[1:])
from Action import action_run
try:
msg = action_run(recdict, args)
finally:
# When "remove" used delete all the arguments.
if args[0].get("remove"):
for arg in args[1:]:
try:
os.remove(arg["name"])
except StandardError, e:
msg_warning(recdict, _('Could not remove "%s": %s')
% (arg["name"], str(e)))
if msg:
recipe_error(rpstack, msg)
def aap_exit(line_nr, recdict, arg):
"""Quit aap."""
aap_quit(line_nr, recdict, arg)
def aap_quit(line_nr, recdict, arg):
"""Quit aap."""
if len(arg) > 0:
raise NormalExit, int(arg)
raise NormalExit
def aap_finish(line_nr, recdict, arg):
"""Quit the current recipe."""
# Throw an exception to cancel executing the rest of the script
# generated from the recipe. This is catched in read_recipe()
raise FinishRecipe
def aap_proxy(line_nr, recdict, arg):
"""Specify a proxy server."""
rpstack = getrpstack(recdict, line_nr)
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg)
if optiondict:
recipe_error(rpstack, _(":proxy command does not accept options"))
if not argdictlist:
recipe_error(rpstack, _(":proxy command requires a file argument"))
if len(argdictlist) == 1:
n = "HTTP"
elif len(argdictlist) == 2:
n = string.upper(argdictlist[0]["name"])
if n != "HTTP" and n != "FTP" and n != "GOPHER":
recipe_error(rpstack, _(':proxy argument must be "http", "ftp" or "gopher"; "%s" is not accepted') % argdictlist[0]["name"])
else:
recipe_error(rpstack, _("Too many arguments for :proxy command"))
n = n + "_PROXY"
os.environ[n] = argdictlist[1]["name"]
def aap_checksum(line_nr, recdict, arg):
"""Compute checksum and compare with value."""
rpstack = getrpstack(recdict, line_nr)
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg)
if not argdictlist:
recipe_error(rpstack, _(":checksum command requires a file argument"))
from Sign import check_md5
# Loop over all items, adding attributes to the node.
for i in argdictlist:
name = i["name"]
if not os.path.exists(name):
msg_note(recdict, _(':checksum: file does not exists: "%s"')
% name)
else:
if not i.get("md5"):
recipe_error(rpstack, _('md5 attribute missing for "%s"')
% name)
md5 = check_md5(recdict, name)
if md5 == "unknown":
recipe_error(rpstack, _('cannot compute md5 checksum for "%s"')
% name)
if md5 != i.get("md5"):
recipe_error(rpstack, _('md5 checksum mismatch for "%s"')
% name)
def aap_mkdownload(line_nr, recdict, arg):
"""Generate a recipe for downloading files."""
rpstack = getrpstack(recdict, line_nr)
work = getwork(recdict)
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg)
if len(argdictlist) < 2:
recipe_error(rpstack,
_(":mkdownload command requires a recipe and file argument"))
rname = os.path.expanduser(argdictlist[0]["name"])
try:
fp = open(rname, "w")
except IOError, e:
msg_error(recdict, (_('Cannot open file for writing "%s": ')
% rname) + str(e))
write_error = _('Cannot write to file "%s": ')
try:
fp.write('# This recipe was generated with ":mkdownload".\n')
fetch = argdictlist[0].get("fetch")
if fetch:
fp.write(":recipe {fetch = %s}\n" % fetch)
fp.write("all fetch:\n")
except IOError, e:
msg_error(recdict, (write_error % rname) + str(e))
from RecPython import get_md5
# Expand wildcards.
xargs = dictlist_expand(argdictlist[1:])
# loop over all file arguments
dirs = []
prev_fetch = ''
for afile in xargs:
fname = afile["name"]
if not os.path.exists(fname):
recipe_error(rpstack,
_(':mkdownload argument does not exist: "%s"') % fname)
fetch = afile.get("fetch")
if not fetch:
node = work.find_node(fname)
if node:
fetch = node.attributes.get("fetch")
if not fetch:
recipe_error(rpstack,
_(':mkdownload argument without fetch attribute: "%s"')
% fname)
try:
adir = os.path.dirname(fname)
if adir and not adir in dirs:
fp.write(" :mkdir {f} %s\n" % listitem2str(adir))
dirs.append(adir)
fp.write(" file = %s\n" % listitem2str(fname))
if fetch != prev_fetch:
fp.write(" fetcha = %s\n" % listitem2str(fetch))
prev_fetch = fetch
fp.write(' @if get_md5(file) != "%s":\n' % get_md5(fname))
fp.write(' :fetch {fetch = $fetcha} $file\n')
except IOError, e:
msg_error(recdict, (write_error % rname) + str(e))
try:
fp.close()
except IOError, e:
msg_error(recdict, (write_error % rname) + str(e))
def aap_cd(line_nr, recdict, arg):
""":cd command"""
aap_chdir(line_nr, recdict, arg, cmd = "cd")
def aap_chdir(line_nr, recdict, arg, cmd = "chdir"):
""":chdir command"""
rpstack = getrpstack(recdict, line_nr)
varlist = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(1, Expand.quote_aap)))
if len(varlist) < 1:
recipe_error(rpstack, _(":%s command requires at least one argument")
% cmd)
dictlist_expanduser(varlist)
# Concatenate all arguments inserting "/" where needed.
adir = varlist[0]["name"]
i = 1
while i < len(varlist):
adir = os.path.join(adir, varlist[i]["name"])
i = i + 1
if adir == '-':
if not recdict["_prevdir"]:
recipe_error(rpstack, _("No previous directory for :%s -") % cmd)
adir = recdict["_prevdir"]
if cmd == "cd" or cmd == "chdir":
recdict["_prevdir"] = os.getcwd()
try:
os.chdir(adir)
except StandardError, e:
recipe_error(rpstack, (_(':%s "%s": ') % (cmd, adir)) + str(e))
msg_changedir(recdict, os.path.abspath(os.getcwd()))
def aap_pushdir(line_nr, recdict, arg):
""":pushdir command"""
recdict["_dirstack"].append(os.getcwd())
aap_chdir(line_nr, recdict, arg, cmd = "pushdir")
def aap_popdir(line_nr, recdict, arg):
""":popdir command"""
rpstack = getrpstack(recdict, line_nr)
if arg:
recipe_error(rpstack, _(':popdir does not take an argument'))
if not recdict["_dirstack"]:
recipe_error(rpstack, _(':popdir: directory stack is empty'))
adir = recdict["_dirstack"].pop()
try:
os.chdir(adir)
except StandardError, e:
recipe_error(rpstack, (_(':popdir to "%s": ') % adir) + str(e))
msg_changedir(recdict, os.path.abspath(adir))
def aap_assertpkg(line_nr, recdict, arg):
"""
:assertpkg command
"""
aap_installpkg(line_nr, recdict, arg, cmdname = "assertpkg")
def aap_installpkg(line_nr, recdict, arg, cmdname = "installpkg"):
"""
:installpkg command
Also used for :assertpkg command.
"""
rpstack = getrpstack(recdict, line_nr)
varlist = str2dictlist(rpstack,
expand(line_nr, recdict, arg, Expand(1, Expand.quote_aap)))
if len(varlist) == 0:
recipe_error(rpstack, _(":%s command requires an argument") % cmdname)
from DoInstall import assert_pkg,install_pkg
for pkg in varlist:
if pkg["name"] == "":
recipe_error(rpstack, _("Empty item in :%s") % cmdname)
if cmdname == "assertpkg":
assert_pkg(rpstack, recdict, pkg["name"],
optional = pkg.get("optional"))
else:
install_pkg(rpstack, recdict, pkg["name"])
def aap_asroot(line_nr, recdict, arg):
""":asroot command"""
# Behave like ":system" on non-Unix systems (there is no super-user)
# or when we can write in "/" (already super-user).
if os.name != "posix" or os.access("/", os.W_OK):
aap_system(line_nr, recdict, arg)
return
rpstack = getrpstack(recdict, line_nr)
cmd = expand(line_nr, recdict, arg, Expand(0, Expand.quote_shell))
if not cmd:
recipe_error(rpstack, _(":asroot command requires an argument"))
recdict['sysresult'] = 1
r = do_as_root(recdict, rpstack, cmd)
if r == 0:
recipe_error(rpstack, _(":asroot command failed"))
if r == 1:
# Success!
recdict['sysresult'] = 0
didwarn = 0
def aap_conf(line_nr, recdict, arg):
"""
:conf testname [args...]
"""
global didwarn
if not didwarn:
msg_warning(recdict, "The :conf command is experimental, it may change in future versions")
didwarn = 1
rpstack = getrpstack(recdict, line_nr)
optiondict, attrdict, argdictlist = get_args(line_nr, recdict, arg,
{"required": "required", "oneof": "oneof"})
if len(argdictlist) < 1:
recipe_error(rpstack,
_(":conf command requires a test name argument"))
if attrdict:
option_error(rpstack, attrdict, ":conf")
# The implementation is quite a bit of code, it's in a separate module.
from DoConf import doconf
doconf(line_nr, recdict, optiondict, argdictlist)
# vim: set sw=4 et sts=4 tw=79 fo+=l:
|