Four problems with Gordon McMillan's Installer

Rolander, Dan Dan.Rolander at marriott.com
Fri Feb 2 12:41:01 EST 2001


Gordon (Williams):

I am using Installer with wxPython 2.2.2, and Python 2.0 under Win2K. I had
some initial problems but everything is fine now. Here are my comments:

1) Yes, the problem with wxPython.wxc.pyd is known. Apparently this started
happening with Python 2.0. You'll see this often-- files will be named with
the full package path name. As far as I know the only workaround is to
rename them after the build.

2) Yes, I also include wx22_2.dll on the misc line, like this--
misc= MYSTANDALONE, wxPython.wx22_2

3) I see the fixcase messages also but they don't seem to affect anything.

4) I have an application that is using wxPython and the time module and I
haven't had a problem with it. BUT... I do have a couple of updated files
from Gordon McMillan that are newer than the 3i distribution and did fix A
LOT of problems I was having before. He's advised they will be part of the
3j distribution whenever that is released. I'm attaching them. I recommend
you replace your existing versions with these.

5) Yes, Gordon's starship pages are out of date and I don't know why they
are still there. Be sure to use http://www.mcmillan-inc.com instead.

I hope this helps,
Dan

-------------- MEInc\dist\bindepend.py ----------------
# copyright 1999 McMillan Enterprises, Inc.
# license: use as you please. No warranty.
#
# use dumpbin.exe (if present) to find the binary
# dependencies of an extension module.
# if dumpbin not available, pick apart the PE hdr of the binary
# while this appears to work well, it is complex and subject to
# problems with changes to PE hdrs (ie, this works only on 32 bit Intel
# Windows format binaries)
#
# Note also that you should check the results to make sure that the
# dlls are redistributable. I've listed most of the common MS dlls
# under "excludes" below; add to this list as necessary (or use the
# "excludes" option in the INSTALL section of the config file).

import os
import time
import string
import sys
import tempfile
import finder

seen = {}
excludes = {'KERNEL32.DLL':1, 
      'ADVAPI.DLL':1, 
      'MSVCRT.DLL':1,
      'ADVAPI32.DLL':1,
      'COMCTL32.DLL':1,
      'CRTDLL.DLL':1,
      'GDI32.DLL':1,
      'MFC42.DLL':1,
      'NTDLL.DLL':1,
      'OLE32.DLL':1,
      'OLEAUT32.DLL':1,
      'RPCRT4.DLL':1,
      'SHELL32.DLL':1,
      'USER32.DLL':1,
      'WINSPOOL.DRV':1,
      'WS2HELP.DLL':1,
      'WS2_32.DLL':1,
      'WSOCK32.DLL':1,
      'WINMM.DLL':1,
      'COMDLG32.DLL':1,
      'ZLIB.DLL':1,
      'ODBC32.DLL':1,
      'VERSION.DLL':1}     

def getfullnameof(mod, xtrapath = None):
  """Return the full path name of MOD.
  
      MOD is the basename of a dll or pyd.
      XTRAPATH is a path or list of paths to search first.
      Return the full path name of MOD.
      Will search the full Windows search path, as well as sys.path"""
  epath = finder.getpath()
  if mod[-4:] in ('.pyd', '.PYD'):
    epath = epath + sys.path
  if xtrapath is not None:
    if type(xtrapath) == type(''):
      epath.insert(0, xtrapath)
    else:
      epath = xtrapath + epath
  for p in epath:
    npth = os.path.join(p, mod)
    if os.path.exists(npth):
      return npth
  return ''
  
def getImports1(pth):
    """Find the binary dependencies of PTH.
    
        This implementation (not used right now) uses the MSVC utility
dumpbin"""
    rslt = []
    tmpf = tempfile.mktemp()
    os.system('dumpbin /IMPORTS "%s" >%s' %(pth, tmpf))
    time.sleep(0.1)
    txt = open(tmpf,'r').readlines()
    os.remove(tmpf)
    i = 0
    while i < len(txt):
        tokens = string.split(txt[i])
        if len(tokens) == 1 and string.find(tokens[0], '.') > 0:
            rslt.append(string.strip(tokens[0]))
        i = i + 1
    return rslt
    
def getImports2(pth):
    """Find the binary dependencies of PTH.
    
        This implementation walks through the PE header"""
    import struct
    rslt = []
    try:
      f = open(pth, 'rb').read()
      pehdrd = struct.unpack('l', f[60:64])[0]
      magic = struct.unpack('l', f[pehdrd:pehdrd+4])[0]
      numsecs = struct.unpack('h', f[pehdrd+6:pehdrd+8])[0]
      numdirs = struct.unpack('l', f[pehdrd+116:pehdrd+120])[0]
      idata = ''
      if magic == 17744:
          importsec, sz = struct.unpack('2l', f[pehdrd+128:pehdrd+136])
          secttbl = pehdrd + 120 + 8*numdirs
          secttblfmt = '8s7l2h'
          seclist = []
          for i in range(numsecs):
              seclist.append(struct.unpack(secttblfmt,
f[secttbl+i*40:secttbl+(i+1)*40]))
              #nm, vsz, va, rsz, praw, preloc, plnnums, qrelocs, qlnnums,
flags \
              # = seclist[-1]
          for i in range(len(seclist)-1):
              if seclist[i][2] <= importsec < seclist[i+1][2]:
                  break
          vbase = seclist[i][2]
          raw = seclist[i][4]
          idatastart = raw + importsec - vbase
          idata = f[idatastart:idatastart+seclist[i][1]]
          i = 0
          while 1:
              vsa =  struct.unpack('5l', idata[i*20:i*20+20])[3]
              if vsa == 0:
                  break
              sa = raw + vsa - vbase
              end = string.find(f, '\000', sa)
              nm = f[sa:end]
              if nm:
                  rslt.append(nm)
              i = i + 1
    except IOError:
      print "bindepend cannot analyze %s - file not found!" % pth
    except struct.error:
      print "bindepend cannot analyze %s - error walking thru pehdr" % pth
    return rslt
    
def Dependencies(lTOC):
  """Expand LTOC to include all the closure of binary dependencies.
  
     LTOC is a logical table of contents, ie, a seq of tuples (name, path).
     Return LTOC expanded by all the binary dependencies of the entries
     in LTOC, except those listed in the module global EXCLUDES"""
  for (nm, pth) in lTOC:
    fullnm = string.upper(os.path.basename(pth))
    if seen.get(string.upper(nm),0):
      continue
    print "analyzing", nm
    seen[string.upper(nm)] = 1
    dlls = getImports(pth)
    for lib in dlls:
        print " found", lib
        if excludes.get(string.upper(lib),0):
          continue
        if seen.get(string.upper(lib),0):
          continue
        npth = getfullnameof(lib)
        if npth:
          lTOC.append((lib, npth))
        else:
          print " lib not found:", lib, "dependency of", 
  return lTOC
  
        
##if getfullnameof('dumpbin.exe') == '':
##    def getImports(pth):
##        return getImports2(pth)
##else:
##    def getImports(pth):
##        return getImports1(pth)
        
def getImports(pth):
    """Forwards to either getImports1 or getImports2
    """
    return getImports2(pth)



-------------- support\archive_rt.py ----------------
#
# 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.

_verbose = 0
_listdir = None
import marshal
import struct
import imp
import sys
import imputil

_c_suffixes = filter(lambda x: x[2] == imp.C_EXTENSION, imp.get_suffixes())

for nm in ('nt', 'posix', 'dos', 'os2', 'mac'):
  if nm in sys.builtin_module_names:
    mod = __import__(nm)
    _listdir = mod.listdir

class Archive(imputil.Importer):
  """ 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
  _bincache = 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()
      if Archive._bincache is None:
        Archive._bincache = {}
        for fnm in _listdir(sys.path[0]):
          for ext, mode, typ in _c_suffixes:
            sz = len(ext)
            if fnm[-sz:] == ext:
              Archive._bincache[fnm[:-sz]] = sys.path[0] + '/' + fnm, (ext,
mode, typ)
              if _verbose:
                print "Archive caching", fnm[:-sz]

  ####### 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):
    if _verbose:
      print "get_code(%s, %s, %s, %s)" % (self, parent, modname, fqname)
    rslt = self.extract(fqname) # None if not found, (ispkg, code) otherwise
    if rslt is None:
      if parent:
        importer = getattr(parent, "__importer__", None)
        if importer:
          func = getattr(importer, "func", None)
          if func == self.get_code:
            file, desc = Archive._bincache.get(fqname, (None, None))
            if file:
              try:
                fp = open(file, desc[1])
              except IOError:
                pass
              else:
                module = imp.load_module(fqname, fp, file, desc)
                if _verbose:
                  print "#found", file
                return 0, module, {'__file__':file}
      if _verbose:
        print "#not found"
      return None
    ispkg, code = rslt
    if ispkg:
      return ispkg, code, {'__path__' : [fqname]}
    return ispkg, code, {}

  ####### 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)
## 

if type(__import__) == type(open):
    imputil.ImportManager().install()
    sys.path.append(imputil.BuiltinImporter())





More information about the Python-list mailing list