# Fedora Developer Shell # # 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; version 2 of the License. # # 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 Library 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. # # Authors: Yaakov M. Nemoy # from __future__ import with_statement from os import listdir, getcwd from os.path import expanduser, expandvars, abspath from re import compile, DOTALL from subprocess import Popen from urllib import urlopen, urlretrieve from base.base import log from base.exceptions import ExecutionException from base.dirfactory import DirFactory from base.module import Module from base.util import pwd, one, log_file from base.vars import orig_src_dir, haskell_compiler from modules.sourceball import SourceBall class Cabal(Module): '''A wrapper around common cabal operations This provides a useful api for other modules around the cabal build system ''' def __init__(self, name, root='~/haskell'): '''creates a new cabal module this api is deprecated, because it should be more autodetecting name is a Package (Directory) that uses cabal for its build system ''' self.name = name self.root = expanduser(root) with pwd(self.root): self.package = DirFactory(name) self.original = orig_src_dir self.compiler = haskell_compiler def copy_in(self, tarball): '''copies a tarball into the package tarball is a path to some tarball ''' tarball = abspath(tarball) with pwd(self.root): self.package.add_sourceball(tarball) def darcs_get(self, url, tgt): '''creates a darcs variant of a cabal package using darcs source url is a url to some darcs repo tgt is the local name of the darcs repo ''' self.package.checkout(tgt, url) def find_setup(self, orig=''): '''returns the name of the Setup.?hs script for cabal. ''' setup_re = compile("Setup\.l?hs") with pwd(self.package.dir): return one(listdir(getcwd()), setup_re.search) def compile_setup(self, orig=''): '''compiles the setup script for faster execution ''' log.debug('dir is ' + self.package.dir) with pwd(self.package.dir): with log_file('ghc.log') as ghc_out: log.debug('ghc.log file is ' + str(ghc_out)) log.debug('source_dir is ' + self.package.source_dir(orig)) with pwd(self.package.source_dir(orig)): setup_f = self.find_setup(orig) p = Popen([self.compiler, '--make', setup_f], stdout = ghc_out, stderr = ghc_out) log.info('Building %s, please wait...' % setup_f) p.communicate() def configure(self, target='home', orig=''): '''runs the configure stage of cabal target is either 'home' or 'root' and will configure the package to install either to the user's home directory, or to the system wide haskell. Some help is needed making this more flexible ''' user = True if target == 'home' else False self.compile_setup(orig) with pwd(self.package.dir): with log_file('cabal.log') as cabal_out: log.debug('source_dir is ' + self.package.source_dir(orig)) with pwd(self.package.source_dir(orig)): args = [abspath('Setup'), 'configure'] + \ (['--user', '--prefix=' + expanduser('~')] if user else []) p = Popen(args, stdout=cabal_out, stderr=cabal_out) log.info('Configuring %s, please wait...' % self.name) p.wait() def build(self, orig=''): '''runs the build stage of cabal This is not safe to run on an unconfigured source dir, because this module does not track the state of cabal systems. The user must do this on their own. ''' self.compile_setup(orig) with pwd(self.package.dir): with log_file('cabal.log') as cabal_out: with pwd(self.package.source_dir(orig)): args = [abspath('Setup'), 'build'] p = Popen(args, stdout=cabal_out, stderr=cabal_out) log.info('Building %s, please wait...' % self.name) p.wait() def install(self, orig=''): '''runs the install stage of cabal This is not safe to run on an unconfigured source dir, because this module does not track the state of cabal systems. The user must do this on their own. ''' self.compile_setup(orig) with pwd(self.package.dir): with log_file('cabal.log') as cabal_out: with pwd(self.package.source_dir(orig)): args = [abspath('Setup'), 'install'] p = Popen(args, stdout=cabal_out, stderr=cabal_out) log.info('Building %s, please wait...' % self.name) p.wait() def install_tag(self, tag): '''assuming a package that supports tagging, install a specific tag tag is the name of the tag in the DVCS ''' with pwd(self.package.dir): with self.package.tag(tag): self.install() def install_source(self, target='home', orig=''): '''perform configure, build, and install steps in one ''' self.configure(target, orig) self.build(orig) self.install(orig) def install_sourceball(self, tarball, target='home'): '''given a tarball, copy it in and install it ''' self.copy_in(tarball) self.install_source(target, '') def get_from_hackage(self, pkg, ver): '''get a specific package from hackage pkg is the name of the package desired ver is the version wanted ''' sb_file = self.hackage_url(pkg, ver) self.copy_in(sb_file) def get_latest(self, pkg): '''get the latest version of a package from hackage pkg is the package desired ''' ver = self.latest_version(pkg) self.get_from_hackage(pkg, ver) def install_from_hackage(self, pkg, ver, target='home'): '''get and install a specific package from hackage pkg is the desired package ver is the version wanted target is the location to install to, either 'home' or 'root' ''' self.get_from_hackage(pkg, ver) self.install_source(target, '') def install_latest(self, pkg, target='home'): '''get and install the latest version of a package from hackage''' self.get_latest(pkg) self.install_source(target, '') def latest_version(self, pkg): '''download information from hackage to find out the latest version of a package ''' hackage_title = compile(r'HackageDB: (.*)-(.*)', DOTALL) site = 'http://hackage.haskell.org/cgi-bin/hackage-scripts/package/' + pkg u = urlopen(site) page = u.read() match = hackage_title.search(page) groups = match.groups() print groups if pkg == groups[0]: return groups[1] else: raise ExecutionException("package does not match package name, can't determine version, sorry") def hackage_url(self, pkg, ver): '''returns the url for tarball for a hackage package''' return 'http://hackage.haskell.org/packages/archive/' + \ pkg + '/' + ver + '/' + pkg + '-' + ver + '.tar.gz' def close(self): self.package.close() __all__ = ['Cabal']