# Part of the A-A-P recipe executive: Add dependencies for a port 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
#
# This file defines the code used for a port recipe.
#
import os.path
import string
from Action import action_run
from Depend import Depend
from Dictlist import str2dictlist, dictlist2str
from Message import *
from Process import recipe_error
from RecPos import RecPos
from Sign import check_md5
from Util import *
from VersCont import repl_file_name, handle_nodelist
from Work import getwork
import Global
# List of "*depend" target names that "dependcheck" needs to use.
depend_list = []
# Last target that will be used.
last_target = None
# Whether last_target was used, no further dependencies to be checked.
last_target_found = 0
cvs_done_file = "done/cvs-yes" # file created when CVS was used.
cvs_notdone_file = "done/cvs-no" # file created when archives were used.
def check_port_dep(work, name):
n = work.find_node(name)
if n and n.get_dependencies():
recipe_error([],
_('"%s" target already defined while $PORTNAME is present') % name)
def add_port_dep(work, rpstack, item, previtem):
"""Add one of the port dependencies "item".
It will depend on "previtem"."""
#
# Note: all build commands have a hard coded minimal indent of two spaces.
#
# Don't do anything when the "done/<name>" file exists.
if item.done_check and os.path.exists("done/" + item.done_check):
cmds = ''
else:
# Update "pre-name" when it was defined.
n = work.find_node("pre-" + item.name)
if n:
n.set_attributes({"virtual": 1})
cmds = " :update pre-" + item.name + '\n'
else:
cmds = ''
# Update "do-name" when it was defined, use the default commands
# otherwise.
n = work.find_node("do-" + item.name)
if n:
n.set_attributes({"virtual": 1})
cmds = cmds + (' :update do-%s\n' % item.name)
else:
cmds = cmds + (' @port_%s(globals())\n' % item.name)
# Update "post-name" when it was defined.
n = work.find_node("post-" + item.name)
if n:
n.set_attributes({"virtual": 1})
cmds = cmds + " :update post-" + item.name + '\n'
# Create the "done" file, except for "install" and "*depend".
if item.done_create:
cmds = cmds + " :mkdir {force} done\n"
cmds = cmds + " :touch {force} done/" + item.name + '\n'
# For "install" handle the runtime dependencies after installing the
# package, reduces cyclic dependencies. Then run the post-install
# tests.
# TODO: if this fails, uninstall the package?
if item.name == "install":
cmds = cmds + " :update rundepend\n"
cmds = cmds + " :update installtest\n"
#
# Add "*depend" items to the list of dependencies to be satisfied.
# This stops at the last target that will be build.
#
global last_target, last_target_found
if item.name[-6:] == "depend" and not last_target_found:
global depend_list
depend_list.append(item.name)
if item.name == last_target:
last_target_found = 1
targets = [{"name": item.name, "virtual" : 1}]
if previtem:
sources = [{"name": previtem.name}]
else:
sources = []
work.add_dependency(rpstack, Depend(targets, {}, sources, work,
rpstack, cmds, recdict = work.recdict))
class port_step:
def __init__(self, name, done_check, done_create):
self.name = name
self.done_check = done_check # "done" file to check for existence
self.done_create = done_create # flag: create "done" file if success
def add_port_defaults(work):
"""Add the dependencies used for a port recipe."""
# The list of virtual targets to be build for a port.
deplist = [port_step("dependcheck", "", 0),
port_step("fetchdepend", "checksum", 0),
port_step("fetch", "fetch", 1),
port_step("checksum", "checksum", 1),
port_step("extractdepend", "patch", 0),
port_step("extract", "extract", 1),
port_step("patch", "patch", 1),
port_step("builddepend", "build", 0),
port_step("config", "config", 1),
port_step("build", "build", 1),
port_step("testdepend", "test", 0),
port_step("test", "test", 1),
port_step("package", "package", 1),
port_step("install", "", 0)]
# Check if the user didn't accidentally overrule one of the targets.
for item in deplist:
check_port_dep(work, item.name)
check_port_dep(work, "rundepend")
check_port_dep(work, "installtest")
for name in ["PORTVERSION", "PORTCOMMENT", "PORTDESCR"]:
if not get_var_val_int(work.recdict, name):
if work.recdict["_no"].has_key(name):
recipe_error([], _('Empty variable "%s"') % name)
else:
recipe_error([], _('Missing variable "%s"') % name)
msg_depend(work.recdict, _('Adding dependencies for port recipe'))
#
# Add the "all" target used for a port recipe.
# This builds the port but doesn't test or install it.
# Testing would be nice, but requires "rundepend" to be done.
#
rpstack = [RecPos("Default port target")]
work.add_dependency(rpstack, Depend([{"name": "all"}], {},
[{"name" : "build"}], work, rpstack, '', recdict = work.recdict))
#
# Find which target is the last to be done. Allows checking only the
# dependencies that are actually used.
#
global last_target
for item in deplist:
if item.name in Global.cmd_args.targets:
last_target = item.name
#
# Add a dependency for each target. It depends on the previous one.
#
previtem = None
for item in deplist:
add_port_dep(work, rpstack, item, previtem)
previtem = item
# "rundepend" and "installtest" are updated from inside "install", need to
# create its dependency separately.
add_port_dep(work, rpstack, port_step("rundepend", "", 0), None)
add_port_dep(work, rpstack, port_step("installtest", "", 0), None)
#
# Add dependencies that are used individually.
#
indeplist = [port_step("clean", "", 0),
port_step("distclean", "", 0),
port_step("uninstall", "", 0),
port_step("makesum", "", 0),
port_step("srcpackage", "", 0)]
for item in indeplist:
add_port_dep(work, rpstack, item, None)
#
# Set default values for variables.
#
rd = work.recdict
if use_cvs(rd):
# Set $WRKSRC to the value used for CVS.
adir = get_var_val_int(rd, "CVSWRKSRC")
if adir:
msg_extra(rd, _("Using $CVSWRKSRC for $WRKSRC: %s") % adir)
rd["WRKSRC"] = adir
else:
modules = str2dictlist([], get_var_val_int(rd, "CVSMODULES"))
s = modules[0]["name"]
msg_extra(rd, _("Using first CVS module for $WRKSRC: %s") % s)
rd["WRKSRC"] = s
rd["CVSWRKSRC"] = s
else:
if not get_var_val_int(rd, "WRKSRC"):
s = (get_var_val_int(rd, "PORTNAME") + "-"
+ get_var_val_int(rd, "PORTVERSION"))
rd["WRKSRC"] = s
msg_extra(rd, _("Using $PORTNAME for $WRKSRC: %s") % s)
def port_clean(recdict, dist = 0):
"""Implementation of the "clean" target.
Also used for "distclean", "dist" is non-zero then."""
alist = ["done",
get_var_val_int(recdict, "WRKDIR"),
get_var_val_int(recdict, "PKGDIR"),
"pkg-plist", "pkg-comment", "pkg-descr"]
if dist:
alist.extend([get_var_val_int(recdict, "DISTDIR"),
get_var_val_int(recdict, "PATCHDISTDIR"),
get_pkgname(recdict) + ".tgz",
Global.aap_dirname])
for adir in alist:
if os.path.exists(adir):
try:
deltree(adir)
except StandardError, e:
recipe_error([], (_('Cannot delete "%s": ') % adir) + str(e))
def port_distclean(recdict):
"""Implementation of the "distclean" target."""
port_clean(recdict, 1)
def port_uninstall(recdict):
"""Implementation of the "uninstall" target."""
msg_info(recdict, _("TODO: uninstall"))
def port_makesum(recdict):
"""Implementation of the "makesum" target."""
# Get the recipe name.
work = getwork(recdict)
if not work.top_recipe: # cannot happen?
recipe_error([], _("No recipe specified to makesum for"))
#
# Get the total list of files and compute the checksum for each.
#
lines = []
for name, adir in [("DISTFILES", "DISTDIR"), ("PATCHFILES", "PATCHDISTDIR")]:
files = []
# Get the normal list of files and then the CVS list (if defined).
for varname in [name, "CVS" + name]:
f = get_var_val_int(recdict, varname)
if f:
for i in str2dictlist([], f):
n = os.path.basename(i["name"])
if not n in files:
files.append(n)
# Make a recipe line for for each file, containing the md5 checksum.
# TODO: use another kind of checksum when specified.
for f in files:
n = os.path.join(get_var_val_int(recdict, adir), f)
if not os.path.exists(n):
recipe_error([], _('File does not exists: "%s"') % n)
md5sum = check_md5(recdict, n)
if md5sum == "unknown":
recipe_error([], _('Cannot compute checksum for "%s"') % n)
lines.append("\t:checksum $%s/%s {md5 = %s}\n" % (adir, f, md5sum))
#
# Replace or append the lines in/to the recipe
#
startline = '#>>> automatically inserted by "aap makesum" <<<\n'
endline = '#>>> end <<<\n'
# Open the original recipe file.
try:
fr = open(work.top_recipe)
except StandardError, e:
recipe_error([], (_('Cannot open recipe file "%s": ')
% work.top_recipe) + str(e))
# Create a new file for the updated recipe.
i = 1
while 1:
temp = work.top_recipe + str(i)
if not os.path.exists(temp):
break
i = i + 1
try:
fw = open(temp, "w")
except StandardError, e:
recipe_error([], (_('Cannot create temp file "%s": ') % temp) + str(e))
def write_checksum_lines(fw, lines, endl):
fw.write("do-checksum:\n")
if lines:
fw.writelines(lines)
else:
fw.write("\t@pass\n")
fw.write(endl)
#
# Copy original to updated recipe, replacing the makesum block.
#
try:
added = 0
while 1:
line = fr.readline()
if not line:
break
fw.write(line)
if line == startline:
if added:
recipe_error([], _("Duplicate makesum start marker"))
added = 1
write_checksum_lines(fw, lines, endline)
while 1:
line = fr.readline()
if not line:
recipe_error([], _("Missing makesum end marker"))
if line == endline:
break
if not added:
# makesum block not found, add it at the end.
fw.write(startline)
write_checksum_lines(fw, lines, endline)
fr.close()
fw.close()
except (StandardError, UserError), e:
try:
fw.close()
except:
pass
os.remove(temp)
recipe_error([], _('Error while copying recipe file: ') + str(e))
#
# Rename original to backup and update to original recipe.
# Most of the work is giving appropriate message when something goes wrong.
# TODO: preserve protection bits.
#
bak = work.top_recipe + "~"
if os.path.exists(bak):
try:
os.remove(bak)
except StandardError, e:
try_delete(temp)
recipe_error([], (_('Cannot delete backup recipe "%s": ') % bak)
+ str(e))
try:
os.rename(work.top_recipe, bak)
except StandardError, e:
try_delete(temp)
recipe_error([], (_('Cannot rename recipe "%s" to "%s": ')
% (work.top_recipe, bak)) + str(e))
try:
os.rename(temp, work.top_recipe)
except StandardError, e:
recipe_error([], (_('Cannot rename recipe to "%s": ')
% work.top_recipe) + str(e))
# renaming failed, try putting the original recipe back.
try:
os.rename(bak, work.top_recipe)
except StandardError, e:
recipe_error([], (_('Cannot rename recipe! It is now called "%s": ')
% bak) + str(e))
# If the rename worked the remove fails, that's normal.
try_delete(temp)
def port_srcpackage(recdict):
"""Implementation of the "srcpackage" target."""
msg_info(recdict, _("TODO: srcpackage"))
# Cached list of packages.
# TODO: Needs to be flushed if a package is installed!
all_packages = None
def get_installed(recdict):
"""Return a list of all installed packages."""
# TODO: this currently only works for systems with pkg_info.
# TODO: pkg_info is slow, because it obtains descriptions. Can we just get
# the contents of /var/db/pkg?
global all_packages
if all_packages is None:
ok, text = redir_system_int(recdict, "pkg_info -aI", use_tee = 0)
if not ok:
msg_error(recdict, _("Could not obtain list of installed packages"))
else:
# Get the package name from the line "name-9.9 description".
all_packages = []
lines = string.split(text, '\n')
for l in lines:
items = string.split(l, None, 1)
if items:
all_packages.append(items[0])
return all_packages
def depend_matches(plist, pat):
"""Select the items from list "plist" that match pattern "pat"."""
import fnmatch
res = []
for i in plist:
# TODO: don't match "vimxx" when looking for "vim", do match "vim-1.1".
if fnmatch.fnmatchcase(i, pat + "*"):
res.append(i)
return res
def depend_item_match(name, op, pat):
"""Return non-zero if package "name" matches with operation "op" and
pattern "pat"."""
# Find the part of "name" until the version number.
ns = 0
while ns < len(name) - 1:
ns = ns + 1
if name[ns - 1] == '-' and name[ns] in string.digits:
break
ps = 0
while ns < len(name) and ps < len(pat):
# Compare each dot separated part.
# First get the digits.
ne = ns
while ne < len(name) and name[ne] in string.digits:
ne = ne + 1
name_part = name[ns:ne]
pe = ps
while pe < len(pat) and pat[pe] in string.digits:
pe = pe + 1
pat_part = pat[ps:pe]
# Fill with zeros so that 9 is smaller than 10.
while len(name_part) < len(pat_part):
name_part = '0' + name_part
while len(name_part) > len(pat_part):
pat_part = '0' + pat_part
# Add text until a dot.
while ne < len(name) and name[ne] != ".":
name_part = name_part + name[ne]
ne = ne + 1
while pe < len(pat) and pat[pe] != ".":
pat_part = pat_part + pat[pe]
pe = pe + 1
# Compare.
if op[0] == "<":
if name_part > pat_part:
return 0
if name_part < pat_part:
return 1
elif op[0] == ">":
if name_part < pat_part:
return 0
if name_part > pat_part:
return 1
# Advance to the next part, skip over the dot.
if ne < len(name):
ne = ne + 1
ns = ne
if pe < len(pat):
pe = pe + 1
ps = pe
if op == "<" and name[ns:] >= pat[ps:]:
return 0
if op == ">" and name[ns:] <= pat[ps:]:
return 0
return 1
def part_end(name, depends, idx):
"""Find the end of the next part in a depends item, up to the next "<",
"!", etc. Return the index of the following char."""
e = idx
while (e < len(depends)
and not is_white(depends[e])
and not depends[e] in "><!)("):
e = e + 1
if e == idx:
recipe_error([], _("Syntax error in %s") % name)
return e
def part_remove(name, depends, idx, matching, pname):
"""Handle following parts of an item "depends[idx:]".
Remove packages from "matching" according to the parts.
Stop when encountering white space or an unmatched ')'.
Return the index of the next char and the remaining matches."""
while (idx < len(depends)
and not is_white(depends[idx])
and not depends[idx] in '()'):
braces = 0
if depends[idx] == '!':
next_op = '!'
idx = idx + 1
if depends[idx] == '(':
# !(>2.0<3.0): remove matching items
# Call ourselves recursively!
idx, notlist = part_remove(name, depends, idx + 1,
matching[:], pname)
if idx >= len(depends) or depends[idx] != ')':
recipe_error([], _("Missing ) in %s") % name)
idx = idx + 1
braces = 1
elif depends[idx] == '>':
idx = idx + 1
if idx < len(depends) and depends[idx] == '=':
idx = idx + 1
next_op = ">="
else:
next_op = ">"
elif depends[idx] == '<':
idx = idx + 1
if idx < len(depends) and depends[idx] == '=':
idx = idx + 1
next_op = "<="
else:
next_op = "<"
if not braces:
# Isolate the one part: package name and optional version spec.
e = part_end(name, depends, idx)
pat = depends[idx:e]
idx = e
# For "!version" find the list items that are to be excluded.
if next_op == '!':
# Find the part of "pname" until the version number.
i = 0
while i < len(pname) - 1:
if pname[i] == '-' and pname[i + 1] in string.digits + '*[':
break
i = i + 1
patlead = pname[:i + 1]
if patlead[-1] != '-':
patlead = patlead + '-'
notlist = depend_matches(matching, patlead + pat)
# Go over all currently matching items, deleting the ones that
# don't meet the condition.
i = 0
while i < len(matching):
if next_op == '!':
match = not matching[i] in notlist
else:
match = depend_item_match(matching[i], next_op, pat)
if match:
i = i + 1
else:
del matching[i]
return idx, matching
def depend_item(recdict, name, depends, idx):
"""Handle an item in a dependency spec.
Return the index of the following character and a list of missing
items"""
# We start with all possible packages and remove the ones not matching.
# First part: use name as a pattern and use all matches.
e = part_end(name, depends, idx)
pname = depends[idx:e]
matching = depend_matches(get_installed(recdict), pname)
# Following parts: Remove matches according to each part.
idx, matching = part_remove(name, depends, e, matching, pname)
if matching:
missing = []
else:
# TODO: use first version number mentioned.
missing = [pname]
return skip_white(depends, idx), missing
def depend_top(recdict, name, depends, idx):
"""Handle a list of items in depends[idx:] with an "and" or "or" relation.
Stop at the end of "depends" or when encountering an unmatched ")".
Returns the index of the end or ")" and a list of missing packages."""
missing = []
had_op = None
while idx < len(depends) and depends[idx] != ')':
if depends[idx] == '(':
# Call ourselves recursively to handle items in parenthesis.
idx, res = depend_top(recdict, name, depends,
skip_white(depends,idx + 1))
if idx >= len(depends) or depends[idx] != ')':
recipe_error([], _('Missing ")" in %s') % name)
idx = idx + 1
else:
idx, res = depend_item(recdict, name, depends, idx)
# Combine with previous results:
# AND means adding more missing items.
# OR means clearing missing items when there are none found now.
# Otherwise it's the first item, set "missing".
if had_op == "and":
missing.extend(res)
elif had_op == "or":
if not res:
missing = []
else:
missing = res
# Check for a following item:
# "|" means an OR operation
# end of string means it's done
# something else means AND operation
idx = skip_white(depends, idx)
if idx < len(depends) and depends[idx] != ')':
if depends[idx] == '|':
new_op = "or"
idx = skip_white(depends, idx + 1)
if idx >= len(depends):
recipe_error([], _("Trailing '|' in %s") % name)
if depends[idx] == ')':
recipe_error([], _("'|' before ')' in %s") % name)
if depends[idx] == '|':
recipe_error([], _("double '|' in %s") % name)
else:
new_op = "and"
if had_op and had_op != new_op:
recipe_error([], _("Illegal combination of AND and OR in %s")
% name)
had_op = new_op
return idx, missing
def depend_do(recdict, name, check_only):
"""Handle the "name" dependencies. When "check_only" is non-zero only
check if the items can be fulfilled, don't actually install them."""
if get_var_val_int(recdict, "AUTODEPEND") != "no":
varname = "DEPEND_" + string.upper(name)
depends = get_var_val_int(recdict, varname)
if not depends and (name == "run" or name == "build"):
varname = "DEPENDS"
depends = get_var_val_int(recdict, varname)
if not depends:
return
# Figure out which desired modules are not yet installed.
idx, missing = depend_top(recdict, varname,
depends, skip_white(depends, 0))
if idx < len(depends):
recipe_error([], _('Unmatched ")" in %s') % varname)
if not missing:
msg_extra(recdict, _("All %s dependencies satisfied") % name)
elif check_only:
msg_extra(recdict, 'Would check %s dependencies %s now'
% (name, str(missing)))
else:
msg_extra(recdict, 'Would install %s dependencies %s now'
% (name, str(missing)))
def port_dependcheck(recdict):
"""Check if required items are present or can be installed.
If not, exit with an error."""
for n in depend_list:
if ((n != "testdepend"
or get_var_val_int(recdict, "SKIPTEST") != "yes")
and (n != "rundepend"
or get_var_val_int(recdict, "SKIPRUNTIME") != "yes")):
depend_do(recdict, n[:-6], 1)
def use_cvs(recdict):
"""Return non-zero when CVS is to be used to obtain files."""
# When CVS was already used we need to use it again.
if os.path.exists(cvs_done_file):
return 1
# When archives were unpacked we don't use CVS.
if os.path.exists(cvs_notdone_file):
return 0
return (get_var_val_int(recdict, "CVSMODULES")
and get_var_val_int(recdict, "CVS") != "no")
def port_fetchdepend(recdict):
"""Obtain required items for building."""
depend_do(recdict, "fetch", 0)
def port_fetch(recdict):
"""Obtain the files for the port."""
work = getwork(recdict)
if use_cvs(recdict):
# Obtain stuff through CVS.
# TODO: support a list of cvsroots
cwd = os.getcwd()
adir = get_var_val_int(recdict, "WRKDIR")
if adir:
# Do this in the $WRKDIR directory.
assert_dir([], recdict, adir)
goto_dir(recdict, adir)
try:
cvsroot = get_var_val_int(recdict, "CVSROOT")
modules = str2dictlist([],
get_var_val_int(recdict, "CVSMODULES"))
# Get each module from CVS.
for f in modules:
if f.has_key("cvsroot"):
root = f["cvsroot"]
else:
root = cvsroot
n = work.get_node(f["name"], 0, f)
n.set_attributes({"fetch" : "cvs://" + root})
l = handle_nodelist([], recdict, [n], 1, "fetch", ["fetch"])
if l:
recipe_error([], _('CVS checkout of %s failed') % f["name"])
finally:
goto_dir(recdict, cwd)
did_use_cvs = cvs_done_file
dfs = get_var_val_int(recdict, "CVSDISTFILES")
pfs = get_var_val_int(recdict, "CVSPATCHFILES")
else:
did_use_cvs = cvs_notdone_file
dfs = get_var_val_int(recdict, "DISTFILES")
if not dfs:
msg_info(recdict, _("DISTFILES not set, no files fetched"))
pfs = get_var_val_int(recdict, "PATCHFILES")
# Check this first, to avoid getting an error after spending a lot of time
# downloading the distfiles.
if pfs and not get_var_val_int(recdict, "PATCH_SITES"):
recipe_error([], _('Patch files defined but PATCH_SITES not defined'))
# remember which files need to be extracted
if not get_var_val_int(recdict, "EXTRACTFILES"):
recdict["EXTRACTFILES"] = dfs
# Obtain files: archives and patches
for dl, sites, adir in [(str2dictlist([], dfs), "MASTER_SITES", "DISTDIR"),
(str2dictlist([], pfs), "PATCH_SITES", "PATCHDISTDIR")]:
# For each item in $MASTER_SITES or $PATCH_SITES append "/%file%", so
# that it can be used as a fetch attribute.
msl = str2dictlist([], get_var_val_int(recdict, sites))
for i in msl:
i["name"] = os.path.join(i["name"], "%file%")
master_fetch = dictlist2str(msl, Expand(0, Expand.quote_aap))
destdir = get_var_val_int(recdict, adir)
for f in dl:
# Download each file into $DISTDIR or $PATCHDISTDIR.
# Let the "distdir" attribute overrule the default directory.
fn = os.path.basename(f["name"])
if f.has_key("distdir"):
fname = os.path.join(f["distdir"], fn)
else:
fname = os.path.join(destdir, fn)
# Skip fetching if the file already exists.
if os.path.exists(fname):
msg_extra(recdict, _('file already exists: "%s"') % fname)
continue
# Make sure the destination directory exists.
assert_dir([], recdict, os.path.dirname(fname))
# Make the fetch attribute include the full path of the file to
# download.
n = work.get_node(fname, 0, f)
if f.has_key("fetch"):
rf = f["fetch"]
else:
rf = master_fetch
# replace %file% with the file name
rf = repl_file_name(rf, f["name"])
n.set_attributes({"fetch" : rf})
l = handle_nodelist([], recdict, [n], 1, "fetch", ["fetch"])
if l:
recipe_error([], _('Obtaining "%s" failed') % f["name"])
# When checkout from CVS was successful, remember that CVS is to be
# used until "aap distclean" is done.
try:
from Commands import touch_file
aap_checkdir([], recdict, did_use_cvs)
touch_file(did_use_cvs, 0644);
except StandardError, e:
recipe_error([], (_('Cannot create "%s": ') % did_use_cvs) + str(e))
def port_checksum(recdict):
"""Check the checksums of obtained files for the port."""
msg_extra(recdict, 'No do-checksum target defined; checking checksums skipped')
def port_extractdepend(recdict):
"""Obtain required items for building."""
depend_do(recdict, "extract", 0)
def port_extract(recdict):
"""Obtain the files for the port."""
l = get_var_val_int(recdict, "EXTRACT_ONLY")
if l:
pass
elif use_cvs(recdict):
l = get_var_val_int(recdict, "CVSDISTFILES")
else:
l = get_var_val_int(recdict, "DISTFILES")
if not l:
return
archlist = str2dictlist([], l)
# Change the names to be in $DISTDIR and make them absolute (changing
# directory below).
distdir = os.path.abspath(get_var_val_int(recdict, "DISTDIR"))
for x in archlist:
fn = os.path.basename(x["name"])
if x.has_key("distdir"):
x["name"] = os.path.abspath(os.path.join(x["distdir"], fn))
else:
x["name"] = os.path.join(distdir, fn)
# extract each file
wrkdir = os.path.abspath(get_var_val_int(recdict, "WRKDIR"))
cwd = os.getcwd()
try:
for f in archlist:
# change to "extractdir"
# Make path absolute now, before changing directories.
if f.has_key("extractdir"):
adir = os.path.join(wrkdir, f["extractdir"])
else:
adir = wrkdir
assert_dir([], recdict, adir)
goto_dir(recdict, adir)
# Invoke the extract action.
msg = action_run(recdict, [{"name" : "extract"}, f])
if msg:
recipe_error([], msg)
finally:
# return from $WRKDIR
goto_dir(recdict, cwd)
def port_patch(recdict):
"""Apply the patch files."""
if use_cvs(recdict):
l = get_var_val_int(recdict, "CVSPATCHFILES")
else:
l = get_var_val_int(recdict, "PATCHFILES")
if not l:
return
patchlist = str2dictlist([], l)
# Change the names to be in $PATCHDISTDIR and make them absolute (changing
# directory below).
patchdistdir = os.path.abspath(get_var_val_int(recdict, "PATCHDISTDIR"))
for x in patchlist:
fn = os.path.basename(x["name"])
if x.has_key("distdir"):
x["name"] = os.path.abspath(os.path.join(x["distdir"], fn))
else:
x["name"] = os.path.join(patchdistdir, fn)
# Handle each patch file separately, it may have attributes for a
# different patch command or directory.
wrkdir = os.path.abspath(get_var_val_int(recdict, "WRKDIR"))
cwd = os.getcwd()
try:
for fd in patchlist:
# Decide what command to use for patching.
if fd.get("patchcmd"):
cmd = fd["patchcmd"]
else:
cmd = get_var_val_int(recdict, "PATCHCMD")
if not cmd:
cmd = "patch -p -f -s < "
# TODO: shell quoting
i = string.find(cmd, "%s")
if i >= 0:
cmd = cmd[:i] + fd["name"] + cmd[i + 2:]
else:
cmd = cmd + fd["name"]
# Go to the directory for patching.
if fd.get("patchdir"):
adir = fd["patchdir"]
else:
adir = get_var_val_int(recdict, "PATCHDIR")
if not adir:
adir = get_var_val_int(recdict, "WRKSRC")
adir = os.path.join(wrkdir, adir)
goto_dir(recdict, adir)
# Execute the patch command.
n = logged_system(recdict, cmd)
if n:
recipe_error([],
_("Shell returned %d when patching:\n%s") % (n, cmd))
finally:
goto_dir(recdict, cwd)
def port_builddepend(recdict):
"""Obtain required items for building."""
depend_do(recdict, "build", 0)
def port_config(recdict):
"""Configure the port."""
cmd = get_var_val_int(recdict, "CONFIGURECMD")
if cmd:
port_exe_cmd(recdict, cmd, "BUILDDIR")
else:
msg_extra(recdict, 'No CONFIGURECMD specified')
def port_build(recdict):
"""Build the port."""
# Decide what command to use for building.
cmd = get_var_val_int(recdict, "BUILDCMD")
if not cmd:
cmd = "aap"
port_exe_cmd(recdict, cmd, "BUILDDIR")
def port_exe_cmd(recdict, cmd, dirname):
"""Execute "cmd" in directory possibly specified with "dirname"."""
# Go to the build/test directory.
cwd = os.getcwd()
adir = get_var_val_int(recdict, dirname)
if not adir:
adir = get_var_val_int(recdict, "WRKSRC")
adir = os.path.join(get_var_val_int(recdict, "WRKDIR"), adir)
try:
goto_dir(recdict, adir)
except StandardError, e:
recipe_error([], (_('Cannot change to directory "%s": ') % adir)
+ str(e))
try:
# Execute the build command.
# When it's the default, avoid starting another instance of ourselves.
# It would redirect (and echo) the output twice.
if ((len(cmd) == 3 and cmd == "aap")
or (len(cmd) >= 4 and cmd[:4] == "aap ")):
from Commands import aap_execute
from Work import setrpstack, getrpstack
# Don't copy our recdict, it would cause things like "PORTNAME" to
# be defined, which isn't appropriate for the build recipe.
rd = {}
setrpstack(rd, getrpstack(recdict))
rd["_work"] = recdict["_work"]
aap_execute(0, rd, "main.aap" + cmd[3:])
else:
n = logged_system(recdict, cmd)
if n:
recipe_error([],
_("Shell returned %d when executing:\n%s") % (n, cmd))
finally:
goto_dir(recdict, cwd)
def port_testdepend(recdict):
"""Obtain required items for testing the port."""
if get_var_val_int(recdict, "SKIPTEST") != "yes":
depend_do(recdict, "test", 0)
def port_test(recdict):
"""Do the tests."""
# Skip this when not testing
if get_var_val_int(recdict, "SKIPTEST") != "yes":
# Decide what command to use for testing.
cmd = get_var_val_int(recdict, "TESTCMD")
if not cmd:
cmd = "aap test"
# Execute the command in the test directory.
port_exe_cmd(recdict, cmd, "TESTDIR")
def _write_var2file(recdict, varname, fname):
# Skip when not actually building.
if skip_commands():
msg_info(recdict, _('skip writing to "%s"') % fname)
return
try:
f = open(fname, "w")
except IOError, e:
recipe_error([], (_('Cannot open "%s" for writing') % fname) + str(e))
try:
s = get_var_val_int(recdict, varname)
f.write(s)
if s[-1] != '\n':
f.write('\n')
f.close()
except IOError, e:
recipe_error([], (_('Cannot write to "%s"') % fname) + str(e))
def get_pkgname(recdict):
n = (get_var_val_int(recdict, "PORTNAME") + "-"
+ get_var_val_int(recdict, "PORTVERSION"))
rv = get_var_val_int(recdict, "PORTREVISION")
if rv:
n = n + '_' + rv
return n
def port_package(recdict):
"""Turn the port into a package."""
# This only works for "pkg_create" for now.
from RecPython import program_path
if not program_path("pkg_create"):
recipe_error([], _('"pkg_create" command not found'))
# Copy or install the files from "work" to the "pack" directory.
pkgdir = os.path.abspath(get_var_val_int(recdict, "PKGDIR"))
# Make sure the pack directory exists and is empty.
if os.path.exists(pkgdir):
try:
deltree(pkgdir)
except StandardError, e:
recipe_error([], (_('Cannot make "%s" directory empty: ') % pkgdir)
+ str(e))
assert_dir([], recdict, pkgdir)
# TODO
if 0 and get_var_val_int(recdict, "PACKFILES"):
# Copy files mentioned in "PACKFILES" to $PKGDIR.
prefix = ''
else:
# Execute the command in the test directory.
cmd = get_var_val_int(recdict, "INSTALLCMD")
if not cmd:
# TODO: quoting
cmd = "aap install DESTDIR=%s" % pkgdir
port_exe_cmd(recdict, cmd, "INSTALLDIR")
prefix = get_var_val_int(recdict, "PREFIX")
if not prefix:
prefix = "/usr/local"
# Remove a leading slash, join() would do the wrong thing.
if prefix and prefix[0] == '/':
prefix = prefix[1:]
# Write packing list in pkg-plist
filesdir = os.path.join(pkgdir, prefix)
try:
filelist = dir_contents(filesdir, 1, 0)
except StandardError, e:
recipe_error([], (_('Could not list files in "%s": ') % filesdir)
+ str(e))
if skip_commands():
msg_info(recdict, _('skip writing to pkg-plist'))
else:
try:
f = open("pkg-plist", "w")
except StandardError, e:
recipe_error([], _('Could not open pkg-plist for writing: ')
+ str(e))
try:
f.write("@cwd /usr/local\n")
f.write("@srcdir %s\n" % filesdir)
f.writelines(map(lambda x: x + '\n', filelist))
f.close()
except StandardError, e:
recipe_error([], _('Could not write to pkg-plist') + str(e))
# Write description in pkg-descr
_write_var2file(recdict, "PORTDESCR", "pkg-descr")
# Write comment in pkg-comment
_write_var2file(recdict, "PORTCOMMENT", "pkg-comment")
# Create the package
pkgname = get_pkgname(recdict)
cmd = ("pkg_create -f pkg-plist -c pkg-comment -d pkg-descr %s" % pkgname)
n = logged_system(recdict, cmd)
if n:
recipe_error([], _("Shell returned %d when packaging:\n%s") % (n, cmd))
sus_out = None
sus_in = None
sus_err = None
def port_install(recdict):
"""Install the package."""
# Open a shell to install the package.
# TODO: skip this if user is root or installing in user home dir.
open_sushell(recdict)
pkgname = get_pkgname(recdict) + ".tgz"
sus_in.write('I' + pkgname + '\n')
sus_in.flush()
while 1:
m = sus_out.readline()
if not m:
recipe_error([], 'Installing %s aborted' % pkgname)
if m[:10] == "PkgInstall":
msg_extra(recdict, m)
if m[11:13] != "OK":
recipe_error([], 'Installing %s failed' % pkgname)
break
msg_info(recdict, m)
def open_sushell(recdict):
"""Open a connection to a su-shell to install packages under root
permissions."""
global sus_out, sus_in, sus_err
import popenerr
import select
if sus_out is None and sus_in is None:
# Run the shell and get the input and output files.
msg_info(recdict, _("Starting a separate shell to run pkg_add."))
msg_info(recdict, _("Please enter the root password:"))
sus_out, sus_in, sus_err = popenerr.popen3('su root -c %s'
% os.path.join(Global.aap_rootdir, "PkgInstall.py"))
# Loop until logging in has been done.
if sus_err:
fdlist = [sus_out, sus_err]
else:
fdlist = [sus_out]
try:
while 1:
# Read both from stdout and stderr until PkgInstall has been
# started. "su" may write to stderr.
inp, outp, exc = select.select([], fdlist, [])
m = outp[0].readline()
if not m:
recipe_error([], _('Starting super-user shell failed'))
if m[:16] == "PkgInstall ready":
break
msg_info(recdict, m)
except StandardError, e:
recipe_error([], _('Error while installing package: ') + str(e))
if sus_out is None or sus_in is None:
recipe_error([], _('Failed to start a shell to install packages'))
def close_sushell(recdict):
"""Close the su-shell."""
global sus_out, sus_in, sus_err
if sus_in:
try:
sus_in.write('Q\n')
sus_in.flush()
sus_out.close()
sus_out = None
sus_in.close()
sus_in = None
try:
sus_err.close() # see popenerr.py why this is necessary
except:
pass
sus_err = None
except:
msg_info(recdict, 'could not close shell for installing packages')
else:
msg_extra(recdict, 'closed shell for installing packages')
def port_rundepend(recdict):
"""Obtain required items for running the port."""
# TODO: add the code to do the work.
if get_var_val_int(recdict, "SKIPRUNTIME") != "yes":
depend_do(recdict, "run", 0)
def port_installtest(recdict):
"""Post-install test: defaults to nothing."""
msg_extra(recdict, _("Default installtest: do nothing"))
# vim: set sw=4 et sts=4 tw=79 fo+=l:
syntax highlighted by Code2HTML, v. 0.9.1