1 # Copyright 2012 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
11 # 2. Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials
14 # provided with the distribution.
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
36 """Bootstrap distribute installation
38 If you want to use setuptools in your package's setup.py, just include this
39 file in the same directory with it, and add this to the top of your setup.py::
41 from distribute_setup import use_setuptools
44 If you want to require a specific version of setuptools, set a download
45 mirror, or use an alternate download directory, you can do so by supplying
46 the appropriate options to ``use_setuptools()``.
48 This file can also be run as a script to install or upgrade setuptools.
56 from distutils import log
59 from site import USER_SITE
66 def _python_cmd(*args):
67 args = (sys.executable,) + args
68 return subprocess.call(args) == 0
71 # will be used for python 2.3
72 def _python_cmd(*args):
73 args = (sys.executable,) + args
74 # quoting arguments if windows
75 if sys.platform == 'win32':
80 args = [quote(arg) for arg in args]
81 return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
83 DEFAULT_VERSION = "0.6.10"
84 DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
85 SETUPTOOLS_FAKED_VERSION = "0.6c11"
87 SETUPTOOLS_PKG_INFO = """\
97 """ % SETUPTOOLS_FAKED_VERSION
100 def _install(tarball):
101 # extracting the tarball
102 tmpdir = tempfile.mkdtemp()
103 log.warn('Extracting in %s', tmpdir)
107 tar = tarfile.open(tarball)
111 # going in the directory
112 subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
114 log.warn('Now working in %s', subdir)
117 log.warn('Installing Distribute')
118 if not _python_cmd('setup.py', 'install'):
119 log.warn('Something went wrong during the installation.')
120 log.warn('See the error message above.')
125 def _build_egg(egg, tarball, to_dir):
126 # extracting the tarball
127 tmpdir = tempfile.mkdtemp()
128 log.warn('Extracting in %s', tmpdir)
132 tar = tarfile.open(tarball)
136 # going in the directory
137 subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
139 log.warn('Now working in %s', subdir)
142 log.warn('Building a Distribute egg in %s', to_dir)
143 _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
147 # returning the result
149 if not os.path.exists(egg):
150 raise IOError('Could not build the egg.')
153 def _do_download(version, download_base, to_dir, download_delay):
154 egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
155 % (version, sys.version_info[0], sys.version_info[1]))
156 if not os.path.exists(egg):
157 tarball = download_setuptools(version, download_base,
158 to_dir, download_delay)
159 _build_egg(egg, tarball, to_dir)
160 sys.path.insert(0, egg)
162 setuptools.bootstrap_install_from = egg
165 def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
166 to_dir=os.curdir, download_delay=15, no_fake=True):
167 # making sure we use the absolute path
168 to_dir = os.path.abspath(to_dir)
169 was_imported = 'pkg_resources' in sys.modules or \
170 'setuptools' in sys.modules
174 if not hasattr(pkg_resources, '_distribute'):
179 return _do_download(version, download_base, to_dir, download_delay)
181 pkg_resources.require("distribute>="+version)
183 except pkg_resources.VersionConflict:
184 e = sys.exc_info()[1]
187 "The required version of distribute (>=%s) is not available,\n"
188 "and can't be installed while this script is running. Please\n"
189 "install a more recent version first, using\n"
190 "'easy_install -U distribute'."
191 "\n\n(Currently using %r)\n" % (version, e.args[0]))
194 del pkg_resources, sys.modules['pkg_resources'] # reload ok
195 return _do_download(version, download_base, to_dir,
197 except pkg_resources.DistributionNotFound:
198 return _do_download(version, download_base, to_dir,
202 _create_fake_setuptools_pkg_info(to_dir)
204 def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
205 to_dir=os.curdir, delay=15):
206 """Download distribute from a specified location and return its filename
208 `version` should be a valid distribute version number that is available
209 as an egg for download under the `download_base` URL (which should end
210 with a '/'). `to_dir` is the directory where the egg will be downloaded.
211 `delay` is the number of seconds to pause before an actual download
214 # making sure we use the absolute path
215 to_dir = os.path.abspath(to_dir)
217 from urllib.request import urlopen
219 from urllib2 import urlopen
220 tgz_name = "distribute-%s.tar.gz" % version
221 url = download_base + tgz_name
222 saveto = os.path.join(to_dir, tgz_name)
224 if not os.path.exists(saveto): # Avoid repeated downloads
226 log.warn("Downloading %s", url)
228 # Read/write all in one block, so we don't create a corrupt file
229 # if the download is interrupted.
231 dst = open(saveto, "wb")
238 return os.path.realpath(saveto)
240 def _no_sandbox(function):
241 def __no_sandbox(*args, **kw):
243 from setuptools.sandbox import DirectorySandbox
244 if not hasattr(DirectorySandbox, '_old'):
245 def violation(*args):
247 DirectorySandbox._old = DirectorySandbox._violation
248 DirectorySandbox._violation = violation
256 return function(*args, **kw)
259 DirectorySandbox._violation = DirectorySandbox._old
260 del DirectorySandbox._old
264 def _patch_file(path, content):
265 """Will backup the file then patch it"""
266 existing_content = open(path).read()
267 if existing_content == content:
269 log.warn('Already patched.')
271 log.warn('Patching...')
280 _patch_file = _no_sandbox(_patch_file)
282 def _same_content(path, content):
283 return open(path).read() == content
285 def _rename_path(path):
286 new_name = path + '.OLD.%s' % time.time()
287 log.warn('Renaming %s into %s', path, new_name)
288 os.rename(path, new_name)
291 def _remove_flat_installation(placeholder):
292 if not os.path.isdir(placeholder):
293 log.warn('Unkown installation at %s', placeholder)
296 for file in os.listdir(placeholder):
297 if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
301 log.warn('Could not locate setuptools*.egg-info')
304 log.warn('Removing elements out of the way...')
305 pkg_info = os.path.join(placeholder, file)
306 if os.path.isdir(pkg_info):
307 patched = _patch_egg_dir(pkg_info)
309 patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
312 log.warn('%s already patched.', pkg_info)
314 # now let's move the files out of the way
315 for element in ('setuptools', 'pkg_resources.py', 'site.py'):
316 element = os.path.join(placeholder, element)
317 if os.path.exists(element):
318 _rename_path(element)
320 log.warn('Could not find the %s element of the '
321 'Setuptools distribution', element)
324 _remove_flat_installation = _no_sandbox(_remove_flat_installation)
326 def _after_install(dist):
327 log.warn('After install bootstrap.')
328 placeholder = dist.get_command_obj('install').install_purelib
329 _create_fake_setuptools_pkg_info(placeholder)
331 def _create_fake_setuptools_pkg_info(placeholder):
332 if not placeholder or not os.path.exists(placeholder):
333 log.warn('Could not find the install location')
335 pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
336 setuptools_file = 'setuptools-%s-py%s.egg-info' % \
337 (SETUPTOOLS_FAKED_VERSION, pyver)
338 pkg_info = os.path.join(placeholder, setuptools_file)
339 if os.path.exists(pkg_info):
340 log.warn('%s already exists', pkg_info)
343 log.warn('Creating %s', pkg_info)
344 f = open(pkg_info, 'w')
346 f.write(SETUPTOOLS_PKG_INFO)
350 pth_file = os.path.join(placeholder, 'setuptools.pth')
351 log.warn('Creating %s', pth_file)
352 f = open(pth_file, 'w')
354 f.write(os.path.join(os.curdir, setuptools_file))
358 _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
360 def _patch_egg_dir(path):
361 # let's check if it's already patched
362 pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
363 if os.path.exists(pkg_info):
364 if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
365 log.warn('%s already patched.', pkg_info)
369 os.mkdir(os.path.join(path, 'EGG-INFO'))
370 pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
371 f = open(pkg_info, 'w')
373 f.write(SETUPTOOLS_PKG_INFO)
378 _patch_egg_dir = _no_sandbox(_patch_egg_dir)
380 def _before_install():
381 log.warn('Before install bootstrap.')
385 def _under_prefix(location):
386 if 'install' not in sys.argv:
388 args = sys.argv[sys.argv.index('install')+1:]
389 for index, arg in enumerate(args):
390 for option in ('--root', '--prefix'):
391 if arg.startswith('%s=' % option):
392 top_dir = arg.split('root=')[-1]
393 return location.startswith(top_dir)
395 if len(args) > index:
396 top_dir = args[index+1]
397 return location.startswith(top_dir)
398 if arg == '--user' and USER_SITE is not None:
399 return location.startswith(USER_SITE)
403 def _fake_setuptools():
404 log.warn('Scanning installed packages')
409 log.warn('Setuptools or Distribute does not seem to be installed.')
411 ws = pkg_resources.working_set
413 setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
417 setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
419 if setuptools_dist is None:
420 log.warn('No setuptools distribution found')
422 # detecting if it was already faked
423 setuptools_location = setuptools_dist.location
424 log.warn('Setuptools installation detected at %s', setuptools_location)
426 # if --root or --preix was provided, and if
427 # setuptools is not located in them, we don't patch it
428 if not _under_prefix(setuptools_location):
429 log.warn('Not patching, --root or --prefix is installing Distribute'
430 ' in another location')
433 # let's see if its an egg
434 if not setuptools_location.endswith('.egg'):
435 log.warn('Non-egg installation')
436 res = _remove_flat_installation(setuptools_location)
440 log.warn('Egg installation')
441 pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
442 if (os.path.exists(pkg_info) and
443 _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
444 log.warn('Already patched.')
446 log.warn('Patching...')
447 # let's create a fake egg replacing setuptools one
448 res = _patch_egg_dir(setuptools_location)
451 log.warn('Patched done.')
456 log.warn('Relaunching...')
457 # we have to relaunch the process
458 # pip marker to avoid a relaunch bug
459 if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
460 sys.argv[0] = 'setup.py'
461 args = [sys.executable] + sys.argv
462 sys.exit(subprocess.call(args))
465 def _extractall(self, path=".", members=None):
466 """Extract all members from the archive to the current working
467 directory and set owner, modification time and permissions on
468 directories afterwards. `path' specifies a different directory
469 to extract to. `members' is optional and must be a subset of the
470 list returned by getmembers().
474 from tarfile import ExtractError
480 for tarinfo in members:
482 # Extract directories with a safe mode.
483 directories.append(tarinfo)
484 tarinfo = copy.copy(tarinfo)
485 tarinfo.mode = 448 # decimal for oct 0700
486 self.extract(tarinfo, path)
488 # Reverse sort directories.
489 if sys.version_info < (2, 4):
490 def sorter(dir1, dir2):
491 return cmp(dir1.name, dir2.name)
492 directories.sort(sorter)
493 directories.reverse()
495 directories.sort(key=operator.attrgetter('name'), reverse=True)
497 # Set correct owner, mtime and filemode on directories.
498 for tarinfo in directories:
499 dirpath = os.path.join(path, tarinfo.name)
501 self.chown(tarinfo, dirpath)
502 self.utime(tarinfo, dirpath)
503 self.chmod(tarinfo, dirpath)
505 e = sys.exc_info()[1]
506 if self.errorlevel > 1:
509 self._dbg(1, "tarfile: %s" % e)
512 def main(argv, version=DEFAULT_VERSION):
513 """Install or upgrade setuptools and EasyInstall"""
514 tarball = download_setuptools()
518 if __name__ == '__main__':