Add NPM and CPAN entry points

parent 58e863c6
*******************
slapos.recipe.build slapos.recipe.build
=================== *******************
.. contents::
Important notice Important notice
---------------- ****************
This is totally experimental recipe for fully flexible software "build". This is totally experimental recipe for fully flexible software "build".
Examples Examples
-------- ********
Recipe to build the software. Recipe to build the software.
...@@ -140,7 +143,7 @@ Which will link ${file:location}/bin/file to ${buildout:bin-directory}/bin/file ...@@ -140,7 +143,7 @@ Which will link ${file:location}/bin/file to ${buildout:bin-directory}/bin/file
and ${file:location}/bin/file to ${buildout:bin-directory}/bin/anotherfile and ${file:location}/bin/file to ${buildout:bin-directory}/bin/anotherfile
Pure download Pure download
------------- *************
:: ::
...@@ -171,3 +174,95 @@ Notes ...@@ -171,3 +174,95 @@ Notes
This recipe suffers from buildout download utility issue, which will do not This recipe suffers from buildout download utility issue, which will do not
try to redownload resource with wrong md5sum. try to redownload resource with wrong md5sum.
slapos.recipe.build:cpan
************************
Downloads and installs perl modules using Comprehensive Perl Archive Network (cpan).
Examples
========
Basic example
-------------
Here is example to install one or several modules::
[buildout]
parts = perl-modules
[perl-modules]
recipe = slapos.recipe.build:cpan
modules =
Class::Date
Other::Module
# Optional argument specifying perl buildout part, if existing.
# If specified, recipe will use the perl installed by buildout.
# If not specified, will take the globally available perl executable.
perl = perl
Specific version
----------------
Note that cpan won't allow you to specify version and will always take latest
version available. To choose a specific version, you will need to specify
the full path in cpan like in ::
[buildout]
parts = perl-modules
[perl-modules]
recipe = slapos.recipe.build:cpan
modules =
D/DL/DLUX/Class-Date-1.1.10.tar.gz
perl = perl
Notes
=====
Currently, the modules will be installed in site-perl directory. Location of this
directory changes depending on the perl installation.
slapos.recipe.build:npm
***********************
Downloads and installs node.js packages using Node Package Manager (NPM).
Examples
========
Basic example
-------------
Here is example to install one or several modules::
[buildout]
parts = node-package
[node-package]
recipe = slapos.recipe.build:npm
modules =
colors
express
# Optional argument specifying perl buildout part, if existing.
# If specified, recipe will use the perl installed by buildout.
# If not specified, will take the globally available perl executable.
node = node-0.6
Specific version
----------------
[buildout]
parts = node-package
[node-package]
recipe = slapos.recipe.build:cpan
modules =
express==1.0.2
node = node-0.6
...@@ -30,7 +30,9 @@ setup(name=name, ...@@ -30,7 +30,9 @@ setup(name=name,
'zc.buildout': [ 'zc.buildout': [
'default = slapos.recipe.build:Script', 'default = slapos.recipe.build:Script',
'cmmi = slapos.recipe.build:Cmmi', 'cmmi = slapos.recipe.build:Cmmi',
'cpan = slapos.recipe.cpan:Cpan',
'download = slapos.recipe.download:Recipe', 'download = slapos.recipe.download:Recipe',
'download-unpacked = slapos.recipe.downloadunpacked:Recipe' 'download-unpacked = slapos.recipe.downloadunpacked:Recipe',
'npm = slapos.recipe.npm:Npm',
]}, ]},
) )
...@@ -382,29 +382,7 @@ class Cmmi(Script): ...@@ -382,29 +382,7 @@ class Cmmi(Script):
Compatibility on parameter level""" Compatibility on parameter level"""
script = """ script = """
if self.options['url']: self._install()
url = self.download(self.options['url'], self.options.get('md5sum'))
extract_dir = self.extract(url)
workdir = guessworkdir(extract_dir)
else:
workdir = self.options['path']
configure_command = self.options.get('configure-command')
if configure_command is None or configure_command.lower() == 'None':
configure_command = ["./configure", "--prefix=%(location)s"] + %(configure-options)r.split()
else:
configure_command = configure_command.split()
self.logger.info('Configuring with: %%r' %% ' '.join(configure_command))
self.applyPatchList(self.options.get('patches'), self.options.get('patch-options'), self.options.get('patch-binary'), workdir)
call(configure_command, cwd=workdir, env=env)
make_command = [self.options.get('make-binary', "make")]
make_options = self.options.get('make-options')
if make_options is not None:
make_command.extend(make_options.split())
self.logger.info('Building with %%r' %% ' '.join(make_command))
call(make_command, cwd=workdir, env=env)
make_command.append('install')
self.logger.info('Installing with %%r' %% ' '.join(make_command))
call(make_command + ['install'], cwd=workdir, env=env)
""" """
def __init__(self, buildout, name, options): def __init__(self, buildout, name, options):
...@@ -414,4 +392,29 @@ call(make_command + ['install'], cwd=workdir, env=env) ...@@ -414,4 +392,29 @@ call(make_command + ['install'], cwd=workdir, env=env)
if options['url'] and options['path']: if options['url'] and options['path']:
raise zc.buildout.UserError('You must use either "url" or "path", not both!') raise zc.buildout.UserError('You must use either "url" or "path", not both!')
if not (options['url'] or options['path']): if not (options['url'] or options['path']):
raise zc.buildout.UserError('You must provide either "url" or "path".') raise zc.buildout.UserError('You must provide either "url" or "path".')
\ No newline at end of file
def _install(self):
if self.options['url']:
url = self.download(self.options['url'], self.options.get('md5sum'))
extract_dir = self.extract(url)
workdir = guessworkdir(extract_dir)
else:
workdir = self.options['path']
configure_command = self.options.get('configure-command')
if configure_command is None or configure_command.lower() == 'None':
configure_command = ["./configure", "--prefix=%(location)s"] + %(configure-options)r.split()
else:
configure_command = configure_command.split()
self.logger.info('Configuring with: %%r' %% ' '.join(configure_command))
self.applyPatchList(self.options.get('patches'), self.options.get('patch-options'), self.options.get('patch-binary'), workdir)
call(configure_command, cwd=workdir, env=env)
make_command = [self.options.get('make-binary', "make")]
make_options = self.options.get('make-options')
if make_options is not None:
make_command.extend(make_options.split())
self.logger.info('Building with %%r' %% ' '.join(make_command))
call(make_command, cwd=workdir, env=env)
make_command.append('install')
self.logger.info('Installing with %%r' %% ' '.join(make_command))
call(make_command + ['install'], cwd=workdir, env=env)
\ No newline at end of file
##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import logging
import os
import subprocess
import zc.buildout
def createExecutable(name, content, mode=0755):
"""Create an executable with content
The parent directory should exists, else it would raise IOError"""
with open(name, 'w') as fileobject:
fileobject.write(content)
os.chmod(fileobject.name, mode)
return os.path.abspath(name)
def createWrapper(node, package):
"""Creates a node package wrapper"""
class Cpan(object):
"""Download and install locally node packages using cpan"""
def __init__(self, buildout, name, options):
self.cleanup_dir_list = []
self.buildout = buildout
self.name = name
self.logger = logging.getLogger(self.name)
self.options = {}
# Check for mandatory fields
if not options.get('modules'):
raise zc.buildout.UserError('No package specified in \'modules\' '
'parameter')
# Get variables
for k in ['location', 'modules', 'environment-section']:
self.options[k] = options.get(k, '').strip()
# Expose ${:location}
if self.options['location'] == '':
self.options['location'] = os.path.join(
buildout['buildout']['parts-directory'], self.name)
# Get perl binary. Mimic zc.recipe.egg behavior.
perl = options.get('perl')
if perl:
_cpan_executable = buildout[perl].get('perl_executable',
os.path.join(buildout[perl]['location'], 'bin', 'cpan'))
else:
self.logger.warn('Using system perl.')
_cpan_executable = 'cpan'
# Get list of packages to install
package_list = [
"%s" % s.strip()
for r in self.options['modules'].splitlines()
for s in r.split(' ')
if r.strip()]
self.popen_arguments = [_cpan_executable, 'install']
self.popen_arguments.extend(package_list)
def install(self):
# Create part directory
location = self.options.get('location')
if not os.path.isdir(location):
os.makedirs(location)
os.chdir(location)
environ = {}
# Set minimal working environment
environ['PATH'] = os.environ['PATH']
# Create cpan config directory in part directory
environ['HOME'] = self.options['location']
# Apply variables from defined environment (code from h.r.cmmi)
environment_section = self.options['environment-section']
if environment_section and environment_section in self.buildout:
# Use environment variables from the designated config section.
environ.update(self.buildout[environment_section])
for variable in self.options.get('environment', '').splitlines():
if variable.strip():
try:
key, value = variable.split('=', 1)
environ[key.strip()] = value
except ValueError:
raise zc.buildout.UserError('Invalid environment variable '
'definition: %s', variable)
# Extrapolate the environment variables using values from the current
# environment.
for key in environ:
environ[key] = environ[key] % os.environ
# Install packages
cpan_process = subprocess.Popen(self.popen_arguments, env=environ)
cpan_process.communicate()
if cpan_process.returncode is not 0:
raise zc.buildout.UserError('Failed to install perl modules.')
# Create wrappers
#for package in self.package_list:
# createExecutable()
# NODE_PATH=${:destination}/node_modules ${nodejs:node_location} ${:cloud9_js_location}
return [self.options['location']]
def update(self):
return None
##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import logging
import os
import subprocess
import zc.buildout
def createExecutable(name, content, mode=0755):
"""Create an executable with content
The parent directory should exists, else it would raise IOError"""
with open(name, 'w') as fileobject:
fileobject.write(content)
os.chmod(fileobject.name, mode)
return os.path.abspath(name)
def createWrapper(node, package):
"""Creates a node package wrapper"""
class Npm(object):
"""Download and install locally node packages from node.js' NPM"""
def __init__(self, buildout, name, options):
self.cleanup_dir_list = []
self.buildout = buildout
self.name = name
self.logger = logging.getLogger(self.name)
self.options = {}
# Check for mandatory fields
if not options.get('packages'):
raise zc.buildout.UserError('No package specified in \'packages\' '
'parameter')
# Get variables
for k in ['location', 'packages', 'environment-section', 'node',
'environment']:
self.options[k] = options.get(k, '').strip()
# Expose ${:location}
if self.options['location'] == '':
self.options['location'] = options['location'] = os.path.join(
buildout['buildout']['parts-directory'], self.name)
# Get npm and node binaries. Mimic zc.recipe.egg behavior.
node = self.options.get('node')
if node:
_node_executable = buildout[node].get('node_executable',
os.path.join(buildout[node]['location'], 'bin', 'node'))
_npm_executable = buildout[node].get('npm_executable',
os.path.join(buildout[node]['location'], 'bin', 'npm'))
self.popen_arguments = [_node_executable, _npm_executable, 'install']
else:
self.logger.warn('Using system node.')
self.popen_arguments = ['npm', 'install']
# Get list of packages to install
self.package_list = [
"%s" % s.strip()
for r in self.options['packages'].splitlines()
for s in r.split(' ')
if r.strip()]
self.popen_arguments.extend(self.package_list)
def install(self):
# Create part directory
location = self.options.get('location')
if not os.path.isdir(location):
os.makedirs(location)
os.chdir(location)
environ = {}
# Set minimal working environment
environ['PATH'] = os.environ['PATH']
# Set NPM envvar to not share cache
environ['NPM_CONFIG_CACHE'] = os.path.join(location, 'cache')
# Arbitrary put packages installation to node_module directory
environ['NODE_PATH'] = os.path.join(location, 'node_modules')
# Override HOME so that npm won't create ~/.npm directory
environ['HOME'] = location
# Apply variables from defined environment (code from h.r.cmmi)
environment_section = self.options['environment-section']
if environment_section and environment_section in self.buildout:
# Use environment variables from the designated config section.
environ.update(self.buildout[environment_section])
for variable in self.options.get('environment', '').splitlines():
if variable.strip():
try:
key, value = variable.split('=', 1)
environ[key.strip()] = value
except ValueError:
raise zc.buildout.UserError('Invalid environment variable '
'definition: %s', variable)
# Extrapolate the environment variables using values from the current
# environment.
for key in environ:
environ[key] = environ[key] % os.environ
# Install packages
npm_process = subprocess.Popen(self.popen_arguments, env=environ)
npm_process.communicate()
if npm_process.returncode is not 0:
raise zc.buildout.UserError('Failed to install npm module(s).')
# Create wrappers
#for package in self.package_list:
# createExecutable()
# NODE_PATH=${:destination}/node_modules ${nodejs:node_location} ${:cloud9_js_location}
return [self.options['location']]
def update(self):
return None
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