summaryrefslogtreecommitdiffstats
path: root/modules/darcs.py
blob: b6a4d9659cea0bb01e608a8cd935358e186e8bbb (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
227
228
229
230
231
232
# 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

import os
import re

from contextlib import contextmanager
from os import getcwd
from os.path import join, basename, exists
from subprocess import Popen, PIPE

from base.util import pwd, log, rm, log_file, copy, move
from modules.revisioncontrol import RevisionControl

hash_re = re.compile(r'hash=\'(\w|-|.*?)\'', re.MULTILINE)
date_re = re.compile(r'date=\'(\d*?)\'', re.MULTILINE)

class Darcs(RevisionControl):
    '''manages single source packages where the primary source is darcs
    '''
    def __init__(self, name=None, url=None, *args):
        if url:
            #split chokes on URLs that end in a /
            url = url[:-1] if url.endswith('/') else url
            tgt = basename(url)
            if not name:
                name = tgt
            self.get(url, tgt, *args)
        super(Darcs, self).__init__(name)
        if url:
            self.cfg['vc_url'] = url
            self.cfg['hackage_name'] = tgt
            self.cfg['source'] = '.'
            with pwd(self.branch_dir):
                self.get(self.dir, 'orig')
            self.set_current_src()
            

    def get(self, src, tgt, *args):
        '''sets up a branch given arguments, taken from a source with a target

        the idioms of branching in different VCSes vary, and a common
        api for this in devshell has not yet been realized

        currently, this creates a new temporary local branch in darcs
        and sets the source to it
        '''
        with log_file('darcs.log') as darcs_out:
            # NB: args is a tuple, must be converted
            p = Popen(['darcs', 'get'] + list(args) + [src, tgt],
                      stdout = darcs_out, stderr = darcs_out)
            log.info('darcs get %s %s, please wait....' % (src, tgt))
            p.communicate()

#     def checkout(self, tgt, url, *args):
#         '''checks out source from an upstream url

#         the difference between this and get is that it reflects an initial pull
#         idiom found in other VCSes. It also handles setting vc_url and the
#         canonical name, based on tgt
#         '''
#         self.cfg['vc_url'] = url
#         self.get(url, tgt, *args)

    @property
    def vc_url(self):
        '''the url where the source was fetched from originally

        this may be moved to revision control in the future
        '''
        return self.cfg['vc_url']

    @property
    def hackage_name(self):
        '''assuming this package is a haskell package, what is the canonical name

        this name may be changed in the future
        '''
        return self.cfg['hackage_name']

    @contextmanager
    def src_dir(self, *args):
        '''executes a code block inside a specific branch and or checkout
        '''
        with src(*args):
            with pwd(self.cfg['source']):
                yield

    @contextmanager
    def src(self, *args):
        '''executes a code block with a particular branch or checkout

        if there are no args, this block is executed in the raw
        '''
        if args:
            old_src = self.cfg['source']
            self.set_cur_to(*args)
        yield
        if args:
            rm(self.cfg['source'])
            self.cfg['source'] = old_src
            self.set_current_src()


    def set_current_src(self):
        '''sets the current internal state to reflect the current head

        chances are, the user should rarely mess with this.

        this may change, because rather than using .patch files for rpm
        handling, we may ask the user to commit all the changes to darcs,
        and then have devshell generate .patch files automatically instead
        '''
        with pwd(self.cfg['source']):
            p = Popen(['darcs', 'changes', '--xml-output', '--last=1'],
                      stdout = PIPE, stderr = PIPE)
            change = p.communicate()[0]
            hash = hash_re.search(change).groups()[0]
            date = date_re.search(change).groups()[0]
            self.cfg['head'] = (hash, date)

    def set_cur_to(self, *args):
        '''passes arbitrary args to darcs get and makes a branch out of it

        this is not really optimal, because it only does things temporary,
        we need to look at as systematic way to handle branching.
        looking at a potential git and a potential cvs module may help
        '''
        if len(args) >= 2 and args[0] == 'new':
            branch_name = args[1]
            args = args[2:]
        else: 
            branch_name = ''
        if len(args) == 1 and args[0] == 'head':
            log.debug('doing head')
            self.cfg['source'] = '.'
        elif len(args) == 2 and args[0] == 'branch':
            self.cfg['source'] = join(self.branch, args[1])
        else:
            log.debug('creating new branch')
            index = str(hash(args))
            index_dir = join(self.branch, index)
            with pwd(self.dir):
                if not exists(index_dir):
                    log.debug('index dir should be ' + index_dir)
                    self.get(self.source(), index_dir, *args)
                self.cfg['source'] = index_dir
            if branch_name:
                with pwd(self.branch_dir):
                    self.get(index, branch_name)
        self.set_current_src()

    def set_cur_to_patch(self, hash):
        '''sets the current branch to fork off a particular hash'''
        self.set_cur_to('--to-match', 'hash ' + hash)

    def set_cur_to_tag(self, tag):
        '''sets the current branch to fork off a particular tag'''
        self.set_cur_to('--tag', tag)

    @property
    def date(self):
        '''get's the timestamp of the latest patch on HEAD of the current branch'''
        return self.cfg['head'][1]

    @property
    def hash(self):
        '''gets the hash of the latest patch on HEAD of the current branch'''
        return self.cfg['head'][0]

    def print_date(self):
        '''prints the timestamp of the latest patch on HEAD of the current branch'''
        log.info('The timestamp is ' + self.date)

    def print_hash(self):
        '''prints the hash of the latest patch on HEAD of the current branch'''
        log.info('The hash is ' + self.hash)

    @contextmanager
    def patch(self, hash):
        '''executes a block of code on top a particular patch'''
        with self.src('--to-match', 'hash ' + hash):
            yield

    @contextmanager
    def tag(self, tag):
        '''executes a particular block of code on top of a particular tag'''
        with self.src('--tag', tag):
            yield


    def setup_sourceball(self, ver, delete_old=False):
        '''gets darcs to spit out a sourceball to be used in rpm making by other modules'''
        log.debug('someone set us up the bomb')
        if delete_old:
            with pwd(self.pkg_src_dir):
                rm(self.sourceball)
        name = self.hackage_name
        date = self.date
        full_name = '%s-%s.%sdarcs' % (name, ver, date)
        log.debug('full name is ' + full_name)
        sourceball = full_name + '.tar.gz'
        with pwd(self.source_dir):
            with log_file('darcs.log') as darcs_out:
                log.debug('we get signal')
                p = Popen(['darcs', 'dist', '-d', full_name],
                          stdout=darcs_out, stderr=darcs_out)
                log.info('generating tarball %s.tar.gz, please wait...'
                         % full_name)
                p.communicate()
        with pwd(self.pkg_src_dir):
            move(join(self.source_dir, sourceball), sourceball)
            self.cfg['sourceball'] = sourceball

__all__ = ['Darcs']