buildout.py 61.6 KB
Newer Older
1
##############################################################################
2
#
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
"""

17
from zc.buildout.rmtree import rmtree
18
import zc.buildout.easy_install
19 20 21 22 23

try:
    from hashlib import md5
except ImportError:
    from md5 import md5
Jim Fulton's avatar
Jim Fulton committed
24

25 26 27 28 29 30
try:
    from UserDict import DictMixin
except ImportError:
    from collections import MutableMapping as DictMixin

import zc.buildout.configparser
Jim Fulton's avatar
Jim Fulton committed
31
import copy
32
import distutils.errors
Jim Fulton's avatar
Jim Fulton committed
33 34
import glob
import itertools
Jim Fulton's avatar
Jim Fulton committed
35
import logging
36
import os
Jim Fulton's avatar
Jim Fulton committed
37
import pkg_resources
38 39
import re
import shutil
40
import subprocess
41
import sys
42
import tempfile
43
import zc.buildout
44
import zc.buildout.download
45

46 47 48 49 50 51 52 53
def _print_options(sep=' ', end='\n', file=None):
    return sep, end, file

def print_(*args, **kw):
    sep, end, file = _print_options(**kw)
    if file is None:
        file = sys.stdout
    file.write(sep.join(map(str, args))+end)
54

55
realpath = zc.buildout.easy_install.realpath
56

57
pkg_resources_loc = pkg_resources.working_set.find(
Jim Fulton's avatar
Jim Fulton committed
58
    pkg_resources.Requirement.parse('distribute')).location
59

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

62
class MissingOption(zc.buildout.UserError, KeyError):
63
    """A required option was missing.
64 65
    """

66
class MissingSection(zc.buildout.UserError, KeyError):
67
    """A required section is missing.
68 69
    """

70
    def __str__(self):
Jim Fulton's avatar
Jim Fulton committed
71
        return "The referenced section, %r, was not defined." % self.args[0]
72

73 74 75 76 77 78 79 80 81 82 83 84

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):
85
    sections = list(data.keys())
86
    sections.sort()
87 88 89
    print_()
    print_("Annotated sections")
    print_("="*len("Annotated sections"))
90
    for section in sections:
91 92 93
        print_()
        print_('[%s]' % section)
        keys = list(data[section].keys())
94 95
        keys.sort()
        for key in keys:
96 97
            value, notes = data[section][key]
            keyvalue = "%s= %s" % (key, value)
98
            print_(keyvalue)
99 100 101 102 103 104 105
            line = '   '
            for note in notes.split():
                if note == '[+]':
                    line = '+= '
                elif note == '[-]':
                    line = '-= '
                else:
106
                    print_(line, note)
107
                    line = '   '
108
    print_()
109

110

111 112 113 114 115 116 117 118 119 120 121 122
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({
123 124
    'allow-hosts': '*',
    'allow-picked-versions': 'true',
125
    'bin-directory': 'bin',
126 127 128 129 130
    'develop-eggs-directory': 'develop-eggs',
    'eggs-directory': 'eggs',
    'executable': sys.executable,
    'find-links': '',
    'install-from-cache': 'false',
131
    'installed': '.installed.cfg',
132
    'log-format': '',
133 134 135 136 137 138
    'log-level': 'INFO',
    'newest': 'true',
    'offline': 'false',
    'parts-directory': 'parts',
    'prefer-final': 'false',
    'python': 'buildout',
139
    'socket-timeout': '',
140
    'use-dependency-links': 'true',
141 142
    }, 'DEFAULT_VALUE')

143
class Buildout(DictMixin):
144

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

149
        __doing__ = 'Initializing.'
150

Jim Fulton's avatar
Jim Fulton 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
                    self._init_config(config_file, args)
Jim Fulton's avatar
Jim Fulton committed
163
                elif command == 'setup':
164 165
                    # Sigh. This model of a buildout instance
                    # with methods is breaking down. :(
Jim Fulton's avatar
Jim Fulton committed
166
                    config_file = None
167
                    data['buildout']['directory'] = ('.', 'COMPUTED_VALUE')
168 169 170
                else:
                    raise zc.buildout.UserError(
                        "Couldn't open %s" % config_file)
171 172 173
            elif command == 'init':
                raise zc.buildout.UserError(
                    "%r already exists." % config_file)
174 175

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

Jim Fulton's avatar
Jim Fulton committed
181 182 183 184 185 186 187 188

        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()
189

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

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

        # apply command-line options
Jim Fulton's avatar
Jim Fulton committed
205
        _update(data, cloptions)
206

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

216 217 218 219 220
        # 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.
221 222 223 224 225 226
        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

227 228 229 230 231 232 233 234
        # 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']
235
        self._links = links and links.split() or ()
236
        allow_hosts = buildout_section['allow-hosts'].split('\n')
237
        self._allow_hosts = tuple([host.strip() for host in allow_hosts
238
                                   if host.strip() != ''])
239
        self._logger = logging.getLogger('zc.buildout')
240 241
        self.offline = (buildout_section['offline'] == 'true')
        self.newest = (buildout_section['newest'] == 'true')
242

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

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

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

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

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

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

Jim Fulton's avatar
Jim Fulton committed
273
        self._setup_logging()
274
        self._setup_socket_timeout()
Jim Fulton's avatar
Jim Fulton committed
275

276
        offline = options['offline']
277 278
        if offline not in ('true', 'false'):
            self._error('Invalid value for offline option: %s', offline)
279
        self.offline = (offline == 'true')
280

Jim Fulton's avatar
Jim Fulton committed
281 282 283
        if self.offline:
            newest = options['newest'] = 'false'
        else:
284
            newest = options['newest']
Jim Fulton's avatar
Jim Fulton committed
285 286
            if newest not in ('true', 'false'):
                self._error('Invalid value for newest option: %s', newest)
287
        self.newest = (newest == 'true')
288

Gary Poster's avatar
Gary Poster committed
289 290 291 292 293
        # This is a hacked version of zc.buildout for 1.4.4.
        # This means that buildout uses the defaults set up above.  The point
        # of it is to keep from migrating to 1.5 unless explicitly
        # requested.  This lets 1.4.4 be an "aspirin release" that people can
        # use if they are having trouble with the 1.5 releases.
294
        versions = {}
Gary Poster's avatar
Gary Poster committed
295 296 297 298
        versions_section = options.get('versions')
        if versions_section:
            versions.update(dict(self[versions_section]))
        zc.buildout.easy_install.default_versions(versions)
299

300
        prefer_final = options['prefer-final']
301 302 303 304
        if prefer_final not in ('true', 'false'):
            self._error('Invalid value for prefer-final option: %s',
                        prefer_final)
        zc.buildout.easy_install.prefer_final(prefer_final=='true')
305

306
        use_dependency_links = options['use-dependency-links']
307 308 309 310 311
        if use_dependency_links not in ('true', 'false'):
            self._error('Invalid value for use-dependency-links option: %s',
                        use_dependency_links)
        zc.buildout.easy_install.use_dependency_links(
            use_dependency_links == 'true')
312

313
        allow_picked_versions = options['allow-picked-versions']
Jim Fulton's avatar
Jim Fulton committed
314 315 316 317
        if allow_picked_versions not in ('true', 'false'):
            self._error('Invalid value for allow-picked-versions option: %s',
                        allow_picked_versions)
        zc.buildout.easy_install.allow_picked_versions(
318
            allow_picked_versions == 'true')
Jim Fulton's avatar
Jim Fulton committed
319

320 321 322 323 324 325 326 327 328 329 330 331
        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)
332

333 334
            zc.buildout.easy_install.download_cache(download_cache)

335 336 337 338 339 340
        install_from_cache = options['install-from-cache']
        if install_from_cache not in ('true', 'false'):
            self._error('Invalid value for install-from-cache option: %s',
                        install_from_cache)
        zc.buildout.easy_install.install_from_cache(
            install_from_cache=='true')
341

342 343 344 345
        # "Use" each of the defaults so they aren't reported as unused options.
        for name in _buildout_default_options:
            options[name]

346 347 348 349 350
        # 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')

351 352
        os.chdir(options['directory'])

353 354 355 356
    def _buildout_path(self, name):
        if '${' in name:
            return name
        return os.path.join(self._buildout_dir, name)
357

Jim Fulton's avatar
Jim Fulton committed
358
    def bootstrap(self, args):
359
        __doing__ = 'Bootstrapping.'
360

361
        self._setup_directories()
Jim Fulton's avatar
Jim Fulton committed
362

Jim Fulton's avatar
Jim Fulton committed
363
        # Now copy buildout and distribute eggs, and record destination eggs:
Jim Fulton's avatar
Jim Fulton committed
364
        entries = []
Jim Fulton's avatar
Jim Fulton committed
365
        for name in 'distribute', 'zc.buildout':
Jim Fulton's avatar
Jim Fulton committed
366 367 368
            r = pkg_resources.Requirement.parse(name)
            dist = pkg_resources.working_set.find(r)
            if dist.precedence == pkg_resources.DEVELOP_DIST:
369
                dest = os.path.join(self['buildout']['develop-eggs-directory'],
Jim Fulton's avatar
Jim Fulton committed
370 371 372 373 374 375 376 377
                                    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 Fulton's avatar
Jim Fulton committed
378 379 380 381
                    if os.path.isdir(dist.location):
                        shutil.copytree(dist.location, dest)
                    else:
                        shutil.copy2(dist.location, dest)
Jim Fulton's avatar
Jim Fulton committed
382 383 384 385 386 387 388 389

        # Create buildout script
        ws = pkg_resources.WorkingSet(entries)
        ws.require('zc.buildout')
        zc.buildout.easy_install.scripts(
            ['zc.buildout'], ws, sys.executable,
            self['buildout']['bin-directory'])

390
    def _init_config(self, config_file, args):
391
        print_('Creating %r.' % config_file)
392 393 394 395
        f = open(config_file, 'w')
        sep = re.compile(r'[\\/]')
        if args:
            eggs = '\n  '.join(a for a in args if not sep.search(a))
396
            sepsub = os.path.sep == '/' and '/' or re.escape(os.path.sep)
397
            paths = '\n  '.join(
398
                sep.sub(sepsub, a)
399
                for a in args if sep.search(a))
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
            f.write('[buildout]\n'
                    'parts = py\n'
                    '\n'
                    '[py]\n'
                    'recipe = zc.recipe.egg\n'
                    'interpreter = py\n'
                    'eggs =\n'
                    )
            if eggs:
                f.write('  %s\n' % eggs)
            if paths:
                f.write('extra-paths =\n  %s\n' % paths)
                for p in [a for a in args if sep.search(a)]:
                    if not os.path.exists(p):
                        os.mkdir(p)

        else:
            f.write('[buildout]\nparts =\n')
        f.close()

    def init(self, args):
        self.bootstrap(())
        if args:
            self.install(())
424

425
    def install(self, install_args):
426
        __doing__ = 'Installing.'
427

Jim Fulton's avatar
Jim Fulton committed
428
        self._load_extensions()
429
        self._setup_directories()
Jim Fulton's avatar
Jim Fulton committed
430

431 432 433 434
        # Add develop-eggs directory to path so that it gets searched
        # for eggs:
        sys.path.insert(0, self['buildout']['develop-eggs-directory'])

435 436 437
        # Check for updates. This could cause the process to be rstarted
        self._maybe_upgrade()

438
        # load installed data
439 440
        (installed_part_options, installed_exists
         )= self._read_installed_part_options()
441

442 443 444 445 446 447 448 449
        # Remove old develop eggs
        self._uninstall(
            installed_part_options['buildout'].get(
                'installed_develop_eggs', '')
            )

        # Build develop eggs
        installed_develop_eggs = self._develop()
450 451
        installed_part_options['buildout']['installed_develop_eggs'
                                           ] = installed_develop_eggs
452

453 454 455
        if installed_exists:
            self._update_installed(
                installed_develop_eggs=installed_develop_eggs)
456

457 458 459 460 461
        # 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 []
462

463 464
        if install_args:
            install_parts = install_args
465 466 467 468 469
            uninstall_missing = False
        else:
            install_parts = conf_parts
            uninstall_missing = True

470 471
        # load and initialize recipes
        [self[part]['recipe'] for part in install_parts]
472 473
        if not install_args:
            install_parts = self._parts
474

475
        if self._log_level < logging.DEBUG:
476 477
            sections = list(self)
            sections.sort()
478 479
            print_()
            print_('Configuration data:')
480 481
            for section in self._data:
                _save_options(section, self[section], sys.stdout)
482
            print_()
483

484 485 486 487

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

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
        # uninstall parts that are no-longer used or who's configs
        # 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 Fulton's avatar
Jim Fulton committed
504
                    else:
505 506 507
                        continue

                # output debugging info
508 509 510 511 512 513 514 515 516 517 518 519 520 521
                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)
522 523 524

            elif not uninstall_missing:
                continue
Jim Fulton's avatar
Jim Fulton committed
525

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
            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
542
                __doing__ = 'Updating %s.', part
543 544 545 546 547 548 549 550 551 552 553 554 555 556
                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:
557
                    installed_files = self[part]._call(update)
558 559 560 561 562 563 564 565 566 567 568
                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 Fulton's avatar
Jim Fulton committed
569
                else:
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
                    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
585
                __doing__ = 'Installing %s.', part
586
                self._logger.info(*__doing__)
587
                installed_files = self[part]._call(recipe.install)
588 589 590 591 592 593 594
                if installed_files is None:
                    self._logger.warning(
                        "The %s install returned None.  A path or "
                        "iterable os paths should be returned.",
                        part)
                    installed_files = ()
                elif isinstance(installed_files, str):
595
                    installed_files = [installed_files]
596 597 598 599 600 601 602 603 604 605 606 607 608 609
                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'] = (
610
                    ' '.join(installed_parts))
611 612 613 614 615
                self._save_installed_options(installed_part_options)
                installed_exists = True
            else:
                assert installed_exists
                self._update_installed(parts=' '.join(installed_parts))
Jim Fulton's avatar
Jim Fulton committed
616

617 618 619 620 621
        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 Fulton's avatar
Jim Fulton committed
622

623 624
        self._unload_extensions()

625 626 627 628
    def _update_installed(self, **buildout_options):
        installed = self['buildout']['installed']
        f = open(installed, 'a')
        f.write('\n[buildout]\n')
629
        for option, value in list(buildout_options.items()):
630 631
            _save_option(option, value, f)
        f.close()
632

633 634
    def _uninstall_part(self, part, installed_part_options):
        # ununstall part
635
        __doing__ = 'Uninstalling %s.', part
636 637 638 639 640 641 642
        self._logger.info(*__doing__)

        # run uinstall recipe
        recipe, entry = _recipe(installed_part_options[part])
        try:
            uninstaller = _install_and_load(
                recipe, 'zc.buildout.uninstall', entry, self)
643
            self._logger.info('Running uninstall recipe.')
644
            uninstaller(part, installed_part_options[part])
645
        except (ImportError, pkg_resources.DistributionNotFound):
646 647 648 649 650 651
            pass

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

652
    def _setup_directories(self):
653
        __doing__ = 'Setting up buildout directories'
654 655 656 657 658

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

662 663 664
    def _develop(self):
        """Install sources by running setup.py develop on them
        """
665 666
        __doing__ = 'Processing directories listed in the develop option'

667
        develop = self['buildout'].get('develop')
668 669 670 671 672 673 674 675 676
        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:
677 678
            try:
                for setup in develop.split():
679
                    setup = self._buildout_path(setup)
680 681
                    files = glob.glob(setup)
                    if not files:
682 683
                        self._logger.warn("Couldn't develop %r (not found)",
                                          setup)
Shane Hathaway's avatar
Shane Hathaway committed
684 685
                    else:
                        files.sort()
686 687 688 689
                    for setup in files:
                        self._logger.info("Develop: %r", setup)
                        __doing__ = 'Processing develop directory %r.', setup
                        zc.buildout.easy_install.develop(setup, dest)
690 691 692 693 694 695 696 697 698
            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
                     ]))
699
                raise
700

701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
            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(
719
                    "Unexpected entry, %r, in develop-eggs directory.", f)
720

721 722 723 724 725 726
    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] = {}
727
            recipe, entry = _recipe(options)
728
            req = pkg_resources.Requirement.parse(recipe)
729
            sig = _dists_sig(pkg_resources.working_set.resolve([req]))
730 731 732
            options['__buildout_signature__'] = ' '.join(sig)

    def _read_installed_part_options(self):
Jim Fulton's avatar
Jim Fulton committed
733 734
        old = self['buildout']['installed']
        if old and os.path.isfile(old):
735 736 737
            fp = open(old)
            sections = zc.buildout.configparser.parse(fp, old)
            fp.close()
738
            result = {}
739 740
            for section, options in sections.items():
                for option, value in options.items():
741 742 743
                    if '%(' in value:
                        for k, v in _spacey_defaults:
                            value = value.replace(k, v)
744
                        options[option] = value
745
                result[section] = Options(self, section, options)
746

747
            return result, True
748
        else:
749 750 751
            return ({'buildout': Options(self, 'buildout', {'parts': ''})},
                    False,
                    )
752 753

    def _uninstall(self, installed):
Jim Fulton's avatar
Jim Fulton committed
754 755 756
        for f in installed.split('\n'):
            if not f:
                continue
757
            f = self._buildout_path(f)
758
            if os.path.isdir(f):
759
                rmtree(f)
760
            elif os.path.isfile(f):
761 762 763 764 765 766 767 768 769 770 771 772 773
                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.
                        ):
774 775
                        raise

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

791

792
    def _save_installed_options(self, installed_options):
Jim Fulton's avatar
Jim Fulton committed
793 794 795 796
        installed = self['buildout']['installed']
        if not installed:
            return
        f = open(installed, 'w')
797 798
        _save_options('buildout', installed_options['buildout'], f)
        for part in installed_options['buildout']['parts'].split():
799
            print_(file=f)
800 801
            _save_options(part, installed_options[part], f)
        f.close()
Jim Fulton's avatar
Jim Fulton committed
802

803 804
    def _error(self, message, *args):
        raise zc.buildout.UserError(message % args)
Jim Fulton's avatar
Jim Fulton committed
805

806 807
    def _setup_socket_timeout(self):
        timeout = self['buildout']['socket-timeout']
808
        if timeout != '':
809 810 811 812 813 814 815 816 817 818
            try:
                timeout = int(timeout)
                import socket
                self._logger.info('Setting socket time out to %d seconds.', timeout)
                socket.setdefaulttimeout(timeout)
            except ValueError:
                self._logger.warning("Default socket timeout is used !\n"
                    "Value in configuration is not numeric: [%s].\n",
                    timeout)

Jim Fulton's avatar
Jim Fulton committed
819 820
    def _setup_logging(self):
        root_logger = logging.getLogger()
821
        self._logger = logging.getLogger('zc.buildout')
Jim Fulton's avatar
Jim Fulton committed
822
        handler = logging.StreamHandler(sys.stdout)
823 824 825 826 827 828 829 830 831
        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)
832

833
        handler.setFormatter(logging.Formatter(log_format))
Jim Fulton's avatar
Jim Fulton committed
834
        root_logger.addHandler(handler)
835

Jim Fulton's avatar
Jim Fulton committed
836 837 838 839 840 841 842 843 844 845 846 847 848
        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)
849 850 851

        level -= verbosity
        root_logger.setLevel(level)
852
        self._log_level = level
853

854
    def _maybe_upgrade(self):
Jim Fulton's avatar
Jim Fulton committed
855
        # See if buildout or distribute need to be upgraded.
856
        # If they do, do the upgrade and restart the buildout process.
857
        __doing__ = 'Checking for upgrades.'
858

Jim Fulton's avatar
Jim Fulton committed
859 860
        if not self.newest:
            return
861

862 863 864
        ws = zc.buildout.easy_install.install(
            [
            (spec + ' ' + self['buildout'].get(spec+'-version', '')).strip()
Jim Fulton's avatar
Jim Fulton committed
865
            for spec in ('zc.buildout', 'distribute')
866 867 868 869
            ],
            self['buildout']['eggs-directory'],
            links = self['buildout'].get('find-links', '').split(),
            index = self['buildout'].get('index'),
Tarek Ziad's avatar
Tarek Ziad committed
870 871
            path = [self['buildout']['develop-eggs-directory']],
            allow_hosts = self._allow_hosts
872 873 874
            )

        upgraded = []
Jim Fulton's avatar
Jim Fulton committed
875
        for project in 'zc.buildout', 'distribute':
876
            req = pkg_resources.Requirement.parse(project)
877 878
            project_location = pkg_resources.working_set.find(req).location
            if ws.find(req).location != project_location:
879 880 881 882
                upgraded.append(ws.find(req))

        if not upgraded:
            return
Jim Fulton's avatar
Jim Fulton committed
883

884
        __doing__ = 'Upgrading.'
885

886 887 888 889 890 891 892 893
        should_run = realpath(
            os.path.join(os.path.abspath(self['buildout']['bin-directory']),
                         'buildout')
            )
        if sys.platform == 'win32':
            should_run += '-script.py'

        if (realpath(os.path.abspath(sys.argv[0])) != should_run):
894 895
            self._logger.debug("Running %r.", realpath(sys.argv[0]))
            self._logger.debug("Local buildout is %r.", should_run)
896
            self._logger.warn("Not upgrading because not running a local "
897
                              "buildout command.")
898 899
            return

Jim Fulton's avatar
Jim Fulton committed
900
        if sys.platform == 'win32' and not self.__windows_restart:
901
            args = list(map(zc.buildout.easy_install._safe_arg, sys.argv))
Jim Fulton's avatar
Jim Fulton committed
902 903 904
            args.insert(1, '-W')
            if not __debug__:
                args.insert(0, '-O')
905
            args.insert(0, zc.buildout.easy_install._safe_arg (sys.executable))
906 907
            os.execv(sys.executable, args)

908 909
        self._logger.info("Upgraded:\n  %s;\nrestarting.",
                          ",\n  ".join([("%s version %s"
910 911 912 913 914 915
                                       % (dist.project_name, dist.version)
                                       )
                                      for dist in upgraded
                                      ]
                                     ),
                          )
916

917 918 919 920 921 922 923 924
        # the new dist is different, so we've upgraded.
        # Update the scripts and return True
        zc.buildout.easy_install.scripts(
            ['zc.buildout'], ws, sys.executable,
            self['buildout']['bin-directory'],
            )

        # Restart
925
        args = sys.argv[:]
926 927
        if not __debug__:
            args.insert(0, '-O')
928 929
        args.insert(0, sys.executable)
        sys.exit(subprocess.call(args))
930

931
    def _load_extensions(self):
932
        __doing__ = 'Loading extensions.'
933 934
        specs = self['buildout'].get('extensions', '').split()
        if specs:
Jim Fulton's avatar
Jim Fulton committed
935
            path = [self['buildout']['develop-eggs-directory']]
Jim Fulton's avatar
Jim Fulton committed
936
            if self.offline:
937
                dest = None
Jim Fulton's avatar
Jim Fulton committed
938
                path.append(self['buildout']['eggs-directory'])
939 940
            else:
                dest = self['buildout']['eggs-directory']
Jim Fulton's avatar
Jim Fulton committed
941
                if not os.path.exists(dest):
942
                    self._logger.info('Creating directory %r.', dest)
Jim Fulton's avatar
Jim Fulton committed
943
                    os.mkdir(dest)
Jim Fulton's avatar
Jim Fulton committed
944

945
            zc.buildout.easy_install.install(
Jim Fulton's avatar
Jim Fulton committed
946
                specs, dest, path=path,
947
                working_set=pkg_resources.working_set,
948 949
                links = self['buildout'].get('find-links', '').split(),
                index = self['buildout'].get('index'),
Tarek Ziad's avatar
Tarek Ziad committed
950
                newest=self.newest, allow_hosts=self._allow_hosts)
951 952 953 954 955

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

956 957
            for ep in pkg_resources.iter_entry_points('zc.buildout.extension'):
                ep.load()(self)
958

959 960 961 962 963 964 965 966
    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)

967
    def setup(self, args):
968 969
        if not args:
            raise zc.buildout.UserError(
Jim Fulton's avatar
Jim Fulton committed
970
                "The setup command requires the path to a setup script or \n"
971
                "directory containing a setup script, and its arguments."
972
                )
973 974 975 976
        setup = args.pop(0)
        if os.path.isdir(setup):
            setup = os.path.join(setup, 'setup.py')

977
        self._logger.info("Running setup script %r.", setup)
978 979 980 981
        setup = os.path.abspath(setup)

        fd, tsetup = tempfile.mkstemp()
        try:
982
            os.write(fd, (zc.buildout.easy_install.runsetup_template % dict(
Jim Fulton's avatar
Jim Fulton committed
983
                distribute=pkg_resources_loc,
984 985
                setupdir=os.path.dirname(setup),
                setup=setup,
986
                __file__ = setup,
987
                )).encode())
Jim Fulton's avatar
Jim Fulton committed
988 989
            args = [sys.executable, tsetup] + args
            zc.buildout.easy_install.call_subprocess(args)
990 991 992
        finally:
            os.close(fd)
            os.remove(tsetup)
993

994 995
    runsetup = setup # backward compat.

996 997 998
    def annotate(self, args):
        _print_annotate(self._annotated)

999
    def __getitem__(self, section):
1000
        __doing__ = 'Getting section %s.', section
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
        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()
1014
        return options
1015 1016 1017 1018 1019 1020 1021 1022

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

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

    def keys(self):
1023
        return list(self._raw.keys())
1024 1025 1026 1027

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

1028 1029 1030
    def __len__(self):
        return len(self._raw)

Jim Fulton's avatar
Jim Fulton committed
1031 1032

def _install_and_load(spec, group, entry, buildout):
1033
    __doing__ = 'Loading recipe %r.', spec
Jim Fulton's avatar
Jim Fulton committed
1034 1035 1036 1037 1038
    try:
        req = pkg_resources.Requirement.parse(spec)

        buildout_options = buildout['buildout']
        if pkg_resources.working_set.find(req) is None:
1039
            __doing__ = 'Installing recipe %s.', spec
Jim Fulton's avatar
Jim Fulton committed
1040
            if buildout.offline:
Jim Fulton's avatar
Jim Fulton committed
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
                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 Fulton's avatar
Jim Fulton committed
1055
                newest=buildout.newest,
Tarek Ziad's avatar
Tarek Ziad committed
1056
                allow_hosts=buildout._allow_hosts
Jim Fulton's avatar
Jim Fulton committed
1057 1058
                )

1059
        __doing__ = 'Loading %s recipe entry %s:%s.', group, spec, entry
Jim Fulton's avatar
Jim Fulton committed
1060 1061 1062
        return pkg_resources.load_entry_point(
            req.project_name, group, entry)

1063 1064
    except Exception:
        v = sys.exc_info()[1]
Jim Fulton's avatar
Jim Fulton committed
1065 1066 1067 1068 1069 1070
        buildout._logger.log(
            1,
            "Could't load %s entry point %s\nfrom %s:\n%s.",
            group, entry, spec, v)
        raise

1071
class Options(DictMixin):
1072 1073 1074 1075 1076

    def __init__(self, buildout, section, data):
        self.buildout = buildout
        self.name = section
        self._raw = data
1077
        self._cooked = {}
1078 1079 1080
        self._data = {}

    def _initialize(self):
1081
        name = self.name
1082
        __doing__ = 'Initializing section %s.', name
1083

Jim Fulton's avatar
Jim Fulton committed
1084 1085 1086
        if '<' in self._raw:
            self._raw = self._do_extend_raw(name, self._raw, [])

1087
        # force substitutions
1088
        for k, v in sorted(self._raw.items()):
1089 1090
            if '${' in v:
                self._dosub(k, v)
1091

1092 1093
        if self.name == 'buildout':
            return # buildout section can never be a part
1094

1095 1096 1097
        recipe = self.get('recipe')
        if not recipe:
            return
1098

1099 1100
        reqs, entry = _recipe(self._data)
        buildout = self.buildout
Jim Fulton's avatar
Jim Fulton committed
1101
        recipe_class = _install_and_load(reqs, 'zc.buildout', entry, buildout)
1102

1103
        __doing__ = 'Initializing part %s.', name
1104 1105
        self.recipe = recipe_class(buildout, name, self)
        buildout._parts.append(name)
1106

Jim Fulton's avatar
Jim Fulton committed
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
    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

1134
    def _dosub(self, option, v):
1135
        __doing__ = 'Getting option %s:%s.', self.name, option
1136 1137 1138 1139
        seen = [(self.name, option)]
        v = '$$'.join([self._sub(s, seen) for s in v.split('$$')])
        self._cooked[option] = v

1140 1141 1142 1143 1144 1145
    def get(self, option, default=None, seen=None):
        try:
            return self._data[option]
        except KeyError:
            pass

1146
        v = self._cooked.get(option)
1147
        if v is None:
1148 1149 1150
            v = self._raw.get(option)
            if v is None:
                return default
1151

1152
        __doing__ = 'Getting option %s:%s.', self.name, option
1153

1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
        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()
1166

1167 1168 1169 1170 1171
        self._data[option] = v
        return v

    _template_split = re.compile('([$]{[^}]*})').split
    _simple = re.compile('[-a-zA-Z0-9 ._]+$').match
1172
    _valid = re.compile('\${[-a-zA-Z0-9 ._]*:[-a-zA-Z0-9 ._]+}$').match
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
    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)
1197

1198 1199 1200 1201
            section, option = s
            if not section:
                section = self.name
            v = self.buildout[section].get(option, None, seen)
1202
            if v is None:
1203 1204 1205 1206 1207
                if option == '_buildout_section_name_':
                    v = self.name
                else:
                    raise MissingOption("Referenced option does not exist:",
                                        section, option)
1208 1209 1210 1211
            subs.append(v)
        subs.append('')

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

1213 1214 1215 1216 1217 1218 1219 1220
    def __getitem__(self, key):
        try:
            return self._data[key]
        except KeyError:
            pass

        v = self.get(key)
        if v is None:
1221
            raise MissingOption("Missing option: %s:%s" % (self.name, key))
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
        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]
1234 1235
            if key in self._cooked:
                del self._cooked[key]
1236 1237 1238
        elif key in self._data:
            del self._data[key]
        else:
1239
            raise KeyError(key)
1240 1241 1242 1243 1244

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

1245 1246 1247 1248 1249 1250
    def __iter__(self):
        return iter(self.keys())

    def __len__(self):
        return len(self.keys())

1251
    def copy(self):
1252 1253 1254 1255
        result = self._raw.copy()
        result.update(self._cooked)
        result.update(self._data)
        return result
1256

1257
    def _call(self, f):
1258
        buildout_directory = self.buildout['buildout']['directory']
1259 1260 1261
        self._created = []
        try:
            try:
1262
                os.chdir(buildout_directory)
1263 1264 1265 1266
                return f()
            except:
                for p in self._created:
                    if os.path.isdir(p):
1267
                        rmtree(p)
1268 1269 1270
                    elif os.path.isfile(p):
                        os.remove(p)
                    else:
Jim Fulton's avatar
Jim Fulton committed
1271
                        self.buildout._logger.warn("Couldn't clean up %r.", p)
1272 1273 1274
                raise
        finally:
            self._created = None
1275
            os.chdir(buildout_directory)
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285

    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

1286 1287 1288
_spacey_nl = re.compile('[ \t\r\f\v]*\n[ \t\r\f\v\n]*'
                        '|'
                        '^[ \t\r\f\v]+'
1289 1290 1291 1292
                        '|'
                        '[ \t\r\f\v]+$'
                        )

1293 1294 1295 1296 1297 1298 1299 1300
_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'),
    ]

1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314
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

1315 1316 1317 1318 1319 1320
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'
1321
    print_(option, '=', value, file=f)
1322

1323
def _save_options(section, options, f):
1324 1325
    print_('[%s]' % section, file=f)
    items = list(options.items())
1326 1327
    items.sort()
    for option, value in items:
1328
        _save_option(option, value, f)
1329

1330 1331 1332 1333 1334 1335 1336 1337
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'

def _open(base, filename, seen, dl_options, override, downloaded):
1338 1339 1340 1341
    """Open a configuration file and return the result as a dictionary,

    Recursively open other files based on buildout options found.
    """
1342 1343
    _update_section(dl_options, override)
    _dl_options = _unannotate_section(dl_options.copy())
1344 1345
    newest = _convert_bool('newest', _dl_options.get('newest', 'false'))
    fallback = newest and not (filename in downloaded)
1346
    download = zc.buildout.download.Download(
1347 1348 1349
        _dl_options, cache=_dl_options.get('extends-cache'),
        fallback=fallback, hash_name=True)
    is_temp = False
1350
    if _isurl(filename):
1351 1352
        path, is_temp = download(filename)
        fp = open(path)
1353 1354 1355 1356 1357 1358 1359
        base = filename[:filename.rfind('/')]
    elif _isurl(base):
        if os.path.isabs(filename):
            fp = open(filename)
            base = os.path.dirname(filename)
        else:
            filename = base + '/' + filename
1360 1361
            path, is_temp = download(filename)
            fp = open(path)
1362 1363 1364 1365 1366
            base = filename[:filename.rfind('/')]
    else:
        filename = os.path.join(base, filename)
        fp = open(filename)
        base = os.path.dirname(filename)
1367
    downloaded.add(filename)
1368

1369
    if filename in seen:
1370
        if is_temp:
1371 1372
            fp.close()
            os.remove(path)
1373
        raise zc.buildout.UserError("Recursive file include", seen, filename)
1374

1375
    root_config_file = not seen
1376 1377
    seen.append(filename)

1378 1379
    result = zc.buildout.configparser.parse(fp, filename)
    fp.close()
1380
    if is_temp:
1381
        os.remove(path)
1382

1383 1384 1385 1386 1387 1388
    options = result.get('buildout', {})
    extends = options.pop('extends', None)
    if 'extended-by' in options:
        raise zc.buildout.UserError(
            'No-longer supported "extended-by" option found in %s.' %
            filename)
1389

1390 1391
    result = _annotate(result, filename)

1392 1393 1394
    if root_config_file and 'buildout' in result:
        dl_options = _update_section(dl_options, result['buildout'])

1395 1396
    if extends:
        extends = extends.split()
1397 1398
        eresult = _open(base, extends.pop(0), seen, dl_options, override,
                        downloaded)
1399
        for fname in extends:
1400 1401
            _update(eresult, _open(base, fname, seen, dl_options, override,
                    downloaded))
Jim Fulton's avatar
Jim Fulton committed
1402
        result = _update(eresult, result)
1403 1404 1405

    seen.pop()
    return result
1406

1407

1408
ignore_directories = '.svn', 'CVS', '__pycache__'
1409
_dir_hashes = {}
1410
def _dir_hash(dir):
1411 1412 1413
    dir_hash = _dir_hashes.get(dir, None)
    if dir_hash is not None:
        return dir_hash
1414
    hash = md5()
1415
    for (dirpath, dirnames, filenames) in os.walk(dir):
1416 1417 1418 1419 1420
        dirnames[:] = sorted(n for n in dirnames if n not in ignore_directories)
        filenames[:] = sorted(f for f in filenames
                              if (not (f.endswith('pyc') or f.endswith('pyo'))
                                  and os.path.exists(os.path.join(dirpath, f)))
                              )
1421 1422
        hash.update(' '.join(dirnames).encode())
        hash.update(' '.join(filenames).encode())
1423
        for name in filenames:
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
            path = os.path.join(dirpath, name)
            if name == 'entry_points.txt':
                f = open(path)
                # Entry points aren't written in stable order. :(
                try:
                    sections = zc.buildout.configparser.parse(f, path)
                    data = repr([(sname, sorted(sections[sname].items()))
                                 for sname in sorted(sections)]).encode('utf-8')
                except Exception:
                    f.close()
                    f = open(path, 'rb')
                    data = f.read()
            else:
                f = open(path, 'rb')
                data = f.read()
            f.close()
            hash.update(data)
1441
    _dir_hashes[dir] = dir_hash = hash.hexdigest()
1442
    return dir_hash
1443

1444
def _dists_sig(dists):
1445 1446 1447 1448 1449 1450
    result = []
    for dist in dists:
        location = dist.location
        if dist.precedence == pkg_resources.DEVELOP_DIST:
            result.append(dist.project_name + '-' + _dir_hash(location))
        else:
1451
            result.append(os.path.basename(location))
1452 1453
    return result

1454
def _update_section(s1, s2):
Jim Fulton's avatar
Jim Fulton committed
1455
    s2 = s2.copy() # avoid mutating the second argument, which is unexpected
1456
    for k, v in list(s2.items()):
1457
        v2, note2 = v
1458 1459
        if k.endswith('+'):
            key = k.rstrip(' +')
1460
            v1, note1 = s1.get(key, ("", ""))
1461
            newnote = ' [+] '.join((note1, note2)).strip()
1462 1463
            s2[key] = "\n".join((v1).split('\n') +
                v2.split('\n')), newnote
1464 1465 1466
            del s2[k]
        elif k.endswith('-'):
            key = k.rstrip(' -')
1467
            v1, note1 = s1.get(key, ("", ""))
1468
            newnote = ' [-] '.join((note1, note2)).strip()
1469 1470 1471
            s2[key] = ("\n".join(
                [v for v in v1.split('\n')
                   if v not in v2.split('\n')]), newnote)
1472
            del s2[k]
1473

1474 1475 1476
    s1.update(s2)
    return s1

1477 1478 1479
def _update(d1, d2):
    for section in d2:
        if section in d1:
1480
            d1[section] = _update_section(d1[section], d2[section])
1481 1482 1483 1484
        else:
            d1[section] = d2[section]
    return d1

1485 1486 1487 1488 1489 1490 1491 1492 1493
def _recipe(options):
    recipe = options['recipe']
    if ':' in recipe:
        recipe, entry = recipe.split(':')
    else:
        entry = 'default'

    return recipe, entry

1494 1495 1496 1497 1498 1499 1500 1501 1502
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
1503

1504 1505 1506 1507 1508 1509 1510
    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)

1511
def _error(*message):
1512
    sys.stderr.write('Error: ' + ' '.join(message) +'\n')
1513 1514
    sys.exit(1)

1515 1516 1517 1518 1519
_internal_error_template = """
An internal error occured due to a bug in either zc.buildout or in a
recipe being used:
"""

1520 1521
def _check_for_unused_options_in_section(buildout, section):
    options = buildout[section]
1522 1523
    unused = [option for option in sorted(options._raw)
              if option not in options._data]
1524
    if unused:
1525
        buildout._logger.warn("Unused options for %s: %s."
1526 1527 1528
                              % (section, ' '.join(map(repr, unused)))
                              )

1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543
_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

1544
     Decrease the level of verbosity.  This option can be used multiple times.
1545 1546 1547 1548

  -c config_file

     Specify the path to the buildout configuration file to be used.
1549 1550
     This defaults to the file named "buildout.cfg" in the current
     working directory.
1551

1552 1553 1554 1555
  -t socket_timeout

     Specify the socket timeout in seconds.

Jim Fulton's avatar
Jim Fulton committed
1556 1557 1558 1559
  -U

     Don't read user defaults.

Jim Fulton's avatar
Jim Fulton committed
1560
  -o
1561 1562

    Run in off-line mode.  This is equivalent to the assignment
Jim Fulton's avatar
Jim Fulton committed
1563 1564 1565 1566
    buildout:offline=true.

  -O

1567
    Run in non-off-line mode.  This is equivalent to the assignment
Jim Fulton's avatar
Jim Fulton committed
1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580
    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

1581
    Run in non-newest mode.  This is equivalent to the assignment
Jim Fulton's avatar
Jim Fulton committed
1582 1583
    buildout:newest=false.  With this setting, buildout will not seek
    new distributions if installed distributions satisfy it's
1584
    requirements.
Jim Fulton's avatar
Jim Fulton committed
1585

1586 1587 1588 1589 1590 1591
  -D

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

1592
Assignments are of the form: section:option=value and are used to
1593
provide configuration options that override those given in the
1594 1595 1596 1597 1598
configuration file.  For example, to run the buildout in offline mode,
use buildout:offline=true.

Options and assignments can be interspersed.

1599
Commands:
1600 1601 1602 1603 1604 1605 1606

  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.

1607 1608 1609 1610 1611 1612
    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.

1613 1614 1615
  bootstrap

    Create a new buildout in the current working directory, copying
Jim Fulton's avatar
Jim Fulton committed
1616
    the buildout and distribute eggs and, creating a basic directory
1617 1618
    structure and a buildout-local buildout script.

1619 1620 1621 1622 1623 1624 1625 1626
  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]

Jim Fulton's avatar
Jim Fulton committed
1627
    Run a given setup script arranging that distribute is in the
1628
    script's path and and that it has been imported so that
Jim Fulton's avatar
Jim Fulton committed
1629 1630
    distribute-provided commands (like bdist_egg) can be used even if
    the setup script doesn't import setuptools.
1631 1632 1633

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

1635 1636 1637 1638 1639 1640 1641
  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).

1642 1643
"""
def _help():
1644
    print_(_usage)
1645 1646
    sys.exit(0)

1647 1648 1649 1650
def main(args=None):
    if args is None:
        args = sys.argv[1:]

Jim Fulton's avatar
Jim Fulton committed
1651 1652
    config_file = 'buildout.cfg'
    verbosity = 0
1653
    options = []
Jim Fulton's avatar
Jim Fulton committed
1654
    windows_restart = False
Jim Fulton's avatar
Jim Fulton committed
1655
    user_defaults = True
1656
    debug = False
Jim Fulton's avatar
Jim Fulton committed
1657 1658 1659 1660
    while args:
        if args[0][0] == '-':
            op = orig_op = args.pop(0)
            op = op[1:]
1661
            while op and op[0] in 'vqhWUoOnNDA':
Jim Fulton's avatar
Jim Fulton committed
1662 1663
                if op[0] == 'v':
                    verbosity += 10
1664
                elif op[0] == 'q':
Jim Fulton's avatar
Jim Fulton committed
1665
                    verbosity -= 10
Jim Fulton's avatar
Jim Fulton committed
1666 1667
                elif op[0] == 'W':
                    windows_restart = True
Jim Fulton's avatar
Jim Fulton committed
1668 1669
                elif op[0] == 'U':
                    user_defaults = False
Jim Fulton's avatar
Jim Fulton committed
1670 1671
                elif op[0] == 'o':
                    options.append(('buildout', 'offline', 'true'))
Jim Fulton's avatar
Jim Fulton committed
1672 1673 1674 1675 1676 1677
                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'))
1678 1679
                elif op[0] == 'D':
                    debug = True
1680 1681
                else:
                    _help()
Jim Fulton's avatar
Jim Fulton committed
1682
                op = op[1:]
1683

1684 1685
            if op[:1] in  ('c', 't'):
                op_ = op[:1]
Jim Fulton's avatar
Jim Fulton committed
1686
                op = op[1:]
1687 1688 1689 1690

                if op_ == 'c':
                    if op:
                        config_file = op
Jim Fulton's avatar
Jim Fulton committed
1691
                    else:
1692 1693 1694 1695 1696 1697
                        if args:
                            config_file = args.pop(0)
                        else:
                            _error("No file name specified for option", orig_op)
                elif op_ == 't':
                    try:
1698 1699 1700
                        timeout_string = args.pop(0)
                        timeout = int(timeout_string)
                        options.append(('buildout', 'socket-timeout', timeout_string))
1701 1702 1703
                    except IndexError:
                        _error("No timeout value specified for option", orig_op)
                    except ValueError:
1704
                        _error("Timeout value must be numeric", orig_op)
Jim Fulton's avatar
Jim Fulton committed
1705
            elif op:
1706 1707
                if orig_op == '--help':
                    _help()
Jim Fulton's avatar
Jim Fulton committed
1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
                _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

    if verbosity:
        options.append(('buildout', 'verbosity', str(verbosity)))
1722 1723 1724

    if args:
        command = args.pop(0)
1725 1726
        if command not in (
            'install', 'bootstrap', 'runsetup', 'setup', 'init',
1727
            'annotate',
1728
            ):
1729 1730 1731 1732
            _error('invalid command:', command)
    else:
        command = 'install'

Jim Fulton's avatar
Jim Fulton committed
1733
    try:
1734
        try:
Jim Fulton's avatar
Jim Fulton committed
1735
            buildout = Buildout(config_file, options,
1736 1737
                                user_defaults, windows_restart,
                                command, args)
1738
            getattr(buildout, command)(args)
1739 1740
        except SystemExit:
            pass
1741 1742
        except Exception:
            v = sys.exc_info()[1]
1743
            _doing()
1744 1745
            exc_info = sys.exc_info()
            import pdb, traceback
1746 1747 1748 1749 1750
            if debug:
                traceback.print_exception(*exc_info)
                sys.stderr.write('\nStarting pdb:\n')
                pdb.post_mortem(exc_info[2])
            else:
1751
                if isinstance(v, (zc.buildout.UserError,
1752
                                  distutils.errors.DistutilsError
1753 1754
                                  )
                              ):
1755 1756
                    _error(str(v))
                else:
1757 1758 1759
                    sys.stderr.write(_internal_error_template)
                    traceback.print_exception(*exc_info)
                    sys.exit(1)
1760 1761


Jim Fulton's avatar
Jim Fulton committed
1762
    finally:
1763
        logging.shutdown()
1764 1765 1766 1767 1768 1769

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