[Checkins] SVN: zc.buildout/trunk/src/zc/buildout/ Improved intro text and description of setup.

Jim Fulton jim at zope.com
Mon Jun 12 16:18:27 EDT 2006


Log message for revision 68604:
  Improved intro text and description of setup.
  
  Added documentation of buildout:directory option.
  
  Added support for extending configurations through multiple
  configuration files.
  
  Added command-line options to:
  
  - Specify a configuration file
  
  - Override configuration options
  
  Renamed several options to use -s rather than _s.
  

Changed:
  U   zc.buildout/trunk/src/zc/buildout/buildout.py
  U   zc.buildout/trunk/src/zc/buildout/buildout.txt
  U   zc.buildout/trunk/src/zc/buildout/tests.py

-=-
Modified: zc.buildout/trunk/src/zc/buildout/buildout.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/buildout.py	2006-06-12 18:40:43 UTC (rev 68603)
+++ zc.buildout/trunk/src/zc/buildout/buildout.py	2006-06-12 20:18:27 UTC (rev 68604)
@@ -51,46 +51,40 @@
 
 class Buildout(dict):
 
-    def __init__(self):
-        self._buildout_dir = os.path.abspath(os.getcwd())
-        self._config_file = self.buildout_path('buildout.cfg')
-        
-        super(Buildout, self).__init__(self._open(
-            directory = self._buildout_dir,
-            eggs_directory = 'eggs',
-            bin_directory = 'bin',
-            parts_directory = 'parts',
-            installed = '.installed.cfg',
-            ))
+    def __init__(self, config_file, cloptions):
+        config_file = os.path.abspath(config_file)
+        self._config_file = config_file
 
-        options = self['buildout']
+        super(Buildout, self).__init__()
 
-        links = options.get('find_links', '')
-        self._links = links and links.split() or ()
+        # default options
+        data = dict(buildout={'directory': os.path.dirname(config_file),
+                              'eggs-directory': 'eggs',
+                              'bin-directory': 'bin',
+                              'parts-directory': 'parts',
+                              'installed': '.installed.cfg',
+                              },
+                       )
 
-        for name in ('bin', 'parts', 'eggs'):
-            d = self.buildout_path(options[name+'_directory'])
-            setattr(self, name, d)
-            if not os.path.exists(d):
-                os.mkdir(d)
+        # load user defaults, which override defaults
+        if 'HOME' in os.environ:
+            user_config = os.path.join(os.environ['HOME'],
+                                       '.buildout', 'default.cfg')
+            if os.path.exists(user_config):
+                _update(data, _open(os.path.dirname(user_config), user_config,
+                                    []))
 
-    _template_split = re.compile('([$]{\w+:\w+})').split
-    def _open(self, **predefined):
-        # Open configuration files
-        parser = ConfigParser.SafeConfigParser()
-        parser.add_section('buildout')
-        for k, v in predefined.iteritems():
-            parser.set('buildout', k, v)
-        parser.read(self._config_file)
-        
-        data = dict([
-            (section,
-             Options(self, section,
-                     [(k, v.strip()) for (k, v) in parser.items(section)])
-             )
-            for section in parser.sections()
-            ])
-        
+        # load configuration files
+        _update(data, _open(os.path.dirname(config_file), config_file, []))
+
+        # apply command-line options
+        for (section, option, value) in cloptions:
+            options = data.get(section)
+            if options is None:
+                options = self[section] = {}
+            options[option] = value
+
+        # do substitutions
         converted = {}
         for section, options in data.iteritems():
             for option, value in options.iteritems():
@@ -100,8 +94,23 @@
                     options[option] = value
                 converted[(section, option)] = value
 
-        return data
+        # copy data into self:
+        for section, options in data.iteritems():
+            self[section] = Options(self, section, options)
+        
+        # initialize some attrs and buildout directories.
+        options = self['buildout']
 
+        links = options.get('find-links', '')
+        self._links = links and links.split() or ()
+
+        self._buildout_dir = options['directory']
+        for name in ('bin', 'parts', 'eggs'):
+            d = self.buildout_path(options[name+'-directory'])
+            setattr(self, name, d)
+            if not os.path.exists(d):
+                os.mkdir(d)
+
     def _dosubs(self, section, option, value, data, converted, seen):
         key = section, option
         r = converted.get(key)
@@ -116,6 +125,7 @@
         seen.pop()
         return value
 
+    _template_split = re.compile('([$]{\w+:\w+})').split
     def _dosubs_esc(self, value, data, converted, seen):
         value = self._template_split(value)
         subs = []
@@ -141,7 +151,7 @@
     def buildout_path(self, *names):
         return os.path.join(self._buildout_dir, *names)
 
-    def install(self):
+    def install(self, install_parts):
         self._develop()
         new_part_options = self._gather_part_info()
         installed_part_options = self._read_installed_part_options()
@@ -150,6 +160,12 @@
 
         new_old_parts = []
         for part in old_parts:
+            if install_parts and (part not in install_parts):
+                # We were asked to install specific parts and this
+                # wasn't one of them.  Leave it alone.
+                new_old_parts.append(part)
+                continue
+                
             installed_options = installed_part_options[part].copy()
             installed = installed_options.pop('__buildout_installed__')
             if installed_options != new_part_options.get(part):
@@ -162,10 +178,11 @@
         new_parts = []
         try:
             for part in new_part_options['buildout']['parts'].split():
-                installed = self._install(part)
-                new_part_options[part]['__buildout_installed__'] = installed
+                if (not install_parts) or (part in install_parts):
+                    installed = self._install(part)
+                    new_part_options[part]['__buildout_installed__'] = installed
+                    installed_part_options[part] = new_part_options[part]
                 new_parts.append(part)
-                installed_part_options[part] = new_part_options[part]
                 new_old_parts = [p for p in new_old_parts if p != part]
         finally:
             new_parts.extend(new_old_parts)
@@ -274,14 +291,62 @@
                      for d in installed]
         return ' '.join(installed)
 
+
     def _save_installed_options(self, installed_options):
-        parser = ConfigParser.SafeConfigParser()
-        for section in installed_options:
-            parser.add_section(section)
-            for option, value in installed_options[section].iteritems():
-                parser.set(section, option, value)
-        parser.write(open(self._installed_path(), 'w'))
+        f = open(self._installed_path(), 'w')
+        _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()
+        
+def _save_options(section, options, f):
+    print >>f, '[%s]' % section
+    items = options.items()
+    items.sort()
+    for option, value in items:
+        print >>f, option, '=', str(value).replace('\n', '\n\t')
     
+
+def _open(base, filename, seen):
+    """Open a configuration file and return the result as a dictionary,
+
+    Recursively open other files based on buildout options found.
+    """
+
+    filename = os.path.join(base, filename)
+    if filename in seen:
+        raise ValueError("Recursive file include", seen, filename)
+
+    base = os.path.dirname(filename)
+    seen.append(filename)
+
+    result = {}
+
+    parser = ConfigParser.SafeConfigParser()
+    parser.readfp(open(filename))
+    extends = extended_by = None
+    for section in parser.sections():
+        options = dict(parser.items(section))
+        if section == 'buildout':
+            extends = options.pop('extends', extends)
+            extended_by = options.pop('extended-by', extended_by)
+        result[section] = options
+
+    if extends:
+        extends = extends.split()
+        extends.reverse()
+        for fname in extends:
+            result = _update(_open(base, fname, seen), result)
+
+    if extended_by:
+        for fname in extended_by.split():
+            result = _update(result, _open(base, fname, seen))
+
+    seen.pop()
+    return result
+    
+
 def _dir_hash(dir):
     hash = md5.new()
     for (dirpath, dirnames, filenames) in os.walk(dir):
@@ -306,5 +371,44 @@
             result.append(location)
     return result
 
-def main():
-    Buildout().install()
+def _update(d1, d2):
+    for section in d2:
+        if section in d1:
+            d1[section].update(d2[section])
+        else:
+            d1[section] = d2[section]
+    return d1
+
+def _error(*message):
+    sys.syderr.write(' '.join(message) +'\n')
+    sys.exit(1)
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+    if args and args[0] == '-c':
+        args.pop(0)
+        if not args:
+            _error("No configuration file specified,")
+        config_file = args.pop(0)
+    else:
+        config_file = 'buildout.cfg'
+
+    options = []
+    while args and '=' 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()))
+
+    buildout = Buildout(config_file, options)
+
+    if args:
+        command = args.pop(0)
+        if command != 'install':
+            _error('invalid command:', command)
+    else:
+        command = 'install'
+
+    getattr(buildout, command)(args)

Modified: zc.buildout/trunk/src/zc/buildout/buildout.txt
===================================================================
--- zc.buildout/trunk/src/zc/buildout/buildout.txt	2006-06-12 18:40:43 UTC (rev 68603)
+++ zc.buildout/trunk/src/zc/buildout/buildout.txt	2006-06-12 20:18:27 UTC (rev 68604)
@@ -2,22 +2,22 @@
 ==================
 
 This document describes how to define buildouts using buildout
-configuation files and recipes.  It doesn't describe how to bootstrap
-a buildout.  To find out how to do that, see bootstrap.txt.  For the
-examples we show here, we've created a sample buildout that already
-contains the mimimal software needed for a buildout. 
+configuation files and recipes.  There are two ways to set up the
+buildout software and create a buildout:
 
-Buildouts are defined using configuration files.  These are in the
-format defined by the Python ConfigParser module, with an extension
-that we'll describe later.  When a buildout is run, it looks for 
-the file buildout.cfg in the directory where the buidout is run.  It
-will optionally look for buildout-instance.cfg.  Typically, buidout.cfg
-contains information common to all instances of a buildout and is
-checked in, and buildout-instance.cfg has instance-specific information.
+1. Install the zc.buildout egg with easy_install and use the buildout
+   script installed in a Python scripts area.
 
+2. Use the buildout bootstrap script to install both the setuptools
+   and zc.buildout eggs into your buildout.  This allows you to use
+   the buildout software without modifying a Python install.
+   The buildout script is installed into your buildout local scripts
+   area.
+
 We have a sample buildout that has already been created for us.  It
 has the absolute minimum information.  We have bin, eggs and parts
-directories, and a configuration file:
+directories, a configuration file, and an .installed,cfg that contains
+informatiion about previously-installed parts:
     
     >>> ls(sample_buildout)
     -  .installed.cfg
@@ -26,24 +26,20 @@
     d  eggs
     d  parts
 
-The bin directory contains scripts.  A minimal buildout has a build
-script and a py_zc.buildout script:
+The bin directory contains scripts.  In the examples shown here, we've
+used a hybrid approach for creating the to ease automated setup.  We
+have a buildout script in our buildout script directory, but the eggs
+actually live elsewhere.
 
     >>> ls(sample_buildout, 'bin')
     -  buildout
 
-The build script is what we run to build things out.  The
-py_zc.buildout script gives us a Python prompt with the Python path
-set to that needed by the zc.buildout package.
-
-The eggs directory is initially empty.  This is typically the case
-when the zc.buildout and setuptools are installed externally to the
-buildout:
-
     >>> ls(sample_buildout, 'eggs')
 
-They can also be installed locally in a buildout, in which case they's
-show up as eggs in the eggs directory.
+Buildouts are defined using configuration files.  These are in the
+format defined by the Python ConfigParser module, with an extension
+that we'll describe later.  When a buildout is run, it looks for the
+file buildout.cfg in the directory where the buidout is run.
 
 The parts directory is initially empty:
 
@@ -62,7 +58,6 @@
     [buildout]
     parts =
 
-
 The minimal configuration file has a buildout section that defines no
 parts:
 
@@ -76,7 +71,7 @@
 
 A part is created by a recipe.  Recipes are always installed as Python
 eggs. They can be downloaded from an package server, such as the
-Python package index, or they can be developed as part of a project.
+Python Package Index, or they can be developed as part of a project.
 Let's create a recipe as part of the sample project.  We'll create a
 recipe for creating directories.  
 
@@ -224,11 +219,10 @@
     parts = data_dir
     <BLANKLINE>
     [data_dir]
+    __buildout_installed__ = mystuff
+    __buildout_signature__ = recipes-O3ypTgKOkHMqMwKvMfvHnA==
     path = mystuff
     recipe = recipes:mkdir
-    __buildout_signature__ = recipes-O3ypTgKOkHMqMwKvMfvHnA==
-    __buildout_installed__ = mystuff
-    <BLANKLINE>
 
 Note that the directory we installed is included in .installed.cfg.
 
@@ -361,26 +355,454 @@
 
 We can see that mydata was not recreated.
 
+Multiple configuration files
+----------------------------
+
+You can use multiple configuration files.  From your main
+configuration file, you can include other configuration files in 2
+ways:
+
+- Your configuration file can "extend" another configuration file.
+  Option are read from the other configuration file if they aren't
+  already defined by your configuration file.
+
+- Your configuration file can be "extended-by" another configuration
+  file, In this case, the options in the other configuration file
+  override options in your configuration file. 
+
+The configuration files your file extends or is extended by can extend
+or be extended by other configuration files.  The same file may be
+used more than once although, of course, cycles aren't allowed.
+
+To see how this works, we use an example:
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... extends = base.cfg
+    ...
+    ... [debug]
+    ... op = buldout
+    ... """)
+
+    >>> write(sample_buildout, 'base.cfg',
+    ... """
+    ... [buildout]
+    ... develop = recipes
+    ... parts = debug
+    ...
+    ... [debug]
+    ... recipe = recipes:debug
+    ... op = base
+    ... """)
+
+    >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+    op buldout
+    recipe recipes:debug
+
+The example is pretty trivial, but the pattern it illustrates is
+pretty common.  In a more practical example, the base buildout might
+represent a product and the extending buildout might be a
+customization. 
+
+Here is a more eleborate example. 
+
+    >>> import tempfile
+    >>> extensions = tempfile.mkdtemp()
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... extends = b1.cfg b2.cfg
+    ... extended-by = e1.cfg %(e2)s
+    ...
+    ... [debug]
+    ... op = %%(name)s
+    ...
+    ... [DEFAULT]
+    ... name = buildout
+    ... """ % dict(e2=os.path.join(extensions, 'e2.cfg')))
+
+
+    >>> write(sample_buildout, 'b1.cfg',
+    ... """
+    ... [buildout]
+    ... extends = base.cfg
+    ...
+    ... [debug]
+    ... op1 = %(name)s 1
+    ... op2 = %(name)s 2
+    ... op3 = %(name)s 3
+    ...
+    ... [DEFAULT]
+    ... name = b1
+    ... """)
+
+    >>> write(sample_buildout, 'b2.cfg',
+    ... """
+    ... [buildout]
+    ... extends = base.cfg
+    ...
+    ... [debug]
+    ... op3 = %(name)s 3
+    ... op4 = %(name)s 4
+    ... op5 = %(name)s 5
+    ...
+    ... [DEFAULT]
+    ... name = b2
+    ... """)
+
+    >>> write(sample_buildout, 'base.cfg',
+    ... """
+    ... [buildout]
+    ... develop = recipes
+    ... parts = debug
+    ...
+    ... [debug]
+    ... recipe = recipes:debug
+    ... name = base
+    ... """)
+
+    >>> write(sample_buildout, 'e1.cfg',
+    ... """
+    ... [debug]
+    ... op1 = %(name)s 1
+    ...
+    ... [DEFAULT]
+    ... name = e1
+    ... """)
+
+    >>> write(extensions, 'e2.cfg',
+    ... """
+    ... [buildout]
+    ... extends = eb.cfg
+    ... extended-by = ee.cfg
+    ... """)
+
+    >>> write(extensions, 'eb.cfg',
+    ... """
+    ... [debug]
+    ... op5 = %(name)s 5
+    ...
+    ... [DEFAULT]
+    ... name = eb
+    ... """)
+
+    >>> write(extensions, 'ee.cfg',
+    ... """
+    ... [debug]
+    ... op6 = %(name)s 6
+    ...
+    ... [DEFAULT]
+    ... name = ee
+    ... """)
+
+    >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+    name ee
+    op buildout
+    op1 e1 1
+    op2 b1 2
+    op3 b2 3
+    op4 b2 4
+    op5 eb 5
+    op6 ee 6
+    recipe recipes:debug
+
+There are several things to note about this example:
+
+- We can name multiple files in an extends or extended-by option.
+
+- We can reference files recursively.
+
+- DEFAULT sections only directly affect the configuration file they're
+  used in, but they can have secondary effects.  For example, the name
+  option showed up in the debug section because it was defined in the
+  debug sections in several of the input files by virtue of being in
+  their DEFAULT sections.
+
+- Relative file names are determined relative to the directory
+  containing the referencing configuration file.  The files eb.cfg and
+  ee.cfg were found in the extensions directory because they were
+  referenced from a file in that directory.
+
+User defaults
+-------------
+
+If the file $HOME/.buildout/defaults.cfg, exists, it is read before
+reading the configuration file.  ($HOME is the value of the HOME
+enviornment variable. The '/' is replaced by the operating system file
+delimiter.)
+
+    >>> home = tempfile.mkdtemp()
+    >>> mkdir(home, '.buildout')
+    >>> write(home, '.buildout', 'default.cfg',
+    ... """
+    ... [debug]
+    ... op1 = 1
+    ... op7 = 7
+    ... """)
+
+    >>> os.environ['HOME'] = home
+    >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+    name ee
+    op buildout
+    op1 e1 1
+    op2 b1 2
+    op3 b2 3
+    op4 b2 4
+    op5 eb 5
+    op6 ee 6
+    op7 7
+    recipe recipes:debug
+
+Command-line usage
+------------------
+
+A number of arguments can be given on the buildout command line.  The
+command usage is::
+
+  buildout [-c file] [options] [command [command arguments]]
+
+The -c option can be used to specify a configuration file, rather than
+buildout.cfg in the current durectory.  Options are of the form::
+
+  section_name:option_name=value
+
+for example, as in:
+
+    >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')
+    ...     + ' debug:op1=foo'),
+    name ee
+    op buildout
+    op1 foo
+    op2 b1 2
+    op3 b2 3
+    op4 b2 4
+    op5 eb 5
+    op6 ee 6
+    op7 7
+    recipe recipes:debug
+
+Currently, the default and only command is 'install' and it takes a
+list of parts to install. if any parts are specified, then only those
+parts are installed.  To illustrate this, we'll update our
+configuration and run the buildout in the usual way:
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... develop = recipes
+    ... parts = debug d1 d2 d3
+    ...
+    ... [d1]
+    ... recipe = recipes:mkdir
+    ... path = d1
+    ...
+    ... [d2]
+    ... recipe = recipes:mkdir
+    ... path = d2
+    ...
+    ... [d3]
+    ... recipe = recipes:mkdir
+    ... path = d3
+    ...
+    ... [debug]
+    ... recipe = recipes:debug
+    ... """)
+
+    >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+    op1 1
+    op7 7
+    recipe recipes:debug
+    Creating directory d1
+    Creating directory d2
+    Creating directory d3
+    
+    >>> ls(sample_buildout)
+    -  .installed.cfg
+    -  b1.cfg
+    -  b2.cfg
+    -  base.cfg
+    d  bin
+    -  buildout.cfg
+    d  d1
+    d  d2
+    d  d3
+    -  e1.cfg
+    d  eggs
+    d  parts
+    d  recipes
+
+    >>> cat(sample_buildout, '.installed.cfg')
+    [buildout]
+    parts = debug d1 d2 d3
+    <BLANKLINE>
+    [debug]
+    __buildout_installed__ = 
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    op1 = 1
+    op7 = 7
+    recipe = recipes:debug
+    <BLANKLINE>
+    [d1]
+    __buildout_installed__ = d1
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    path = d1
+    recipe = recipes:mkdir
+    <BLANKLINE>
+    [d2]
+    __buildout_installed__ = d2
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    path = d2
+    recipe = recipes:mkdir
+    <BLANKLINE>
+    [d3]
+    __buildout_installed__ = d3
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    path = d3
+    recipe = recipes:mkdir
+
+Now we'll update our configuration file:
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... develop = recipes
+    ... parts = debug d2 d3 d4
+    ...
+    ... [d2]
+    ... recipe = recipes:mkdir
+    ... path = data2
+    ...
+    ... [d3]
+    ... recipe = recipes:mkdir
+    ... path = data3
+    ...
+    ... [d4]
+    ... recipe = recipes:mkdir
+    ... path = data4
+    ...
+    ... [debug]
+    ... recipe = recipes:debug
+    ... x = 1
+    ... """)
+
+and run the buildout specifying just d2 and d3:
+
+    >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')
+    ...              + ' install d3 d4'),
+    Creating directory data3
+    Creating directory data4
+    
+    >>> ls(sample_buildout)
+    -  .installed.cfg
+    -  b1.cfg
+    -  b2.cfg
+    -  base.cfg
+    d  bin
+    -  buildout.cfg
+    d  d1
+    d  d2
+    d  data3
+    d  data4
+    -  e1.cfg
+    d  eggs
+    d  parts
+    d  recipes
+    
+Only the d2 and d3 recipes ran.  d2 was removed and data2 and data3
+were created.
+
+The .installed.cfg is only updated for the recipes that ran:
+
+    >>> cat(sample_buildout, '.installed.cfg')
+    [buildout]
+    parts = debug d2 d3 d4 d1
+    <BLANKLINE>
+    [debug]
+    __buildout_installed__ = 
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    op1 = 1
+    op7 = 7
+    recipe = recipes:debug
+    <BLANKLINE>
+    [d2]
+    __buildout_installed__ = d2
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    path = d2
+    recipe = recipes:mkdir
+    <BLANKLINE>
+    [d3]
+    __buildout_installed__ = data3
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    path = data3
+    recipe = recipes:mkdir
+    <BLANKLINE>
+    [d4]
+    __buildout_installed__ = data4
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    path = data4
+    recipe = recipes:mkdir
+    <BLANKLINE>
+    [d1]
+    __buildout_installed__ = d1
+    __buildout_signature__ = recipes-IX/o5hMSw90MtZVxRpjz0Q==
+    path = d1
+    recipe = recipes:mkdir
+
+Note that the installed data for debug, d1, and d2 haven't changed,
+because we didn't install those parts and that the d1 and d2
+directories are still there.
+
+Now, if we run the buildout without arguments:
+
+    >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+    op1 1
+    op7 7
+    recipe recipes:debug
+    x 1
+    Creating directory data2
+
+We see the output of the debug recipe and that data2 was created.  We
+also see that d1 and d2 have gone away:
+
+    >>> ls(sample_buildout)
+    -  .installed.cfg
+    -  b1.cfg
+    -  b2.cfg
+    -  base.cfg
+    d  bin
+    -  buildout.cfg
+    d  data2
+    d  data3
+    d  data4
+    -  e1.cfg
+    d  eggs
+    d  parts
+    d  recipes
+
 Alternate directory locations
 -----------------------------
 
 The buildout normally puts the bin, eggs, and parts directories in the
-directory it is run from. You can provide alternate locations, and
-even names for these directories.
+directory in the directory containing the configuration file. You can
+provide alternate locations, and even names for these directories.
 
-    >>> import tempfile
     >>> alt = tempfile.mkdtemp()
 
-
     >>> write(sample_buildout, 'buildout.cfg',
     ... """
     ... [buildout]
     ... develop = recipes
     ... parts = 
-    ... eggs_directory = %(alt)s/basket
-    ... bin_directory = %(alt)s/scripts
-    ... parts_directory = %(alt)s/work
-    ... """ % dict(alt=alt))
+    ... eggs-directory = %(basket)s
+    ... bin-directory = %(scripts)s
+    ... parts-directory = %(work)s
+    ... """ % dict(
+    ...    basket = os.path.join(alt, 'basket'),
+    ...    scripts = os.path.join(alt, 'scripts'),
+    ...    work = os.path.join(alt, 'work'),
+    ... ))
 
     >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
 
@@ -394,3 +816,32 @@
 
     >>> import shutil
     >>> shutil.rmtree(alt)
+
+You can also specify an alternate buildout directory:
+
+    >>> alt = tempfile.mkdtemp()
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... directory = %(alt)s
+    ... develop = %(recipes)s
+    ... parts = 
+    ... """ % dict(
+    ...    alt=alt,
+    ...    recipes=os.path.join(sample_buildout, 'recipes'),
+    ...    ))
+ 
+    >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+
+    >>> ls(alt)
+    -  .installed.cfg
+    d  bin
+    d  eggs
+    d  parts
+
+    >>> ls(alt, 'eggs')    
+    -  recipes.egg-link
+
+    >>> import shutil
+    >>> shutil.rmtree(alt)

Modified: zc.buildout/trunk/src/zc/buildout/tests.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/tests.py	2006-06-12 18:40:43 UTC (rev 68603)
+++ zc.buildout/trunk/src/zc/buildout/tests.py	2006-06-12 20:18:27 UTC (rev 68604)
@@ -29,7 +29,7 @@
     >>> import os
     >>> os.chdir(sample_buildout)
     >>> import zc.buildout.buildout
-    >>> buildout = zc.buildout.buildout.Buildout()
+    >>> buildout = zc.buildout.buildout.Buildout('buildout.cfg', [])
     >>> buildout['eek']
     Traceback (most recent call last):
     ...
@@ -62,10 +62,8 @@
            [('buildout', 'y'), ('buildout', 'z'), ('buildout', 'x')],
            ('buildout', 'y'))
 
-'''
+'''    
 
-    
-
 def runsetup(d):
     here = os.getcwd()
     try:
@@ -119,6 +117,18 @@
 def linkerTearDown(test):
     shutil.rmtree(test.globs['_sample_eggs_container'])
     zc.buildout.testing.buildoutTearDown(test)
+
+def buildoutSetUp(test):
+    zc.buildout.testing.buildoutSetUp(test)
+    test.globs['_oldhome'] = os.environ.get('HOME')
+
+def buildoutTearDoen(test):
+    if test.globs['_oldhome'] is not None:
+        os.environ['HOME'] = test.globs['_oldhome']
+
+    shutil.rmtree(test.globs['extensions'])
+    shutil.rmtree(test.globs['home'])
+    zc.buildout.testing.buildoutTearDown(test)
     
 
 def test_suite():



More information about the Checkins mailing list