~barry/mailman/events-and-web

6648.1.1 by Barry Warsaw
Basic buildout infrastructure.
1
##############################################################################
2
#
6933 by Barry Warsaw
zc.buildout 1.5
3
# Copyright (c) 2006 Zope Foundation and Contributors.
6648.1.1 by Barry Warsaw
Basic buildout infrastructure.
4
# All Rights Reserved.
5
#
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
"""Bootstrap a buildout-based project
15
16
Simply run this script in a directory containing a buildout.cfg.
17
The script accepts buildout command-line options, so you can
18
use the -c option to specify an alternate configuration file.
19
"""
20
6933 by Barry Warsaw
zc.buildout 1.5
21
import os, shutil, sys, tempfile, textwrap, urllib, urllib2, subprocess
22
from optparse import OptionParser
6648.1.1 by Barry Warsaw
Basic buildout infrastructure.
23
24
if sys.platform == 'win32':
25
    def quote(c):
26
        if ' ' in c:
27
            return '"%s"' % c # work around spawn lamosity on windows
28
        else:
29
            return c
30
else:
6933 by Barry Warsaw
zc.buildout 1.5
31
    quote = str
32
33
# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
34
stdout, stderr = subprocess.Popen(
35
    [sys.executable, '-Sc',
36
     'try:\n'
37
     '    import ConfigParser\n'
38
     'except ImportError:\n'
39
     '    print 1\n'
40
     'else:\n'
41
     '    print 0\n'],
42
    stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
43
has_broken_dash_S = bool(int(stdout.strip()))
44
45
# In order to be more robust in the face of system Pythons, we want to
46
# run without site-packages loaded.  This is somewhat tricky, in
47
# particular because Python 2.6's distutils imports site, so starting
48
# with the -S flag is not sufficient.  However, we'll start with that:
49
if not has_broken_dash_S and 'site' in sys.modules:
50
    # We will restart with python -S.
51
    args = sys.argv[:]
52
    args[0:0] = [sys.executable, '-S']
53
    args = map(quote, args)
54
    os.execv(sys.executable, args)
55
# Now we are running with -S.  We'll get the clean sys.path, import site
56
# because distutils will do it later, and then reset the path and clean
57
# out any namespace packages from site-packages that might have been
58
# loaded by .pth files.
59
clean_path = sys.path[:]
60
import site
61
sys.path[:] = clean_path
62
for k, v in sys.modules.items():
63
    if (hasattr(v, '__path__') and
64
        len(v.__path__)==1 and
65
        not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))):
66
        # This is a namespace package.  Remove it.
67
        sys.modules.pop(k)
68
69
is_jython = sys.platform.startswith('java')
70
71
setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
72
distribute_source = 'http://python-distribute.org/distribute_setup.py'
73
74
# parsing arguments
75
def normalize_to_url(option, opt_str, value, parser):
76
    if value:
77
        if '://' not in value: # It doesn't smell like a URL.
78
            value = 'file://%s' % (
79
                urllib.pathname2url(
80
                    os.path.abspath(os.path.expanduser(value))),)
81
        if opt_str == '--download-base' and not value.endswith('/'):
82
            # Download base needs a trailing slash to make the world happy.
83
            value += '/'
84
    else:
85
        value = None
86
    name = opt_str[2:].replace('-', '_')
87
    setattr(parser.values, name, value)
88
89
usage = '''\
90
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
91
92
Bootstraps a buildout-based project.
93
94
Simply run this script in a directory containing a buildout.cfg, using the
95
Python that you want bin/buildout to use.
96
97
Note that by using --setup-source and --download-base to point to
98
local resources, you can keep this script from going over the network.
99
'''
100
101
parser = OptionParser(usage=usage)
102
parser.add_option("-v", "--version", dest="version",
103
                          help="use a specific zc.buildout version")
104
parser.add_option("-d", "--distribute",
105
                   action="store_true", dest="use_distribute", default=False,
106
                   help="Use Distribute rather than Setuptools.")
107
parser.add_option("--setup-source", action="callback", dest="setup_source",
108
                  callback=normalize_to_url, nargs=1, type="string",
109
                  help=("Specify a URL or file location for the setup file. "
110
                        "If you use Setuptools, this will default to " +
111
                        setuptools_source + "; if you use Distribute, this "
112
                        "will default to " + distribute_source +"."))
113
parser.add_option("--download-base", action="callback", dest="download_base",
114
                  callback=normalize_to_url, nargs=1, type="string",
115
                  help=("Specify a URL or directory for downloading "
116
                        "zc.buildout and either Setuptools or Distribute. "
117
                        "Defaults to PyPI."))
118
parser.add_option("--eggs",
119
                  help=("Specify a directory for storing eggs.  Defaults to "
120
                        "a temporary directory that is deleted when the "
121
                        "bootstrap script completes."))
122
parser.add_option("-t", "--accept-buildout-test-releases",
123
                  dest='accept_buildout_test_releases',
124
                  action="store_true", default=False,
125
                  help=("Normally, if you do not specify a --version, the "
126
                        "bootstrap script and buildout gets the newest "
127
                        "*final* versions of zc.buildout and its recipes and "
128
                        "extensions for you.  If you use this flag, "
129
                        "bootstrap and buildout will get the newest releases "
130
                        "even if they are alphas or betas."))
131
parser.add_option("-c", None, action="store", dest="config_file",
132
                   help=("Specify the path to the buildout configuration "
133
                         "file to be used."))
134
135
options, args = parser.parse_args()
136
137
# if -c was provided, we push it back into args for buildout's main function
138
if options.config_file is not None:
139
    args += ['-c', options.config_file]
140
141
if options.eggs:
142
    eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
143
else:
144
    eggs_dir = tempfile.mkdtemp()
145
146
if options.setup_source is None:
147
    if options.use_distribute:
148
        options.setup_source = distribute_source
149
    else:
150
        options.setup_source = setuptools_source
151
152
if options.accept_buildout_test_releases:
153
    args.append('buildout:accept-buildout-test-releases=true')
154
args.append('bootstrap')
155
156
try:
157
    import pkg_resources
158
    import setuptools # A flag.  Sometimes pkg_resources is installed alone.
159
    if not hasattr(pkg_resources, '_distribute'):
160
        raise ImportError
161
except ImportError:
162
    ez_code = urllib2.urlopen(
163
        options.setup_source).read().replace('\r\n', '\n')
164
    ez = {}
165
    exec ez_code in ez
166
    setup_args = dict(to_dir=eggs_dir, download_delay=0)
167
    if options.download_base:
168
        setup_args['download_base'] = options.download_base
169
    if options.use_distribute:
170
        setup_args['no_fake'] = True
171
    ez['use_setuptools'](**setup_args)
172
    reload(sys.modules['pkg_resources'])
173
    import pkg_resources
174
    # This does not (always?) update the default working set.  We will
175
    # do it.
176
    for path in sys.path:
177
        if path not in pkg_resources.working_set.entries:
178
            pkg_resources.working_set.add_entry(path)
179
180
cmd = [quote(sys.executable),
181
       '-c',
182
       quote('from setuptools.command.easy_install import main; main()'),
183
       '-mqNxd',
184
       quote(eggs_dir)]
185
186
if not has_broken_dash_S:
187
    cmd.insert(1, '-S')
188
189
find_links = options.download_base
190
if not find_links:
191
    find_links = os.environ.get('bootstrap-testing-find-links')
192
if find_links:
193
    cmd.extend(['-f', quote(find_links)])
194
195
if options.use_distribute:
196
    setup_requirement = 'distribute'
197
else:
198
    setup_requirement = 'setuptools'
199
ws = pkg_resources.working_set
200
setup_requirement_path = ws.find(
201
    pkg_resources.Requirement.parse(setup_requirement)).location
202
env = dict(
203
    os.environ,
204
    PYTHONPATH=setup_requirement_path)
205
206
requirement = 'zc.buildout'
207
version = options.version
208
if version is None and not options.accept_buildout_test_releases:
209
    # Figure out the most recent final version of zc.buildout.
210
    import setuptools.package_index
211
    _final_parts = '*final-', '*final'
212
    def _final_version(parsed_version):
213
        for part in parsed_version:
214
            if (part[:1] == '*') and (part not in _final_parts):
215
                return False
216
        return True
217
    index = setuptools.package_index.PackageIndex(
218
        search_path=[setup_requirement_path])
219
    if find_links:
220
        index.add_find_links((find_links,))
221
    req = pkg_resources.Requirement.parse(requirement)
222
    if index.obtain(req) is not None:
223
        best = []
224
        bestv = None
225
        for dist in index[req.project_name]:
226
            distv = dist.parsed_version
227
            if _final_version(distv):
228
                if bestv is None or distv > bestv:
229
                    best = [dist]
230
                    bestv = distv
231
                elif distv == bestv:
232
                    best.append(dist)
233
        if best:
234
            best.sort()
235
            version = best[-1].version
236
if version:
237
    requirement = '=='.join((requirement, version))
238
cmd.append(requirement)
6648.1.1 by Barry Warsaw
Basic buildout infrastructure.
239
240
if is_jython:
241
    import subprocess
6933 by Barry Warsaw
zc.buildout 1.5
242
    exitcode = subprocess.Popen(cmd, env=env).wait()
243
else: # Windows prefers this, apparently; otherwise we would prefer subprocess
244
    exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
245
if exitcode != 0:
246
    sys.stdout.flush()
247
    sys.stderr.flush()
248
    print ("An error occurred when trying to install zc.buildout. "
249
           "Look above this message for any errors that "
250
           "were output by easy_install.")
251
    sys.exit(exitcode)
252
253
ws.add_entry(eggs_dir)
254
ws.require(requirement)
6648.1.1 by Barry Warsaw
Basic buildout infrastructure.
255
import zc.buildout.buildout
6933 by Barry Warsaw
zc.buildout 1.5
256
zc.buildout.buildout.main(args)
257
if not options.eggs: # clean up temporary egg directory
258
    shutil.rmtree(eggs_dir)