~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to bootstrap.py

  • Committer: Gary Poster
  • Date: 2010-03-20 01:13:25 UTC
  • mto: (7675.623.23 launchpad)
  • mto: This revision was merged to the branch mainline in revision 10633.
  • Revision ID: gary.poster@canonical.com-20100320011325-sub5s75lb4buu0gc
update to newer version of buildout branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
The script accepts buildout command-line options, so you can
18
18
use the -c option to specify an alternate configuration file.
19
19
 
20
 
$Id: bootstrap.py 101930 2009-07-15 18:34:35Z gary $
 
20
$Id$
21
21
"""
22
22
 
23
 
import os, re, shutil, sys, tempfile, textwrap, urllib, urllib2
24
 
 
25
 
# We have to manually parse our options rather than using one of the stdlib
26
 
# tools because we want to pass the ones we don't recognize along to
27
 
# zc.buildout.buildout.main.
28
 
 
29
 
configuration = {
30
 
    '--ez_setup-source': 'http://peak.telecommunity.com/dist/ez_setup.py',
31
 
    '--version': '',
32
 
    '--download-base': None,
33
 
    '--eggs': None}
34
 
 
35
 
helpstring = __doc__ + textwrap.dedent('''
36
 
    This script recognizes the following options itself.  The first option it
37
 
    encounters that is not one of these will cause the script to stop parsing
38
 
    options and pass the rest on to buildout.  Therefore, if you want to use
39
 
    any of the following options *and* buildout command-line options like
40
 
    -c, first use the following options, and then use the buildout options.
41
 
 
42
 
    Options: 
43
 
      --version=ZC_BUILDOUT_VERSION
44
 
                Specify a version number of the zc.buildout to use
45
 
      --ez_setup-source=URL_OR_FILE
46
 
                Specify a URL or file location for the ez_setup file.
47
 
                Defaults to
48
 
                %(--ez_setup-source)s
49
 
      --download-base=URL_OR_DIRECTORY
50
 
                Specify a URL or directory for downloading setuptools and
51
 
                zc.buildout.  Defaults to PyPI.
52
 
      --eggs=DIRECTORY
53
 
                Specify a directory for storing eggs.  Defaults to a temporary
54
 
                directory that is deleted when the bootstrap script completes.
55
 
 
56
 
    By using --ez_setup-source and --download-base to point to local resources,
57
 
    you can keep this script from going over the network.
58
 
    ''' % configuration)
59
 
match_equals = re.compile(r'(%s)=(.*)' % ('|'.join(configuration),)).match
60
 
args = sys.argv[1:]
61
 
if args == ['--help']:
62
 
    print helpstring
63
 
    sys.exit(0)
64
 
 
65
 
# If we end up using a temporary directory for storing our eggs, this will
66
 
# hold the path of that directory.  On the other hand, if an explicit directory
67
 
# is specified in the argv, this will remain None.
68
 
tmpeggs = None
69
 
 
70
 
while args:
71
 
    val = args[0]
72
 
    if val in configuration:
73
 
        del args[0]
74
 
        if not args or args[0].startswith('-'):
75
 
            print "ERROR: %s requires an argument."
76
 
            print helpstring
77
 
            sys.exit(1)
78
 
        configuration[val] = args[0]
79
 
    else:
80
 
        match = match_equals(val)
81
 
        if match and match.group(1) in configuration:
82
 
            configuration[match.group(1)] = match.group(2)
 
23
import os, shutil, sys, tempfile, textwrap, urllib, urllib2
 
24
from optparse import OptionParser
 
25
 
 
26
if sys.platform == 'win32':
 
27
    def quote(c):
 
28
        if ' ' in c:
 
29
            return '"%s"' % c # work around spawn lamosity on windows
83
30
        else:
84
 
            break
85
 
    del args[0]
86
 
 
87
 
for name in ('--ez_setup-source', '--download-base'):
88
 
    val = configuration[name]
89
 
    if val is not None and '://' not in val: # We're being lazy.
90
 
        configuration[name] = 'file://%s' % (
91
 
            urllib.pathname2url(os.path.abspath(os.path.expanduser(val))),)
92
 
 
93
 
if (configuration['--download-base'] and
94
 
    not configuration['--download-base'].endswith('/')):
95
 
    # Download base needs a trailing slash to make the world happy.
96
 
    configuration['--download-base'] += '/'
97
 
 
98
 
if not configuration['--eggs']:
99
 
    configuration['--eggs'] = tmpeggs = tempfile.mkdtemp()
100
 
else:
101
 
    configuration['--eggs'] = os.path.abspath(
102
 
        os.path.expanduser(configuration['--eggs']))
103
 
 
104
 
# The requirement is what we will pass to setuptools to specify zc.buildout.
105
 
requirement = 'zc.buildout'
106
 
if configuration['--version']:
107
 
    requirement += '==' + configuration['--version']
 
31
            return c
 
32
else:
 
33
    quote = str
 
34
 
 
35
# In order to be more robust in the face of system Pythons, we want to
 
36
# run without site-packages loaded.  This is somewhat tricky, in
 
37
# particular because Python 2.6's distutils imports site, so starting
 
38
# with the -S flag is not sufficient.  However, we'll start with that:
 
39
if 'site' in sys.modules:
 
40
    # We will restart with python -S.
 
41
    args = sys.argv[:]
 
42
    args[0:0] = [sys.executable, '-S']
 
43
    args = map(quote, args)
 
44
    os.execv(sys.executable, args)
 
45
# Now we are running with -S.  We'll get the clean sys.path, import site
 
46
# because distutils will do it later, and then reset the path and clean
 
47
# out any namespace packages from site-packages that might have been
 
48
# loaded by .pth files.
 
49
clean_path = sys.path[:]
 
50
import site
 
51
sys.path[:] = clean_path
 
52
for k, v in sys.modules.items():
 
53
    if (hasattr(v, '__path__') and
 
54
        len(v.__path__)==1 and
 
55
        not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))):
 
56
        # This is a namespace package.  Remove it.
 
57
        sys.modules.pop(k)
 
58
 
 
59
is_jython = sys.platform.startswith('java')
 
60
 
 
61
setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
 
62
distribute_source = 'http://python-distribute.org/distribute_setup.py'
 
63
 
 
64
# parsing arguments
 
65
def normalize_to_url(option, opt_str, value, parser):
 
66
    if value:
 
67
        if '://' not in value: # It doesn't smell like a URL.
 
68
            value = 'file://%s' % (
 
69
                urllib.pathname2url(
 
70
                    os.path.abspath(os.path.expanduser(value))),)
 
71
        if opt_str == '--download-base' and not value.endswith('/'):
 
72
            # Download base needs a trailing slash to make the world happy.
 
73
            value += '/'
 
74
    else:
 
75
        value = None
 
76
    name = opt_str[2:].replace('-', '_')
 
77
    setattr(parser.values, name, value)
 
78
 
 
79
usage = '''\
 
80
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
 
81
 
 
82
Bootstraps a buildout-based project.
 
83
 
 
84
Simply run this script in a directory containing a buildout.cfg, using the
 
85
Python that you want bin/buildout to use.
 
86
 
 
87
Note that by using --setup-source and --download-base to point to
 
88
local resources, you can keep this script from going over the network.
 
89
'''
 
90
 
 
91
parser = OptionParser(usage=usage)
 
92
parser.add_option("-v", "--version", dest="version",
 
93
                          help="use a specific zc.buildout version")
 
94
parser.add_option("-d", "--distribute",
 
95
                   action="store_true", dest="use_distribute", default=False,
 
96
                   help="Use Distribute rather than Setuptools.")
 
97
parser.add_option("--setup-source", action="callback", dest="setup_source",
 
98
                  callback=normalize_to_url, nargs=1, type="string",
 
99
                  help=("Specify a URL or file location for the setup file. "
 
100
                        "If you use Setuptools, this will default to " +
 
101
                        setuptools_source + "; if you use Distribute, this "
 
102
                        "will default to " + distribute_source +"."))
 
103
parser.add_option("--download-base", action="callback", dest="download_base",
 
104
                  callback=normalize_to_url, nargs=1, type="string",
 
105
                  help=("Specify a URL or directory for downloading "
 
106
                        "zc.buildout and either Setuptools or Distribute. "
 
107
                        "Defaults to PyPI."))
 
108
parser.add_option("--eggs",
 
109
                  help=("Specify a directory for storing eggs.  Defaults to "
 
110
                        "a temporary directory that is deleted when the "
 
111
                        "bootstrap script completes."))
 
112
parser.add_option("-c", None, action="store", dest="config_file",
 
113
                   help=("Specify the path to the buildout configuration "
 
114
                         "file to be used."))
 
115
 
 
116
options, args = parser.parse_args()
 
117
 
 
118
# if -c was provided, we push it back into args for buildout' main function
 
119
if options.config_file is not None:
 
120
    args += ['-c', options.config_file]
 
121
 
 
122
if options.eggs:
 
123
    eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
 
124
else:
 
125
    eggs_dir = tempfile.mkdtemp()
 
126
 
 
127
if options.setup_source is None:
 
128
    if options.use_distribute:
 
129
        options.setup_source = distribute_source
 
130
    else:
 
131
        options.setup_source = setuptools_source
 
132
 
 
133
args = args + ['bootstrap']
 
134
 
108
135
 
109
136
try:
 
137
    to_reload = False
 
138
    import pkg_resources
 
139
    to_reload = True
 
140
    if not hasattr(pkg_resources, '_distribute'):
 
141
        raise ImportError
110
142
    import setuptools # A flag.  Sometimes pkg_resources is installed alone.
111
 
    import pkg_resources
112
143
except ImportError:
 
144
    ez_code = urllib2.urlopen(
 
145
        options.setup_source).read().replace('\r\n', '\n')
113
146
    ez = {}
114
 
    exec urllib2.urlopen(configuration['--ez_setup-source']).read() in ez
115
 
    setuptools_args = dict(to_dir=configuration['--eggs'], download_delay=0)
116
 
    if configuration['--download-base']:
117
 
        setuptools_args['download_base'] = configuration['--download-base']
118
 
    ez['use_setuptools'](**setuptools_args)
119
 
    import pkg_resources
 
147
    exec ez_code in ez
 
148
    setup_args = dict(to_dir=eggs_dir, download_delay=0)
 
149
    if options.download_base:
 
150
        setup_args['download_base'] = options.download_base
 
151
    if options.use_distribute:
 
152
        setup_args['no_fake'] = True
 
153
    ez['use_setuptools'](**setup_args)
 
154
    if to_reload:
 
155
        reload(pkg_resources)
 
156
    else:
 
157
        import pkg_resources
120
158
    # This does not (always?) update the default working set.  We will
121
159
    # do it.
122
160
    for path in sys.path:
123
161
        if path not in pkg_resources.working_set.entries:
124
162
            pkg_resources.working_set.add_entry(path)
125
163
 
126
 
if sys.platform == 'win32':
127
 
    def quote(c):
128
 
        if ' ' in c:
129
 
            return '"%s"' % c # work around spawn lamosity on windows
130
 
        else:
131
 
            return c
132
 
else:
133
 
    def quote (c):
134
 
        return c
135
164
cmd = [quote(sys.executable),
136
165
       '-c',
137
166
       quote('from setuptools.command.easy_install import main; main()'),
138
167
       '-mqNxd',
139
 
       quote(configuration['--eggs'])]
140
 
 
141
 
if configuration['--download-base']:
142
 
    cmd.extend(['-f', quote(configuration['--download-base'])])
143
 
 
 
168
       quote(eggs_dir)]
 
169
 
 
170
if options.download_base:
 
171
    cmd.extend(['-f', quote(options.download_base)])
 
172
 
 
173
requirement = 'zc.buildout'
 
174
if options.version:
 
175
    requirement = '=='.join((requirement, options.version))
144
176
cmd.append(requirement)
145
177
 
 
178
if options.use_distribute:
 
179
    setup_requirement = 'distribute'
 
180
else:
 
181
    setup_requirement = 'setuptools'
146
182
ws = pkg_resources.working_set
147
183
env = dict(
148
184
    os.environ,
149
 
    PYTHONPATH=ws.find(pkg_resources.Requirement.parse('setuptools')).location)
 
185
    PYTHONPATH=ws.find(
 
186
        pkg_resources.Requirement.parse(setup_requirement)).location)
150
187
 
151
 
is_jython = sys.platform.startswith('java')
152
188
if is_jython:
153
189
    import subprocess
154
190
    exitcode = subprocess.Popen(cmd, env=env).wait()
155
 
else: # Windows needs this, apparently; otherwise we would prefer subprocess
 
191
else: # Windows prefers this, apparently; otherwise we would prefer subprocess
156
192
    exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
157
193
if exitcode != 0:
158
194
    sys.stdout.flush()
 
195
    sys.stderr.flush()
159
196
    print ("An error occured when trying to install zc.buildout. "
160
197
           "Look above this message for any errors that "
161
198
           "were output by easy_install.")
162
199
    sys.exit(exitcode)
163
200
 
164
 
ws.add_entry(configuration['--eggs'])
 
201
ws.add_entry(eggs_dir)
165
202
ws.require(requirement)
166
203
import zc.buildout.buildout
167
 
args.append('bootstrap')
168
204
zc.buildout.buildout.main(args)
169
 
if tmpeggs is not None:
170
 
    shutil.rmtree(tmpeggs)
 
205
if not options.eggs: # clean up temporary egg directory
 
206
    shutil.rmtree(eggs_dir)