buildout.py 65.4 KB
Newer Older
1
##############################################################################
2
#
icemac's avatar
icemac committed
3
# Copyright (c) 2005-2009 Zope Foundation and Contributors.
4 5 6 7 8 9 10 11 12 13 14 15 16
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Buildout main script
"""

jim's avatar
jim committed
17 18 19 20 21 22 23 24 25
from rmtree import rmtree
try:
    from hashlib import md5
except ImportError:
    # Python 2.4 and older
    from md5 import md5

import ConfigParser
import copy
26
import distutils.errors
jim's avatar
jim committed
27 28
import glob
import itertools
jim's avatar
jim committed
29
import logging
30
import os
jim's avatar
jim committed
31
import pkg_resources
32 33 34
import re
import shutil
import sys
35
import tempfile
36
import UserDict
37
import warnings
alex_plugaru's avatar
alex_plugaru committed
38
import subprocess
jim's avatar
jim committed
39
import zc.buildout
40
import zc.buildout.download
41 42
import zc.buildout.easy_install

43

44
realpath = zc.buildout.easy_install.realpath
45

jim's avatar
jim committed
46 47
pkg_resources_loc = pkg_resources.working_set.find(
    pkg_resources.Requirement.parse('setuptools')).location
48

49
_isurl = re.compile('([a-zA-Z0-9+.-]+)://').match
50

georgyberdyshev's avatar
georgyberdyshev committed
51 52
is_jython = sys.platform.startswith('java')

53 54 55
_sys_executable_has_broken_dash_S = (
    zc.buildout.easy_install._has_broken_dash_S(sys.executable))

jim's avatar
jim committed
56
class MissingOption(zc.buildout.UserError, KeyError):
57
    """A required option was missing.
58 59
    """

jim's avatar
jim committed
60
class MissingSection(zc.buildout.UserError, KeyError):
61
    """A required section is missing.
62 63
    """

64 65
    def __str__(self):
        return "The referenced section, %r, was not defined." % self[0]
66

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

def _annotate_section(section, note):
    for key in section:
        section[key] = (section[key], note)
    return section

def _annotate(data, note):
    for key in data:
        data[key] = _annotate_section(data[key], note)
    return data

def _print_annotate(data):
    sections = data.keys()
    sections.sort()
    print
    print "Annotated sections"
    print "="*len("Annotated sections")
    for section in sections:
        print
        print '[%s]' % section
        keys = data[section].keys()
        keys.sort()
        for key in keys:
90 91 92 93 94 95 96 97 98 99 100 101
            value, notes = data[section][key]
            keyvalue = "%s= %s" % (key, value)
            print keyvalue
            line = '   '
            for note in notes.split():
                if note == '[+]':
                    line = '+= '
                elif note == '[-]':
                    line = '-= '
                else:
                    print line, note
                    line = '   '
102 103
    print

104

105 106 107 108 109 110 111 112 113 114 115 116
def _unannotate_section(section):
    for key in section:
        value, note = section[key]
        section[key] = value
    return section

def _unannotate(data):
    for key in data:
        data[key] = _unannotate_section(data[key])
    return data

_buildout_default_options = _annotate_section({
117
    'accept-buildout-test-releases': 'false',
118 119
    'allow-hosts': '*',
    'allow-picked-versions': 'true',
gary's avatar
gary committed
120
    'allowed-eggs-from-site-packages': '*',
121
    'bin-directory': 'bin',
122 123
    'develop-eggs-directory': 'develop-eggs',
    'eggs-directory': 'eggs',
124
    'executable': sys.executable,
gary's avatar
gary committed
125
    'exec-sitecustomize': 'true',
126
    'find-links': '',
gary's avatar
gary committed
127
    'include-site-packages': 'true',
128 129
    'install-from-cache': 'false',
    'installed': '.installed.cfg',
130
    'log-format': '',
131 132 133 134 135 136 137 138 139 140
    'log-level': 'INFO',
    'newest': 'true',
    'offline': 'false',
    'parts-directory': 'parts',
    'prefer-final': 'false',
    'python': 'buildout',
    'relative-paths': 'false',
    'socket-timeout': '',
    'unzip': 'false',
    'use-dependency-links': 'true',
141 142
    }, 'DEFAULT_VALUE')

143

144
class Buildout(UserDict.DictMixin):
145

jim's avatar
jim committed
146
    def __init__(self, config_file, cloptions,
147
                 user_defaults=True, windows_restart=False, command=None):
148

149
        __doing__ = 'Initializing.'
150

jim's avatar
jim committed
151
        self.__windows_restart = windows_restart
152 153

        # default options
154
        data = dict(buildout=_buildout_default_options.copy())
155
        self._buildout_dir = os.getcwd()
156

157 158 159 160
        if not _isurl(config_file):
            config_file = os.path.abspath(config_file)
            base = os.path.dirname(config_file)
            if not os.path.exists(config_file):
161
                if command == 'init':
162
                    print 'Creating %r.' % config_file
163
                    open(config_file, 'w').write('[buildout]\nparts = \n')
jim's avatar
jim committed
164
                elif command == 'setup':
165 166
                    # Sigh. This model of a buildout instance
                    # with methods is breaking down. :(
jim's avatar
jim committed
167
                    config_file = None
168
                    data['buildout']['directory'] = ('.', 'COMPUTED_VALUE')
169 170 171 172 173
                else:
                    raise zc.buildout.UserError(
                        "Couldn't open %s" % config_file)

            if config_file:
174 175
                data['buildout']['directory'] = (os.path.dirname(config_file),
                    'COMPUTED_VALUE')
176 177 178
        else:
            base = None

jim's avatar
jim committed
179 180 181 182 183 184 185 186

        cloptions = dict(
            (section, dict((option, (value, 'COMMAND_LINE_VALUE'))
                           for (_, option, value) in v))
            for (section, v) in itertools.groupby(sorted(cloptions),
                                                  lambda v: v[0])
            )
        override = cloptions.get('buildout', {}).copy()
187

188
        # load user defaults, which override defaults
189 190
        if user_defaults:
            user_config = os.path.join(os.path.expanduser('~'),
191 192 193
                                       '.buildout', 'default.cfg')
            if os.path.exists(user_config):
                _update(data, _open(os.path.dirname(user_config), user_config,
194
                                    [], data['buildout'].copy(), override, set()))
195 196

        # load configuration files
197
        if config_file:
198
            _update(data, _open(os.path.dirname(config_file), config_file, [],
199
                                data['buildout'].copy(), override, set()))
200 201

        # apply command-line options
jim's avatar
jim committed
202
        _update(data, cloptions)
203

204 205
        self._annotated = copy.deepcopy(data)
        self._raw = _unannotate(data)
206 207
        self._data = {}
        self._parts = []
208 209
        # provide some defaults before options are parsed
        # because while parsing options those attributes might be
210
        # used already (Gottfried Ganssauge)
211
        buildout_section = data['buildout']
212

213 214 215 216 217
        # Try to make sure we have absolute paths for standard
        # directories. We do this before doing substitutions, in case
        # a one of these gets read by another section.  If any
        # variable references are used though, we leave it as is in
        # _buildout_path.
218 219 220 221 222 223
        if 'directory' in buildout_section:
            self._buildout_dir = buildout_section['directory']
            for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
                d = self._buildout_path(buildout_section[name+'-directory'])
                buildout_section[name+'-directory'] = d

224 225 226 227 228 229 230 231
        # Attributes on this buildout object shouldn't be used by
        # recipes in their __init__.  It can cause bugs, because the
        # recipes will be instantiated below (``options = self['buildout']``)
        # before this has completed initializing.  These attributes are
        # left behind for legacy support but recipe authors should
        # beware of using them.  A better practice is for a recipe to
        # use the buildout['buildout'] options.
        links = buildout_section['find-links']
232
        self._links = links and links.split() or ()
233
        allow_hosts = buildout_section['allow-hosts'].split('\n')
234
        self._allow_hosts = tuple([host.strip() for host in allow_hosts
235
                                   if host.strip() != ''])
236
        self._logger = logging.getLogger('zc.buildout')
237 238
        self.offline = (buildout_section['offline'] == 'true')
        self.newest = (buildout_section['newest'] == 'true')
239 240
        self.accept_buildout_test_releases = (
            buildout_section['accept-buildout-test-releases'] == 'true')
241

242 243 244
        ##################################################################
        ## WARNING!!!
        ## ALL ATTRIBUTES MUST HAVE REASONABLE DEFAULTS AT THIS POINT
245 246 247
        ## OTHERWISE ATTRIBUTEERRORS MIGHT HAPPEN ANY TIME FROM RECIPES.
        ## RECIPES SHOULD GENERALLY USE buildout['buildout'] OPTIONS, NOT
        ## BUILDOUT ATTRIBUTES.
248
        ##################################################################
249 250 251
        # initialize some attrs and buildout directories.
        options = self['buildout']

252
        # now reinitialize
253 254
        links = options.get('find-links', '')
        self._links = links and links.split() or ()
255

256
        allow_hosts = options['allow-hosts'].split('\n')
257
        self._allow_hosts = tuple([host.strip() for host in allow_hosts
tarek's avatar
tarek committed
258
                                   if host.strip() != ''])
259 260

        self._buildout_dir = options['directory']
261 262 263

        # Make sure we have absolute paths for standard directories.  We do this
        # a second time here in case someone overrode these in their configs.
264
        for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
265 266
            d = self._buildout_path(options[name+'-directory'])
            options[name+'-directory'] = d
267

jim's avatar
jim committed
268 269 270
        if options['installed']:
            options['installed'] = os.path.join(options['directory'],
                                                options['installed'])
jim's avatar
jim committed
271

jim's avatar
jim committed
272 273
        self._setup_logging()

274 275 276 277
        versions = options.get('versions')
        if versions:
            zc.buildout.easy_install.default_versions(dict(self[versions]))

278

279 280 281 282 283 284 285 286
        self.offline = options.get_bool('offline')
        if self.offline:
            options['newest'] = 'false'
        self.newest = options.get_bool('newest')
        zc.buildout.easy_install.prefer_final(
            options.get_bool('prefer-final'))
        self.accept_buildout_test_releases = options.get_bool(
            'accept-buildout-test-releases')
287
        zc.buildout.easy_install.use_dependency_links(
288
            options.get_bool('use-dependency-links'))
jim's avatar
jim committed
289
        zc.buildout.easy_install.allow_picked_versions(
290 291 292 293
            options.get_bool('allow-picked-versions'))
        zc.buildout.easy_install.install_from_cache(
            options.get_bool('install-from-cache'))
        zc.buildout.easy_install.always_unzip(options.get_bool('unzip'))
gary's avatar
gary committed
294 295 296 297 298 299 300 301 302 303 304 305
        allowed_eggs = tuple(name.strip() for name in options[
            'allowed-eggs-from-site-packages'].split('\n'))
        self.include_site_packages = options.get_bool('include-site-packages')
        self.exec_sitecustomize = options.get_bool('exec-sitecustomize')
        if (_sys_executable_has_broken_dash_S and
            (not self.include_site_packages or allowed_eggs != ('*',))):
            # We can't do this if the executable has a broken -S.
            warnings.warn(zc.buildout.easy_install.BROKEN_DASH_S_WARNING)
            self.include_site_packages = True
        zc.buildout.easy_install.allowed_eggs_from_site_packages(allowed_eggs)
        zc.buildout.easy_install.include_site_packages(
            self.include_site_packages)
jim's avatar
jim committed
306

jim's avatar
jim committed
307 308 309 310 311 312 313 314 315 316 317 318
        download_cache = options.get('download-cache')
        if download_cache:
            download_cache = os.path.join(options['directory'], download_cache)
            if not os.path.isdir(download_cache):
                raise zc.buildout.UserError(
                    'The specified download cache:\n'
                    '%r\n'
                    "Doesn't exist.\n"
                    % download_cache)
            download_cache = os.path.join(download_cache, 'dist')
            if not os.path.isdir(download_cache):
                os.mkdir(download_cache)
319

jim's avatar
jim committed
320 321
            zc.buildout.easy_install.download_cache(download_cache)

322 323 324 325
        # "Use" each of the defaults so they aren't reported as unused options.
        for name in _buildout_default_options:
            options[name]

326 327 328 329 330
        # Do the same for extends-cache which is not among the defaults but
        # wasn't recognized as having been used since it was used before
        # tracking was turned on.
        options.get('extends-cache')

331 332
        os.chdir(options['directory'])

333 334 335 336
    def _buildout_path(self, name):
        if '${' in name:
            return name
        return os.path.join(self._buildout_dir, name)
337

jim's avatar
jim committed
338
    def bootstrap(self, args):
339
        __doing__ = 'Bootstrapping.'
jim's avatar
jim committed
340

341
        self._setup_directories()
jim's avatar
jim committed
342

343 344 345 346 347 348 349 350 351 352
        options = self['buildout']

        # Get a base working set for our distributions that corresponds to the
        # stated desires in the configuration.
        distributions = ['setuptools', 'zc.buildout']
        if options.get('offline') == 'true':
            ws = zc.buildout.easy_install.working_set(
                distributions, options['executable'],
                [options['develop-eggs-directory'],
                 options['eggs-directory']],
353
                prefer_final=not self.accept_buildout_test_releases,
354 355 356 357 358 359 360 361 362 363
                )
        else:
            ws = zc.buildout.easy_install.install(
                distributions, options['eggs-directory'],
                links=self._links,
                index=options.get('index'),
                executable=options['executable'],
                path=[options['develop-eggs-directory']],
                newest=self.newest,
                allow_hosts=self._allow_hosts,
364
                prefer_final=not self.accept_buildout_test_releases,
365 366
                )

fdrake's avatar
fdrake committed
367
        # Now copy buildout and setuptools eggs, and record destination eggs:
jim's avatar
jim committed
368 369 370
        entries = []
        for name in 'setuptools', 'zc.buildout':
            r = pkg_resources.Requirement.parse(name)
371
            dist = ws.find(r)
jim's avatar
jim committed
372
            if dist.precedence == pkg_resources.DEVELOP_DIST:
373
                dest = os.path.join(self['buildout']['develop-eggs-directory'],
jim's avatar
jim committed
374 375 376 377 378 379 380 381
                                    name+'.egg-link')
                open(dest, 'w').write(dist.location)
                entries.append(dist.location)
            else:
                dest = os.path.join(self['buildout']['eggs-directory'],
                                    os.path.basename(dist.location))
                entries.append(dest)
                if not os.path.exists(dest):
jim's avatar
jim committed
382 383 384 385
                    if os.path.isdir(dist.location):
                        shutil.copytree(dist.location, dest)
                    else:
                        shutil.copy2(dist.location, dest)
jim's avatar
jim committed
386

387 388 389
        # Create buildout script.
        # Ideally the (possibly) new version of buildout would get a
        # chance to write the script.  Not sure how to do that.
jim's avatar
jim committed
390 391
        ws = pkg_resources.WorkingSet(entries)
        ws.require('zc.buildout')
392 393 394 395 396 397 398 399 400 401
        partsdir = os.path.join(options['parts-directory'], 'buildout')
        if not os.path.exists(partsdir):
            os.mkdir(partsdir)
        # (Honor the relative-paths option.)
        relative_paths = options.get('relative-paths', 'false')
        if relative_paths == 'true':
            relative_paths = options['directory']
        else:
            assert relative_paths == 'false'
            relative_paths = ''
402 403 404 405 406 407 408 409
        if (self.accept_buildout_test_releases and
            self._annotated['buildout']['accept-buildout-test-releases'][1] ==
            'COMMAND_LINE_VALUE'):
            # Bootstrap was called with '--accept-buildout-test-releases'.
            # Continue to honor that setting.
            script_initialization = _early_release_initialization_code
        else:
            script_initialization = ''
410 411
        zc.buildout.easy_install.sitepackage_safe_scripts(
            options['bin-directory'], ws, options['executable'], partsdir,
412
            reqs=['zc.buildout'], relative_paths=relative_paths,
gary's avatar
gary committed
413 414 415 416
            include_site_packages=self.include_site_packages,
            script_initialization=script_initialization,
            exec_sitecustomize=self.exec_sitecustomize,
            )
jim's avatar
jim committed
417

418 419
    init = bootstrap

420
    def install(self, install_args):
421
        __doing__ = 'Installing.'
jim's avatar
jim committed
422

jim's avatar
jim committed
423
        self._load_extensions()
424
        self._setup_directories()
jim's avatar
jim committed
425

426 427 428 429
        # Add develop-eggs directory to path so that it gets searched
        # for eggs:
        sys.path.insert(0, self['buildout']['develop-eggs-directory'])

430
        # Check for updates. This could cause the process to be restarted.
431 432
        self._maybe_upgrade()

433
        # load installed data
434 435
        (installed_part_options, installed_exists
         )= self._read_installed_part_options()
436

437 438 439 440 441 442 443 444
        # Remove old develop eggs
        self._uninstall(
            installed_part_options['buildout'].get(
                'installed_develop_eggs', '')
            )

        # Build develop eggs
        installed_develop_eggs = self._develop()
445 446
        installed_part_options['buildout']['installed_develop_eggs'
                                           ] = installed_develop_eggs
447

448 449 450
        if installed_exists:
            self._update_installed(
                installed_develop_eggs=installed_develop_eggs)
451

452 453 454 455 456
        # get configured and installed part lists
        conf_parts = self['buildout']['parts']
        conf_parts = conf_parts and conf_parts.split() or []
        installed_parts = installed_part_options['buildout']['parts']
        installed_parts = installed_parts and installed_parts.split() or []
457

458 459
        if install_args:
            install_parts = install_args
460 461 462 463 464
            uninstall_missing = False
        else:
            install_parts = conf_parts
            uninstall_missing = True

465 466
        # load and initialize recipes
        [self[part]['recipe'] for part in install_parts]
467 468
        if not install_args:
            install_parts = self._parts
469

470
        if self._log_level < logging.DEBUG:
471 472
            sections = list(self)
            sections.sort()
473
            print
474 475 476
            print 'Configuration data:'
            for section in self._data:
                _save_options(section, self[section], sys.stdout)
477
            print
478

479 480 481 482

        # compute new part recipe signatures
        self._compute_part_signatures(install_parts)

483
        # uninstall parts that are no-longer used or whose configs
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
        # have changed
        for part in reversed(installed_parts):
            if part in install_parts:
                old_options = installed_part_options[part].copy()
                installed_files = old_options.pop('__buildout_installed__')
                new_options = self.get(part)
                if old_options == new_options:
                    # The options are the same, but are all of the
                    # installed files still there?  If not, we should
                    # reinstall.
                    if not installed_files:
                        continue
                    for f in installed_files.split('\n'):
                        if not os.path.exists(self._buildout_path(f)):
                            break
jim's avatar
jim committed
499
                    else:
500 501 502
                        continue

                # output debugging info
503 504 505 506 507 508 509 510 511 512 513 514 515 516
                if self._logger.getEffectiveLevel() < logging.DEBUG:
                    for k in old_options:
                        if k not in new_options:
                            self._logger.debug("Part %s, dropped option %s.",
                                               part, k)
                        elif old_options[k] != new_options[k]:
                            self._logger.debug(
                                "Part %s, option %s changed:\n%r != %r",
                                part, k, new_options[k], old_options[k],
                                )
                    for k in new_options:
                        if k not in old_options:
                            self._logger.debug("Part %s, new option %s.",
                                               part, k)
517 518 519

            elif not uninstall_missing:
                continue
jim's avatar
jim committed
520

521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
            self._uninstall_part(part, installed_part_options)
            installed_parts = [p for p in installed_parts if p != part]

            if installed_exists:
                self._update_installed(parts=' '.join(installed_parts))

        # Check for unused buildout options:
        _check_for_unused_options_in_section(self, 'buildout')

        # install new parts
        for part in install_parts:
            signature = self[part].pop('__buildout_signature__')
            saved_options = self[part].copy()
            recipe = self[part].recipe
            if part in installed_parts: # update
                need_to_save_installed = False
537
                __doing__ = 'Updating %s.', part
538 539 540 541 542 543 544 545 546 547 548 549 550 551
                self._logger.info(*__doing__)
                old_options = installed_part_options[part]
                old_installed_files = old_options['__buildout_installed__']

                try:
                    update = recipe.update
                except AttributeError:
                    update = recipe.install
                    self._logger.warning(
                        "The recipe for %s doesn't define an update "
                        "method. Using its install method.",
                        part)

                try:
552
                    installed_files = self[part]._call(update)
553 554 555 556 557 558 559 560 561 562 563
                except:
                    installed_parts.remove(part)
                    self._uninstall(old_installed_files)
                    if installed_exists:
                        self._update_installed(
                            parts=' '.join(installed_parts))
                    raise

                old_installed_files = old_installed_files.split('\n')
                if installed_files is None:
                    installed_files = old_installed_files
jim's avatar
jim committed
564
                else:
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
                    if isinstance(installed_files, str):
                        installed_files = [installed_files]
                    else:
                        installed_files = list(installed_files)

                    need_to_save_installed = [
                        p for p in installed_files
                        if p not in old_installed_files]

                    if need_to_save_installed:
                        installed_files = (old_installed_files
                                           + need_to_save_installed)

            else: # install
                need_to_save_installed = True
580
                __doing__ = 'Installing %s.', part
581
                self._logger.info(*__doing__)
582
                installed_files = self[part]._call(recipe.install)
583 584 585
                if installed_files is None:
                    self._logger.warning(
                        "The %s install returned None.  A path or "
586
                        "iterable of paths should be returned.",
587 588 589
                        part)
                    installed_files = ()
                elif isinstance(installed_files, str):
590
                    installed_files = [installed_files]
591 592 593 594 595 596 597 598 599 600 601 602 603 604
                else:
                    installed_files = list(installed_files)

            installed_part_options[part] = saved_options
            saved_options['__buildout_installed__'
                          ] = '\n'.join(installed_files)
            saved_options['__buildout_signature__'] = signature

            installed_parts = [p for p in installed_parts if p != part]
            installed_parts.append(part)
            _check_for_unused_options_in_section(self, part)

            if need_to_save_installed:
                installed_part_options['buildout']['parts'] = (
605
                    ' '.join(installed_parts))
606 607 608 609 610
                self._save_installed_options(installed_part_options)
                installed_exists = True
            else:
                assert installed_exists
                self._update_installed(parts=' '.join(installed_parts))
jim's avatar
jim committed
611

612 613 614 615 616
        if installed_develop_eggs:
            if not installed_exists:
                self._save_installed_options(installed_part_options)
        elif (not installed_parts) and installed_exists:
            os.remove(self['buildout']['installed'])
jim's avatar
jim committed
617

618 619
        self._unload_extensions()

620 621 622 623 624 625 626
    def _update_installed(self, **buildout_options):
        installed = self['buildout']['installed']
        f = open(installed, 'a')
        f.write('\n[buildout]\n')
        for option, value in buildout_options.items():
            _save_option(option, value, f)
        f.close()
627

jim's avatar
jim committed
628
    def _uninstall_part(self, part, installed_part_options):
629
        # uninstall part
630
        __doing__ = 'Uninstalling %s.', part
jim's avatar
jim committed
631 632
        self._logger.info(*__doing__)

633
        # run uninstall recipe
jim's avatar
jim committed
634 635 636 637
        recipe, entry = _recipe(installed_part_options[part])
        try:
            uninstaller = _install_and_load(
                recipe, 'zc.buildout.uninstall', entry, self)
638
            self._logger.info('Running uninstall recipe.')
jim's avatar
jim committed
639 640 641 642 643 644 645 646
            uninstaller(part, installed_part_options[part])
        except (ImportError, pkg_resources.DistributionNotFound), v:
            pass

        # remove created files and directories
        self._uninstall(
            installed_part_options[part]['__buildout_installed__'])

647
    def _setup_directories(self):
jim's avatar
jim committed
648
        __doing__ = 'Setting up buildout directories'
649 650 651 652 653

        # Create buildout directories
        for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
            d = self['buildout'][name+'-directory']
            if not os.path.exists(d):
654
                self._logger.info('Creating directory %r.', d)
655 656
                os.mkdir(d)

657 658 659
    def _develop(self):
        """Install sources by running setup.py develop on them
        """
jim's avatar
jim committed
660 661
        __doing__ = 'Processing directories listed in the develop option'

662
        develop = self['buildout'].get('develop')
663 664 665 666 667 668 669 670 671
        if not develop:
            return ''

        dest = self['buildout']['develop-eggs-directory']
        old_files = os.listdir(dest)

        env = dict(os.environ, PYTHONPATH=pkg_resources_loc)
        here = os.getcwd()
        try:
672 673
            try:
                for setup in develop.split():
674
                    setup = self._buildout_path(setup)
675 676
                    files = glob.glob(setup)
                    if not files:
677 678
                        self._logger.warn("Couldn't develop %r (not found)",
                                          setup)
shane's avatar
shane committed
679 680
                    else:
                        files.sort()
681 682 683 684
                    for setup in files:
                        self._logger.info("Develop: %r", setup)
                        __doing__ = 'Processing develop directory %r.', setup
                        zc.buildout.easy_install.develop(setup, dest)
685 686 687 688 689 690 691 692 693
            except:
                # if we had an error, we need to roll back changes, by
                # removing any files we created.
                self._sanity_check_develop_eggs_files(dest, old_files)
                self._uninstall('\n'.join(
                    [os.path.join(dest, f)
                     for f in os.listdir(dest)
                     if f not in old_files
                     ]))
694
                raise
695

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
            else:
                self._sanity_check_develop_eggs_files(dest, old_files)
                return '\n'.join([os.path.join(dest, f)
                                  for f in os.listdir(dest)
                                  if f not in old_files
                                  ])

        finally:
            os.chdir(here)


    def _sanity_check_develop_eggs_files(self, dest, old_files):
        for f in os.listdir(dest):
            if f in old_files:
                continue
            if not (os.path.isfile(os.path.join(dest, f))
                    and f.endswith('.egg-link')):
                self._logger.warning(
714
                    "Unexpected entry, %r, in develop-eggs directory.", f)
715

716 717 718 719 720 721
    def _compute_part_signatures(self, parts):
        # Compute recipe signature and add to options
        for part in parts:
            options = self.get(part)
            if options is None:
                options = self[part] = {}
722
            recipe, entry = _recipe(options)
723
            req = pkg_resources.Requirement.parse(recipe)
724
            sig = _dists_sig(pkg_resources.working_set.resolve([req]))
725 726 727
            options['__buildout_signature__'] = ' '.join(sig)

    def _read_installed_part_options(self):
jim's avatar
jim committed
728 729
        old = self['buildout']['installed']
        if old and os.path.isfile(old):
730
            parser = ConfigParser.RawConfigParser()
731
            parser.optionxform = lambda s: s
732
            parser.read(old)
733 734 735 736 737 738 739 740 741
            result = {}
            for section in parser.sections():
                options = {}
                for option, value in parser.items(section):
                    if '%(' in value:
                        for k, v in _spacey_defaults:
                            value = value.replace(k, v)
                    options[option] = value
                result[section] = Options(self, section, options)
742

743
            return result, True
744
        else:
745 746 747
            return ({'buildout': Options(self, 'buildout', {'parts': ''})},
                    False,
                    )
748 749

    def _uninstall(self, installed):
jim's avatar
jim committed
750 751 752
        for f in installed.split('\n'):
            if not f:
                continue
753
            f = self._buildout_path(f)
754
            if os.path.isdir(f):
755
                rmtree(f)
756
            elif os.path.isfile(f):
757 758 759 760 761 762 763 764 765 766 767 768 769
                try:
                    os.remove(f)
                except OSError:
                    if not (
                        sys.platform == 'win32' and
                        (realpath(os.path.join(os.path.dirname(sys.argv[0]),
                                               'buildout.exe'))
                         ==
                         realpath(f)
                         )
                        # Sigh. This is the exectable used to run the buildout
                        # and, of course, it's in use. Leave it.
                        ):
770 771
                        raise

772 773
    def _install(self, part):
        options = self[part]
774
        recipe, entry = _recipe(options)
775 776 777 778 779 780 781
        recipe_class = pkg_resources.load_entry_point(
            recipe, 'zc.buildout', entry)
        installed = recipe_class(self, part, options).install()
        if installed is None:
            installed = []
        elif isinstance(installed, basestring):
            installed = [installed]
782
        base = self._buildout_path('')
783 784 785 786
        installed = [d.startswith(base) and d[len(base):] or d
                     for d in installed]
        return ' '.join(installed)

787

788
    def _save_installed_options(self, installed_options):
jim's avatar
jim committed
789 790 791 792
        installed = self['buildout']['installed']
        if not installed:
            return
        f = open(installed, 'w')
793 794 795 796 797
        _save_options('buildout', installed_options['buildout'], f)
        for part in installed_options['buildout']['parts'].split():
            print >>f
            _save_options(part, installed_options[part], f)
        f.close()
jim's avatar
jim committed
798

jim's avatar
jim committed
799 800
    def _error(self, message, *args):
        raise zc.buildout.UserError(message % args)
jim's avatar
jim committed
801 802 803

    def _setup_logging(self):
        root_logger = logging.getLogger()
804
        self._logger = logging.getLogger('zc.buildout')
jim's avatar
jim committed
805
        handler = logging.StreamHandler(sys.stdout)
806 807 808 809 810 811 812 813 814
        log_format = self['buildout']['log-format']
        if not log_format:
            # No format specified. Use different formatter for buildout
            # and other modules, showing logger name except for buildout
            log_format = '%(name)s: %(message)s'
            buildout_handler = logging.StreamHandler(sys.stdout)
            buildout_handler.setFormatter(logging.Formatter('%(message)s'))
            self._logger.propagate = False
            self._logger.addHandler(buildout_handler)
815

816
        handler.setFormatter(logging.Formatter(log_format))
jim's avatar
jim committed
817
        root_logger.addHandler(handler)
818

jim's avatar
jim committed
819 820 821 822 823 824 825 826 827 828 829 830 831
        level = self['buildout']['log-level']
        if level in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'):
            level = getattr(logging, level)
        else:
            try:
                level = int(level)
            except ValueError:
                self._error("Invalid logging level %s", level)
        verbosity = self['buildout'].get('verbosity', 0)
        try:
            verbosity = int(verbosity)
        except ValueError:
            self._error("Invalid verbosity %s", verbosity)
832 833 834

        level -= verbosity
        root_logger.setLevel(level)
jim's avatar
jim committed
835
        self._log_level = level
836

837 838
    def _maybe_upgrade(self):
        # See if buildout or setuptools need to be upgraded.
jim's avatar
jim committed
839
        # If they do, do the upgrade and restart the buildout process.
840
        __doing__ = 'Checking for upgrades.'
jim's avatar
jim committed
841

jim's avatar
jim committed
842 843
        if not self.newest:
            return
844

845 846 847 848 849 850 851
        options = self['buildout']

        specs = ['zc.buildout']
        if zc.buildout.easy_install.is_distribute:
            specs.append('distribute')
        else:
            specs.append('setuptools')
852 853
        ws = zc.buildout.easy_install.install(
            [
854 855
            (spec + ' ' + options.get(spec+'-version', '')).strip()
            for spec in specs
856
            ],
857 858 859 860 861
            options['eggs-directory'],
            links = options.get('find-links', '').split(),
            index = options.get('index'),
            path = [options['develop-eggs-directory']],
            allow_hosts = self._allow_hosts,
862
            prefer_final=not self.accept_buildout_test_releases,
863 864 865 866 867
            )

        upgraded = []
        for project in 'zc.buildout', 'setuptools':
            req = pkg_resources.Requirement.parse(project)
868 869
            project_location = pkg_resources.working_set.find(req).location
            if ws.find(req).location != project_location:
870 871 872 873
                upgraded.append(ws.find(req))

        if not upgraded:
            return
jim's avatar
jim committed
874

875
        __doing__ = 'Upgrading.'
jim's avatar
jim committed
876

877
        should_run = realpath(
878
            os.path.join(os.path.abspath(options['bin-directory']),
879 880 881 882 883 884
                         'buildout')
            )
        if sys.platform == 'win32':
            should_run += '-script.py'

        if (realpath(os.path.abspath(sys.argv[0])) != should_run):
885 886
            self._logger.debug("Running %r.", realpath(sys.argv[0]))
            self._logger.debug("Local buildout is %r.", should_run)
887
            self._logger.warn("Not upgrading because not running a local "
888
                              "buildout command.")
889 890
            return

jim's avatar
jim committed
891 892 893 894 895
        if sys.platform == 'win32' and not self.__windows_restart:
            args = map(zc.buildout.easy_install._safe_arg, sys.argv)
            args.insert(1, '-W')
            if not __debug__:
                args.insert(0, '-O')
896
            args.insert(0, zc.buildout.easy_install._safe_arg (sys.executable))
897 898
            os.execv(sys.executable, args)

jim's avatar
jim committed
899 900
        self._logger.info("Upgraded:\n  %s;\nrestarting.",
                          ",\n  ".join([("%s version %s"
901 902 903 904 905 906
                                       % (dist.project_name, dist.version)
                                       )
                                      for dist in upgraded
                                      ]
                                     ),
                          )
907

908 909
        # the new dist is different, so we've upgraded.
        # Update the scripts and return True
910 911
        # Ideally the new version of buildout would get a chance to write the
        # script.  Not sure how to do that.
912 913 914 915 916 917
        partsdir = os.path.join(options['parts-directory'], 'buildout')
        if os.path.exists(partsdir):
            # This is primarily for unit tests, in which .py files change too
            # fast for Python to know to regenerate the .pyc/.pyo files.
            shutil.rmtree(partsdir)
        os.mkdir(partsdir)
918 919 920 921 922 923 924 925
        if (self.accept_buildout_test_releases and
            self._annotated['buildout']['accept-buildout-test-releases'][1] ==
            'COMMAND_LINE_VALUE'):
            # Bootstrap was called with '--accept-buildout-test-releases'.
            # Continue to honor that setting.
            script_initialization = _early_release_initialization_code
        else:
            script_initialization = ''
gary's avatar
gary committed
926 927 928 929 930 931 932
        # (Honor the relative-paths option.)
        relative_paths = options.get('relative-paths', 'false')
        if relative_paths == 'true':
            relative_paths = options['directory']
        else:
            assert relative_paths == 'false'
            relative_paths = ''
933
        zc.buildout.easy_install.sitepackage_safe_scripts(
gary's avatar
gary committed
934 935 936 937 938 939
            options['bin-directory'], ws, options['executable'], partsdir,
            reqs=['zc.buildout'], relative_paths=relative_paths,
            include_site_packages=self.include_site_packages,
            script_initialization=script_initialization,
            exec_sitecustomize=self.exec_sitecustomize,
            )
940 941 942 943 944

        # Restart
        args = map(zc.buildout.easy_install._safe_arg, sys.argv)
        if not __debug__:
            args.insert(0, '-O')
945 946 947 948 949 950 951 952
        args.insert(0, zc.buildout.easy_install._safe_arg(sys.executable))
        # We want to make sure that our new site.py is used for rerunning
        # buildout, so we put the partsdir in PYTHONPATH for our restart.
        # This overrides any set PYTHONPATH, but since we generally are
        # trying to run with a completely "clean" python (only the standard
        # library) then that should be fine.
        env = os.environ.copy()
        env['PYTHONPATH'] = partsdir
alex_plugaru's avatar
alex_plugaru committed
953
        sys.exit(subprocess.Popen(args, env=env).wait())
954

955
    def _load_extensions(self):
956
        __doing__ = 'Loading extensions.'
957 958
        specs = self['buildout'].get('extensions', '').split()
        if specs:
jim's avatar
jim committed
959
            path = [self['buildout']['develop-eggs-directory']]
jim's avatar
jim committed
960
            if self.offline:
961
                dest = None
jim's avatar
jim committed
962
                path.append(self['buildout']['eggs-directory'])
963 964
            else:
                dest = self['buildout']['eggs-directory']
jim's avatar
jim committed
965
                if not os.path.exists(dest):
966
                    self._logger.info('Creating directory %r.', dest)
jim's avatar
jim committed
967
                    os.mkdir(dest)
jim's avatar
jim committed
968

969
            zc.buildout.easy_install.install(
jim's avatar
jim committed
970
                specs, dest, path=path,
971
                working_set=pkg_resources.working_set,
972 973
                links = self['buildout'].get('find-links', '').split(),
                index = self['buildout'].get('index'),
974
                newest=self.newest, allow_hosts=self._allow_hosts,
975
                prefer_final=not self.accept_buildout_test_releases)
976 977 978 979 980

            # Clear cache because extensions might now let us read pages we
            # couldn't read before.
            zc.buildout.easy_install.clear_index_cache()

981 982
            for ep in pkg_resources.iter_entry_points('zc.buildout.extension'):
                ep.load()(self)
983

984 985 986 987 988 989 990 991
    def _unload_extensions(self):
        __doing__ = 'Unloading extensions.'
        specs = self['buildout'].get('extensions', '').split()
        if specs:
            for ep in pkg_resources.iter_entry_points(
                'zc.buildout.unloadextension'):
                ep.load()(self)

jim's avatar
jim committed
992
    def setup(self, args):
993 994
        if not args:
            raise zc.buildout.UserError(
jim's avatar
jim committed
995
                "The setup command requires the path to a setup script or \n"
996
                "directory containing a setup script, and its arguments."
997
                )
998 999 1000 1001
        setup = args.pop(0)
        if os.path.isdir(setup):
            setup = os.path.join(setup, 'setup.py')

1002
        self._logger.info("Running setup script %r.", setup)
1003 1004 1005
        setup = os.path.abspath(setup)

        fd, tsetup = tempfile.mkstemp()
1006
        exe = zc.buildout.easy_install._safe_arg(sys.executable)
1007
        try:
1008
            os.write(fd, zc.buildout.easy_install.runsetup_template % dict(
1009
                setuptools=pkg_resources_loc,
1010 1011
                setupdir=os.path.dirname(setup),
                setup=setup,
1012
                __file__ = setup,
1013
                ))
georgyberdyshev's avatar
georgyberdyshev committed
1014 1015
            if is_jython:
                arg_list = list()
1016

georgyberdyshev's avatar
georgyberdyshev committed
1017
                for a in args:
1018
                    arg_list.append(zc.buildout.easy_install._safe_arg(a))
1019

1020
                subprocess.Popen([exe] + list(tsetup) + arg_list).wait()
1021

georgyberdyshev's avatar
georgyberdyshev committed
1022
            else:
1023
                os.spawnl(os.P_WAIT, sys.executable, exe, tsetup,
georgyberdyshev's avatar
georgyberdyshev committed
1024 1025
                        *[zc.buildout.easy_install._safe_arg(a)
                            for a in args])
1026 1027 1028
        finally:
            os.close(fd)
            os.remove(tsetup)
jim's avatar
jim committed
1029

1030 1031
    runsetup = setup # backward compat.

1032 1033 1034
    def annotate(self, args):
        _print_annotate(self._annotated)

1035
    def __getitem__(self, section):
1036
        __doing__ = 'Getting section %s.', section
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
        try:
            return self._data[section]
        except KeyError:
            pass

        try:
            data = self._raw[section]
        except KeyError:
            raise MissingSection(section)

        options = Options(self, section, data)
        self._data[section] = options
        options._initialize()
1050
        return options
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063

    def __setitem__(self, key, value):
        raise NotImplementedError('__setitem__')

    def __delitem__(self, key):
        raise NotImplementedError('__delitem__')

    def keys(self):
        return self._raw.keys()

    def __iter__(self):
        return iter(self._raw)

jim's avatar
jim committed
1064 1065

def _install_and_load(spec, group, entry, buildout):
1066
    __doing__ = 'Loading recipe %r.', spec
jim's avatar
jim committed
1067 1068 1069 1070 1071
    try:
        req = pkg_resources.Requirement.parse(spec)

        buildout_options = buildout['buildout']
        if pkg_resources.working_set.find(req) is None:
1072
            __doing__ = 'Installing recipe %s.', spec
jim's avatar
jim committed
1073
            if buildout.offline:
jim's avatar
jim committed
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
                dest = None
                path = [buildout_options['develop-eggs-directory'],
                        buildout_options['eggs-directory'],
                        ]
            else:
                dest = buildout_options['eggs-directory']
                path = [buildout_options['develop-eggs-directory']]

            zc.buildout.easy_install.install(
                [spec], dest,
                links=buildout._links,
                index=buildout_options.get('index'),
                path=path,
                working_set=pkg_resources.working_set,
jim's avatar
jim committed
1088
                newest=buildout.newest,
1089
                allow_hosts=buildout._allow_hosts,
1090
                prefer_final=not buildout.accept_buildout_test_releases)
jim's avatar
jim committed
1091

1092
        __doing__ = 'Loading %s recipe entry %s:%s.', group, spec, entry
jim's avatar
jim committed
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
        return pkg_resources.load_entry_point(
            req.project_name, group, entry)

    except Exception, v:
        buildout._logger.log(
            1,
            "Could't load %s entry point %s\nfrom %s:\n%s.",
            group, entry, spec, v)
        raise

1103

1104 1105 1106 1107 1108 1109
class Options(UserDict.DictMixin):

    def __init__(self, buildout, section, data):
        self.buildout = buildout
        self.name = section
        self._raw = data
1110
        self._cooked = {}
1111 1112 1113
        self._data = {}

    def _initialize(self):
jim's avatar
jim committed
1114
        name = self.name
1115
        __doing__ = 'Initializing section %s.', name
1116

jim's avatar
jim committed
1117 1118 1119
        if '<' in self._raw:
            self._raw = self._do_extend_raw(name, self._raw, [])

1120
        # force substitutions
1121 1122 1123
        for k, v in self._raw.items():
            if '${' in v:
                self._dosub(k, v)
1124

1125 1126
        if self.name == 'buildout':
            return # buildout section can never be a part
1127

1128 1129 1130
        recipe = self.get('recipe')
        if not recipe:
            return
1131

1132 1133
        reqs, entry = _recipe(self._data)
        buildout = self.buildout
jim's avatar
jim committed
1134
        recipe_class = _install_and_load(reqs, 'zc.buildout', entry, buildout)
1135

1136
        __doing__ = 'Initializing part %s.', name
jim's avatar
jim committed
1137 1138
        self.recipe = recipe_class(buildout, name, self)
        buildout._parts.append(name)
1139

jim's avatar
jim committed
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
    def _do_extend_raw(self, name, data, doing):
        if name == 'buildout':
            return data
        if name in doing:
            raise zc.buildout.UserError("Infinite extending loop %r" % name)
        doing.append(name)
        try:
            to_do = data.pop('<', None)
            if to_do is None:
                return data
            __doing__ = 'Loading input sections for %r', name

            result = {}
            for iname in to_do.split('\n'):
                iname = iname.strip()
                if not iname:
                    continue
                raw = self.buildout._raw.get(iname)
                if raw is None:
                    raise zc.buildout.UserError("No section named %r" % iname)
                result.update(self._do_extend_raw(iname, raw, doing))

            result.update(data)
            return result
        finally:
            assert doing.pop() == name

1167
    def _dosub(self, option, v):
1168
        __doing__ = 'Getting option %s:%s.', self.name, option
1169 1170 1171 1172
        seen = [(self.name, option)]
        v = '$$'.join([self._sub(s, seen) for s in v.split('$$')])
        self._cooked[option] = v

1173 1174 1175 1176 1177 1178
    def get(self, option, default=None, seen=None):
        try:
            return self._data[option]
        except KeyError:
            pass

1179
        v = self._cooked.get(option)
1180
        if v is None:
1181 1182 1183
            v = self._raw.get(option)
            if v is None:
                return default
1184

1185
        __doing__ = 'Getting option %s:%s.', self.name, option
jim's avatar
jim committed
1186

1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198
        if '${' in v:
            key = self.name, option
            if seen is None:
                seen = [key]
            elif key in seen:
                raise zc.buildout.UserError(
                    "Circular reference in substitutions.\n"
                    )
            else:
                seen.append(key)
            v = '$$'.join([self._sub(s, seen) for s in v.split('$$')])
            seen.pop()
1199

1200 1201 1202 1203 1204
        self._data[option] = v
        return v

    _template_split = re.compile('([$]{[^}]*})').split
    _simple = re.compile('[-a-zA-Z0-9 ._]+$').match
1205
    _valid = re.compile('\${[-a-zA-Z0-9 ._]*:[-a-zA-Z0-9 ._]+}$').match
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
    def _sub(self, template, seen):
        value = self._template_split(template)
        subs = []
        for ref in value[1::2]:
            s = tuple(ref[2:-1].split(':'))
            if not self._valid(ref):
                if len(s) < 2:
                    raise zc.buildout.UserError("The substitution, %s,\n"
                                                "doesn't contain a colon."
                                                % ref)
                if len(s) > 2:
                    raise zc.buildout.UserError("The substitution, %s,\n"
                                                "has too many colons."
                                                % ref)
                if not self._simple(s[0]):
                    raise zc.buildout.UserError(
                        "The section name in substitution, %s,\n"
                        "has invalid characters."
                        % ref)
                if not self._simple(s[1]):
                    raise zc.buildout.UserError(
                        "The option name in substitution, %s,\n"
                        "has invalid characters."
                        % ref)
1230

1231 1232 1233 1234
            section, option = s
            if not section:
                section = self.name
            v = self.buildout[section].get(option, None, seen)
1235
            if v is None:
1236 1237 1238 1239 1240
                if option == '_buildout_section_name_':
                    v = self.name
                else:
                    raise MissingOption("Referenced option does not exist:",
                                        section, option)
1241 1242 1243 1244
            subs.append(v)
        subs.append('')

        return ''.join([''.join(v) for v in zip(value[::2], subs)])
1245

1246 1247 1248 1249 1250 1251 1252 1253
    def __getitem__(self, key):
        try:
            return self._data[key]
        except KeyError:
            pass

        v = self.get(key)
        if v is None:
jim's avatar
jim committed
1254
            raise MissingOption("Missing option: %s:%s" % (self.name, key))
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
        return v

    def __setitem__(self, option, value):
        if not isinstance(value, str):
            raise TypeError('Option values must be strings', value)
        self._data[option] = value

    def __delitem__(self, key):
        if key in self._raw:
            del self._raw[key]
            if key in self._data:
                del self._data[key]
1267 1268
            if key in self._cooked:
                del self._cooked[key]
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278
        elif key in self._data:
            del self._data[key]
        else:
            raise KeyError, key

    def keys(self):
        raw = self._raw
        return list(self._raw) + [k for k in self._data if k not in raw]

    def copy(self):
1279 1280 1281 1282
        result = self._raw.copy()
        result.update(self._cooked)
        result.update(self._data)
        return result
1283

1284
    def _call(self, f):
1285
        buildout_directory = self.buildout['buildout']['directory']
1286 1287 1288
        self._created = []
        try:
            try:
1289
                os.chdir(buildout_directory)
1290 1291 1292 1293
                return f()
            except:
                for p in self._created:
                    if os.path.isdir(p):
1294
                        rmtree(p)
1295 1296 1297
                    elif os.path.isfile(p):
                        os.remove(p)
                    else:
jim's avatar
jim committed
1298
                        self.buildout._logger.warn("Couldn't clean up %r.", p)
1299 1300 1301
                raise
        finally:
            self._created = None
1302
            os.chdir(buildout_directory)
1303 1304 1305 1306 1307 1308 1309 1310 1311 1312

    def created(self, *paths):
        try:
            self._created.extend(paths)
        except AttributeError:
            raise TypeError(
                "Attempt to register a created path while not installing",
                self.name)
        return self._created

1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338
    def query_bool(self, name, default=None):
        """Given a name, return a boolean value for that name.

        ``default``, if given, should be 'true', 'false', or None.
        """
        if default is not None:
            value = self.setdefault(name, default=default)
        else:
            value = self.get(name)
            if value is None:
                return value
        return _convert_bool(name, value)

    def get_bool(self, name):
        """Given a name, return a boolean value for that name.
        """
        return _convert_bool(name, self[name])


def _convert_bool(name, value):
    if value not in ('true', 'false'):
        raise zc.buildout.UserError(
            'Invalid value for %s option: %s' % (name, value))
    else:
        return value == 'true'

1339 1340 1341
_spacey_nl = re.compile('[ \t\r\f\v]*\n[ \t\r\f\v\n]*'
                        '|'
                        '^[ \t\r\f\v]+'
1342 1343 1344 1345
                        '|'
                        '[ \t\r\f\v]+$'
                        )

1346 1347 1348 1349 1350 1351 1352 1353
_spacey_defaults = [
    ('%(__buildout_space__)s',   ' '),
    ('%(__buildout_space_n__)s', '\n'),
    ('%(__buildout_space_r__)s', '\r'),
    ('%(__buildout_space_f__)s', '\f'),
    ('%(__buildout_space_v__)s', '\v'),
    ]

1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367
def _quote_spacey_nl(match):
    match = match.group(0).split('\n', 1)
    result = '\n\t'.join(
        [(s
          .replace(' ', '%(__buildout_space__)s')
          .replace('\r', '%(__buildout_space_r__)s')
          .replace('\f', '%(__buildout_space_f__)s')
          .replace('\v', '%(__buildout_space_v__)s')
          .replace('\n', '%(__buildout_space_n__)s')
          )
         for s in match]
        )
    return result

1368 1369 1370 1371 1372 1373 1374
def _save_option(option, value, f):
    value = _spacey_nl.sub(_quote_spacey_nl, value)
    if value.startswith('\n\t'):
        value = '%(__buildout_space_n__)s' + value[2:]
    if value.endswith('\n\t'):
        value = value[:-2] + '%(__buildout_space_n__)s'
    print >>f, option, '=', value
1375

1376 1377 1378 1379 1380
def _save_options(section, options, f):
    print >>f, '[%s]' % section
    items = options.items()
    items.sort()
    for option, value in items:
1381
        _save_option(option, value, f)
1382

1383
def _open(base, filename, seen, dl_options, override, downloaded):
1384 1385 1386 1387
    """Open a configuration file and return the result as a dictionary,

    Recursively open other files based on buildout options found.
    """
1388 1389
    _update_section(dl_options, override)
    _dl_options = _unannotate_section(dl_options.copy())
1390 1391
    newest = _convert_bool('newest', _dl_options.get('newest', 'false'))
    fallback = newest and not (filename in downloaded)
1392
    download = zc.buildout.download.Download(
1393 1394 1395
        _dl_options, cache=_dl_options.get('extends-cache'),
        fallback=fallback, hash_name=True)
    is_temp = False
1396
    if _isurl(filename):
1397 1398
        path, is_temp = download(filename)
        fp = open(path)
1399 1400 1401 1402 1403 1404 1405
        base = filename[:filename.rfind('/')]
    elif _isurl(base):
        if os.path.isabs(filename):
            fp = open(filename)
            base = os.path.dirname(filename)
        else:
            filename = base + '/' + filename
1406 1407
            path, is_temp = download(filename)
            fp = open(path)
1408 1409 1410 1411 1412
            base = filename[:filename.rfind('/')]
    else:
        filename = os.path.join(base, filename)
        fp = open(filename)
        base = os.path.dirname(filename)
1413
    downloaded.add(filename)
1414

1415
    if filename in seen:
1416
        if is_temp:
1417 1418
            fp.close()
            os.remove(path)
jim's avatar
jim committed
1419
        raise zc.buildout.UserError("Recursive file include", seen, filename)
1420

1421
    root_config_file = not seen
1422 1423 1424 1425
    seen.append(filename)

    result = {}

1426
    parser = ConfigParser.RawConfigParser()
1427
    parser.optionxform = lambda s: s
1428
    parser.readfp(fp)
1429
    if is_temp:
1430 1431
        fp.close()
        os.remove(path)
1432

1433
    extends = None
1434 1435 1436 1437
    for section in parser.sections():
        options = dict(parser.items(section))
        if section == 'buildout':
            extends = options.pop('extends', extends)
1438 1439 1440 1441
            if 'extended-by' in options:
                raise zc.buildout.UserError(
                    'No-longer supported "extended-by" option found in %s.' %
                    filename)
1442 1443
        result[section] = options

1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
    # find and expose _profile_base_location_ and _profile_location_
    for section, value in result.iteritems():
        _profile_base_location_ = None
        _profile_location_ = None
        for k,v in value.iteritems():
          if '${:_profile_base_location_}' in v:
            _profile_base_location_ = base
          if '${:_profile_location_}' in v:
            _profile_location_ = filename
          if _profile_base_location_ is not None and _profile_location_ is not None:
            break
        if _profile_base_location_ is not None:
          value['_profile_base_location_'] = _profile_base_location_
        if _profile_location_ is not None:
          value['_profile_location_'] = _profile_location_

1460 1461
    result = _annotate(result, filename)

1462 1463 1464
    if root_config_file and 'buildout' in result:
        dl_options = _update_section(dl_options, result['buildout'])

1465 1466
    if extends:
        extends = extends.split()
1467 1468
        eresult = _open(base, extends.pop(0), seen, dl_options, override,
                        downloaded)
1469
        for fname in extends:
1470 1471
            _update(eresult, _open(base, fname, seen, dl_options, override,
                    downloaded))
jim's avatar
jim committed
1472
        result = _update(eresult, result)
1473 1474 1475

    seen.pop()
    return result
1476

1477

jim's avatar
jim committed
1478
ignore_directories = '.svn', 'CVS'
1479
_dir_hashes = {}
1480
def _dir_hash(dir):
1481 1482 1483
    dir_hash = _dir_hashes.get(dir, None)
    if dir_hash is not None:
        return dir_hash
1484
    hash = md5()
1485
    for (dirpath, dirnames, filenames) in os.walk(dir):
jim's avatar
jim committed
1486
        dirnames[:] = [n for n in dirnames if n not in ignore_directories]
1487
        filenames[:] = [f for f in filenames
1488 1489
                        if (not (f.endswith('pyc') or f.endswith('pyo'))
                            and os.path.exists(os.path.join(dirpath, f)))
1490 1491 1492 1493 1494
                        ]
        hash.update(' '.join(dirnames))
        hash.update(' '.join(filenames))
        for name in filenames:
            hash.update(open(os.path.join(dirpath, name)).read())
1495 1496
    _dir_hashes[dir] = dir_hash = hash.digest().encode('base64').strip()
    return dir_hash
1497

1498
def _dists_sig(dists):
1499 1500 1501 1502 1503 1504
    result = []
    for dist in dists:
        location = dist.location
        if dist.precedence == pkg_resources.DEVELOP_DIST:
            result.append(dist.project_name + '-' + _dir_hash(location))
        else:
1505
            result.append(os.path.basename(location))
1506 1507
    return result

1508
def _update_section(s1, s2):
1509
    s2 = s2.copy() # avoid mutating the second argument, which is unexpected
1510
    for k, v in s2.items():
1511
        v2, note2 = v
1512 1513
        if k.endswith('+'):
            key = k.rstrip(' +')
1514
            v1, note1 = s1.get(key, ("", ""))
1515
            newnote = ' [+] '.join((note1, note2)).strip()
1516 1517
            s2[key] = "\n".join((v1).split('\n') +
                v2.split('\n')), newnote
1518 1519 1520
            del s2[k]
        elif k.endswith('-'):
            key = k.rstrip(' -')
1521
            v1, note1 = s1.get(key, ("", ""))
1522
            newnote = ' [-] '.join((note1, note2)).strip()
1523 1524 1525
            s2[key] = ("\n".join(
                [v for v in v1.split('\n')
                   if v not in v2.split('\n')]), newnote)
1526
            del s2[k]
1527

1528 1529 1530
    s1.update(s2)
    return s1

1531 1532 1533
def _update(d1, d2):
    for section in d2:
        if section in d1:
1534
            d1[section] = _update_section(d1[section], d2[section])
1535 1536 1537 1538
        else:
            d1[section] = d2[section]
    return d1

1539 1540 1541 1542 1543 1544 1545 1546 1547
def _recipe(options):
    recipe = options['recipe']
    if ':' in recipe:
        recipe, entry = recipe.split(':')
    else:
        entry = 'default'

    return recipe, entry

jim's avatar
jim committed
1548 1549 1550 1551 1552 1553 1554 1555 1556
def _doing():
    _, v, tb = sys.exc_info()
    message = str(v)
    doing = []
    while tb is not None:
        d = tb.tb_frame.f_locals.get('__doing__')
        if d:
            doing.append(d)
        tb = tb.tb_next
1557

jim's avatar
jim committed
1558 1559 1560 1561 1562 1563 1564
    if doing:
        sys.stderr.write('While:\n')
        for d in doing:
            if not isinstance(d, str):
                d = d[0] % d[1:]
            sys.stderr.write('  %s\n' % d)

1565
def _error(*message):
1566
    sys.stderr.write('Error: ' + ' '.join(message) +'\n')
1567 1568
    sys.exit(1)

jim's avatar
jim committed
1569
_internal_error_template = """
mgedmin's avatar
mgedmin committed
1570
An internal error occurred due to a bug in either zc.buildout or in a
jim's avatar
jim committed
1571 1572 1573
recipe being used:
"""

1574 1575 1576 1577
def _check_for_unused_options_in_section(buildout, section):
    options = buildout[section]
    unused = [option for option in options._raw if option not in options._data]
    if unused:
1578
        buildout._logger.warn("Unused options for %s: %s."
1579 1580 1581
                              % (section, ' '.join(map(repr, unused)))
                              )

1582 1583 1584 1585 1586 1587 1588
_early_release_initialization_code = """\
sys.argv.insert(1, 'buildout:accept-buildout-test-releases=true')
print ('NOTE: Accepting early releases of build system packages.  Rerun '
       'bootstrap without --accept-buildout-test-releases (-t) to return to '
       'default behavior.')
"""

jim's avatar
jim committed
1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603
_usage = """\
Usage: buildout [options] [assignments] [command [command arguments]]

Options:

  -h, --help

     Print this message and exit.

  -v

     Increase the level of verbosity.  This option can be used multiple times.

  -q

alga's avatar
alga committed
1604
     Decrease the level of verbosity.  This option can be used multiple times.
jim's avatar
jim committed
1605 1606 1607 1608

  -c config_file

     Specify the path to the buildout configuration file to be used.
alga's avatar
alga committed
1609 1610
     This defaults to the file named "buildout.cfg" in the current
     working directory.
jim's avatar
jim committed
1611

1612 1613 1614 1615
  -t socket_timeout

     Specify the socket timeout in seconds.

jim's avatar
jim committed
1616 1617 1618 1619
  -U

     Don't read user defaults.

jim's avatar
jim committed
1620
  -o
1621 1622

    Run in off-line mode.  This is equivalent to the assignment
jim's avatar
jim committed
1623 1624 1625 1626
    buildout:offline=true.

  -O

1627
    Run in non-off-line mode.  This is equivalent to the assignment
jim's avatar
jim committed
1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640
    buildout:offline=false.  This is the default buildout mode.  The
    -O option would normally be used to override a true offline
    setting in a configuration file.

  -n

    Run in newest mode.  This is equivalent to the assignment
    buildout:newest=true.  With this setting, which is the default,
    buildout will try to find the newest versions of distributions
    available that satisfy its requirements.

  -N

1641
    Run in non-newest mode.  This is equivalent to the assignment
jim's avatar
jim committed
1642 1643
    buildout:newest=false.  With this setting, buildout will not seek
    new distributions if installed distributions satisfy it's
1644
    requirements.
jim's avatar
jim committed
1645

jim's avatar
jim committed
1646 1647 1648 1649 1650 1651
  -D

    Debug errors.  If an error occurs, then the post-mortem debugger
    will be started. This is especially useful for debuging recipe
    problems.

1652 1653 1654 1655 1656
  -s

    Squelch warnings about using an executable with a broken -S
    implementation.

jim's avatar
jim committed
1657
Assignments are of the form: section:option=value and are used to
alga's avatar
alga committed
1658
provide configuration options that override those given in the
jim's avatar
jim committed
1659 1660 1661 1662 1663
configuration file.  For example, to run the buildout in offline mode,
use buildout:offline=true.

Options and assignments can be interspersed.

alga's avatar
alga committed
1664
Commands:
jim's avatar
jim committed
1665 1666 1667 1668 1669 1670 1671

  install [parts]

    Install parts.  If no command arguments are given, then the parts
    definition from the configuration file is used.  Otherwise, the
    arguments specify the parts to be installed.

1672 1673 1674 1675 1676 1677
    Note that the semantics differ depending on whether any parts are
    specified.  If parts are specified, then only those parts will be
    installed. If no parts are specified, then the parts specified by
    the buildout parts option will be installed along with all of
    their dependencies.

jim's avatar
jim committed
1678 1679 1680 1681 1682 1683
  bootstrap

    Create a new buildout in the current working directory, copying
    the buildout and setuptools eggs and, creating a basic directory
    structure and a buildout-local buildout script.

1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698
  init

    Initialize a buildout, creating a buildout.cfg file if it doesn't
    exist and then performing the same actions as for the buildout
    command.

  setup script [setup command and options]

    Run a given setup script arranging that setuptools is in the
    script's path and and that it has been imported so that
    setuptools-provided commands (like bdist_egg) can be used even if
    the setup script doesn't import setuptools itself.

    The script can be given either as a script path or a path to a
    directory containing a setup.py script.
1699

1700 1701 1702 1703 1704 1705 1706
  annotate

    Display annotated sections. All sections are displayed, sorted
    alphabetically. For each section, all key-value pairs are displayed,
    sorted alphabetically, along with the origin of the value (file name or
    COMPUTED_VALUE, DEFAULT_VALUE, COMMAND_LINE_VALUE).

jim's avatar
jim committed
1707 1708 1709 1710 1711
"""
def _help():
    print _usage
    sys.exit(0)

1712 1713 1714 1715
def main(args=None):
    if args is None:
        args = sys.argv[1:]

jim's avatar
jim committed
1716 1717
    config_file = 'buildout.cfg'
    verbosity = 0
1718
    options = []
jim's avatar
jim committed
1719
    windows_restart = False
jim's avatar
jim committed
1720
    user_defaults = True
jim's avatar
jim committed
1721
    debug = False
1722
    ignore_broken_dash_s = False
jim's avatar
jim committed
1723 1724 1725 1726
    while args:
        if args[0][0] == '-':
            op = orig_op = args.pop(0)
            op = op[1:]
1727
            while op and op[0] in 'vqhWUoOnNDAs':
jim's avatar
jim committed
1728 1729
                if op[0] == 'v':
                    verbosity += 10
jim's avatar
jim committed
1730
                elif op[0] == 'q':
jim's avatar
jim committed
1731
                    verbosity -= 10
jim's avatar
jim committed
1732 1733
                elif op[0] == 'W':
                    windows_restart = True
jim's avatar
jim committed
1734 1735
                elif op[0] == 'U':
                    user_defaults = False
jim's avatar
jim committed
1736 1737
                elif op[0] == 'o':
                    options.append(('buildout', 'offline', 'true'))
jim's avatar
jim committed
1738 1739 1740 1741 1742 1743
                elif op[0] == 'O':
                    options.append(('buildout', 'offline', 'false'))
                elif op[0] == 'n':
                    options.append(('buildout', 'newest', 'true'))
                elif op[0] == 'N':
                    options.append(('buildout', 'newest', 'false'))
jim's avatar
jim committed
1744 1745
                elif op[0] == 'D':
                    debug = True
1746 1747
                elif op[0] == 's':
                    ignore_broken_dash_s = True
jim's avatar
jim committed
1748 1749
                else:
                    _help()
jim's avatar
jim committed
1750
                op = op[1:]
1751

1752 1753
            if op[:1] in  ('c', 't'):
                op_ = op[:1]
jim's avatar
jim committed
1754
                op = op[1:]
1755 1756 1757 1758

                if op_ == 'c':
                    if op:
                        config_file = op
jim's avatar
jim committed
1759
                    else:
1760 1761 1762 1763 1764 1765
                        if args:
                            config_file = args.pop(0)
                        else:
                            _error("No file name specified for option", orig_op)
                elif op_ == 't':
                    try:
1766
                        timeout = int(args.pop(0))
1767
                    except IndexError:
1768 1769 1770 1771 1772 1773 1774 1775
                        _error("No timeout value specified for option", orig_op)
                    except ValueError:
                        _error("No timeout value must be numeric", orig_op)

                    import socket
                    print 'Setting socket time out to %d seconds' % timeout
                    socket.setdefaulttimeout(timeout)

jim's avatar
jim committed
1776
            elif op:
jim's avatar
jim committed
1777 1778
                if orig_op == '--help':
                    _help()
jim's avatar
jim committed
1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790
                _error("Invalid option", '-'+op[0])
        elif '=' in args[0]:
            option, value = args.pop(0).split('=', 1)
            if len(option.split(':')) != 2:
                _error('Invalid option:', option)
            section, option = option.split(':')
            options.append((section.strip(), option.strip(), value.strip()))
        else:
            # We've run out of command-line options and option assignnemnts
            # The rest should be commands, so we'll stop here
            break

1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801
    if verbosity < 0 or ignore_broken_dash_s:
        broken_dash_S_filter_action = 'ignore'
    elif verbosity == 0: # This is the default.
        broken_dash_S_filter_action = 'once'
    else:
        broken_dash_S_filter_action = 'default'
    warnings.filterwarnings(
        broken_dash_S_filter_action,
        re.escape(
            zc.buildout.easy_install.BROKEN_DASH_S_WARNING),
        UserWarning)
jim's avatar
jim committed
1802 1803
    if verbosity:
        options.append(('buildout', 'verbosity', str(verbosity)))
1804 1805 1806

    if args:
        command = args.pop(0)
1807 1808
        if command not in (
            'install', 'bootstrap', 'runsetup', 'setup', 'init',
1809
            'annotate',
1810
            ):
1811 1812 1813
            _error('invalid command:', command)
    else:
        command = 'install'
alex_plugaru's avatar
alex_plugaru committed
1814
    
jim's avatar
jim committed
1815
    try:
1816
        try:
jim's avatar
jim committed
1817
            buildout = Buildout(config_file, options,
1818
                                user_defaults, windows_restart, command)
1819
            getattr(buildout, command)(args)
jim's avatar
jim committed
1820 1821
        except Exception, v:
            _doing()
1822 1823
            exc_info = sys.exc_info()
            import pdb, traceback
jim's avatar
jim committed
1824 1825 1826 1827 1828
            if debug:
                traceback.print_exception(*exc_info)
                sys.stderr.write('\nStarting pdb:\n')
                pdb.post_mortem(exc_info[2])
            else:
1829 1830 1831 1832
                if isinstance(v, (zc.buildout.UserError,
                                  distutils.errors.DistutilsError,
                                  )
                              ):
jim's avatar
jim committed
1833 1834
                    _error(str(v))
                else:
1835 1836 1837
                    sys.stderr.write(_internal_error_template)
                    traceback.print_exception(*exc_info)
                    sys.exit(1)
1838 1839


jim's avatar
jim committed
1840
    finally:
1841
        logging.shutdown()
1842 1843 1844 1845 1846 1847

if sys.version_info[:2] < (2, 4):
    def reversed(iterable):
        result = list(iterable);
        result.reverse()
        return result