Beginings of supporting multiple versions installed side-by-side
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@29517 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -27,6 +27,7 @@ from distutils.dir_util import mkpath
|
|||||||
from distutils.dep_util import newer
|
from distutils.dep_util import newer
|
||||||
from distutils.spawn import spawn
|
from distutils.spawn import spawn
|
||||||
|
|
||||||
|
import distutils.command.install
|
||||||
import distutils.command.install_data
|
import distutils.command.install_data
|
||||||
import distutils.command.install_headers
|
import distutils.command.install_headers
|
||||||
import distutils.command.clean
|
import distutils.command.clean
|
||||||
@@ -121,6 +122,21 @@ UNDEF_NDEBUG = 1 # Python 2.2 on Unix/Linux by default defines NDEBUG,
|
|||||||
NO_SCRIPTS = 0 # Don't install the tool scripts
|
NO_SCRIPTS = 0 # Don't install the tool scripts
|
||||||
NO_HEADERS = 0 # Don't install the wxPython *.h and *.i files
|
NO_HEADERS = 0 # Don't install the wxPython *.h and *.i files
|
||||||
|
|
||||||
|
INSTALL_MULTIVERSION = 1 # Install the packages such that multiple versions
|
||||||
|
# can co-exist. When turned on the wx and wxPython
|
||||||
|
# pacakges will be installed in a versioned subdir
|
||||||
|
# of site-packages, and a *.pth file will be
|
||||||
|
# created that adds that dir to the sys.path. In
|
||||||
|
# addition, a wxselect.py module will be installed
|
||||||
|
# to site-pacakges that will allow applications to
|
||||||
|
# choose a specific version if more than one are
|
||||||
|
# installed.
|
||||||
|
|
||||||
|
FLAVOUR = "" # Optional flavour string to be appended to VERSION
|
||||||
|
# in MULTIVERSION installs
|
||||||
|
|
||||||
|
INSTALL_WXRC = 0 # Should the Python version of wxrc be installed?
|
||||||
|
|
||||||
WX_CONFIG = None # Usually you shouldn't need to touch this, but you can set
|
WX_CONFIG = None # Usually you shouldn't need to touch this, but you can set
|
||||||
# it to pass an alternate version of wx-config or alternate
|
# it to pass an alternate version of wx-config or alternate
|
||||||
# flags, eg. as required by the .deb in-tree build. By
|
# flags, eg. as required by the .deb in-tree build. By
|
||||||
@@ -218,7 +234,7 @@ for flag in ['BUILD_GLCANVAS', 'BUILD_OGL', 'BUILD_STC',
|
|||||||
'BUILD_GIZMOS', 'BUILD_DLLWIDGET', 'BUILD_IEWIN', 'BUILD_ACTIVEX',
|
'BUILD_GIZMOS', 'BUILD_DLLWIDGET', 'BUILD_IEWIN', 'BUILD_ACTIVEX',
|
||||||
'CORE_ONLY', 'PREP_ONLY', 'USE_SWIG', 'UNICODE',
|
'CORE_ONLY', 'PREP_ONLY', 'USE_SWIG', 'UNICODE',
|
||||||
'UNDEF_NDEBUG', 'NO_SCRIPTS', 'NO_HEADERS', 'BUILD_RENAMERS',
|
'UNDEF_NDEBUG', 'NO_SCRIPTS', 'NO_HEADERS', 'BUILD_RENAMERS',
|
||||||
'FULL_DOCS',
|
'FULL_DOCS', 'INSTALL_MULTIVERSION', 'INSTALL_WXRC',
|
||||||
'FINAL', 'HYBRID', ]:
|
'FINAL', 'HYBRID', ]:
|
||||||
for x in range(len(sys.argv)):
|
for x in range(len(sys.argv)):
|
||||||
if sys.argv[x].find(flag) == 0:
|
if sys.argv[x].find(flag) == 0:
|
||||||
@@ -229,7 +245,8 @@ for flag in ['BUILD_GLCANVAS', 'BUILD_OGL', 'BUILD_STC',
|
|||||||
|
|
||||||
# String options
|
# String options
|
||||||
for option in ['WX_CONFIG', 'WXDLLVER', 'BUILD_BASE', 'WXPORT', 'SWIG',
|
for option in ['WX_CONFIG', 'WXDLLVER', 'BUILD_BASE', 'WXPORT', 'SWIG',
|
||||||
'CONTRIBS_INC', 'WXPY_SRC']:
|
'CONTRIBS_INC', 'WXPY_SRC', 'FLAVOUR',
|
||||||
|
]:
|
||||||
for x in range(len(sys.argv)):
|
for x in range(len(sys.argv)):
|
||||||
if sys.argv[x].find(option) == 0:
|
if sys.argv[x].find(option) == 0:
|
||||||
pos = sys.argv[x].find('=') + 1
|
pos = sys.argv[x].find('=') + 1
|
||||||
@@ -397,6 +414,15 @@ class wx_extra_clean(distutils.command.clean.clean):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class wx_install(distutils.command.install.install):
|
||||||
|
"""
|
||||||
|
Turns off install_path_file
|
||||||
|
"""
|
||||||
|
def initialize_options(self):
|
||||||
|
distutils.command.install.install.initialize_options(self)
|
||||||
|
self.install_path_file = 0
|
||||||
|
|
||||||
|
|
||||||
class wx_install_headers(distutils.command.install_headers.install_headers):
|
class wx_install_headers(distutils.command.install_headers.install_headers):
|
||||||
"""
|
"""
|
||||||
Install the header files to the WXPREFIX, with an extra dir per
|
Install the header files to the WXPREFIX, with an extra dir per
|
||||||
@@ -508,7 +534,7 @@ def adjustCFLAGS(cflags, defines, includes):
|
|||||||
|
|
||||||
|
|
||||||
def adjustLFLAGS(lfags, libdirs, libs):
|
def adjustLFLAGS(lfags, libdirs, libs):
|
||||||
'''Extrace the -L and -l flags and put them in libdirs and libs as needed'''
|
'''Extract the -L and -l flags and put them in libdirs and libs as needed'''
|
||||||
newLFLAGS = []
|
newLFLAGS = []
|
||||||
for flag in lflags:
|
for flag in lflags:
|
||||||
if flag[:2] == '-L':
|
if flag[:2] == '-L':
|
||||||
@@ -520,6 +546,31 @@ def adjustLFLAGS(lfags, libdirs, libs):
|
|||||||
|
|
||||||
return newLFLAGS
|
return newLFLAGS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def getExtraPath(shortVer=True, addOpts=False):
|
||||||
|
"""Get the dirname that wxPython will be installed under."""
|
||||||
|
|
||||||
|
if shortVer:
|
||||||
|
# short version, just Major.Minor
|
||||||
|
ep = "wx-%d.%d" % (VER_MAJOR, VER_MINOR)
|
||||||
|
# plus release if minor is odd
|
||||||
|
if VER_MINOR % 2 == 1:
|
||||||
|
ep += ".%d" % VER_RELEASE
|
||||||
|
else:
|
||||||
|
# long version, full version
|
||||||
|
ep = "wx-%d.%d.%d.%d" % (VER_MAJOR, VER_MINOR, VER_RELEASE, VER_SUBREL)
|
||||||
|
|
||||||
|
if addOpts:
|
||||||
|
ep += "-%s-%s" % (WXPORT, (UNICODE and 'unicode' or 'ansi'))
|
||||||
|
|
||||||
|
if FLAVOUR:
|
||||||
|
ep += "-" + FLAVOUR
|
||||||
|
|
||||||
|
return ep
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# sanity checks
|
# sanity checks
|
||||||
|
|
||||||
|
@@ -647,16 +647,19 @@ if NO_SCRIPTS:
|
|||||||
else:
|
else:
|
||||||
SCRIPTS = [opj('scripts/helpviewer'),
|
SCRIPTS = [opj('scripts/helpviewer'),
|
||||||
opj('scripts/img2png'),
|
opj('scripts/img2png'),
|
||||||
opj('scripts/img2xpm'),
|
|
||||||
opj('scripts/img2py'),
|
opj('scripts/img2py'),
|
||||||
opj('scripts/xrced'),
|
opj('scripts/img2xpm'),
|
||||||
opj('scripts/pyshell'),
|
|
||||||
opj('scripts/pycrust'),
|
|
||||||
opj('scripts/pywrap'),
|
|
||||||
opj('scripts/pywrap'),
|
|
||||||
opj('scripts/pyalacarte'),
|
opj('scripts/pyalacarte'),
|
||||||
opj('scripts/pyalamode'),
|
opj('scripts/pyalamode'),
|
||||||
|
opj('scripts/pycrust'),
|
||||||
|
opj('scripts/pyshell'),
|
||||||
|
opj('scripts/pywrap'),
|
||||||
|
opj('scripts/pywrap'),
|
||||||
|
opj('scripts/xrced'),
|
||||||
]
|
]
|
||||||
|
if INSTALL_WXRC:
|
||||||
|
SCRIPTS += [opj('scripts/wxrc')]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DATA_FILES += find_data_files('wx/tools/XRCed', '*.txt', '*.xrc')
|
DATA_FILES += find_data_files('wx/tools/XRCed', '*.txt', '*.xrc')
|
||||||
@@ -676,12 +679,23 @@ else:
|
|||||||
zip(i_files, ["/wxPython/i_files"]*len(i_files))
|
zip(i_files, ["/wxPython/i_files"]*len(i_files))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if INSTALL_MULTIVERSION:
|
||||||
|
EXTRA_PATH = getExtraPath()
|
||||||
|
open("src/wx.pth", "w").write(EXTRA_PATH)
|
||||||
|
CLEANUP.append("src/wx.pth")
|
||||||
|
else:
|
||||||
|
EXTRA_PATH = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Do the Setup/Build/Install/Whatever
|
# Do the Setup/Build/Install/Whatever
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if not PREP_ONLY:
|
if not PREP_ONLY:
|
||||||
|
|
||||||
setup(name = 'wxPython',
|
setup(name = 'wxPython',
|
||||||
version = VERSION,
|
version = VERSION,
|
||||||
description = DESCRIPTION,
|
description = DESCRIPTION,
|
||||||
@@ -716,6 +730,8 @@ if __name__ == "__main__":
|
|||||||
'wx.tools.XRCed',
|
'wx.tools.XRCed',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
extra_path = EXTRA_PATH,
|
||||||
|
|
||||||
ext_package = PKGDIR,
|
ext_package = PKGDIR,
|
||||||
ext_modules = wxpExtensions,
|
ext_modules = wxpExtensions,
|
||||||
|
|
||||||
@@ -726,12 +742,40 @@ if __name__ == "__main__":
|
|||||||
data_files = DATA_FILES,
|
data_files = DATA_FILES,
|
||||||
headers = HEADERS,
|
headers = HEADERS,
|
||||||
|
|
||||||
cmdclass = { 'install_data': wx_smart_install_data,
|
# Override some of the default distutils command classes with my own
|
||||||
|
cmdclass = { 'install' : wx_install,
|
||||||
|
'install_data': wx_smart_install_data,
|
||||||
'install_headers': wx_install_headers,
|
'install_headers': wx_install_headers,
|
||||||
'clean': wx_extra_clean,
|
'clean': wx_extra_clean,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if INSTALL_MULTIVERSION:
|
||||||
|
setup(name = 'wxPython-common',
|
||||||
|
version = VERSION,
|
||||||
|
description = DESCRIPTION,
|
||||||
|
long_description = LONG_DESCRIPTION,
|
||||||
|
author = AUTHOR,
|
||||||
|
author_email = AUTHOR_EMAIL,
|
||||||
|
url = URL,
|
||||||
|
download_url = DOWNLOAD_URL,
|
||||||
|
license = LICENSE,
|
||||||
|
platforms = PLATFORMS,
|
||||||
|
classifiers = filter(None, CLASSIFIERS.split("\n")),
|
||||||
|
keywords = KEYWORDS,
|
||||||
|
|
||||||
|
package_dir = { '': 'wxversion' },
|
||||||
|
py_modules = ['wxversion'],
|
||||||
|
|
||||||
|
data_files = [('', ['src/wx.pth'])],
|
||||||
|
|
||||||
|
options = { 'build' : { 'build_base' : BUILD_BASE },
|
||||||
|
},
|
||||||
|
|
||||||
|
cmdclass = { 'install_data': wx_smart_install_data,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
210
wxPython/wxversion/wxversion.py
Normal file
210
wxPython/wxversion/wxversion.py
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
#----------------------------------------------------------------------
|
||||||
|
# Name: wxversion
|
||||||
|
# Purpose: Allows a wxPython program to search for alternate
|
||||||
|
# installations of the wxPython packages and modify sys.path
|
||||||
|
# so they will be found when "import wx" is done.
|
||||||
|
#
|
||||||
|
# Author: Robin Dunn
|
||||||
|
#
|
||||||
|
# Created: 24-Sept-2004
|
||||||
|
# RCS-ID: $Id$
|
||||||
|
# Copyright: (c) 2004 by Total Control Software
|
||||||
|
# Licence: wxWindows license
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
"""
|
||||||
|
If you have more than one version of wxPython installed this module
|
||||||
|
allows your application to choose which version of wxPython will be
|
||||||
|
imported when it does 'import wx'. You use it like this:
|
||||||
|
|
||||||
|
import wxversion
|
||||||
|
wxversion.require('2.4')
|
||||||
|
import wx
|
||||||
|
|
||||||
|
Of course the default wxPython version can also be controlled by
|
||||||
|
setting PYTHONPATH or by editing the wx.pth path configuration file,
|
||||||
|
but using wxversion will allow an application to manage the version
|
||||||
|
selection itself rather than depend on the user to setup the
|
||||||
|
environment correctly.
|
||||||
|
|
||||||
|
It works by searching the sys.path for directories matching wx-* and
|
||||||
|
then comparing them to what was passed to the require function. If a
|
||||||
|
match is found then that path is inserted into sys.path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys, os, glob, fnmatch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def require(versions):
|
||||||
|
"""
|
||||||
|
Search for a wxPython installation that matches version.
|
||||||
|
|
||||||
|
:param version: Specifies the version to look for, it can either be
|
||||||
|
a sting or a list of strings. Each string is
|
||||||
|
compared to the installed wxPythons and the best
|
||||||
|
match is added to the sys.path, allowing an 'import
|
||||||
|
wx' to find that version.
|
||||||
|
|
||||||
|
The version string is composed of the dotted
|
||||||
|
version number (at least 2 of the 4 components)
|
||||||
|
optionally followed by hyphen ('-') separated
|
||||||
|
options (wx port, unicode/ansi, flavour, etc.) A
|
||||||
|
match is determined by how much of the installed
|
||||||
|
version matches what is given in the version
|
||||||
|
parameter. If the version number components don't
|
||||||
|
match then the score is zero, otherwise the score
|
||||||
|
is increased for every specified optional component
|
||||||
|
that is specified and that matches.
|
||||||
|
"""
|
||||||
|
assert not sys.modules.has_key('wx') and not sys.modules.has_key('wxPython'), \
|
||||||
|
"wxversion.require() must be called before wxPython is imported"
|
||||||
|
|
||||||
|
bestMatch = None
|
||||||
|
bestScore = 0
|
||||||
|
if type(versions) == str:
|
||||||
|
versions = [versions]
|
||||||
|
|
||||||
|
packages = _find_installed()
|
||||||
|
for pkg in packages:
|
||||||
|
for ver in versions:
|
||||||
|
score = pkg.Score(_wxPackageInfo(ver))
|
||||||
|
if score > bestScore:
|
||||||
|
bestMatch = pkg
|
||||||
|
bestScore = score
|
||||||
|
|
||||||
|
assert bestMatch is not None, \
|
||||||
|
"Required version of wxPython not found"
|
||||||
|
|
||||||
|
sys.path.insert(0, bestMatch.pathname)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_pattern = "wx-[0-9].*"
|
||||||
|
def _find_installed():
|
||||||
|
installed = []
|
||||||
|
for pth in sys.path:
|
||||||
|
|
||||||
|
# empty means to look in the current dir
|
||||||
|
if not pth:
|
||||||
|
pth = '.'
|
||||||
|
|
||||||
|
# skip it if it's not a package dir
|
||||||
|
if not os.path.isdir(pth):
|
||||||
|
continue
|
||||||
|
|
||||||
|
base = os.path.basename(pth)
|
||||||
|
|
||||||
|
# if it's a wx path that's already in the sys.path then skip it
|
||||||
|
if fnmatch.fnmatchcase(base, _pattern):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# now look in the dir for matching subdirs
|
||||||
|
for name in glob.glob(os.path.join(pth, _pattern)):
|
||||||
|
# make sure it's a directory
|
||||||
|
if not os.path.isdir(name):
|
||||||
|
continue
|
||||||
|
# and has a wx subdir
|
||||||
|
if not os.path.exists(os.path.join(name, 'wx')):
|
||||||
|
continue
|
||||||
|
installed.append(_wxPackageInfo(name, True))
|
||||||
|
|
||||||
|
installed.sort()
|
||||||
|
installed.reverse()
|
||||||
|
return installed
|
||||||
|
|
||||||
|
|
||||||
|
class _wxPackageInfo(object):
|
||||||
|
def __init__(self, pathname, stripFirst=False):
|
||||||
|
self.pathname = pathname
|
||||||
|
base = os.path.basename(pathname)
|
||||||
|
segments = base.split('-')
|
||||||
|
if stripFirst:
|
||||||
|
segments = segments[1:]
|
||||||
|
self.version = tuple([int(x) for x in segments[0].split('.')])
|
||||||
|
self.options = segments[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def Score(self, other):
|
||||||
|
score = 0
|
||||||
|
# whatever version components given in other must match exactly
|
||||||
|
if len(self.version) > len(other.version):
|
||||||
|
v = self.version[:len(other.version)]
|
||||||
|
else:
|
||||||
|
v = self.version
|
||||||
|
if v != other.version:
|
||||||
|
return 0
|
||||||
|
score += 1
|
||||||
|
for opt in other.options:
|
||||||
|
if opt in self.options:
|
||||||
|
score += 1
|
||||||
|
return score
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: factor self.options into the sort order?
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.version < other.version
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self.version > other.version
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.version == other.version
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
def test(version):
|
||||||
|
savepath = sys.path[:]
|
||||||
|
require(version)
|
||||||
|
print "Asked for %s:\t got: %s" % (version, sys.path[0])
|
||||||
|
sys.path = savepath[:]
|
||||||
|
|
||||||
|
|
||||||
|
# make some test dirs
|
||||||
|
names = ['wx-2.4',
|
||||||
|
'wx-2.5.2',
|
||||||
|
'wx-2.5.2.9-gtk2-unicode',
|
||||||
|
'wx-2.5.2.9-gtk-ansi',
|
||||||
|
'wx-2.5.1',
|
||||||
|
'wx-2.5.2.8-gtk2-unicode',
|
||||||
|
'wx-2.5.3']
|
||||||
|
for name in names:
|
||||||
|
d = os.path.join('/tmp', name)
|
||||||
|
os.mkdir(d)
|
||||||
|
os.mkdir(os.path.join(d, 'wx'))
|
||||||
|
|
||||||
|
# setup sys.path to see those dirs
|
||||||
|
sys.path.append('/tmp')
|
||||||
|
|
||||||
|
|
||||||
|
# now run some tests
|
||||||
|
test("2.4")
|
||||||
|
test("2.5")
|
||||||
|
test("2.5-gtk2")
|
||||||
|
test("2.5.2")
|
||||||
|
test("2.5-ansi")
|
||||||
|
test("2.5-unicode")
|
||||||
|
|
||||||
|
# There isn't a unicode match for this one, but it will give the best
|
||||||
|
# available 2.4. Should it give an error instead? I don't think so...
|
||||||
|
test("2.4-unicode")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# expecting an error on this one
|
||||||
|
test("2.6")
|
||||||
|
except AssertionError:
|
||||||
|
print "Asked for 2.6:\t got: Assertion"
|
||||||
|
|
||||||
|
# Try asking for multiple versions
|
||||||
|
test(["2.6", "2.5.3", "2.5.2-gtk2"])
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
for name in names:
|
||||||
|
d = os.path.join('/tmp', name)
|
||||||
|
os.rmdir(os.path.join(d, 'wx'))
|
||||||
|
os.rmdir(d)
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user