1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import re
20 import time
21 import os
22
23 from twisted.python import log
24 from twisted.internet import defer, utils
25
26 from buildbot import util
27 from buildbot.changes import base
30 """Something went wrong with the poll. This is used as a distinctive
31 exception type so that unit tests can detect and ignore it."""
32
34 """Splits the branchfile argument and assuming branch is
35 the first path component in branchfile, will return
36 branch and file else None."""
37
38 index = branchfile.find('/')
39 if index == -1: return None, None
40 branch, file = branchfile.split('/', 1)
41 return branch, file
42
43 -class P4Source(base.PollingChangeSource, util.ComparableMixin):
44 """This source will poll a perforce repository for changes and submit
45 them to the change master."""
46
47 compare_attrs = ["p4port", "p4user", "p4passwd", "p4base",
48 "p4bin", "pollInterval"]
49
50 env_vars = ["P4CLIENT", "P4PORT", "P4PASSWD", "P4USER",
51 "P4CHARSET"]
52
53 changes_line_re = re.compile(
54 r"Change (?P<num>\d+) on \S+ by \S+@\S+ '.*'$")
55 describe_header_re = re.compile(
56 r"Change \d+ by (?P<who>\S+)@\S+ on (?P<when>.+)$")
57 file_re = re.compile(r"^\.\.\. (?P<path>[^#]+)#\d+ [/\w]+$")
58 datefmt = '%Y/%m/%d %H:%M:%S'
59
60 parent = None
61 last_change = None
62 loop = None
63
64 - def __init__(self, p4port=None, p4user=None, p4passwd=None,
65 p4base='//', p4bin='p4',
66 split_file=lambda branchfile: (None, branchfile),
67 pollInterval=60 * 10, histmax=None, pollinterval=-2):
79
81 return "p4source %s %s" % (self.p4port, self.p4base)
82
84 d = self._poll()
85 d.addErrback(log.err, 'P4 poll failed')
86 return d
87
92
93 @defer.deferredGenerator
95 args = []
96 if self.p4port:
97 args.extend(['-p', self.p4port])
98 if self.p4user:
99 args.extend(['-u', self.p4user])
100 if self.p4passwd:
101 args.extend(['-P', self.p4passwd])
102 args.extend(['changes'])
103 if self.last_change is not None:
104 args.extend(['%s...@%d,now' % (self.p4base, self.last_change+1)])
105 else:
106 args.extend(['-m', '1', '%s...' % (self.p4base,)])
107
108 wfd = defer.waitForDeferred(self._get_process_output(args))
109 yield wfd
110 result = wfd.getResult()
111
112 last_change = self.last_change
113 changelists = []
114 for line in result.split('\n'):
115 line = line.strip()
116 if not line: continue
117 m = self.changes_line_re.match(line)
118 if not m:
119 raise P4PollerError("Unexpected 'p4 changes' output: %r" % result)
120 num = int(m.group('num'))
121 if last_change is None:
122
123
124 log.msg('P4Poller: starting at change %d' % num)
125 self.last_change = num
126 return
127 changelists.append(num)
128 changelists.reverse()
129
130
131 for num in changelists:
132 args = []
133 if self.p4port:
134 args.extend(['-p', self.p4port])
135 if self.p4user:
136 args.extend(['-u', self.p4user])
137 if self.p4passwd:
138 args.extend(['-P', self.p4passwd])
139 args.extend(['describe', '-s', str(num)])
140 wfd = defer.waitForDeferred(self._get_process_output(args))
141 yield wfd
142 result = wfd.getResult()
143
144 lines = result.split('\n')
145
146
147 lines[0] = lines[0].rstrip()
148 m = self.describe_header_re.match(lines[0])
149 if not m:
150 raise P4PollerError("Unexpected 'p4 describe -s' result: %r" % result)
151 who = m.group('who')
152 when = time.mktime(time.strptime(m.group('when'), self.datefmt))
153 comments = ''
154 while not lines[0].startswith('Affected files'):
155 comments += lines.pop(0) + '\n'
156 lines.pop(0)
157
158 branch_files = {}
159 while lines:
160 line = lines.pop(0).strip()
161 if not line: continue
162 m = self.file_re.match(line)
163 if not m:
164 raise P4PollerError("Invalid file line: %r" % line)
165 path = m.group('path')
166 if path.startswith(self.p4base):
167 branch, file = self.split_file(path[len(self.p4base):])
168 if (branch == None and file == None): continue
169 if branch_files.has_key(branch):
170 branch_files[branch].append(file)
171 else:
172 branch_files[branch] = [file]
173
174 for branch in branch_files:
175 d = self.master.addChange(
176 author=who,
177 files=branch_files[branch],
178 comments=comments,
179 revision=str(num),
180 when_timestamp=util.epoch2datetime(when),
181 branch=branch)
182 wfd = defer.waitForDeferred(d)
183 yield wfd
184 wfd.getResult()
185
186 self.last_change = num
187