mirror of https://github.com/python/cpython
Factored out classes for handling source and binary distributions. Source
now means "distutils-based source", binary "bdist format archive". Also fixed various lurking bugs.
This commit is contained in:
parent
f68043c4a9
commit
0dacac48b9
|
@ -31,6 +31,9 @@ NO_EXECUTE=0
|
|||
|
||||
PIMP_VERSION="0.1"
|
||||
|
||||
# Flavors:
|
||||
# source: setup-based package
|
||||
# binary: tar (or other) archive created with setup.py bdist.
|
||||
DEFAULT_FLAVORORDER=['source', 'binary']
|
||||
DEFAULT_DOWNLOADDIR='/tmp'
|
||||
DEFAULT_BUILDDIR='/tmp'
|
||||
|
@ -38,11 +41,11 @@ DEFAULT_INSTALLDIR=distutils.sysconfig.get_python_lib()
|
|||
DEFAULT_PIMPDATABASE="http://www.cwi.nl/~jack/pimp/pimp-%s.plist" % distutils.util.get_platform()
|
||||
|
||||
ARCHIVE_FORMATS = [
|
||||
(".tar.Z", "zcat \"%s\" | tar xf -"),
|
||||
(".taz", "zcat \"%s\" | tar xf -"),
|
||||
(".tar.gz", "zcat \"%s\" | tar xf -"),
|
||||
(".tgz", "zcat \"%s\" | tar xf -"),
|
||||
(".tar.bz", "bzcat \"%s\" | tar xf -"),
|
||||
(".tar.Z", "zcat \"%s\" | tar -xf -"),
|
||||
(".taz", "zcat \"%s\" | tar -xf -"),
|
||||
(".tar.gz", "zcat \"%s\" | tar -xf -"),
|
||||
(".tgz", "zcat \"%s\" | tar -xf -"),
|
||||
(".tar.bz", "bzcat \"%s\" | tar -xf -"),
|
||||
]
|
||||
|
||||
class MyURLopener(urllib.FancyURLopener):
|
||||
|
@ -132,6 +135,11 @@ class PimpDatabase:
|
|||
self._maintainer = ""
|
||||
self._description = ""
|
||||
|
||||
def close(self):
|
||||
"""Clean up"""
|
||||
self._packages = []
|
||||
self.preferences = None
|
||||
|
||||
def appendURL(self, url, included=0):
|
||||
"""Append packages from the database with the given URL.
|
||||
Only the first database should specify included=0, so the
|
||||
|
@ -161,7 +169,14 @@ class PimpDatabase:
|
|||
to our internal storage."""
|
||||
|
||||
for p in packages:
|
||||
pkg = PimpPackage(self, dict(p))
|
||||
p = dict(p)
|
||||
flavor = p.get('Flavor')
|
||||
if flavor == 'source':
|
||||
pkg = PimpPackage_source(self, p)
|
||||
elif flavor == 'binary':
|
||||
pkg = PimpPackage_binary(self, p)
|
||||
else:
|
||||
pkg = PimpPackage(self, dict(p))
|
||||
self._packages.append(pkg)
|
||||
|
||||
def list(self):
|
||||
|
@ -175,6 +190,7 @@ class PimpDatabase:
|
|||
rv = []
|
||||
for pkg in self._packages:
|
||||
rv.append(pkg.fullname())
|
||||
rv.sort()
|
||||
return rv
|
||||
|
||||
def dump(self, pathOrFile):
|
||||
|
@ -266,7 +282,7 @@ class PimpPackage:
|
|||
def version(self): return self._dict['Version']
|
||||
def flavor(self): return self._dict['Flavor']
|
||||
def description(self): return self._dict['Description']
|
||||
def homepage(self): return self._dict['Home-page']
|
||||
def homepage(self): return self._dict.get('Home-page')
|
||||
def downloadURL(self): return self._dict['Download-URL']
|
||||
|
||||
def fullname(self):
|
||||
|
@ -342,7 +358,7 @@ class PimpPackage:
|
|||
rv = []
|
||||
if not self._dict.get('Download-URL'):
|
||||
return [(None, "This package needs to be installed manually")]
|
||||
if not self._dict['Prerequisites']:
|
||||
if not self._dict.get('Prerequisites'):
|
||||
return []
|
||||
for item in self._dict['Prerequisites']:
|
||||
if type(item) == str:
|
||||
|
@ -381,7 +397,7 @@ class PimpPackage:
|
|||
rv = fp.close()
|
||||
return rv
|
||||
|
||||
def downloadSinglePackage(self, output=None):
|
||||
def downloadPackageOnly(self, output=None):
|
||||
"""Download a single package, if needed.
|
||||
|
||||
An MD5 signature is used to determine whether download is needed,
|
||||
|
@ -422,7 +438,7 @@ class PimpPackage:
|
|||
checksum = md5.new(data).hexdigest()
|
||||
return checksum == self._dict['MD5Sum']
|
||||
|
||||
def unpackSinglePackage(self, output=None):
|
||||
def unpackPackageOnly(self, output=None):
|
||||
"""Unpack a downloaded package archive."""
|
||||
|
||||
filename = os.path.split(self.archiveFilename)[1]
|
||||
|
@ -433,12 +449,12 @@ class PimpPackage:
|
|||
return "unknown extension for archive file: %s" % filename
|
||||
basename = filename[:-len(ext)]
|
||||
cmd = cmd % self.archiveFilename
|
||||
self._buildDirname = os.path.join(self._db.preferences.buildDir, basename)
|
||||
if self._cmd(output, self._db.preferences.buildDir, cmd):
|
||||
return "unpack command failed"
|
||||
setupname = os.path.join(self._buildDirname, "setup.py")
|
||||
if not os.path.exists(setupname) and not NO_EXECUTE:
|
||||
return "no setup.py found after unpack of archive"
|
||||
|
||||
def installPackageOnly(self, output=None):
|
||||
"""Default install method, to be overridden by subclasses"""
|
||||
return "Cannot automatically install package %s" % self.fullname()
|
||||
|
||||
def installSinglePackage(self, output=None):
|
||||
"""Download, unpack and install a single package.
|
||||
|
@ -448,39 +464,27 @@ class PimpPackage:
|
|||
|
||||
if not self._dict['Download-URL']:
|
||||
return "%s: This package needs to be installed manually" % _fmtpackagename(self)
|
||||
msg = self.downloadSinglePackage(output)
|
||||
msg = self.downloadPackageOnly(output)
|
||||
if msg:
|
||||
return "download %s: %s" % (self.fullname(), msg)
|
||||
|
||||
msg = self.unpackSinglePackage(output)
|
||||
msg = self.unpackPackageOnly(output)
|
||||
if msg:
|
||||
return "unpack %s: %s" % (self.fullname(), msg)
|
||||
|
||||
if self._dict.has_key('Pre-install-command'):
|
||||
if self._cmd(output, self._buildDirname, self._dict['Pre-install-command']):
|
||||
return "pre-install %s: running \"%s\" failed" % \
|
||||
(self.fullname(), self._dict['Pre-install-command'])
|
||||
return self.installPackageOnly(output)
|
||||
|
||||
old_contents = os.listdir(self._db.preferences.installDir)
|
||||
installcmd = self._dict.get('Install-command')
|
||||
if not installcmd:
|
||||
installcmd = '"%s" setup.py install' % sys.executable
|
||||
if self._cmd(output, self._buildDirname, installcmd):
|
||||
return "install %s: running \"%s\" failed" % self.fullname()
|
||||
def beforeInstall(self):
|
||||
"""Bookkeeping before installation: remember what we have in site-packages"""
|
||||
self._old_contents = os.listdir(self._db.preferences.installDir)
|
||||
|
||||
def afterInstall(self):
|
||||
"""Bookkeeping after installation: interpret any new .pth files that have
|
||||
appeared"""
|
||||
|
||||
new_contents = os.listdir(self._db.preferences.installDir)
|
||||
self._interpretPthFiles(old_contents, new_contents)
|
||||
|
||||
if self._dict.has_key('Post-install-command'):
|
||||
if self._cmd(output, self._buildDirname, self._dict['Post-install-command']):
|
||||
return "post-install %s: running \"%s\" failed" % \
|
||||
(self.fullname(), self._dict['Post-install-command'])
|
||||
return None
|
||||
|
||||
def _interpretPthFiles(self, old_contents, new_contents):
|
||||
"""Evaluate any new .pth files that have appeared after installing"""
|
||||
for fn in new_contents:
|
||||
if fn in old_contents:
|
||||
if fn in self._old_contents:
|
||||
continue
|
||||
if fn[-4:] != '.pth':
|
||||
continue
|
||||
|
@ -502,6 +506,88 @@ class PimpPackage:
|
|||
if not line in sys.path:
|
||||
sys.path.append(line)
|
||||
|
||||
class PimpPackage_binary(PimpPackage):
|
||||
|
||||
def unpackPackageOnly(self, output=None):
|
||||
"""We don't unpack binary packages until installing"""
|
||||
pass
|
||||
|
||||
def installPackageOnly(self, output=None):
|
||||
"""Install a single source package.
|
||||
|
||||
If output is given it should be a file-like object and it
|
||||
will receive a log of what happened."""
|
||||
|
||||
msgs = []
|
||||
if self._dict.has_key('Pre-install-command'):
|
||||
msg.append("%s: Pre-install-command ignored" % self.fullname())
|
||||
if self._dict.has_key('Install-command'):
|
||||
msgs.append("%s: Install-command ignored" % self.fullname())
|
||||
if self._dict.has_key('Post-install-command'):
|
||||
msgs.append("%s: Post-install-command ignored" % self.fullname())
|
||||
|
||||
self.beforeInstall()
|
||||
|
||||
# Install by unpacking
|
||||
filename = os.path.split(self.archiveFilename)[1]
|
||||
for ext, cmd in ARCHIVE_FORMATS:
|
||||
if filename[-len(ext):] == ext:
|
||||
break
|
||||
else:
|
||||
return "unknown extension for archive file: %s" % filename
|
||||
|
||||
# Modify where the files are extracted
|
||||
prefixmod = '-C /'
|
||||
cmd = cmd % self.archiveFilename
|
||||
if self._cmd(output, self._db.preferences.buildDir, cmd, prefixmod):
|
||||
return "unpack command failed"
|
||||
|
||||
self.afterInstall()
|
||||
|
||||
if self._dict.has_key('Post-install-command'):
|
||||
if self._cmd(output, self._buildDirname, self._dict['Post-install-command']):
|
||||
return "post-install %s: running \"%s\" failed" % \
|
||||
(self.fullname(), self._dict['Post-install-command'])
|
||||
return None
|
||||
|
||||
|
||||
class PimpPackage_source(PimpPackage):
|
||||
|
||||
def unpackPackageOnly(self, output=None):
|
||||
"""Unpack a source package and check that setup.py exists"""
|
||||
PimpPackage.unpackPackageOnly(self, output)
|
||||
# Test that a setup script has been create
|
||||
self._buildDirname = os.path.join(self._db.preferences.buildDir, basename)
|
||||
setupname = os.path.join(self._buildDirname, "setup.py")
|
||||
if not os.path.exists(setupname) and not NO_EXECUTE:
|
||||
return "no setup.py found after unpack of archive"
|
||||
|
||||
def installPackageOnly(self, output=None):
|
||||
"""Install a single source package.
|
||||
|
||||
If output is given it should be a file-like object and it
|
||||
will receive a log of what happened."""
|
||||
|
||||
if self._dict.has_key('Pre-install-command'):
|
||||
if self._cmd(output, self._buildDirname, self._dict['Pre-install-command']):
|
||||
return "pre-install %s: running \"%s\" failed" % \
|
||||
(self.fullname(), self._dict['Pre-install-command'])
|
||||
|
||||
self.beforeInstall()
|
||||
installcmd = self._dict.get('Install-command')
|
||||
if not installcmd:
|
||||
installcmd = '"%s" setup.py install' % sys.executable
|
||||
if self._cmd(output, self._buildDirname, installcmd):
|
||||
return "install %s: running \"%s\" failed" % self.fullname()
|
||||
|
||||
self.afterInstall()
|
||||
|
||||
if self._dict.has_key('Post-install-command'):
|
||||
if self._cmd(output, self._buildDirname, self._dict['Post-install-command']):
|
||||
return "post-install %s: running \"%s\" failed" % \
|
||||
(self.fullname(), self._dict['Post-install-command'])
|
||||
return None
|
||||
|
||||
|
||||
class PimpInstaller:
|
||||
"""Installer engine: computes dependencies and installs
|
||||
|
|
Loading…
Reference in New Issue