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)