/usr/local/Hypy

view distribute_setup.py @ 139:d0a738aea316

Added tag 0.8.4.1 for changeset bbc3e3a3c4fb
author Cory Dodt <corymercurial@spam.goonmill.org>
date Fri Oct 09 10:32:45 2009 -0700 (2009-10-09)
parents
children
line source
1 #!python
2 """Bootstrap distribute installation
4 If you want to use setuptools in your package's setup.py, just include this
5 file in the same directory with it, and add this to the top of your setup.py::
7 from distribute_setup import use_setuptools
8 use_setuptools()
10 If you want to require a specific version of setuptools, set a download
11 mirror, or use an alternate download directory, you can do so by supplying
12 the appropriate options to ``use_setuptools()``.
14 This file can also be run as a script to install or upgrade setuptools.
15 """
16 import os
17 import sys
18 import time
19 import fnmatch
20 import tempfile
21 import tarfile
22 from distutils import log
24 try:
25 from site import USER_SITE
26 except ImportError:
27 USER_SITE = None
29 try:
30 import subprocess
32 def _python_cmd(*args):
33 args = (sys.executable,) + args
34 return subprocess.call(args) == 0
36 except ImportError:
37 # will be used for python 2.3
38 def _python_cmd(*args):
39 args = (sys.executable,) + args
40 # quoting arguments if windows
41 if sys.platform == 'win32':
42 def quote(arg):
43 if ' ' in arg:
44 return '"%s"' % arg
45 return arg
46 args = [quote(arg) for arg in args]
47 return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
49 DEFAULT_VERSION = "0.6.3"
50 DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
51 SETUPTOOLS_PKG_INFO = """\
52 Metadata-Version: 1.0
53 Name: setuptools
54 Version: 0.6c9
55 Summary: xxxx
56 Home-page: xxx
57 Author: xxx
58 Author-email: xxx
59 License: xxx
60 Description: xxx
61 """
64 def _install(tarball):
65 # extracting the tarball
66 tmpdir = tempfile.mkdtemp()
67 log.warn('Extracting in %s', tmpdir)
68 old_wd = os.getcwd()
69 try:
70 os.chdir(tmpdir)
71 tar = tarfile.open(tarball)
72 _extractall(tar)
73 tar.close()
75 # going in the directory
76 subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
77 os.chdir(subdir)
78 log.warn('Now working in %s', subdir)
80 # installing
81 log.warn('Installing Distribute')
82 assert _python_cmd('setup.py', 'install')
83 finally:
84 os.chdir(old_wd)
87 def _build_egg(tarball, to_dir):
88 # extracting the tarball
89 tmpdir = tempfile.mkdtemp()
90 log.warn('Extracting in %s', tmpdir)
91 old_wd = os.getcwd()
92 try:
93 os.chdir(tmpdir)
94 tar = tarfile.open(tarball)
95 _extractall(tar)
96 tar.close()
98 # going in the directory
99 subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
100 os.chdir(subdir)
101 log.warn('Now working in %s', subdir)
103 # building an egg
104 log.warn('Building a Distribute egg in %s', to_dir)
105 _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
107 # returning the result
108 for file in os.listdir(to_dir):
109 if fnmatch.fnmatch(file, 'distribute-%s*.egg' % DEFAULT_VERSION):
110 return os.path.join(to_dir, file)
112 raise IOError('Could not build the egg.')
113 finally:
114 os.chdir(old_wd)
117 def _do_download(version, download_base, to_dir, download_delay):
118 tarball = download_setuptools(version, download_base,
119 to_dir, download_delay)
120 egg = _build_egg(tarball, to_dir)
121 sys.path.insert(0, egg)
122 import setuptools
123 setuptools.bootstrap_install_from = egg
126 def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
127 to_dir=os.curdir, download_delay=15):
128 # making sure we use the absolute path
129 to_dir = os.path.abspath(to_dir)
130 was_imported = 'pkg_resources' in sys.modules or \
131 'setuptools' in sys.modules
132 try:
133 import pkg_resources
134 if not hasattr(pkg_resources, '_distribute'):
135 raise ImportError
136 except ImportError:
137 return _do_download(version, download_base, to_dir, download_delay)
138 try:
139 pkg_resources.require("distribute>="+version)
140 return
141 except pkg_resources.VersionConflict, e:
142 if was_imported:
143 print >>sys.stderr, (
144 "The required version of distribute (>=%s) is not available, and\n"
145 "can't be installed while this script is running. Please install\n"
146 " a more recent version first, using 'easy_install -U distribute'."
147 "\n\n(Currently using %r)") % (version, e.args[0])
148 sys.exit(2)
149 else:
150 del pkg_resources, sys.modules['pkg_resources'] # reload ok
151 return _do_download(version, download_base, to_dir, download_delay)
152 except pkg_resources.DistributionNotFound:
153 return _do_download(version, download_base, to_dir, download_delay)
156 def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
157 to_dir=os.curdir, delay=15):
158 """Download distribute from a specified location and return its filename
160 `version` should be a valid distribute version number that is available
161 as an egg for download under the `download_base` URL (which should end
162 with a '/'). `to_dir` is the directory where the egg will be downloaded.
163 `delay` is the number of seconds to pause before an actual download
164 attempt.
165 """
166 # making sure we use the absolute path
167 to_dir = os.path.abspath(to_dir)
168 import urllib2
169 tgz_name = "distribute-%s.tar.gz" % version
170 url = download_base + tgz_name
171 saveto = os.path.join(to_dir, tgz_name)
172 src = dst = None
173 if not os.path.exists(saveto): # Avoid repeated downloads
174 try:
175 log.warn("Downloading %s", url)
176 src = urllib2.urlopen(url)
177 # Read/write all in one block, so we don't create a corrupt file
178 # if the download is interrupted.
179 data = src.read()
180 dst = open(saveto, "wb")
181 dst.write(data)
182 finally:
183 if src:
184 src.close()
185 if dst:
186 dst.close()
187 return os.path.realpath(saveto)
190 def _patch_file(path, content):
191 """Will backup the file then patch it"""
192 existing_content = open(path).read()
193 if existing_content == content:
194 # already patched
195 log.warn('Already patched.')
196 return False
197 log.warn('Patching...')
198 _rename_path(path)
199 f = open(path, 'w')
200 try:
201 f.write(content)
202 finally:
203 f.close()
204 return True
207 def _same_content(path, content):
208 return open(path).read() == content
211 def _rename_path(path):
212 new_name = path + '.OLD.%s' % time.time()
213 log.warn('Renaming %s into %s', path, new_name)
214 try:
215 from setuptools.sandbox import DirectorySandbox
216 def _violation(*args):
217 pass
218 DirectorySandbox._violation = _violation
219 except ImportError:
220 pass
222 os.rename(path, new_name)
223 return new_name
226 def _remove_flat_installation(placeholder):
227 if not os.path.isdir(placeholder):
228 log.warn('Unkown installation at %s', placeholder)
229 return False
230 found = False
231 for file in os.listdir(placeholder):
232 if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
233 found = True
234 break
235 if not found:
236 log.warn('Could not locate setuptools*.egg-info')
237 else:
238 log.warn('Removing elements out of the way...')
239 pkg_info = os.path.join(placeholder, file)
240 if os.path.isdir(pkg_info):
241 patched = _patch_egg_dir(pkg_info)
242 else:
243 patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
245 if not patched:
246 log.warn('%s already patched.', pkg_info)
247 return False
248 # now let's move the files out of the way
249 for element in ('setuptools', 'pkg_resources.py', 'site.py'):
250 element = os.path.join(placeholder, element)
251 if os.path.exists(element):
252 _rename_path(element)
253 else:
254 log.warn('Could not find the %s element of the '
255 'Setuptools distribution', element)
256 return True
259 def _after_install(dist):
260 log.warn('After install bootstrap.')
261 placeholder = dist.get_command_obj('install').install_purelib
262 if not placeholder or not os.path.exists(placeholder):
263 log.warn('Could not find the install location')
264 return
265 pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
266 setuptools_file = 'setuptools-0.6c9-py%s.egg-info' % pyver
267 pkg_info = os.path.join(placeholder, setuptools_file)
268 if os.path.exists(pkg_info):
269 log.warn('%s already exists', pkg_info)
270 return
271 log.warn('Creating %s', pkg_info)
272 f = open(pkg_info, 'w')
273 try:
274 f.write(SETUPTOOLS_PKG_INFO)
275 finally:
276 f.close()
277 pth_file = os.path.join(placeholder, 'setuptools.pth')
278 log.warn('Creating %s', pth_file)
279 f = open(pth_file, 'w')
280 try:
281 f.write(os.path.join(os.curdir, setuptools_file))
282 finally:
283 f.close()
286 def _patch_egg_dir(path):
287 # let's check if it's already patched
288 pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
289 if os.path.exists(pkg_info):
290 if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
291 log.warn('%s already patched.', pkg_info)
292 return False
293 _rename_path(path)
294 os.mkdir(path)
295 os.mkdir(os.path.join(path, 'EGG-INFO'))
296 pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
297 f = open(pkg_info, 'w')
298 try:
299 f.write(SETUPTOOLS_PKG_INFO)
300 finally:
301 f.close()
302 return True
305 def _before_install():
306 log.warn('Before install bootstrap.')
307 fake_setuptools()
310 def _under_prefix(location):
311 if 'install' not in sys.argv:
312 return True
313 args = sys.argv[sys.argv.index('install')+1:]
314 for index, arg in enumerate(args):
315 for option in ('--root', '--prefix'):
316 if arg.startswith('%s=' % option):
317 top_dir = arg.split('root=')[-1]
318 return location.startswith(top_dir)
319 elif arg == option:
320 if len(args) > index:
321 top_dir = args[index+1]
322 return location.startswith(top_dir)
323 elif option == '--user' and USER_SITE is not None:
324 return location.startswith(USER_SITE)
325 return True
328 def fake_setuptools():
329 log.warn('Scanning installed packages')
330 try:
331 import pkg_resources
332 except ImportError:
333 # we're cool
334 log.warn('Setuptools or Distribute does not seem to be installed.')
335 return
336 ws = pkg_resources.working_set
337 setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
338 if setuptools_dist is None:
339 log.warn('No setuptools distribution found')
340 return
341 # detecting if it was already faked
342 setuptools_location = setuptools_dist.location
343 log.warn('Setuptools installation detected at %s', setuptools_location)
345 # if --root or --preix was provided, and if
346 # setuptools is not located in them, we don't patch it
347 if not _under_prefix(setuptools_location):
348 log.warn('Not patching, --root or --prefix is installing Distribute'
349 ' in another location')
350 return
352 # let's see if its an egg
353 if not setuptools_location.endswith('.egg'):
354 log.warn('Non-egg installation')
355 res = _remove_flat_installation(setuptools_location)
356 if not res:
357 return
358 else:
359 log.warn('Egg installation')
360 pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
361 if (os.path.exists(pkg_info) and
362 _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
363 log.warn('Already patched.')
364 return
365 log.warn('Patching...')
366 # let's create a fake egg replacing setuptools one
367 res = _patch_egg_dir(setuptools_location)
368 if not res:
369 return
370 log.warn('Patched done.')
371 _relaunch()
374 def _relaunch():
375 log.warn('Relaunching...')
376 # we have to relaunch the process
377 args = [sys.executable] + sys.argv
378 sys.exit(subprocess.call(args))
381 def _extractall(self, path=".", members=None):
382 """Extract all members from the archive to the current working
383 directory and set owner, modification time and permissions on
384 directories afterwards. `path' specifies a different directory
385 to extract to. `members' is optional and must be a subset of the
386 list returned by getmembers().
387 """
388 import copy
389 import operator
390 from tarfile import ExtractError
391 directories = []
393 if members is None:
394 members = self
396 for tarinfo in members:
397 if tarinfo.isdir():
398 # Extract directories with a safe mode.
399 directories.append(tarinfo)
400 tarinfo = copy.copy(tarinfo)
401 tarinfo.mode = 0700
402 self.extract(tarinfo, path)
404 # Reverse sort directories.
405 if sys.version_info < (2, 4):
406 def sorter(dir1, dir2):
407 return cmp(dir1.name, dir2.name)
408 directories.sort(sorter)
409 directories.reverse()
410 else:
411 directories.sort(key=operator.attrgetter('name'), reverse=True)
413 # Set correct owner, mtime and filemode on directories.
414 for tarinfo in directories:
415 dirpath = os.path.join(path, tarinfo.name)
416 try:
417 self.chown(tarinfo, dirpath)
418 self.utime(tarinfo, dirpath)
419 self.chmod(tarinfo, dirpath)
420 except ExtractError, e:
421 if self.errorlevel > 1:
422 raise
423 else:
424 self._dbg(1, "tarfile: %s" % e)
427 def main(argv, version=DEFAULT_VERSION):
428 """Install or upgrade setuptools and EasyInstall"""
429 tarball = download_setuptools()
430 _install(tarball)
433 if __name__ == '__main__':
434 main(sys.argv[1:])