Filewatcher File Search
FTP Search
  
Directory (beta)
  
Content Search (beta)
   
pkg://katana-build-2.0.0-1.noarch.rpm:11897/usr/share/katana/build/stage2.py  info  downloads

import classfile
import copy
import operator
import os
import re
import sys
import zipfile

# Where to look for stuff
OURDIR = os.path.split(os.path.abspath(sys.argv[0]))[0]
LIBDIR = os.path.split(os.path.split(sys.modules["os"].__file__)[0])[0]
JARDIR = "/usr/share/java"
EXTDIR = "/usr/share/java-ext"

# Commands we use
GIJCMD = "gij"
GCJCMD = ("gcj " + os.environ.get("RPM_OPT_FLAGS", "")).rstrip()
NMCMD  = "nm -DC"

# Regular expressions
NM_CLASS_RE = re.compile(r"^[0-9a-fA-F]*\s+D\s+(\S+)::class\$$")

class LibraryList:
    def __init__(self, libs):
        self.libs = []
        self.extend(libs)

    def __getattr__(self, attr):
        return getattr(self.libs, attr)

    def getBuildCommands(self):
        tgtlibs = copy.deepcopy(self.libs)
        alllibs = LibraryList(copy.copy(tgtlibs) + [SystemLibrary()])
        failures = []
        for lib in tgtlibs:
            lib.locate_dependencies(alllibs, failures)
        if failures:
            raise MissingClassesError(tgtlibs, failures)

        # Bomb out if there are dependency loops
        failures = [lib for lib in tgtlibs if lib.depends_on(lib)]
        if failures:
            raise LoopError(failures)

        # This will find a build order such that all dependencies are
        # satisfied assuming there are no loops (which there aren't)
        for i in xrange(len(tgtlibs) / 2):
            j = len(tgtlibs) - i - 1
            for k, l in zip(xrange(i, len(tgtlibs)), xrange(j, i, -1)):
                mn = [(i, l), (k, j)]
                if mn[0] == mn[1]:
                    mn.pop()
                for m, n in mn:
                    if tgtlibs[m].depends_on(tgtlibs[n]):
                        tgtlibs[m:m + 1], tgtlibs[n:n + 1] = \
                            [tgtlibs[n]], [tgtlibs[m]]

        return reduce(operator.add, [lib.build_commands() for lib in tgtlibs])

class Library:
    def classes(self):
        if not hasattr(self, "_classes"):
            self._classes = self.list_classes()
        return self._classes

class JarLibrary(Library):
    def __init__(self, path):
        assert path.endswith(".jar")
        self.path = path

    def list_classes(self):
        return [
            ".".join(item[:-6].split(os.sep))
            for item in zipfile.ZipFile(self.path, "r").namelist()
            if item.endswith(".class")]

class SystemLibrary(JarLibrary):
    """The system class library."""

    def __init__(self):
        pipe = os.popen("%s --version" % GIJCMD, "r")
        version = pipe.read()
        if pipe.close() is not None:
            raise IOError, "%s failed" % GIJCMD
        version = version[version.find("version "):].split(" ")[1]
        JarLibrary.__init__(
            self, os.path.join(JARDIR, "libgcj-%s.jar" % version))

class TargetLibrary(JarLibrary):
    """A library we will be building."""

    def __init__(self, jar):
        JarLibrary.__init__(self, jar)
        name, version = os.path.split(jar[:-4])[1].split("-")
        self.soname = "-".join(("", name.replace(".", "-"), version))

    def __repr__(self):
        return "<TargetLibrary soname=%s>" % self.soname

    def locate_dependencies(self, libs, failures):
        assert not hasattr(self, "deps")
        self.deps = []
        for cls in self.referenced_classes():
            # Hunt in the libraries we already have loaded first
            for lib in libs:
                if lib is self:
                    continue
                if cls not in lib.classes():
                    continue
                if not isinstance(lib, SystemLibrary) and lib not in self.deps:
                    self.deps.append(lib)
                break
            else:
                # Hunt for the solib using libgcj's method
                bits = cls.split(".")
                for i in xrange(len(bits), 0, -1):
                    lib = "-".join(bits[:i])
                    path = LIBDIR
                    if lib in ("org-w3c-dom", "org-xml-sax"):
                        # these are out of the way so they can
                        # co-exist with libgcj's implementations of
                        # the same libraries.
                        path = os.path.join(path, "xml-commons")
                    path = os.path.join(path, "lib-%s.so" % lib)
                    if os.path.exists(path):
                        lib = NativeLibrary(path)
                        libs.append(lib)
                        if cls in lib.classes():
                            self.deps.append(lib)
                            break
                else:
                    # XXX copy the rest from katana-build.py
                    failures.append(cls)

    def referenced_classes(self):
        if not hasattr(self, "_fwddeps"):
            self.__read_deps()
        return [cls for cls in self._revdeps.keys()
                if not self._fwddeps.has_key(cls)]

    def classes_referencing(self, cls):
        return self._revdeps.get(cls, [])

    def depends_on(self, other, chain = ()):
        if self in chain:
            # we're in a loop
            return False
        chain += (self,)
        if other in self.deps:
            return True
        for pkg in self.deps:
            if pkg.depends_on(other, chain):
                return True
        return False

    def build_commands(self):
        args = ["-shared", "-fPIC", self.path]
        classpath = []
        for item in reduce(
            operator.add, [lib.classpath() for lib in self.deps], []):
            if item not in classpath:
                classpath.append(item)
        if classpath:
            args.extend(["-classpath", ":".join(classpath)])
        for lib in self.deps:
            args.extend(lib.lflags())
        args.extend(["-o", "lib%s.so" % self.soname])
        compile_command, hash = [GCJCMD], {}
        for arg in args:
            if not hash.has_key(arg):
                compile_command.append(arg)
                hash[arg] = 1
        compile_command = " ".join(compile_command)
        link_command = "ln -sf lib%s.so lib%s.so" % (
            self.soname, "-".join(self.soname.split("-")[:-1]))
        return compile_command, link_command


    def classpath(self):
        return [self.path] + reduce(
            operator.add, [lib.classpath() for lib in self.deps], [])

    def lflags(self):
        return ["-L.", "-l%s" % self.soname]

    # Dependency resolver

    def __read_deps(self):
        self._fwddeps = {}
        zf = zipfile.ZipFile(self.path, "r")
        for cls in self.classes():
            self._fwddeps[cls] = {}
            cf = classfile.Class(zf.read(cls.replace(".", os.sep) + ".class"))
            for tag, value in cf.constants.values():
                if tag == "Class":
                    value = cf.constants[value][1]
                    if value[0] =="[":
                        self.__add_dep_from_field_descriptor(cls, value[1:])
                    else:
                        self.__add_dep(cls, value)
                elif tag == "NameAndType":
                    value = cf.constants[value[1]][1]
                    if value[0] =="(":
                        self.__add_deps_from_method_descriptor(cls, value)
                    else:
                        self.__add_dep_from_field_descriptor(cls, value)
            for field in cf.fields:
                self.__add_dep_from_field_descriptor(
                    cls, cf.constants[field.descriptor][1])
            for method in cf.methods:
                self.__add_deps_from_method_descriptor(
                    cls, cf.constants[method.descriptor][1])
            self._fwddeps[cls] = self._fwddeps[cls].keys()
        self._revdeps = {}
        for cls, deps in self._fwddeps.items():
            for dep in deps:
                if not self._revdeps.has_key(dep):
                    self._revdeps[dep] = {}
                self._revdeps[dep][cls] = 1
        for cls in self._revdeps.keys():
            self._revdeps[cls] = self._revdeps[cls].keys()

    def __add_dep_from_field_descriptor(self, cls, dsc):
        dsc = self.__add_dep_from_first_field(cls, dsc)
        assert not dsc

    def __add_deps_from_method_descriptor(self, cls, dsc):
        assert dsc[0] =="("
        dsc = dsc[1:]
        assert dsc.find(")") != -1
        while not dsc[0] ==")":
            dsc = self.__add_dep_from_first_field(cls, dsc)
        if dsc != ")V":
            self.__add_dep_from_field_descriptor(cls, dsc[1:])

    def __add_dep_from_first_field(self, cls, dsc):
        type, dsc = dsc[0], dsc[1:]
        if type == "L":
            i = dsc.find(";")
            assert i != -1
            self.__add_dep(cls, dsc[:i])
            return dsc[i + 1:]
        elif type == "[":
            return self.__add_dep_from_first_field(cls, dsc)
        elif type in "BCDFIJSZ":
            return dsc
        raise TypeError, type

    def __add_dep(self, cls, dep):
        dep = dep.replace(os.sep, ".")
        if cls != dep:
            self._fwddeps[cls][dep] = 1

class NativeLibrary(Library):
    def __init__(self, path):
        path = os.path.realpath(path)
        assert path.endswith(".so")
        self.path = path

    def list_classes(self):
        classes = []
        fp = os.popen("%s %s" % (NMCMD, self.path), "r")
        for line in fp.xreadlines():
            m = NM_CLASS_RE.search(line.rstrip())
            if m:
                classes.append(m.group(1).replace("::", "."))
        if fp.close() is not None:
            raise IOError, "%s failed" % NMCMD
        return classes

    def depends_on(self, other, chain = None):
        return False

    def classpath(self):
        paths = []
        for jar in [
            line.rstrip() for line in open(os.path.join(os.path.join(
                os.path.split(OURDIR)[0],
                os.path.split(self.path)[1][:-3] + ".cp")), "r").xreadlines()]:
            paths.append(self.__locate_jar(jar))
        return paths

    def lflags(self):
        return ["-l%s" % os.path.split(self.path)[1][3:-3]]

    def __locate_jar(self, jar):
        class Helper:
            def __init__(self, file):
                self.file, self.path = file, None
            def visit(self, dir, files):
                if self.file in files:
                    self.path = os.path.join(dir, self.file)
                    files[:] = []
        for dir, file in ((JARDIR, jar),
                          (EXTDIR, "-".join(jar.split("-")[:-1]) + ".jar")):
            helper = Helper(file)
            os.path.walk(dir, Helper.visit, helper)
            if helper.path is not None:
                return helper.path
        raise KeyError, "Can't find file: '%s'" % jar
    
class DependencyError:
    pass

class MissingClassesError(DependencyError):
    def __init__(self, libs, classes):
        failures = self.__list_failures(libs, classes)
        offenders = self.__get_initial_offenders(failures)
        while self.__add_offenders(offenders, libs):
            pass

        failures.sort()
        self.msg = "\n  ".join(
            ["the following classes could not be located:"] +
            ["\n    ".join(
                ["%s (required by:" % cls] +
                [dep for dep in deps]) + ")" for cls, deps in failures])

        offenders.sort()
        self.suggest = "\n  ".join(
            ["delete the following classes:"] + ["\n    ".join(offenders)])
            
    def __list_failures(self, libs, classes):
        failures = {}
        for cls in classes:
            deps = failures.get(cls, [])
            for lib in libs:
                for dep in lib.classes_referencing(cls):
                    if dep not in deps:
                        deps.append(dep)
            failures[cls] = deps
        # sort for printing
        for deps in failures.values():
            deps.sort()
        failures = failures.items()
        return failures

    def __get_initial_offenders(self, failures):
        offenders = []
        for cls, deps in failures:
            for dep in deps:
                if dep not in offenders:
                    offenders.append(dep)
        return offenders

    def __add_offenders(self, offenders, libs):
        new_offenders = []
        for cls in offenders:
            for lib in libs:
                for dep in lib.classes_referencing(cls):
                    if dep not in offenders and dep not in new_offenders:
                        new_offenders.append(dep)
        offenders.extend(new_offenders)
        return len(new_offenders) > 0

class LoopError(DependencyError):
    def __init__(self, libs):
        for lib in libs:
            lib.name = ".".join(lib.soname.split("-")[1:-1])

        self.links = []
        self.msg = "\n  ".join(
            ["the following dependency loops were found:"] +
            reduce(operator.add, [self.findloops(lib) for lib in libs]))

        info = []
        for src, tgt in self.links:
            info.append("classes in %s that link to %s:\n" % (
                src.name, tgt.name))
            tgtclasses = {}
            for tgtclass in tgt.classes():
                tgtclasses[tgtclass] = 1
            srcclasses = []
            for srcclass, refclasses in src._fwddeps.items():
                for refclass in refclasses:
                    if tgtclasses.has_key(refclass):
                        srcclasses.append(srcclass)
                        break
            info[-1] += "\n".join(["  %s" % cls for cls in srcclasses])
        self.info = "\n\n".join(info)
        
    def findloops(self, startlib, *args):
        if args:
            lib, prefix, chain, result = args
        else:
            lib, prefix, chain, result = startlib, "", (), []
        result.append(prefix + lib.name)
        if lib not in chain and not (lib == startlib and prefix):
            if len(chain) == 1:
                self.links.append((startlib, lib))
            prefix = prefix and "    " + prefix or " -> "
            chain += (lib,)
            for dep in lib.deps:
                if dep.depends_on(startlib) and dep not in chain[1:]:
                    self.findloops(startlib, dep, prefix, chain, result)
        return result

if __name__ == "__main__":
    try:
        targets = LibraryList(map(TargetLibrary, sys.argv[1:]))
        for command in targets.getBuildCommands():
            print command

    except DependencyError, exception:
        print >>sys.stderr, "katana: error:", exception.msg
        if hasattr(exception, "suggest"):
            print >>sys.stderr, "\nkatana: suggestion:", exception.suggest
        if hasattr(exception, "info"):
            print >>sys.stderr, "\nkatana: information:", exception.info
        sys.exit(1)
Results 1 - 1
Help - FTP Sites List - Software Dir.
Searching half a billion files worldwide
© 1997-2008 IT MARUHN