Commit ef053caa authored by Kai Lautaportti's avatar Kai Lautaportti

Enhanced version of the zc.recipe.cmmi recipe for building and installing

source distributions.


git-svn-id: https://svn.hexagonit.fi/hexagonit.recipe.cmmi/trunk@4508 b96f28ea-bbdf-0310-b3b3-e15a02b9f88d
parent 12046443
Change History
**************
1.0.0
=====
Initial public release.
********************************************
Recipe for compiling and installing software
********************************************
.. contents::
The recipe provides the means to compile and install source
distributions using ``configure`` and ``make``. It is inspired by the
zc.recipe.cmmi_ recipe.
.. _zc.recipe.cmmi : http://cheeseshop.python.org/pypi/zc.recipe.cmmi
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
Supported options
=================
url
URL to the package that will be downloaded and extracted. The
supported package formats are .tar.gz, .tar.bz2, and .zip. The
value must be a full URL,
e.g. http://python.org/ftp/python/2.4.4/Python-2.4.4.tgz.
md5sum
MD5 checksum for the package file. If available the MD5
checksum of the downloaded package will be compared to this value
and if the values do not match the execution of the recipe will
fail.
make-binary
Path to the ``make`` program. Defaults to 'make' which
should work on any system that has the ``make`` program available
in the system ``PATH``.
make-targets
Targets for the ``make`` command. Defaults to 'install'
which will be enough to install most software packages. You only
need to use this if you want to build alternate targets. Each
target must be given on a separate line.
configure-options
Extra options to be given to the ``configure`` script. By default
only the ``--prefix`` option is passed which is set to the part
directory. Each option must be given on a separate line. Note that
in addition to configure options you can also pass in environment
variables such as ``CFLAGS`` and ``LDFLAGS`` to control the build
process.
patch-binary
Path to the ``patch`` program. Defaults to 'patch' which should
work on any system that has the ``patch`` program available in the
system ``PATH``.
patch-options
Options passed to the ``patch`` program. Defaults to ``-p0``.
patches
List of patch files to the applied to the extracted source. Each
file should be given on a separate line.
keep-compile-dir
Switch to optionally keep the temporary directory where the
package was compiled. This is mostly useful for other recipes that
use this recipe to compile a software but wish to do some
additional steps not handled by this recipe. The location of the
compile directory is stored in ``options['compile-directory']``.
Accepted values are 'true' or 'false', defaults to 'false'.
Additionally, the recipe honors the ``download-directory`` option set
in the ``[buildout]`` section and stores the downloaded files under
it. If the value is not set a directory called ``downloads`` will be
created in the root of the buildout and the ``download-directory``
option set accordingly.
The recipe will first check if there is a local copy of the package
before downloading it from the net. Files can be shared among
different buildouts by setting the ``download-directory`` to the same
location.
Example usage
=============
We'll use a simple tarball to demonstrate the recipe.
>>> import os.path
>>> src = join(os.path.dirname(__file__), 'testdata')
>>> ls(src)
- package-0.0.0.tar.gz
The package contains a dummy ``configure`` script that will simply
echo the options it was called with and create a ``Makefile`` that
will do the same.
Let's create a buildout to build and install the package.
>>> write('buildout.cfg',
... """
... [buildout]
... parts = package
...
... [package]
... recipe = hexagonit.recipe.cmmi
... url = file://%s/package-0.0.0.tar.gz
... """ % src)
This will download, extract and build our demo package with the
default build options.
>>> print system(buildout)
Installing package.
package: Creating download directory: /sample_buildout/downloads
package: Extracting package to /tmp/...package
configure --prefix=/sample_buildout/parts/package
building package
installing package
As we can see the configure script was called with the ``--prefix``
option by default followed by calls to ``make`` and ``make install``.
The above options are enough to build most packages. However, in some
cases it is not enough and we need to control the build process
more. Let's try again with a new buildout and provide more options.
>>> write('buildout.cfg',
... """
... [buildout]
... parts = package
...
... [package]
... recipe = hexagonit.recipe.cmmi
... url = file://%(src)s/package-0.0.0.tar.gz
... md5sum = 6b94295c042a91ea3203857326bc9209
... configure-options =
... --with-threads
... --without-foobar
... CFLAGS=-I/sw/include
... LDFLAGS=-L/sw/lib
... make-targets =
... install
... install-lib
... patches =
... patches/configure.patch
... patches/Makefile.dist.patch
... """ % dict(src=src))
This configuration uses custom configure options, multiple make
targets and also patches the source code before the scripts are run.
>>> print system(buildout)
Uninstalling package.
Installing package.
package: Using a cached copy from /sample_buildout/downloads/package-0.0.0.tar.gz
package: MD5 checksum OK
package: Extracting package to /tmp/...
package: Applying patches
patching file configure
patching file Makefile.dist
patched-configure --prefix=/sample_buildout/parts/package --with-threads --without-foobar CFLAGS=-I/sw/include LDFLAGS=-L/sw/lib
building patched package
installing patched package
installing patched package-lib
For more specific needs you can write your own recipe that uses
``hexagonit.recipe.cmmi`` and set the ``keep-compile-dir`` option to
``true``. You can then continue from where this recipe finished by
reading the location of the compile directory from
``options['compile-directory']``.
import zc.buildout
import urlparse
import tempfile
import logging
import urllib
import shutil
import md5
import os
import hexagonit.recipe.download
class Recipe:
"""zc.buildout recipe for compiling and installing software"""
def __init__(self, buildout, name, options):
self.options = options
self.buildout = buildout
self.name = name
log = logging.getLogger(self.name)
options['location'] = os.path.join(
buildout['buildout']['parts-directory'],
self.name)
options['prefix'] = options['location']
options['url'] = options['url']
def update(self):
pass
def run(self, cmd):
log = logging.getLogger(self.name)
if os.system(cmd):
log.error('Error executing command: %s' % cmd)
raise zc.buildout.UserError('System error')
def install(self):
log = logging.getLogger(self.name)
make_cmd = self.options.get('make-binary', 'make').strip()
make_targets = ' '.join(self.options.get('make-targets', 'install').split())
configure_options = ' '.join(self.options.get('configure-options','').split())
patch_cmd = self.options.get('patch-binary', 'patch').strip()
patch_options = ' '.join(self.options.get('patch-options', '-p0').split())
patches = self.options.get('patches', '').split()
compile_dir = tempfile.mkdtemp(self.name)
# Download the source using hexagonit.recipe.download
try:
opt = self.options.copy()
opt['destination'] = compile_dir
hexagonit.recipe.download.Recipe(
self.buildout, self.name, opt).install()
except:
shutil.rmtree(compile_dir)
raise
os.mkdir(self.options['location'])
os.chdir(compile_dir)
try:
if not os.path.isfile('configure'):
contents = os.listdir(compile_dir)
if len(contents) == 1:
os.chdir(contents[0])
if not os.path.isfile('configure'):
log.error('Unable to find the configure script')
raise zc.buildout.UserError('Invalid package contents')
if patches:
log.info('Applying patches')
for patch in patches:
self.run('%s %s < %s' % (patch_cmd, patch_options, patch))
self.run('./configure --prefix=%s %s' % (self.options['prefix'], configure_options))
self.run(make_cmd)
self.run('%s %s' % (make_cmd, make_targets))
except:
log.error('Compilation error. The package is left as is at %s where '
'you can inspect what went wrong' % os.getcwd())
raise
if self.options.get('keep-compile-dir', '').lower() in ('true', 'yes', '1', 'on'):
self.options['compile-dir'] = os.getcwd()
else:
shutil.rmtree(compile_dir)
return [self.options['location']]
import unittest
import zc.buildout.tests
import zc.buildout.testing
import re
from zope.testing import doctest, renormalizing
optionflags = (doctest.ELLIPSIS |
doctest.NORMALIZE_WHITESPACE |
doctest.REPORT_ONLY_FIRST_FAILURE)
def setUp(test):
zc.buildout.testing.buildoutSetUp(test)
zc.buildout.testing.install('hexagonit.recipe.download', test)
zc.buildout.testing.install_develop('hexagonit.recipe.cmmi', test)
def test_suite():
suite = unittest.TestSuite((
doctest.DocFileSuite(
'README.txt',
setUp=setUp,
tearDown=zc.buildout.testing.buildoutTearDown,
optionflags=optionflags,
checker=renormalizing.RENormalizing([
(re.compile('--prefix=\S+sample-buildout'),
'--prefix=/sample_buildout'),
(re.compile('\s/\S+sample-buildout'),
' /sample_buildout'),
]),
),
))
return suite
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
[egg_info]
;tag_build = dev
;tag_svn_revision = true
from setuptools import setup, find_packages
import os
version = '0.0.0'
name = 'hexagonit.recipe.cmmi'
def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
setup(name=name,
version=version,
description="zc.buildout recipe for ...",
long_description= (
read('README.txt')
+ '\n' +
read('CHANGES.txt')
+ '\n' +
'Detailed Documentation\n'
'**********************\n'
+ '\n' +
read('hexagonit','recipe','cmmi','README.txt')
+ '\n' +
'Download\n'
'***********************\n'
),
classifiers=[
'Framework :: Buildout',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Topic :: Software Development :: Build Tools',
'Topic :: Software Development :: Libraries :: Python Modules',
],
keywords='development buildout recipe',
author='Kai Lautaportti',
author_email='kai.lautaportti@hexagonit.fi',
url='http://cheeseshop.python.org/pypi/%s' % name,
license='GPL',
packages=find_packages(exclude=['ez_setup']),
namespace_packages=['hexagonit', 'hexagonit.recipe'],
include_package_data=True,
zip_safe=False,
install_requires = ['zc.buildout', 'setuptools', 'hexagonit.recipe.download'],
tests_require = ['zope.testing'],
test_suite = '%s.tests.test_suite' % name,
entry_points = { 'zc.buildout' : ['default = %s:Recipe' % name] },
)
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