Browse Source

*** empty log message ***

Joe Shochet 24 years ago
parent
commit
7bb41d9131

+ 8 - 7
direct/src/pyinst/Builder.py

@@ -98,11 +98,12 @@ class PYZTarget(Target):
     def __init__(self, cfg, sectnm, cnvrts):
     def __init__(self, cfg, sectnm, cnvrts):
         Target.__init__(self, cfg, sectnm, cnvrts)
         Target.__init__(self, cfg, sectnm, cnvrts)
         # to use a PYZTarget, you'll need imputil and archive 
         # to use a PYZTarget, you'll need imputil and archive 
-        archivebuilder.GetCompiled([os.path.join(pyinsthome, 'support', 'imputil.py')])
-        imputil = resource.makeresource('imputil.py', [os.path.join(pyinsthome, 'support')])
+        archivebuilder.GetCompiled([os.path.join(pyinsthome, 'imputil.py')])
+        print "pyinsthome:", pyinsthome
+        imputil = resource.makeresource('imputil.py', [pyinsthome])
         self._dependencies.append(imputil)
         self._dependencies.append(imputil)
-        archivebuilder.GetCompiled([os.path.join(pyinsthome, 'support', 'archive_rt.py')])
-        archmodule = resource.makeresource('archive_rt.py', [os.path.join(pyinsthome, 'support')]) 
+        archivebuilder.GetCompiled([os.path.join(pyinsthome, 'archive_rt.py')])
+        archmodule = resource.makeresource('archive_rt.py', [pyinsthome]) 
         self._dependencies.merge(archmodule.dependencies())
         self._dependencies.merge(archmodule.dependencies())
         self._dependencies.append(archmodule)
         self._dependencies.append(archmodule)
         self.toc.addFilter(archmodule)
         self.toc.addFilter(archmodule)
@@ -244,8 +245,8 @@ class ArchiveTarget(CollectTarget):
     usefullname = 1
     usefullname = 1
     def __init__(self, cfg, sectnm, cnvrts):
     def __init__(self, cfg, sectnm, cnvrts):
         CollectTarget.__init__(self, cfg, sectnm, cnvrts)  
         CollectTarget.__init__(self, cfg, sectnm, cnvrts)  
-        archivebuilder.GetCompiled([os.path.join(pyinsthome, 'support', 'carchive_rt.py')])
-        carchmodule = resource.makeresource('carchive_rt.py', [os.path.join(pyinsthome, 'support')])
+        archivebuilder.GetCompiled([os.path.join(pyinsthome, 'carchive_rt.py')])
+        carchmodule = resource.makeresource('carchive_rt.py', [pyinsthome])
         self._dependencies.merge(carchmodule.dependencies())
         self._dependencies.merge(carchmodule.dependencies())
         self._dependencies.append(carchmodule)
         self._dependencies.append(carchmodule)
 
 
@@ -456,7 +457,7 @@ def main(opts, args):
     global pyinsthome
     global pyinsthome
     global copyFile
     global copyFile
     pyinsthome = os.path.abspath(os.path.dirname(sys.argv[0]))
     pyinsthome = os.path.abspath(os.path.dirname(sys.argv[0]))
-    sys.path.insert(0, os.path.join(pyinsthome, 'support'))
+    # sys.path.insert(0, os.path.join(pyinsthome, 'support'))
     import installutils
     import installutils
     copyFile = installutils.copyFile
     copyFile = installutils.copyFile
     global logfile
     global logfile

+ 226 - 0
direct/src/pyinst/archive_rt.py

@@ -0,0 +1,226 @@
+#
+# Gordon McMillan (as inspired and influenced by Greg Stein)
+#
+
+# subclasses may not need marshal or struct, but since they're
+# builtin, importing is safe.
+#
+# While an Archive is really an abstraction for any "filesystem
+# within a file", it is tuned for use with imputil.FuncImporter.
+# This assumes it contains python code objects, indexed by the
+# the internal name (ie, no '.py').
+# See carchive.py for a more general archive (contains anything)
+# that can be understood by a C program.
+
+#archive_rt is a stripped down version of MEInc.Dist.archive.
+#It has had all building logic removed.
+#It's purpose is to bootstrap the Python installation.
+
+import marshal
+import struct
+
+class Archive:
+  """ A base class for a repository of python code objects.
+      The extract method is used by imputil.ArchiveImporter
+      to get code objects by name (fully qualified name), so
+      an enduser "import a.b" would become
+        extract('a.__init__')
+        extract('a.b')
+  """
+  MAGIC = 'PYL\0'
+  HDRLEN = 12        # default is MAGIC followed by python's magic, int pos of toc
+  TOCPOS = 8
+  TRLLEN = 0        # default - no trailer
+  TOCTMPLT = {}     #
+  os = None
+  def __init__(self, path=None, start=0):
+    "Initialize an Archive. If path is omitted, it will be an empty Archive."
+    self.toc = None
+    self.path = path
+    self.start = start
+    import imp
+    self.pymagic = imp.get_magic()
+    if path is not None:
+      self.lib = open(self.path, 'rb')
+      self.checkmagic()
+      self.loadtoc()
+
+  ####### Sub-methods of __init__ - override as needed #############
+  def checkmagic(self):
+    """ Overridable.
+        Check to see if the file object self.lib actually has a file
+        we understand.
+    """
+    self.lib.seek(self.start)	#default - magic is at start of file
+    if self.lib.read(len(self.MAGIC)) != self.MAGIC:
+      raise RuntimeError, "%s is not a valid %s archive file" \
+		% (self.path, self.__class__.__name__)
+    if self.lib.read(len(self.pymagic)) != self.pymagic:
+      raise RuntimeError, "%s has version mismatch to dll" % (self.path)
+
+  def loadtoc(self):
+    """ Overridable.
+        Default: After magic comes an int (4 byte native) giving the
+        position of the TOC within self.lib.
+        Default: The TOC is a marshal-able string.
+    """
+    self.lib.seek(self.start + self.TOCPOS)
+    (offset,) = struct.unpack('=i', self.lib.read(4))
+    self.lib.seek(self.start + offset)
+    self.toc = marshal.load(self.lib)
+
+  ######## This is what is called by FuncImporter #######
+  ## Since an Archive is flat, we ignore parent and modname.
+
+  def get_code(self, parent, modname, fqname):
+    print "parent: ", parent
+    print "modname: ", modname
+    print "fqname: ", fqname
+    return self.extract(fqname) # None if not found, (ispkg, code) otherwise
+    if rslt is None:
+      return None
+    ispkg, code = rslt
+    if ispkg:
+      return ispkg, code, {'__path__' : []}
+    return rslt
+
+  ####### Core method - Override as needed  #########
+  def extract(self, name):
+    """ Get the object corresponding to name, or None.
+        For use with imputil ArchiveImporter, object is a python code object.
+        'name' is the name as specified in an 'import name'.
+        'import a.b' will become:
+        extract('a') (return None because 'a' is not a code object)
+        extract('a.__init__') (return a code object)
+        extract('a.b') (return a code object)
+        Default implementation:
+          self.toc is a dict
+          self.toc[name] is pos
+          self.lib has the code object marshal-ed at pos
+    """
+    ispkg, pos = self.toc.get(name, (0,None))
+    if pos is None:
+      return None
+    self.lib.seek(self.start + pos)
+    return ispkg, marshal.load(self.lib)
+
+  ########################################################################
+  # Informational methods
+
+  def contents(self):
+    """Return a list of the contents
+       Default implementation assumes self.toc is a dict like object.
+       Not required by ArchiveImporter.
+    """
+    return self.toc.keys()
+
+  ########################################################################
+  # Building
+  
+  ####### Top level method - shouldn't need overriding #######
+##  def build(self, path, lTOC):
+##    """Create an archive file of name 'path'.
+##       lTOC is a 'logical TOC' - a list of (name, path, ...)
+##       where name is the internal name, eg 'a'
+##       and path is a file to get the object from, eg './a.pyc'.
+##    """
+##    self.path = path
+##    self.lib = open(path, 'wb')
+##    #reserve space for the header
+##    if self.HDRLEN:
+##      self.lib.write('\0'*self.HDRLEN)
+##
+##    #create an empty toc
+##
+##    if type(self.TOCTMPLT) == type({}):
+##      self.toc = {}
+##    else:       # assume callable  
+##      self.toc = self.TOCTMPLT()
+##
+##    for tocentry in lTOC:
+##      self.add(tocentry)   # the guts of the archive
+##
+##    tocpos = self.lib.tell() 
+##    self.save_toc(tocpos)
+##    if self.TRLLEN:
+##      self.save_trailer(tocpos)
+##    if self.HDRLEN:
+##      self.update_headers(tocpos) 
+##    self.lib.close()
+##
+##
+##  ####### manages keeping the internal TOC and the guts in sync #######
+##  def add(self, entry):
+##    """Override this to influence the mechanics of the Archive.
+##       Assumes entry is a seq beginning with (nm, pth, ...) where
+##       nm is the key by which we'll be asked for the object.
+##       pth is the name of where we find the object. Overrides of
+##       get_obj_from can make use of further elements in entry.
+##    """
+##    if self.os is None:
+##      import os
+##      self.os = os
+##    nm = entry[0]
+##    pth = entry[1]
+##    ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__'
+##    self.toc[nm] = (ispkg, self.lib.tell())
+##    f = open(entry[1], 'rb')
+##    f.seek(8)	#skip magic and timestamp
+##    self.lib.write(f.read())
+##
+##  def save_toc(self, tocpos):
+##    """Default - toc is a dict
+##       Gets marshaled to self.lib
+##    """
+##    marshal.dump(self.toc, self.lib)
+##
+##  def save_trailer(self, tocpos):
+##    """Default - not used"""
+##    pass
+##
+##  def update_headers(self, tocpos):
+##    """Default - MAGIC + Python's magic + tocpos"""
+##    self.lib.seek(self.start)
+##    self.lib.write(self.MAGIC)
+##    self.lib.write(self.pymagic)
+##    self.lib.write(struct.pack('=i', tocpos))
+   
+##############################################################
+#
+# ZlibArchive - an archive with compressed entries
+#
+
+class ZlibArchive(Archive):
+  MAGIC = 'PYZ\0'
+  TOCPOS = 8
+  HDRLEN = 12
+  TRLLEN = 0
+  TOCTMPLT = {}
+  LEVEL = 9
+
+  def __init__(self, path=None, offset=0):
+    Archive.__init__(self, path, offset)
+    # dynamic import so not imported if not needed
+    global zlib
+    import zlib
+   
+  def extract(self, name):
+    (ispkg, pos, lngth) = self.toc.get(name, (0, None, 0))
+    if pos is None:
+      return None
+    self.lib.seek(self.start + pos)
+    return ispkg, marshal.loads(zlib.decompress(self.lib.read(lngth)))
+
+##  def add(self, entry):
+##    if self.os is None:
+##      import os
+##      self.os = os
+##    nm = entry[0]
+##    pth = entry[1]
+##    ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__'
+##    f = open(pth, 'rb')
+##    f.seek(8)	#skip magic and timestamp
+##    obj = zlib.compress(f.read(), self.LEVEL)
+##    self.toc[nm] = (ispkg, self.lib.tell(), len(obj))
+##    self.lib.write(obj)
+## 

+ 157 - 0
direct/src/pyinst/carchive_rt.py

@@ -0,0 +1,157 @@
+# copyright 1999 McMillan Enterprises, Inc.
+# license: use as you please. No warranty.
+#
+# A subclass of Archive that can be understood
+# by a C program. See uplaunch.cpp for unpacking
+# from C.
+
+#carchive_rt is a stripped down version of MEInc.Dist.carchive.
+#It has had all building logic removed.
+#It's purpose is to bootstrap the Python installation.
+
+import archive_rt
+import struct
+import zlib
+import strop
+
+class CTOC:
+  ENTRYSTRUCT = 'iiiibc' #(structlen, dpos, dlen, ulen, flag, typcd) followed by name
+  def __init__(self):
+    self.data = []
+  
+  def frombinary(self, s):
+    entrylen = struct.calcsize(self.ENTRYSTRUCT)
+    p = 0
+    while p<len(s):
+      (slen, dpos, dlen, ulen, flag, typcd) = struct.unpack(self.ENTRYSTRUCT, 
+                                                  s[p:p+entrylen]) 
+      nmlen = slen - entrylen 
+      p = p + entrylen
+      (nm,) = struct.unpack(`nmlen`+'s', s[p:p+nmlen])
+      p = p + nmlen 
+      self.data.append((dpos, dlen, ulen, flag, typcd, nm[:-1]))
+
+##  def tobinary(self):
+##    import string
+##    entrylen = struct.calcsize(self.ENTRYSTRUCT)
+##    rslt = []
+##    for (dpos, dlen, ulen, flag, typcd, nm) in self.data:
+##      nmlen = len(nm) + 1	# add 1 for a '\0'
+##      rslt.append(struct.pack(self.ENTRYSTRUCT+`nmlen`+'s',
+##        nmlen+entrylen, dpos, dlen, ulen, flag, typcd, nm+'\0'))
+##    return string.join(rslt, '')
+##
+##  def add(self, dpos, dlen, ulen, flag, typcd, nm):
+##    self.data.append(dpos, dlen, ulen, flag, typcd, nm)
+
+  def get(self, ndx):
+    return self.data[ndx]
+
+  def __getitem__(self, ndx):
+    return self.data[ndx]
+
+  def find(self, name):
+    for i in range(len(self.data)):
+      if self.data[i][-1] == name:
+        return i
+    return -1
+
+class CArchive(archive_rt.Archive):
+  MAGIC = 'MEI\014\013\012\013\015'
+  HDRLEN = 0
+  TOCTMPLT = CTOC
+  TRLSTRUCT = '8siii'
+  TRLLEN = 20
+  LEVEL = 9
+  def __init__(self, path=None, start=0, len=0):
+    self.len = len
+    archive_rt.Archive.__init__(self, path, start)
+
+  def checkmagic(self):
+    #magic is at EOF; if we're embedded, we need to figure where that is
+    if self.len:
+      self.lib.seek(self.start+self.len, 0)
+    else:
+      self.lib.seek(0, 2)
+    filelen = self.lib.tell()
+    if self.len:
+      self.lib.seek(self.start+self.len-self.TRLLEN, 0)
+    else:
+      self.lib.seek(-self.TRLLEN, 2)
+    (magic, totallen, tocpos, toclen) = struct.unpack(self.TRLSTRUCT, 
+						self.lib.read(self.TRLLEN))
+    if magic != self.MAGIC:
+      raise RuntimeError, "%s is not a valid %s archive file" \
+		% (self.path, self.__class__.__name__)
+    self.pkgstart = filelen - totallen
+    if self.len:
+      if totallen != self.len or self.pkgstart != self.start:
+        raise RuntimeError, "Problem with embedded archive in %s" % self.path
+    self.tocpos, self.toclen = tocpos, toclen
+
+  def loadtoc(self):
+    self.toc = self.TOCTMPLT()
+    self.lib.seek(self.pkgstart+self.tocpos)
+    tocstr = self.lib.read(self.toclen)
+    self.toc.frombinary(tocstr)
+
+  def extract(self, name):
+    if type(name) == type(''):
+      ndx = self.toc.find(name)
+      if ndx == -1:
+        return None
+    else:
+      ndx = name
+    (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx)
+    self.lib.seek(self.pkgstart+dpos)
+    rslt = self.lib.read(dlen)
+    if flag == 1:
+      rslt = zlib.decompress(rslt)
+    if typcd == 'M':
+      return (1, rslt)
+    return (0, rslt)
+
+  def contents(self):
+    rslt = []
+    for (dpos, dlen, ulen, flag, typcd, nm) in self.toc:
+      rslt.append(nm)
+    return rslt
+
+##  def add(self, entry):
+##    (nm, pathnm, flag, typcd) = entry[:4]
+##    if flag == 2:
+##        s = open(pathnm, 'r').read()
+##        s = s + '\0'
+##    else:
+##        s = open(pathnm, 'rb').read()
+##    ulen = len(s)
+##    if flag == 1:
+##      s = zlib.compress(s, self.LEVEL)
+##    dlen = len(s)
+##    where = self.lib.tell()
+##    if typcd == 'm':
+##      if strop.find(pathnm, '.__init__.py') > -1:
+##        typcd = 'M'
+##    self.toc.add(where, dlen, ulen, flag, typcd, nm)
+##    self.lib.write(s)
+##
+##  def save_toc(self, tocpos):
+##    self.tocpos = tocpos
+##    tocstr = self.toc.tobinary()
+##    self.toclen = len(tocstr)
+##    self.lib.write(tocstr)
+##
+##  def save_trailer(self, tocpos):
+##    totallen = tocpos + self.toclen + self.TRLLEN
+##    trl = struct.pack(self.TRLSTRUCT, self.MAGIC, totallen, 
+##                      tocpos, self.toclen)
+##    self.lib.write(trl)
+
+  def openEmbedded(self, name):
+    ndx = self.toc.find(name)
+    if ndx == -1:
+      raise KeyError, "Member '%s' not found in %s" % (name, self.path)
+    (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx)
+    if flag:
+      raise ValueError, "Cannot open compressed archive %s in place"
+    return CArchive(self.path, self.pkgstart+dpos, dlen)

+ 487 - 0
direct/src/pyinst/imputil.py

@@ -0,0 +1,487 @@
+#
+# imputil.py
+#
+# Written by Greg Stein. Public Domain.
+# No Copyright, no Rights Reserved, and no Warranties.
+#
+# Utilities to help out with custom import mechanisms.
+#
+# Additional modifications were contribed by Marc-Andre Lemburg and
+# Gordon McMillan.
+#
+
+__version__ = '0.3'
+
+# note: avoid importing non-builtin modules
+import imp
+import sys
+import strop
+import __builtin__	### why this instead of just using __builtins__ ??
+
+# for the DirectoryImporter
+import struct
+import marshal
+
+class Importer:
+  "Base class for replacing standard import functions."
+
+  def install(self):
+    self.__chain_import = __builtin__.__import__
+    self.__chain_reload = __builtin__.reload
+    __builtin__.__import__ = self._import_hook
+    __builtin__.reload = self._reload_hook
+
+  ######################################################################
+  #
+  # PRIVATE METHODS
+  #
+  def _import_hook(self, name, globals=None, locals=None, fromlist=None):
+    """Python calls this hook to locate and import a module.
+
+    This method attempts to load the (dotted) module name. If it cannot
+    find it, then it delegates the import to the next import hook in the
+    chain (where "next" is defined as the import hook that was in place
+    at the time this Importer instance was installed).
+    """
+
+    # determine the context of this import
+    parent = self._determine_import_context(globals)
+
+    # import the module within the context, or from the default context
+    top, tail = self._import_top_module(parent, name)
+    if top is None:
+      # the module was not found; delegate to the next import hook
+      return self.__chain_import(name, globals, locals, fromlist)
+
+    # the top module may be under the control of a different importer.
+    # if so, then defer to that importer for completion of the import.
+    # note it may be self, or is undefined so we (self) may as well
+    # finish the import.
+    importer = top.__dict__.get('__importer__', self)
+    return importer._finish_import(top, tail, fromlist)
+
+  def _finish_import(self, top, tail, fromlist):
+    # if "a.b.c" was provided, then load the ".b.c" portion down from
+    # below the top-level module.
+    bottom = self._load_tail(top, tail)
+
+    # if the form is "import a.b.c", then return "a"
+    if not fromlist:
+      # no fromlist: return the top of the import tree
+      return top
+
+    # the top module was imported by self, or it was not imported through
+    # the Importer mechanism and self is simply handling the import of
+    # the sub-modules and fromlist.
+    #
+    # this means that the bottom module was also imported by self, or we
+    # are handling things in the absence of a prior Importer
+    #
+    # ### why the heck are we handling it? what is the example scenario
+    # ### where this happens? note that we can't determine is_package()
+    # ### for non-Importer modules.
+    #
+    # since we imported/handled the bottom module, this means that we can
+    # also handle its fromlist (and reliably determine is_package()).
+
+    # if the bottom node is a package, then (potentially) import some modules.
+    #
+    # note: if it is not a package, then "fromlist" refers to names in
+    #       the bottom module rather than modules.
+    # note: for a mix of names and modules in the fromlist, we will
+    #       import all modules and insert those into the namespace of
+    #       the package module. Python will pick up all fromlist names
+    #       from the bottom (package) module; some will be modules that
+    #       we imported and stored in the namespace, others are expected
+    #       to be present already.
+    if self._is_package(bottom.__dict__):
+      self._import_fromlist(bottom, fromlist)
+
+    # if the form is "from a.b import c, d" then return "b"
+    return bottom
+
+  def _reload_hook(self, module):
+    "Python calls this hook to reload a module."
+
+    # reloading of a module may or may not be possible (depending on the
+    # importer), but at least we can validate that it's ours to reload
+    importer = module.__dict__.get('__importer__', None)
+    if importer is not self:
+      return self.__chain_reload(module)
+
+    # okay. it is ours, but we don't know what to do (yet)
+    ### we should blast the module dict and do another get_code(). need to
+    ### flesh this out and add proper docco...
+    raise SystemError, "reload not yet implemented"
+
+  def _determine_import_context(self, globals):
+    """Returns the context in which a module should be imported.
+
+    The context could be a loaded (package) module and the imported module
+    will be looked for within that package. The context could also be None,
+    meaning there is no context -- the module should be looked for as a
+    "top-level" module.
+    """
+
+    if not globals or \
+       globals.get('__importer__', None) is not self:
+      # globals does not refer to one of our modules or packages.
+      # That implies there is no relative import context, and it
+      # should just pick it off the standard path.
+      return None
+
+    # The globals refer to a module or package of ours. It will define
+    # the context of the new import. Get the module/package fqname.
+    parent_fqname = globals['__name__']
+
+    # for a package, return itself (imports refer to pkg contents)
+    if self._is_package(globals):
+      parent = sys.modules[parent_fqname]
+      assert globals is parent.__dict__
+      return parent
+
+    i = strop.rfind(parent_fqname, '.')
+
+    # a module outside of a package has no particular import context
+    if i == -1:
+      return None
+
+    # for a module in a package, return the package (imports refer to siblings)
+    parent_fqname = parent_fqname[:i]
+    parent = sys.modules[parent_fqname]
+    assert parent.__name__ == parent_fqname
+    return parent
+
+  def _import_top_module(self, parent, name):
+    """Locate the top of the import tree (relative or absolute).
+
+    parent defines the context in which the import should occur. See
+    _determine_import_context() for details.
+
+    Returns a tuple (module, tail). module is the loaded (top-level) module,
+    or None if the module is not found. tail is the remaining portion of
+    the dotted name.
+    """
+    i = strop.find(name, '.')
+    if i == -1:
+      head = name
+      tail = ""
+    else:
+      head = name[:i]
+      tail = name[i+1:]
+    if parent:
+      fqname = "%s.%s" % (parent.__name__, head)
+    else:
+      fqname = head
+    module = self._import_one(parent, head, fqname)
+    if module:
+      # the module was relative, or no context existed (the module was
+      # simply found on the path).
+      return module, tail
+    if parent:
+      # we tried relative, now try an absolute import (from the path)
+      module = self._import_one(None, head, head)
+      if module:
+        return module, tail
+
+    # the module wasn't found
+    return None, None
+
+  def _import_one(self, parent, modname, fqname):
+    "Import a single module."
+
+    # has the module already been imported?
+    try:
+      return sys.modules[fqname]
+    except KeyError:
+      pass
+
+    # load the module's code, or fetch the module itself
+    result = self.get_code(parent, modname, fqname)
+    if result is None:
+      return None
+
+    # did get_code() return an actual module? (rather than a code object)
+    is_module = type(result[1]) is type(sys)
+
+    # use the returned module, or create a new one to exec code into
+    if is_module:
+      module = result[1]
+    else:
+      module = imp.new_module(fqname)
+
+    ### record packages a bit differently??
+    module.__importer__ = self
+    module.__ispkg__ = result[0]
+
+    # if present, the third item is a set of values to insert into the module
+    if len(result) > 2:
+      module.__dict__.update(result[2])
+
+    # the module is almost ready... make it visible
+    sys.modules[fqname] = module
+
+    # execute the code within the module's namespace
+    if not is_module:
+      exec result[1] in module.__dict__
+
+    # insert the module into its parent
+    if parent:
+      setattr(parent, modname, module)
+    return module
+
+  def _load_tail(self, m, tail):
+    """Import the rest of the modules, down from the top-level module.
+
+    Returns the last module in the dotted list of modules.
+    """
+    if tail:
+      for part in strop.splitfields(tail, '.'):
+        fqname = "%s.%s" % (m.__name__, part)
+        m = self._import_one(m, part, fqname)
+        if not m:
+          raise ImportError, "No module named " + fqname
+    return m
+
+  def _import_fromlist(self, package, fromlist):
+    'Import any sub-modules in the "from" list.'
+
+    # if '*' is present in the fromlist, then look for the '__all__' variable
+    # to find additional items (modules) to import.
+    if '*' in fromlist:
+      fromlist = list(fromlist) + list(package.__dict__.get('__all__', []))
+
+    for sub in fromlist:
+      # if the name is already present, then don't try to import it (it
+      # might not be a module!).
+      if sub != '*' and not hasattr(package, sub):
+        subname = "%s.%s" % (package.__name__, sub)
+        submod = self._import_one(package, sub, subname)
+        if not submod:
+          raise ImportError, "cannot import name " + subname
+
+  def _is_package(self, module_dict):
+    """Determine if a given module (dictionary) specifies a package.
+
+    The package status is in the module-level name __ispkg__. The module
+    must also have been imported by self, so that we can reliably apply
+    semantic meaning to __ispkg__.
+
+    ### weaken the test to issubclass(Importer)?
+    """
+    return module_dict.get('__importer__', None) is self and \
+           module_dict['__ispkg__']
+
+  ######################################################################
+  #
+  # METHODS TO OVERRIDE
+  #
+  def get_code(self, parent, modname, fqname):
+    """Find and retrieve the code for the given module.
+
+    parent specifies a parent module to define a context for importing. It
+    may be None, indicating no particular context for the search.
+
+    modname specifies a single module (not dotted) within the parent.
+
+    fqname specifies the fully-qualified module name. This is a (potentially)
+    dotted name from the "root" of the module namespace down to the modname.
+    If there is no parent, then modname==fqname.
+
+    This method should return None, a 2-tuple, or a 3-tuple.
+
+    * If the module was not found, then None should be returned.
+
+    * The first item of the 2- or 3-tuple should be the integer 0 or 1,
+      specifying whether the module that was found is a package or not.
+
+    * The second item is the code object for the module (it will be
+      executed within the new module's namespace). This item can also
+      be a fully-loaded module object (e.g. loaded from a shared lib).
+
+    * If present, the third item is a dictionary of name/value pairs that
+      will be inserted into new module before the code object is executed.
+      This provided in case the module's code expects certain values (such
+      as where the module was found). When the second item is a module
+      object, then these names/values will be inserted *after* the module
+      has been loaded/initialized.
+    """
+    raise RuntimeError, "get_code not implemented"
+
+
+######################################################################
+#
+# Simple function-based importer
+#
+class FuncImporter(Importer):
+  "Importer subclass to use a supplied function rather than method overrides."
+  def __init__(self, func):
+    self.func = func
+  def get_code(self, parent, modname, fqname):
+    return self.func(parent, modname, fqname)
+
+def install_with(func):
+  FuncImporter(func).install()
+
+
+######################################################################
+#
+# Base class for archive-based importing
+#
+class PackageArchiveImporter(Importer):
+  "Importer subclass to import from (file) archives."
+
+  def get_code(self, parent, modname, fqname):
+    if parent:
+      # if a parent "package" is provided, then we are importing a sub-file
+      # from the archive.
+      result = self.get_subfile(parent.__archive__, modname)
+      if result is None:
+        return None
+      if type(result) == type(()):
+        return (0,) + result
+      return 0, result
+
+    # no parent was provided, so the archive should exist somewhere on the
+    # default "path".
+    archive = self.get_archive(modname)
+    if archive is None:
+      return None
+    return 1, "", {'__archive__':archive}
+
+  def get_archive(self, modname):
+    """Get an archive of modules.
+
+    This method should locate an archive and return a value which can be
+    used by get_subfile to load modules from it. The value may be a simple
+    pathname, an open file, or a complex object that caches information
+    for future imports.
+
+    Return None if the archive was not found.
+    """
+    raise RuntimeError, "get_archive not implemented"
+
+  def get_subfile(self, archive, modname):
+    """Get code from a subfile in the specified archive.
+
+    Given the specified archive (as returned by get_archive()), locate
+    and return a code object for the specified module name.
+
+    A 2-tuple may be returned, consisting of a code object and a dict
+    of name/values to place into the target module.
+
+    Return None if the subfile was not found.
+    """
+    raise RuntimeError, "get_subfile not implemented"
+
+
+class PackageArchive(PackageArchiveImporter):
+  "PackageArchiveImporter subclass that refers to a specific archive."
+
+  def __init__(self, modname, archive_pathname):
+    self.__modname = modname
+    self.__path = archive_pathname
+
+  def get_archive(self, modname):
+    if modname == self.__modname:
+      return self.__path
+    return None
+
+  # get_subfile is passed the full pathname of the archive
+
+
+######################################################################
+#
+# Emulate the standard directory-based import mechanism
+#
+
+class DirectoryImporter(Importer):
+  "Importer subclass to emulate the standard importer."
+
+  def __init__(self, dir):
+    self.dir = dir
+    self.ext_char = __debug__ and 'c' or 'o'
+    self.ext = '.py' + self.ext_char
+
+  def get_code(self, parent, modname, fqname):
+    if parent:
+      dir = parent.__pkgdir__
+    else:
+      dir = self.dir
+
+    # pull the os module from our instance data. we don't do this at the
+    # top-level, because it isn't a builtin module (and we want to defer
+    # loading non-builtins until as late as possible).
+    try:
+      os = self.os
+    except AttributeError:
+      import os
+      self.os = os
+
+    pathname = os.path.join(dir, modname)
+    if os.path.isdir(pathname):
+      values = { '__pkgdir__' : pathname }
+      ispkg = 1
+      pathname = os.path.join(pathname, '__init__')
+    else:
+      values = { }
+      ispkg = 0
+
+    t_py = self._timestamp(pathname + '.py')
+    t_pyc = self._timestamp(pathname + self.ext)
+    if t_py is None and t_pyc is None:
+      return None
+    code = None
+    if t_py is None or (t_pyc is not None and t_pyc >= t_py):
+      f = open(pathname + self.ext, 'rb')
+      if f.read(4) == imp.get_magic():
+        t = struct.unpack('<I', f.read(4))[0]
+        if t == t_py:
+          code = marshal.load(f)
+      f.close()
+    if code is None:
+      code = self._compile(pathname + '.py', t_py)
+    return ispkg, code, values
+
+  def _timestamp(self, pathname):
+    try:
+      s = self.os.stat(pathname)
+    except OSError:
+      return None
+    return long(s[8])
+
+  def _compile(self, pathname, timestamp):
+    codestring = open(pathname, 'r').read()
+    if codestring and codestring[-1] != '\n':
+      codestring = codestring + '\n'
+    code = __builtin__.compile(codestring, pathname, 'exec')
+
+    # try to cache the compiled code
+    try:
+      f = open(pathname + self.ext_char, 'wb')
+      f.write('\0\0\0\0')
+      f.write(struct.pack('<I', timestamp))
+      marshal.dump(code, f)
+      f.flush()
+      f.seek(0, 0)
+      f.write(imp.get_magic())
+      f.close()
+    except OSError:
+      pass
+
+    return code
+
+  def __repr__(self):
+    return '<%s.%s for "%s" at 0x%x>' % (self.__class__.__module__,
+                                         self.__class__.__name__,
+                                         self.dir,
+                                         id(self))
+
+def _test_dir():
+  "Debug/test function to create DirectoryImporters from sys.path."
+  path = sys.path[:]
+  path.reverse()
+  for d in path:
+    DirectoryImporter(d).install()
+
+######################################################################

+ 91 - 0
direct/src/pyinst/installutils.py

@@ -0,0 +1,91 @@
+# copyright 1999 McMillan Enterprises, Inc.
+# demo code - use as you please.
+import os
+import stat
+
+def copyFile(srcFiles, destFile, append=0):
+    '''
+    Copy one or more files to another file.  If srcFiles is a list, then all
+    will be concatenated together to destFile.  The append flag is also valid
+    for single file copies.
+
+    destFile will have the mode, ownership and timestamp of the last file
+    copied/appended.
+    '''
+    if type(srcFiles) == type([]):
+        # in case we need to overwrite on the first file...
+        copyFile(srcFiles[0], destFile, append)
+        for file in srcFiles[1:]:
+            copyFile(file, destFile, 1)
+        return
+
+    mode = 'wb'
+    if append:
+        mode = 'ab'
+    print " ", srcFiles, "->",
+    input = open(srcFiles, 'rb')
+    if input:
+        print destFile
+        output = open(destFile, mode)
+        while 1:
+            bytesRead = input.read(8192)
+            if bytesRead:
+                output.write(bytesRead)
+            else:
+                break
+
+        input.close()
+        output.close()
+
+        stats = os.stat(srcFiles)
+        os.chmod(destFile, stats[stat.ST_MODE])
+        try:        # FAT16 file systems have only one file time
+            os.utime(destFile, (stats[stat.ST_ATIME], stats[stat.ST_MTIME]))
+        except:
+            pass
+        try:        
+            os.chown(destFile, stats[stat.ST_UID], stats[stat.ST_GID])
+        except:
+            pass
+
+def ensure(dirct):
+    dirnm = dirct
+    plist = []
+    try:
+        while not os.path.exists(dirnm):
+            dirnm, base = os.path.split(dirnm)
+            if base == '':
+                break
+            plist.insert(0, base)
+        for d in plist:
+            dirnm = os.path.join(dirnm, d)
+            os.mkdir(dirnm)
+    except:
+        return 0
+    return 1
+
+def getinstalldir(prompt="Enter an installation directory: "):
+    while 1:
+        installdir = raw_input("Enter an installation directory: ")
+        installdir = os.path.normpath(installdir)
+        if ensure(installdir):
+            break
+        else:
+            print installdir, "is not a valid pathname"
+            r = raw_input("Try again (y/n)?: ")
+            if r in 'nN':
+                sys.exit(0)
+    return installdir
+
+def installCArchive(nm, basedir, suffixdir):
+    import carchive_rt
+    fulldir = os.path.join(basedir, suffixdir)
+    if ensure(fulldir):
+        pkg = carchive_rt.CArchive(nm)
+        for fnm in pkg.contents():
+            stuff = pkg.extract(fnm)[1]
+            outnm = os.path.join(fulldir, fnm)
+            if ensure(os.path.dirname(outnm)):
+                open(outnm, 'wb').write(stuff)
+        pkg = None
+        os.remove(nm)

+ 85 - 85
direct/src/pyinst/ltoc.py

@@ -1,85 +1,85 @@
-import os, sys, UserList
-from MEInc.Dist import finder, tocfilter, resource
-
-class lTOC(UserList.UserList):
-    """ A class for managing lists of resources.
-        Should be a UserList subclass. Doh. 
-        Like a list, but has merge(other) and filter() methods """
-    def __init__(self, reslist=None, filters=None):
-        UserList.UserList.__init__(self, reslist)
-        self.filters = []
-        if filters is not None:
-            self.filters = filters[:]
-    def prepend(self, res):
-        self.resources.insert(0, res)
-    def merge(self, other):
-        ' merge in another ltoc, discarding dups and preserving order '
-        tmp = {}
-        for res in self.data:
-            tmp[res.name] = 0
-        for res in other:
-            if tmp.get(res.name, 1):
-                self.data.append(res)
-                tmp[res.name] = 0
-    def filter(self):
-        ' invoke all filters '
-        for i in range(len(self.data)):
-            res = self.data[i]
-            if res:
-                for f in self.filters:
-                    if f.matches(res):
-                        self.data[i] = None
-                        break
-        self.data = filter(None, self.data)
-        return self
-    def unique(self):
-        ' remove all duplicate entries, preserving order '
-        new = self.__class__()
-        new.merge(self)
-        self.data = new.data
-    def toList(self):
-        ' return self as a list of (name, path, typ) '
-        tmp = []
-        for res in self.data:
-            tmp.append((res.name, res.path, res.typ))
-        return tmp
-    def addFilter(self, filter):
-        if type(filter) == type(''):
-            self.filters.append(finder.makeresource(filter).asFilter())
-        else:
-            if type(filter) == type(self):
-                if isinstance(filter, tocfilter._Filter):
-                    self.filters.append(filter)
-                elif isinstance(filter, resource.resource):
-                    self.filters.append(filter.asFilter())
-                else:
-                    raise ValueError, "can't make filter from %s", repr(filter)
-            else:
-                raise ValueError, "can't make filter from %s", repr(filter)
-        print " added filter", `self.filters[-1]`             
-            
-   
-if __name__ == '__main__':
-    sys.path.insert(0, '.')
-    import finder
-    import pprint
-    s = finder.scriptresource('finder.py', './finder.py')
-    ##    pyltoc = lTOC(s.modules)
-    ##    l1 = pyltoc.toList()
-    ##    print "Raw py ltoc:", pprint.pprint(l1)
-    ##    f1 = ModFilter(['dospath', 'macpath', 'posixpath'])
-    ##    l2 = lTOC(s.modules).filter(f1).toList()
-    ##    print "Filter out dospath, macpath, posixpath:", pprint.pprint(l2)
-    ##    f2 = DirFilter(['.'])
-    ##    l3 = lTOC(s.modules).filter(f2).toList()
-    ##    print "Filter out current dir:", pprint.pprint(l3)
-    ##    f3 = StdLibFilter()
-    ##    l4 = lTOC(s.modules).filter(f3).toList()
-    ##    print "Filter out stdlib:", pprint.pprint(l4)
-    ##    #print "Filter out current dir and stdlib:", lTOC(s.modules).filter(f2, f3).toList()
-    binltoc = lTOC(s.binaries)
-    print "Raw bin ltoc:", pprint.pprint(binltoc.toList())
-    binltoc.addFilter('c:/winnt/system32')
-    pprint.pprint(binltoc.filter().toList())
-    
-    
+import os, sys, UserList
+import finder, tocfilter, resource
+
+class lTOC(UserList.UserList):
+    """ A class for managing lists of resources.
+        Should be a UserList subclass. Doh. 
+        Like a list, but has merge(other) and filter() methods """
+    def __init__(self, reslist=None, filters=None):
+        UserList.UserList.__init__(self, reslist)
+        self.filters = []
+        if filters is not None:
+            self.filters = filters[:]
+    def prepend(self, res):
+        self.resources.insert(0, res)
+    def merge(self, other):
+        ' merge in another ltoc, discarding dups and preserving order '
+        tmp = {}
+        for res in self.data:
+            tmp[res.name] = 0
+        for res in other:
+            if tmp.get(res.name, 1):
+                self.data.append(res)
+                tmp[res.name] = 0
+    def filter(self):
+        ' invoke all filters '
+        for i in range(len(self.data)):
+            res = self.data[i]
+            if res:
+                for f in self.filters:
+                    if f.matches(res):
+                        self.data[i] = None
+                        break
+        self.data = filter(None, self.data)
+        return self
+    def unique(self):
+        ' remove all duplicate entries, preserving order '
+        new = self.__class__()
+        new.merge(self)
+        self.data = new.data
+    def toList(self):
+        ' return self as a list of (name, path, typ) '
+        tmp = []
+        for res in self.data:
+            tmp.append((res.name, res.path, res.typ))
+        return tmp
+    def addFilter(self, filter):
+        if type(filter) == type(''):
+            self.filters.append(finder.makeresource(filter).asFilter())
+        else:
+            if type(filter) == type(self):
+                if isinstance(filter, tocfilter._Filter):
+                    self.filters.append(filter)
+                elif isinstance(filter, resource.resource):
+                    self.filters.append(filter.asFilter())
+                else:
+                    raise ValueError, "can't make filter from %s", repr(filter)
+            else:
+                raise ValueError, "can't make filter from %s", repr(filter)
+        print " added filter", `self.filters[-1]`             
+            
+   
+if __name__ == '__main__':
+    sys.path.insert(0, '.')
+    import finder
+    import pprint
+    s = finder.scriptresource('finder.py', './finder.py')
+    ##    pyltoc = lTOC(s.modules)
+    ##    l1 = pyltoc.toList()
+    ##    print "Raw py ltoc:", pprint.pprint(l1)
+    ##    f1 = ModFilter(['dospath', 'macpath', 'posixpath'])
+    ##    l2 = lTOC(s.modules).filter(f1).toList()
+    ##    print "Filter out dospath, macpath, posixpath:", pprint.pprint(l2)
+    ##    f2 = DirFilter(['.'])
+    ##    l3 = lTOC(s.modules).filter(f2).toList()
+    ##    print "Filter out current dir:", pprint.pprint(l3)
+    ##    f3 = StdLibFilter()
+    ##    l4 = lTOC(s.modules).filter(f3).toList()
+    ##    print "Filter out stdlib:", pprint.pprint(l4)
+    ##    #print "Filter out current dir and stdlib:", lTOC(s.modules).filter(f2, f3).toList()
+    binltoc = lTOC(s.binaries)
+    print "Raw bin ltoc:", pprint.pprint(binltoc.toList())
+    binltoc.addFilter('c:/winnt/system32')
+    pprint.pprint(binltoc.filter().toList())
+    
+