Commit a4291845 authored by jim's avatar jim

Merged revisions 71277 to 71397 from dev branch:

Feature Changes
---------------

- Variable substitutions now reflect option data written by recipes.

- A part referenced by a part in a parts list is now added to the parts
  list before the referencing part.  This means that you can omit
  parts from the parts list if they are referenced by other parts.

- Added a develop function to the easy_install module to aid in
  creating develop eggs with custom build_ext options.

- The build and develop functions in the easy_install module now
  return the path of the egg or egg link created.

- Removed the limitation that parts named in the install command can
  only name configured parts.

- Removed support ConfigParser-style variable substitutions
  (e.g. %(foo)s). Only the string-template style of variable
  (e.g. ${section:option}) substitutions will be supported.
  Supporting both violates "there's only one way to do it".

- Deprecated the buildout-section extendedBy option.


git-svn-id: http://svn.zope.org/repos/main/zc.buildout/trunk@71398 62d5b8a3-27da-0310-9561-8e5933582275
parent b296bd0d
...@@ -20,9 +20,34 @@ priorities include: ...@@ -20,9 +20,34 @@ priorities include:
Change History Change History
************** **************
1.0.0b12 (2006-10-?) 1.0.0b13 (2006-12-04)
===================== =====================
Feature Changes
---------------
- Variable substitutions now reflect option data written by recipes.
- A part referenced by a part in a parts list is now added to the parts
list before the referencing part. This means that you can omit
parts from the parts list if they are referenced by other parts.
- Added a develop function to the easy_install module to aid in
creating develop eggs with custom build_ext options.
- The build and develop functions in the easy_install module now
return the path of the egg or egg link created.
- Removed the limitation that parts named in the install command can
only name configured parts.
- Removed support ConfigParser-style variable substitutions
(e.g. %(foo)s). Only the string-template style of variable
(e.g. ${section:option}) substitutions will be supported.
Supporting both violates "there's only one way to do it".
- Deprecated the buildout-section extendedBy option.
Bugs Fixed Bugs Fixed
---------- ----------
......
...@@ -25,6 +25,8 @@ setup( ...@@ -25,6 +25,8 @@ setup(
+ '\n' + + '\n' +
read('src', 'zc', 'buildout', 'testing.txt') read('src', 'zc', 'buildout', 'testing.txt')
+ '\n' + + '\n' +
read('src', 'zc', 'buildout', 'easy_install.txt')
+ '\n' +
'Download\n' 'Download\n'
'**********************\n' '**********************\n'
), ),
......
This diff is collapsed.
This diff is collapsed.
...@@ -34,10 +34,13 @@ logger = logging.getLogger('zc.buildout.easy_install') ...@@ -34,10 +34,13 @@ logger = logging.getLogger('zc.buildout.easy_install')
url_match = re.compile('[a-z0-9+.-]+://').match url_match = re.compile('[a-z0-9+.-]+://').match
setuptools_loc = pkg_resources.working_set.find(
pkg_resources.Requirement.parse('setuptools')
).location
# Include buildout and setuptools eggs in paths # Include buildout and setuptools eggs in paths
buildout_and_setuptools_path = [ buildout_and_setuptools_path = [
pkg_resources.working_set.find( setuptools_loc,
pkg_resources.Requirement.parse('setuptools')).location,
pkg_resources.working_set.find( pkg_resources.working_set.find(
pkg_resources.Requirement.parse('zc.buildout')).location, pkg_resources.Requirement.parse('zc.buildout')).location,
] ]
...@@ -404,12 +407,15 @@ def build(spec, dest, build_ext, ...@@ -404,12 +407,15 @@ def build(spec, dest, build_ext,
dist = _satisfied(requirement, env, dest, executable, index_url, links) dist = _satisfied(requirement, env, dest, executable, index_url, links)
if dist is not None: if dist is not None:
return dist return [dist.location]
# Get an editable version of the package to a temporary directory: undo = []
tmp = tempfile.mkdtemp('editable')
tmp2 = tempfile.mkdtemp('editable')
try: try:
tmp = tempfile.mkdtemp('build')
undo.append(lambda : shutil.rmtree(tmp))
tmp2 = tempfile.mkdtemp('build')
undo.append(lambda : shutil.rmtree(tmp2))
index = _get_index(executable, index_url, links) index = _get_index(executable, index_url, links)
dist = index.fetch_distribution(requirement, tmp2, False, True) dist = index.fetch_distribution(requirement, tmp2, False, True)
if dist is None: if dist is None:
...@@ -442,13 +448,105 @@ def build(spec, dest, build_ext, ...@@ -442,13 +448,105 @@ def build(spec, dest, build_ext,
setuptools.command.setopt.edit_config( setuptools.command.setopt.edit_config(
setup_cfg, dict(build_ext=build_ext)) setup_cfg, dict(build_ext=build_ext))
# Now run easy_install for real: tmp3 = tempfile.mkdtemp('build', dir=dest)
undo.append(lambda : shutil.rmtree(tmp3))
_call_easy_install(base, env, pkg_resources.WorkingSet(), _call_easy_install(base, env, pkg_resources.WorkingSet(),
dest, links, index_url, executable, True) tmp3, links, index_url, executable, True)
return _copyeggs(tmp3, dest, '.egg', undo)
finally: finally:
shutil.rmtree(tmp) undo.reverse()
shutil.rmtree(tmp2) [f() for f in undo]
def _rm(*paths):
for path in paths:
if os.path.isdir(path):
shutil.rmtree(path)
elif os.path.exists(path):
os.remove(path)
def _copyeggs(src, dest, suffix, undo):
result = []
undo.append(lambda : _rm(*result))
for name in os.listdir(src):
if name.endswith(suffix):
new = os.path.join(dest, name)
_rm(new)
os.rename(os.path.join(src, name), new)
result.append(new)
assert len(result) == 1
undo.pop()
return result[0]
def develop(setup, dest,
build_ext=None,
executable=sys.executable):
if os.path.isdir(setup):
directory = setup
setup = os.path.join(directory, 'setup.py')
else:
directory = os.path.dirname(setup)
undo = []
try:
if build_ext:
setup_cfg = os.path.join(directory, 'setup.cfg')
if os.path.exists(setup_cfg):
os.rename(setup_cfg, setup_cfg+'-develop-aside')
def restore_old_setup():
if os.path.exists(setup_cfg):
os.remove(setup_cfg)
os.rename(setup_cfg+'-develop-aside', setup_cfg)
undo.append(restore_old_setup)
else:
open(setup_cfg, 'w')
undo.append(lambda: os.remove(setup_cfg))
setuptools.command.setopt.edit_config(
setup_cfg, dict(build_ext=build_ext))
fd, tsetup = tempfile.mkstemp()
undo.append(lambda: os.remove(tsetup))
undo.append(lambda: os.close(fd))
os.write(fd, runsetup_template % dict(
setuptools=setuptools_loc,
setupdir=directory,
setup=setup,
__file__ = setup,
))
tmp3 = tempfile.mkdtemp('build', dir=dest)
undo.append(lambda : shutil.rmtree(tmp3))
args = [
zc.buildout.easy_install._safe_arg(tsetup),
'-q', 'develop', '-mxN',
'-d', _safe_arg(tmp3),
]
log_level = logger.getEffectiveLevel()
if log_level <= logging.DEBUG:
if log_level == logging.DEBUG:
del args[1]
else:
args[1] == '-v'
logger.debug("in: %s\n%r", directory, args)
assert os.spawnl(os.P_WAIT, executable, executable, *args) == 0
return _copyeggs(tmp3, dest, '.egg-link', undo)
finally:
undo.reverse()
[f() for f in undo]
def working_set(specs, executable, path): def working_set(specs, executable, path):
return install(specs, None, executable=executable, path=path) return install(specs, None, executable=executable, path=path)
...@@ -595,6 +693,15 @@ if _interactive: ...@@ -595,6 +693,15 @@ if _interactive:
import code import code
code.interact(banner="", local=globals()) code.interact(banner="", local=globals())
''' '''
runsetup_template = """
import sys
sys.path.insert(0, %(setuptools)r)
import os, setuptools
__file__ = %(__file__)r
os.chdir(%(setupdir)r)
sys.argv[0] = %(setup)r
execfile(%(setup)r)
"""
Minimal Python interface to easy_install Python API for egg and script installation
======================================== ==========================================
The easy_install module provides a minimal interface to the setuptools The easy_install module provides some functions to provide support for
easy_install command that provides some additional semantics: egg and script installation. It provides functionality at the python
level that is similar to easy_install, with a few exceptions:
- By default, we look for new packages *and* the packages that - By default, we look for new packages *and* the packages that
they depend on. This is somewhat like (and uses) the --upgrade they depend on. This is somewhat like (and uses) the --upgrade
...@@ -21,8 +22,8 @@ easy_install command that provides some additional semantics: ...@@ -21,8 +22,8 @@ easy_install command that provides some additional semantics:
- Distutils options for building extensions can be passed. - Distutils options for building extensions can be passed.
The easy_install module provides a method, install, for installing one The easy_install module provides a method, install, for installing one
or more packages and their dependencies. The or more packages and their dependencies. The install function takes 2
install function takes 2 positional arguments: positional arguments:
- An iterable of setuptools requirement strings for the distributions - An iterable of setuptools requirement strings for the distributions
to be installed, and to be installed, and
...@@ -426,18 +427,18 @@ You can also pass script initialization code: ...@@ -426,18 +427,18 @@ You can also pass script initialization code:
eggrecipedemo.main(1, 2) eggrecipedemo.main(1, 2)
Handling custom build options for extensions Handling custom build options for extensions provided in source distributions
-------------------------------------------- -----------------------------------------------------------------------------
Sometimes, we need to control how extension modules are built. The Sometimes, we need to control how extension modules are built. The
build method provides this level of control. It takes a single build function provides this level of control. It takes a single
package specification, downloads a source distribution, and builds it package specification, downloads a source distribution, and builds it
with specified custom build options. with specified custom build options.
The build method takes 3 positional arguments: The build function takes 3 positional arguments:
spec spec
A package specification A package specification for a source distribution
dest dest
A destination directory A destination directory
...@@ -486,9 +487,13 @@ extension, extdemo.c:: ...@@ -486,9 +487,13 @@ extension, extdemo.c::
PyMODINIT_FUNC PyMODINIT_FUNC
initextdemo(void) initextdemo(void)
{ {
PyObject *d; PyObject *m;
d = Py_InitModule3("extdemo", methods, ""); m = Py_InitModule3("extdemo", methods, "");
PyDict_SetItemString(d, "val", PyInt_FromLong(EXTDEMO)); #ifdef TWO
PyModule_AddObject(m, "val", PyInt_FromLong(2));
#else
PyModule_AddObject(m, "val", PyInt_FromLong(EXTDEMO));
#endif
} }
The extension depends on a system-dependnt include file, extdemo.h, The extension depends on a system-dependnt include file, extdemo.h,
...@@ -497,9 +502,11 @@ that defines a constant, EXTDEMO, that is exposed by the extension. ...@@ -497,9 +502,11 @@ that defines a constant, EXTDEMO, that is exposed by the extension.
We'll add an include directory to our sample buildout and add the We'll add an include directory to our sample buildout and add the
needed include file to it: needed include file to it:
>>> mkdir(sample_buildout, 'include') >>> mkdir('include')
>>> open(os.path.join(sample_buildout, 'include', 'extdemo.h'), 'w').write( >>> write('include', 'extdemo.h',
... "#define EXTDEMO 42\n") ... """
... #define EXTDEMO 42
... """)
Now, we can use the build function to create an egg from the source Now, we can use the build function to create an egg from the source
distribution: distribution:
...@@ -508,6 +515,9 @@ distribution: ...@@ -508,6 +515,9 @@ distribution:
... 'extdemo', dest, ... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')}, ... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/') ... links=[link_server], index=link_server+'index/')
'/sample-install/extdemo-1.4-py2.4-unix-i686.egg'
The function returns the list of eggs
Now if we look in our destination directory, we see we have an extdemo egg: Now if we look in our destination directory, we see we have an extdemo egg:
...@@ -516,3 +526,68 @@ Now if we look in our destination directory, we see we have an extdemo egg: ...@@ -516,3 +526,68 @@ Now if we look in our destination directory, we see we have an extdemo egg:
d demoneeded-1.1-py2.4.egg d demoneeded-1.1-py2.4.egg
d extdemo-1.4-py2.4-unix-i686.egg d extdemo-1.4-py2.4-unix-i686.egg
Handling custom build options for extensions in develop eggs
------------------------------------------------------------
The develop function is similar to the build function, except that,
rather than building an egg from a source directory containing a
setup.py script.
The develop function takes 2 positional arguments:
setup
The path to a setup script, typically named "setup.py", or a
directory containing a setup.py script.
dest
The directory to install the egg link to
It supports some optional keyword argument:
build_ext
A dictionary of options to be passed to the distutils build_ext
command when building extensions.
executable
A path to a Python executable. Distributions will ne installed
using this executable and will be for the matching Python version.
We have a local directory containing the extdemo source:
>>> ls(extdemo)
- MANIFEST
- MANIFEST.in
- README
- extdemo.c
- setup.py
Now, we can use the develop function to create a develop egg from the source
distribution:
>>> zc.buildout.easy_install.develop(
... extdemo, dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')})
'/sample-install/extdemo.egg-link'
The name of the egg link created is returned.
Now if we look in our destination directory, we see we have an extdemo
egg link:
>>> ls(dest)
d demo-0.3-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.4-py2.4-linux-i686.egg
- extdemo.egg-link
And that the source directory contains the compiled extension:
>>> ls(extdemo)
- MANIFEST
- MANIFEST.in
- README
d build
- extdemo.c
d extdemo.egg-info
- extdemo.so
- setup.py
...@@ -45,7 +45,7 @@ We should be able to deal with setup scripts that aren't setuptools based. ...@@ -45,7 +45,7 @@ We should be able to deal with setup scripts that aren't setuptools based.
... ''') ... ''')
>>> print system(join('bin', 'buildout')), >>> print system(join('bin', 'buildout')),
buildout: Develop: /sample-buildout/foo/setup.py buildout: Develop: /sample-buildout/foo
>>> ls('develop-eggs') >>> ls('develop-eggs')
- foo.egg-link - foo.egg-link
...@@ -71,9 +71,8 @@ We should be able to deal with setup scripts that aren't setuptools based. ...@@ -71,9 +71,8 @@ We should be able to deal with setup scripts that aren't setuptools based.
... ''') ... ''')
>>> print system(join('bin', 'buildout')+' -v'), # doctest: +ELLIPSIS >>> print system(join('bin', 'buildout')+' -v'), # doctest: +ELLIPSIS
Configuration data: zc.buildout...
... buildout: Develop: /sample-buildout/foo
buildout: Develop: /sample-buildout/foo/setup.py
... ...
Installed /sample-buildout/foo Installed /sample-buildout/foo
... ...
...@@ -86,7 +85,7 @@ We should be able to deal with setup scripts that aren't setuptools based. ...@@ -86,7 +85,7 @@ We should be able to deal with setup scripts that aren't setuptools based.
def buildout_error_handling(): def buildout_error_handling():
r"""Buildout error handling r"""Buildout error handling
Asking for a section that doesn't exist, yields a key error: Asking for a section that doesn't exist, yields a missing section error:
>>> import os >>> import os
>>> os.chdir(sample_buildout) >>> os.chdir(sample_buildout)
...@@ -95,7 +94,7 @@ Asking for a section that doesn't exist, yields a key error: ...@@ -95,7 +94,7 @@ Asking for a section that doesn't exist, yields a key error:
>>> buildout['eek'] >>> buildout['eek']
Traceback (most recent call last): Traceback (most recent call last):
... ...
KeyError: 'eek' MissingSection: The referenced section, 'eek', was not defined.
Asking for an option that doesn't exist, a MissingOption error is raised: Asking for an option that doesn't exist, a MissingOption error is raised:
...@@ -109,8 +108,7 @@ It is an error to create a variable-reference cycle: ...@@ -109,8 +108,7 @@ It is an error to create a variable-reference cycle:
>>> write(sample_buildout, 'buildout.cfg', >>> write(sample_buildout, 'buildout.cfg',
... ''' ... '''
... [buildout] ... [buildout]
... develop = recipes ... parts =
... parts = data_dir debug
... x = ${buildout:y} ... x = ${buildout:y}
... y = ${buildout:z} ... y = ${buildout:z}
... z = ${buildout:x} ... z = ${buildout:x}
...@@ -183,7 +181,7 @@ Al parts have to have a section: ...@@ -183,7 +181,7 @@ Al parts have to have a section:
... ''') ... ''')
>>> print system(os.path.join(sample_buildout, 'bin', 'buildout')), >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
Error: No section was specified for part x Error: The referenced section, 'x', was not defined.
and all parts have to have a specified recipe: and all parts have to have a specified recipe:
...@@ -265,15 +263,15 @@ def test_comparing_saved_options_with_funny_characters(): ...@@ -265,15 +263,15 @@ def test_comparing_saved_options_with_funny_characters():
>>> os.chdir(sample_buildout) >>> os.chdir(sample_buildout)
>>> buildout = os.path.join(sample_buildout, 'bin', 'buildout') >>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
>>> print system(buildout), # doctest: +ELLIPSIS >>> print system(buildout),
buildout: Develop: ...setup.py buildout: Develop: /sample-buildout/recipes
buildout: Installing debug buildout: Installing debug
If we run the buildout again, we shoudn't get a message about If we run the buildout again, we shoudn't get a message about
uninstalling anything because the configuration hasn't changed. uninstalling anything because the configuration hasn't changed.
>>> print system(buildout), # doctest: +ELLIPSIS >>> print system(buildout),
buildout: Develop: ...setup.py buildout: Develop: /sample-buildout/recipes
buildout: Updating debug buildout: Updating debug
""" """
...@@ -317,20 +315,21 @@ Then try to install it again: ...@@ -317,20 +315,21 @@ Then try to install it again:
""" """
def error_for_indefined_install_parts(): # Why?
""" ## def error_for_undefined_install_parts():
Any parts we pass to install on the command line must be ## """
listed in the configuration. ## Any parts we pass to install on the command line must be
## listed in the configuration.
>>> print system(join('bin', 'buildout') + ' install foo'), ## >>> print system(join('bin', 'buildout') + ' install foo'),
buildout: Invalid install parts: foo. ## buildout: Invalid install parts: foo.
Install parts must be listed in the configuration. ## Install parts must be listed in the configuration.
>>> print system(join('bin', 'buildout') + ' install foo bar'), ## >>> print system(join('bin', 'buildout') + ' install foo bar'),
buildout: Invalid install parts: foo bar. ## buildout: Invalid install parts: foo bar.
Install parts must be listed in the configuration. ## Install parts must be listed in the configuration.
""" ## """
bootstrap_py = os.path.join( bootstrap_py = os.path.join(
...@@ -515,7 +514,7 @@ Create a develop egg: ...@@ -515,7 +514,7 @@ Create a develop egg:
... """) ... """)
>>> print system(join('bin', 'buildout')), >>> print system(join('bin', 'buildout')),
buildout: Develop: /sample-buildout/foo/setup.py buildout: Develop: /sample-buildout/foo
>>> ls('develop-eggs') >>> ls('develop-eggs')
- foox.egg-link - foox.egg-link
...@@ -536,8 +535,8 @@ Create another: ...@@ -536,8 +535,8 @@ Create another:
... """) ... """)
>>> print system(join('bin', 'buildout')), >>> print system(join('bin', 'buildout')),
buildout: Develop: /sample-buildout/foo/setup.py buildout: Develop: /sample-buildout/foo
buildout: Develop: /sample-buildout/bar/setup.py buildout: Develop: /sample-buildout/bar
>>> ls('develop-eggs') >>> ls('develop-eggs')
- foox.egg-link - foox.egg-link
...@@ -552,7 +551,7 @@ Remove one: ...@@ -552,7 +551,7 @@ Remove one:
... parts = ... parts =
... """) ... """)
>>> print system(join('bin', 'buildout')), >>> print system(join('bin', 'buildout')),
buildout: Develop: /sample-buildout/bar/setup.py buildout: Develop: /sample-buildout/bar
It is gone It is gone
...@@ -611,7 +610,7 @@ a devlop egg, we will also generate a warning. ...@@ -611,7 +610,7 @@ a devlop egg, we will also generate a warning.
... """) ... """)
>>> print system(join('bin', 'buildout')), >>> print system(join('bin', 'buildout')),
buildout: Develop: /sample-buildout/foo/setup.py buildout: Develop: /sample-buildout/foo
Now, if we generate a working set using the egg link, we will get a warning Now, if we generate a working set using the egg link, we will get a warning
and we will get setuptools included in the working set. and we will get setuptools included in the working set.
...@@ -659,7 +658,6 @@ We do not get a warning, but we do get setuptools included in the working set: ...@@ -659,7 +658,6 @@ We do not get a warning, but we do get setuptools included in the working set:
... ])] ... ])]
['foox', 'setuptools'] ['foox', 'setuptools']
>>> print handler, >>> print handler,
We get the same behavior if the it is a depedency that uses a We get the same behavior if the it is a depedency that uses a
...@@ -682,8 +680,8 @@ namespace package. ...@@ -682,8 +680,8 @@ namespace package.
... """) ... """)
>>> print system(join('bin', 'buildout')), >>> print system(join('bin', 'buildout')),
buildout: Develop: /sample-buildout/foo/setup.py buildout: Develop: /sample-buildout/foo
buildout: Develop: /sample-buildout/bar/setup.py buildout: Develop: /sample-buildout/bar
>>> [dist.project_name >>> [dist.project_name
... for dist in zc.buildout.easy_install.working_set( ... for dist in zc.buildout.easy_install.working_set(
...@@ -703,6 +701,52 @@ namespace package. ...@@ -703,6 +701,52 @@ namespace package.
>>> handler.uninstall() >>> handler.uninstall()
''' '''
def develop_preserves_existing_setup_cfg():
"""
See "Handling custom build options for extensions in develop eggs" in
easy_install.txt. This will be very similar except that we'll have an
existing setup.cfg:
>>> write(extdemo, "setup.cfg",
... '''
... # sampe cfg file
...
... [foo]
... bar = 1
...
... [build_ext]
... define = X,Y
... ''')
>>> mkdir('include')
>>> write('include', 'extdemo.h',
... '''
... #define EXTDEMO 42
... ''')
>>> dest = tmpdir('dest')
>>> zc.buildout.easy_install.develop(
... extdemo, dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')})
'/tmp/tmp7AFYXv/_TEST_/dest/extdemo.egg-link'
>>> ls(dest)
- extdemo.egg-link
>>> cat(extdemo, "setup.cfg")
<BLANKLINE>
# sampe cfg file
<BLANKLINE>
[foo]
bar = 1
<BLANKLINE>
[build_ext]
define = X,Y
"""
def create_sample_eggs(test, executable=sys.executable): def create_sample_eggs(test, executable=sys.executable):
write = test.globs['write'] write = test.globs['write']
...@@ -762,9 +806,13 @@ static PyMethodDef methods[] = {{NULL}}; ...@@ -762,9 +806,13 @@ static PyMethodDef methods[] = {{NULL}};
PyMODINIT_FUNC PyMODINIT_FUNC
initextdemo(void) initextdemo(void)
{ {
PyObject *d; PyObject *m;
d = Py_InitModule3("extdemo", methods, ""); m = Py_InitModule3("extdemo", methods, "");
PyDict_SetItemString(d, "val", PyInt_FromLong(EXTDEMO)); #ifdef TWO
PyModule_AddObject(m, "val", PyInt_FromLong(2));
#else
PyModule_AddObject(m, "val", PyInt_FromLong(EXTDEMO));
#endif
} }
""" """
...@@ -778,8 +826,8 @@ setup(name = "extdemo", version = "1.4", url="http://www.zope.org", ...@@ -778,8 +826,8 @@ setup(name = "extdemo", version = "1.4", url="http://www.zope.org",
""" """
def add_source_dist(test): def add_source_dist(test):
import tarfile
tmp = tempfile.mkdtemp('test-sdist') tmp = test.globs['extdemo'] = test.globs['tmpdir']('extdemo')
write = test.globs['write'] write = test.globs['write']
try: try:
write(tmp, 'extdemo.c', extdemo_c); write(tmp, 'extdemo.c', extdemo_c);
...@@ -930,7 +978,7 @@ def test_suite(): ...@@ -930,7 +978,7 @@ def test_suite():
]), ]),
), ),
doctest.DocTestSuite( doctest.DocTestSuite(
setUp=zc.buildout.testing.buildoutSetUp, setUp=easy_install_SetUp,
tearDown=zc.buildout.testing.buildoutTearDown, tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([ checker=renormalizing.RENormalizing([
zc.buildout.testing.normalize_path, zc.buildout.testing.normalize_path,
......
...@@ -74,7 +74,7 @@ new versions found in new releases: ...@@ -74,7 +74,7 @@ new versions found in new releases:
zc.buildout version 99.99, zc.buildout version 99.99,
setuptools version 99.99; setuptools version 99.99;
restarting. restarting.
buildout: Develop: /sample-buildout/showversions/setup.py buildout: Develop: /sample-buildout/showversions
buildout: Installing show-versions buildout: Installing show-versions
zc.buildout 99.99 zc.buildout 99.99
setuptools 99.99 setuptools 99.99
...@@ -122,7 +122,7 @@ We'll actually "upgrade" to an earlier version. ...@@ -122,7 +122,7 @@ We'll actually "upgrade" to an earlier version.
zc.buildout version 1.0.0, zc.buildout version 1.0.0,
setuptools version 0.6; setuptools version 0.6;
restarting. restarting.
buildout: Develop: /sample-buildout/showversions/setup.py buildout: Develop: /sample-buildout/showversions
buildout: Updating show-versions buildout: Updating show-versions
zc.buildout 1.0.0 zc.buildout 1.0.0
setuptools 0.6 setuptools 0.6
...@@ -143,7 +143,7 @@ We won't upgrade in offline mode: ...@@ -143,7 +143,7 @@ We won't upgrade in offline mode:
... """ % dict(new_releases=new_releases)) ... """ % dict(new_releases=new_releases))
>>> print system(buildout), >>> print system(buildout),
buildout: Develop: /sample-buildout/showversions/setup.py buildout: Develop: /sample-buildout/showversions
buildout: Updating show-versions buildout: Updating show-versions
zc.buildout 1.0.0 zc.buildout 1.0.0
setuptools 0.6 setuptools 0.6
...@@ -174,4 +174,3 @@ directory: ...@@ -174,4 +174,3 @@ directory:
buildout: Not upgrading because not running a local buildout command buildout: Not upgrading because not running a local buildout command
>>> ls('bin') >>> ls('bin')
...@@ -8,6 +8,27 @@ To do ...@@ -8,6 +8,27 @@ To do
Change History Change History
************** **************
1.0.0b3 (2006-12-04)
====================
Feature Changes
---------------
- Added a develop recipe for creating develop eggs.
This is useful to:
- Specify custom extension building options,
- Specify a version of Python to use, and to
- Cause develop eggs to be created after other parts.
- The develop and build recipes now return the paths created, so that
created eggs or egg links are removed when a part is removed (or
changed).
1.0.0b2 (2006-10-16) 1.0.0b2 (2006-10-16)
==================== ====================
......
...@@ -42,7 +42,9 @@ setup( ...@@ -42,7 +42,9 @@ setup(
tests_require = ['zope.testing'], tests_require = ['zope.testing'],
test_suite = name+'.tests.test_suite', test_suite = name+'.tests.test_suite',
entry_points = {'zc.buildout': ['default = %s:Egg' % name, entry_points = {'zc.buildout': ['default = %s:Egg' % name,
'script = %s:Egg' % name,
'custom = %s:Custom' % name, 'custom = %s:Custom' % name,
'develop = %s:Develop' % name,
] ]
}, },
zip_safe=False, zip_safe=False,
......
from zc.recipe.egg.egg import Egg from zc.recipe.egg.egg import Egg
from zc.recipe.egg.custom import Custom from zc.recipe.egg.custom import Custom, Develop
...@@ -19,12 +19,27 @@ $Id$ ...@@ -19,12 +19,27 @@ $Id$
import os, re, zipfile import os, re, zipfile
import zc.buildout.easy_install import zc.buildout.easy_install
class Custom: class Base:
def __init__(self, buildout, name, options): def __init__(self, buildout, name, options):
self.buildout = buildout self.name, self.options = name, options
self.name = name
self.options = options options['_d'] = buildout['buildout']['develop-eggs-directory']
python = options.get('python', buildout['buildout']['python'])
options['executable'] = buildout[python]['executable']
self.build_ext = build_ext(buildout, options)
def update(self):
return self.install()
class Custom(Base):
def __init__(self, buildout, name, options):
Base.__init__(self, buildout, name, options)
links = options.get('find-links', links = options.get('find-links',
buildout['buildout'].get('find-links')) buildout['buildout'].get('find-links'))
if links: if links:
...@@ -39,47 +54,66 @@ class Custom: ...@@ -39,47 +54,66 @@ class Custom:
options['index'] = index options['index'] = index
self.index = index self.index = index
options['_b'] = buildout['buildout']['bin-directory']
options['_e'] = buildout['buildout']['eggs-directory'] options['_e'] = buildout['buildout']['eggs-directory']
options['_d'] = buildout['buildout']['develop-eggs-directory']
assert options.get('unzip') in ('true', 'false', None) assert options.get('unzip') in ('true', 'false', None)
python = options.get('python', buildout['buildout']['python']) if buildout['buildout'].get('offline') == 'true':
options['executable'] = buildout[python]['executable'] self.install = lambda: ()
build_ext = {}
for be_option in ('include-dirs', 'library-dirs', 'rpath'):
value = options.get(be_option)
if value is None:
continue
value = [
os.path.join(
buildout['buildout']['directory'],
v.strip()
)
for v in value.strip().split('\n')
if v.strip()
]
build_ext[be_option] = ':'.join(value)
options[be_option] = ':'.join(value)
self.build_ext = build_ext
def install(self): def install(self):
if self.buildout['buildout'].get('offline') == 'true':
return ()
options = self.options options = self.options
distribution = options.get('eggs', self.name).strip() distribution = options.get('eggs', self.name).strip()
build_ext = dict([ return zc.buildout.easy_install.build(
(k, options[k])
for k in ('include-dirs', 'library-dirs', 'rpath')
if k in options
])
zc.buildout.easy_install.build(
distribution, options['_d'], self.build_ext, distribution, options['_d'], self.build_ext,
self.links, self.index, options['executable'], [options['_e']], self.links, self.index, options['executable'], [options['_e']],
) )
return () class Develop(Base):
def __init__(self, buildout, name, options):
Base.__init__(self, buildout, name, options)
options['setup'] = os.path.join(buildout['buildout']['directory'],
options['setup'])
def install(self):
options = self.options
return zc.buildout.easy_install.develop(
options['setup'], options['_d'], self.build_ext,
options['executable'],
)
def build_ext(buildout, options):
result = {}
for be_option in ('include-dirs', 'library-dirs', 'rpath'):
value = options.get(be_option)
if value is None:
continue
value = [
os.path.join(
buildout['buildout']['directory'],
v.strip()
)
for v in value.strip().split('\n')
if v.strip()
]
result[be_option] = os.pathsep.join(value)
options[be_option] = os.pathsep.join(value)
swig = options.get('swig')
if swig:
options['swig'] = result['swig'] = os.path.join(
buildout['buildout']['directory'],
swig,
)
for be_option in ('define', 'undef', 'libraries', 'link-objects',
'debug', 'force', 'compiler', 'swig-cpp', 'swig-opts',
):
value = options.get(be_option)
if value is None:
continue
result[be_option] = value
update = install return result
Custon eggs Creating eggs with extensions neededing custom build settings
=========== =============================================================
Sometimes, It's necessary to provide extra control over how an egg is Sometimes, It's necessary to provide extra control over how an egg is
created. This is commonly true for eggs with extension modules that created. This is commonly true for eggs with extension modules that
...@@ -20,6 +20,42 @@ rpath ...@@ -20,6 +20,42 @@ rpath
A new-line separated list of directories to search for dynamic libraries A new-line separated list of directories to search for dynamic libraries
at run time. at run time.
define
A comma-separated list of names of C preprocessor variables to
define.
undef
A comman separated list of names of C preprocessor variables to
undefine.
libraries
The name of an additional library to link with. Due to limitations
in distutils and desprite the option name, only a single library
can be specified.
link-objects
The name of an link object to link afainst. Due to limitations
in distutils and desprite the option name, only a single link object
can be specified.
debug
Compile/link with debugging information
force
Forcibly build everything (ignore file timestamps)
compiler
Specify the compiler type
swig
The path to the swig executable
swig-cpp
Make SWIG create C++ files (default is C)
swig-opts
List of SWIG command line options
In addition, the following options can be used to specify the egg: In addition, the following options can be used to specify the egg:
egg egg
...@@ -57,9 +93,13 @@ package that has a simple extension module:: ...@@ -57,9 +93,13 @@ package that has a simple extension module::
PyMODINIT_FUNC PyMODINIT_FUNC
initextdemo(void) initextdemo(void)
{ {
PyObject *d; PyObject *m;
d = Py_InitModule3("extdemo", methods, ""); m = Py_InitModule3("extdemo", methods, "");
PyDict_SetItemString(d, "val", PyInt_FromLong(EXTDEMO)); #ifdef TWO
PyModule_AddObject(m, "val", PyInt_FromLong(2));
#else
PyModule_AddObject(m, "val", PyInt_FromLong(EXTDEMO));
#endif
} }
The extension depends on a system-dependnt include file, extdemo.h, The extension depends on a system-dependnt include file, extdemo.h,
...@@ -71,10 +111,11 @@ extdemo-1.4.tar.gz, on a distribution server. ...@@ -71,10 +111,11 @@ extdemo-1.4.tar.gz, on a distribution server.
We have a sample buildout that we'll add an include directory to with We have a sample buildout that we'll add an include directory to with
the necessary include file: the necessary include file:
>>> mkdir(sample_buildout, 'include') >>> mkdir('include')
>>> import os >>> write('include', 'extdemo.h',
>>> open(os.path.join(sample_buildout, 'include', 'extdemo.h'), 'w').write( ... """
... "#define EXTDEMO 42\n") ... #define EXTDEMO 42
... """)
We'll also update the buildout configuration file to define a part for We'll also update the buildout configuration file to define a part for
the egg: the egg:
...@@ -91,8 +132,7 @@ the egg: ...@@ -91,8 +132,7 @@ the egg:
... include-dirs = include ... include-dirs = include
... """ % dict(server=link_server)) ... """ % dict(server=link_server))
>>> os.chdir(sample_buildout) >>> buildout = join('bin', 'buildout')
>>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
>>> print system(buildout), >>> print system(buildout),
buildout: Installing extdemo buildout: Installing extdemo
...@@ -113,3 +153,177 @@ Note that no scripts or dependencies are installed. To install ...@@ -113,3 +153,177 @@ Note that no scripts or dependencies are installed. To install
dependencies or scripts for a custom egg, define another part and use dependencies or scripts for a custom egg, define another part and use
the zc.recipe.egg recipe, listing the custom egg as one of the eggs to the zc.recipe.egg recipe, listing the custom egg as one of the eggs to
be installed. The zc.recipe.egg recipe will use the installed egg. be installed. The zc.recipe.egg recipe will use the installed egg.
Let's define a script that uses out ext demo:
>>> mkdir('demo')
>>> write('demo', 'demo.py',
... """
... import extdemo
... def main():
... print extdemo.val
... """)
>>> write('demo', 'setup.py',
... """
... from setuptools import setup
... setup(name='demo')
... """)
>>> write('buildout.cfg',
... """
... [buildout]
... develop = demo
... parts = extdemo demo
...
... [extdemo]
... recipe = zc.recipe.egg:custom
... find-links = %(server)s
... index = %(server)s/index
... include-dirs = include
...
... [demo]
... recipe = zc.recipe.egg
... eggs = demo
... extdemo
... entry-points = demo=demo:main
... """ % dict(server=link_server))
>>> print system(buildout),
buildout: Develop: /sample-buildout/demo
buildout: Updating extdemo
buildout: Installing demo
When we run the script, we'll 42 printed:
>>> print system(join('bin', 'demo')),
42
Controlling develop-egg generation
==================================
If you want to provide custom build options for a develop egg, you can
use the develop recipe. The recipe has the following options:
path
The path to a setup script or directory containing a startup
script. This is required.
include-dirs
A new-line separated list of directories to search for include
files.
library-dirs
A new-line separated list of directories to search for libraries
to link with.
rpath
A new-line separated list of directories to search for dynamic libraries
at run time.
define
A comma-separated list of names of C preprocessor variables to
define.
undef
A comman separated list of names of C preprocessor variables to
undefine.
libraries
The name of an additional library to link with. Due to limitations
in distutils and desprite the option name, only a single library
can be specified.
link-objects
The name of an link object to link afainst. Due to limitations
in distutils and desprite the option name, only a single link object
can be specified.
debug
Compile/link with debugging information
force
Forcibly build everything (ignore file timestamps)
compiler
Specify the compiler type
swig
The path to the swig executable
swig-cpp
Make SWIG create C++ files (default is C)
swig-opts
List of SWIG command line options
python
The name of a section to get the Python executable from.
If not specified, then the buildout python option is used. The
Python executable is found in the executable option of the named
section.
To illustrate this, we'll use a directory containing the extdemo
example from the earlier section:
>>> ls(extdemo)
- MANIFEST
- MANIFEST.in
- README
- extdemo.c
- setup.py
>>> write('buildout.cfg',
... """
... [buildout]
... develop = demo
... parts = extdemo demo
...
... [extdemo]
... setup = %(extdemo)s
... recipe = zc.recipe.egg:develop
... include-dirs = include
... define = TWO
...
... [demo]
... recipe = zc.recipe.egg
... eggs = demo
... extdemo
... entry-points = demo=demo:main
... """ % dict(extdemo=extdemo))
Note that we added a define option to cause the preprocessor variable
TWO to be defined. This will cause the module-variable, 'val', to be
set with a value of 2.
>>> print system(buildout),
buildout: Develop: /tmp/tmpCXjRps/_TEST_/sample-buildout/demo
buildout: Uninstalling extdemo
buildout: Installing extdemo
buildout: Updating demo
Our develop-eggs now includes an egg link for extdemo:
>>> ls('develop-eggs')
- demo.egg-link
- extdemo.egg-link
- zc.recipe.egg.egg-link
and the extdemo now has a built extension:
>>> ls(extdemo)
- MANIFEST
- MANIFEST.in
- README
d build
- extdemo.c
d extdemo.egg-info
- extdemo.so
- setup.py
Because develop eggs take precedence over non-develop eggs, the demo
script will use the new develop egg:
>>> print system(join('bin', 'demo')),
2
...@@ -78,6 +78,7 @@ def test_suite(): ...@@ -78,6 +78,7 @@ def test_suite():
'custom.txt', 'custom.txt',
setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown, setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([ checker=renormalizing.RENormalizing([
zc.buildout.testing.normalize_path,
(re.compile("(d ((ext)?demo(needed)?|other)" (re.compile("(d ((ext)?demo(needed)?|other)"
"-\d[.]\d-py)\d[.]\d(-\S+)?[.]egg"), "-\d[.]\d-py)\d[.]\d(-\S+)?[.]egg"),
'\\1V.V.egg'), '\\1V.V.egg'),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment