summaryrefslogtreecommitdiffstats
path: root/modules/cabal.py
blob: 842aaf7741490e7ac518597a8d462ef8228e9ba4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# 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 <ynemoy@redhat.com>
#
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'<title.*?>HackageDB: (.*)-(.*)</title.*?>', 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']