[Zope-CVS] CVS: Packages/zpkgtools/zpkgtools - package.py:1.1

Fred L. Drake, Jr. fred at zope.com
Mon Mar 15 16:49:09 EST 2004


Update of /cvs-repository/Packages/zpkgtools/zpkgtools
In directory cvs.zope.org:/tmp/cvs-serv16691

Added Files:
	package.py 
Log Message:
add support for yet another group of metadata; this is used to record
information about building the package (extensions, scripts, etc.)


=== Added File Packages/zpkgtools/zpkgtools/package.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Support for handling package configuration files."""

import os
import posixpath
import re
import urllib

from distutils.core import Extension
from StringIO import StringIO

from zpkgtools import cfgparser


PACKAGE_CONF = "package.conf"

# SCHEMA is defined at the end of the module to allow referenced
# functions to be defined first.


def loadPackageInfo(pkgname, directory, reldir, file=None):
    if not file:
        file = PACKAGE_CONF
    path = os.path.join(directory, file)
    if os.path.exists(path):
        path = os.path.realpath(path)
        url = "file://" + urllib.pathname2url(path)
        f = open(path)
    else:
        # Initialize using the cfgparser so we still get a package
        # data object with the right attributes:
        url = "<no file>"
        f = StringIO("")
    try:
        p = cfgparser.Parser(f, url, SCHEMA)
        pkginfo = p.load()
    finally:
        f.close()
    pkginfo.extensions = [create_extension(ext, pkgname, directory, reldir)
                          for ext in pkginfo.extension]
    if reldir:
        pkginfo.documentation = [posixpath.join(reldir, fn)
                                 for fn in pkginfo.documentation]
        pkginfo.script = [posixpath.join(reldir, fn)
                          for fn in pkginfo.script]
    return pkginfo


def cpp_definition(s):
    r"""Return a 2-tuple representing a CPP #define.

    The first element of the tuple is the name to define, and the
    second is the value to use as the replacement text.  In the input,
    the two parts should be separated by an equal sign.

    >>> cpp_definition('NAME=VALUE')
    ('NAME', 'VALUE')
    >>> cpp_definition('NAME=')
    ('NAME', '')

    Whitespace around the equal sign are ignored:

    >>> cpp_definition('NAME   =\tVALUE')
    ('NAME', 'VALUE')

    If there is no equal sign, and defininition with no replacement
    text is used (equivalent to '#define NAME'):

    >>> cpp_definition('NAME')
    ('NAME', None)

    ValueError is raised if there is an error in the input:

    >>> cpp_definition('not-a-cpp-symbol')
    Traceback (most recent call last):
      ...
    ValueError: not a valid C identifier: 'not-a-cpp-symbol'

    """
    if "=" in s:
        name, value = s.split("=", 1)
        name = name.rstrip()
        value = value.lstrip()
    else:
        name = s
        value = None
    if _cpp_ident_match(name) is None:
        raise ValueError("not a valid C identifier: %r" % name)
    return name, value


def cpp_names(s):
    r"""Return a list of CPP symbols from a string.

    >>> cpp_names('NAME')
    ['NAME']
    >>> cpp_names('NAME1 NAME_2 A_B_C A123')
    ['NAME1', 'NAME_2', 'A_B_C', 'A123']

    If something is included which is not a valid identifier for CPP,
    ValueError is raised:

    >>> cpp_names('not-really!')
    Traceback (most recent call last):
      ...
    ValueError: not a valid C identifier: 'not-really!'

    >>> cpp_names('NAME ANOTHER not-really!')
    Traceback (most recent call last):
      ...
    ValueError: not a valid C identifier: 'not-really!'

    """
    names = s.split()
    for name in names:
        if _cpp_ident_match(name) is None:
            raise ValueError("not a valid C identifier: %r" % name)
    return names

_cpp_ident_match = re.compile("[A-Za-z_][A-Za-z_0-9]*$").match


def extension(section):
    section.name = section.getSectionName()
    if not section.name:
        raise ValueError("extensions must be named")
    if not section.source:
        raise ValueError("at least one extension source file must be listed")
    if len(section.language) > 1:
        raise ValueError("language can only be specified once")
    return section


def create_extension(section, pkgname, directory, reldir):
    kwargs = {}
    if pkgname:
        kwargs["name"] = "%s.%s" % (pkgname, section.name)
    else:
        kwargs["name"] = section.name
    kwargs["sources"] = [posixpath.join(reldir, fn)
                         for fn in section.source]
    if section.define:
        kwargs["define_macros"] = section.define
    if section.undefine:
        kwargs["undef_macros"] = undefs = []
        for L in section.undefine:
            undefs.extend(L)
    if section.depends_on:
        kwargs["depends"] = [posixpath.join(reldir, fn)
                             for fn in section.depends_on]
    if section.language:
        kwargs["language"] = section.language[0]
    return Extension(**kwargs)


def path_ref(s):
    """Datatype for a local path reference.

    >>> path_ref('README.txt')
    'README.txt'
    >>> path_ref('./README.txt')
    'README.txt'
    >>> path_ref('foo/bar/file.txt')
    'foo/bar/file.txt'

    If a reference is not a relative path, ValueError is raised:

    >>> path_ref('/absolute/path')
    Traceback (most recent call last):
      ...
    ValueError: absolute paths are not allowed: '/absolute/path'

    >>> path_ref('/')
    Traceback (most recent call last):
      ...
    ValueError: absolute paths are not allowed: '/'

    References which contain Windows drive letters are not allowed:

    >>> path_ref('c:README.txt')
    Traceback (most recent call last):
      ...
    ValueError: Windows drive letters are not allowed: 'c:README.txt'

    If a reference is relative but points outside the local directory
    hierarchy, ValueError is raised:

    >>> path_ref('../somefile')
    Traceback (most recent call last):
      ...
    ValueError: relative paths may not point outside the containing tree: '../somefile'

    """
    if not s:
        raise ValueError("path references may not be empty")
    if s.find(":") == 1:
        # looks like a windows drive letter:
        raise ValueError("Windows drive letters are not allowed: %r" % s)
    p = posixpath.normpath(s)
    if p[:1] == "/":
        raise ValueError("absolute paths are not allowed: %r" % s)
    parts = p.split("/")
    if parts[0] == "..":
        raise ValueError("relative paths may not point outside"
                         " the containing tree: %r" % s)
    return p


SCHEMA = cfgparser.Schema(
    ({"script": path_ref, "documentation": path_ref}, ["extension"], None),
    {"extension": ({"source": path_ref, "depends-on": path_ref,
                    "define" : cpp_definition, "undefine": cpp_names,
                    "language": str,
                    },
                   (), extension),
     })




More information about the Zope-CVS mailing list