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

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