Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: build/util/lastchange.py

Issue 7493073: Unify the version string to be displayed on "About Chromium" dialog. (5/6) (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Remove 'r' from regular expressions Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | webkit/build/webkit_version.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """ 6 """
7 lastchange.py -- Chromium revision fetching utility. 7 lastchange.py -- Chromium revision fetching utility.
8 """ 8 """
9 9
10 import re 10 import re
11 import optparse 11 import optparse
12 import os 12 import os
13 import subprocess 13 import subprocess
14 import sys 14 import sys
15 15
16 _GIT_SVN_ID_REGEX = re.compile(r'.*git-svn-id:\s*([^@]*)@([0-9]+)', re.DOTALL)
17
16 class VersionInfo(object): 18 class VersionInfo(object):
17 def __init__(self, url, root, revision): 19 def __init__(self, url, revision):
18 self.url = url 20 self.url = url
19 self.root = root
20 self.revision = revision 21 self.revision = revision
21 22
22 23
23 def FetchSVNRevision(directory): 24 def FetchSVNRevision(directory, svn_url_regex):
24 """ 25 """
25 Fetch the Subversion branch and revision for a given directory. 26 Fetch the Subversion branch and revision for a given directory.
26 27
27 Errors are swallowed. 28 Errors are swallowed.
28 29
29 Returns: 30 Returns:
30 a VersionInfo object or None on error. 31 A VersionInfo object or None on error.
31 """ 32 """
32 try: 33 try:
33 proc = subprocess.Popen(['svn', 'info'], 34 proc = subprocess.Popen(['svn', 'info'],
34 stdout=subprocess.PIPE, 35 stdout=subprocess.PIPE,
35 stderr=subprocess.PIPE, 36 stderr=subprocess.PIPE,
36 cwd=directory, 37 cwd=directory,
37 shell=(sys.platform=='win32')) 38 shell=(sys.platform=='win32'))
38 except OSError: 39 except OSError:
39 # command is apparently either not installed or not executable. 40 # command is apparently either not installed or not executable.
40 return None 41 return None
41 if not proc: 42 if not proc:
42 return None 43 return None
43 44
44 attrs = {} 45 attrs = {}
45 for line in proc.stdout: 46 for line in proc.stdout:
46 line = line.strip() 47 line = line.strip()
47 if not line: 48 if not line:
48 continue 49 continue
49 key, val = line.split(': ', 1) 50 key, val = line.split(': ', 1)
50 attrs[key] = val 51 attrs[key] = val
51 52
52 try: 53 try:
53 url = attrs['URL'] 54 match = svn_url_regex.search(attrs['URL'])
54 root = attrs['Repository Root'] 55 if match:
56 url = match.group(2)
57 else:
58 url = ''
55 revision = attrs['Revision'] 59 revision = attrs['Revision']
56 except KeyError: 60 except KeyError:
57 return None 61 return None
58 62
59 return VersionInfo(url, root, revision) 63 return VersionInfo(url, revision)
60 64
61 65
62 def RunGitCommand(directory, command): 66 def RunGitCommand(directory, command):
63 """ 67 """
64 Launches git subcommand. 68 Launches git subcommand.
65 69
66 Errors are swallowed. 70 Errors are swallowed.
67 71
68 Returns: 72 Returns:
69 process object or None. 73 A process object or None.
70 """ 74 """
71 command = ['git'] + command 75 command = ['git'] + command
72 # Force shell usage under cygwin & win32. This is a workaround for 76 # Force shell usage under cygwin & win32. This is a workaround for
73 # mysterious loss of cwd while invoking cygwin's git. 77 # mysterious loss of cwd while invoking cygwin's git.
74 # We can't just pass shell=True to Popen, as under win32 this will 78 # We can't just pass shell=True to Popen, as under win32 this will
75 # cause CMD to be used, while we explicitly want a cygwin shell. 79 # cause CMD to be used, while we explicitly want a cygwin shell.
76 if sys.platform in ('cygwin', 'win32'): 80 if sys.platform in ('cygwin', 'win32'):
77 command = ['sh', '-c', ' '.join(command)] 81 command = ['sh', '-c', ' '.join(command)]
78 try: 82 try:
79 proc = subprocess.Popen(command, 83 proc = subprocess.Popen(command,
80 stdout=subprocess.PIPE, 84 stdout=subprocess.PIPE,
81 stderr=subprocess.PIPE, 85 stderr=subprocess.PIPE,
82 cwd=directory) 86 cwd=directory)
83 return proc 87 return proc
84 except OSError: 88 except OSError:
85 return None 89 return None
86 90
87 91
88 def FetchGitRevision(directory): 92 def FetchGitRevision(directory):
89 """ 93 """
90 Fetch the Git hash for a given directory. 94 Fetch the Git hash for a given directory.
91 95
92 Errors are swallowed. 96 Errors are swallowed.
93 97
94 Returns: 98 Returns:
95 a VersionInfo object or None on error. 99 A VersionInfo object or None on error.
96 """ 100 """
97 proc = RunGitCommand(directory, ['rev-parse', 'HEAD']) 101 proc = RunGitCommand(directory, ['rev-parse', 'HEAD'])
98 if proc: 102 if proc:
99 output = proc.communicate()[0].strip() 103 output = proc.communicate()[0].strip()
100 if proc.returncode == 0 and output: 104 if proc.returncode == 0 and output:
101 return VersionInfo('git', 'git', output[:7]) 105 return VersionInfo('git', output[:7])
102 return None 106 return None
103 107
104 108
105 def IsGitSVN(directory): 109 def FetchGitSVNURLAndRevision(directory, svn_url_regex):
106 """ 110 """
107 Checks whether git-svn has been set up. 111 Fetch the Subversion URL and revision through Git.
108 112
109 Errors are swallowed. 113 Errors are swallowed.
110 114
111 Returns: 115 Returns:
112 whether git-svn has been set up. 116 A tuple containing the Subversion URL and revision.
113 """ 117 """
114 # To test whether git-svn has been set up, query the config for any 118 proc = RunGitCommand(directory, ['log', '-1',
115 # svn-related configuration. This command exits with an error code 119 '--grep=git-svn-id', '--format=%b'])
116 # if there aren't any matches, so ignore its output.
117 proc = RunGitCommand(directory, ['config', '--get-regexp', '^svn'])
118 if proc: 120 if proc:
119 return (proc.wait() == 0) 121 output = proc.communicate()[0].strip()
120 return False 122 if proc.returncode == 0 and output:
121 123 # Extract the latest SVN revision and the SVN URL.
122 124 # The target line is the last "git-svn-id: ..." line like this:
123 def FetchGitSVNURL(directory): 125 # git-svn-id: svn://svn.chromium.org/chrome/trunk/src@85528 0039d316....
124 """ 126 match = _GIT_SVN_ID_REGEX.search(output)
125 Fetch URL of SVN repository bound to git.
126
127 Errors are swallowed.
128
129 Returns:
130 SVN URL.
131 """
132 if IsGitSVN(directory):
133 proc = RunGitCommand(directory, ['svn', 'info', '--url'])
134 if proc:
135 output = proc.communicate()[0].strip()
136 if proc.returncode == 0:
137 match = re.search(r'^\w+://.*$', output, re.M)
138 if match:
139 return match.group(0)
140 return ''
141
142
143 def FetchGitSVNRoot(directory):
144 """
145 Fetch root of SVN repository bound to git.
146
147 Errors are swallowed.
148
149 Returns:
150 SVN root repository.
151 """
152 if IsGitSVN(directory):
153 git_command = ['config', '--get-regexp', '^svn-remote.svn.url$']
154 proc = RunGitCommand(directory, git_command)
155 if proc:
156 output = proc.communicate()[0].strip()
157 if proc.returncode == 0:
158 # Zero return code implies presence of requested configuration variable.
159 # Its value is second (last) field of output.
160 match = re.search(r'\S+$', output)
161 if match:
162 return match.group(0)
163 return ''
164
165
166 def LookupGitSVNRevision(directory, depth):
167 """
168 Fetch the Git-SVN identifier for the local tree.
169 Parses first |depth| commit messages.
170
171 Errors are swallowed.
172 """
173 if not IsGitSVN(directory):
174 return None
175 git_re = re.compile(r'^\s*git-svn-id:\s+(\S+)@(\d+)')
176 proc = RunGitCommand(directory, ['log', '-' + str(depth)])
177 if proc:
178 for line in proc.stdout:
179 match = git_re.match(line)
180 if match: 127 if match:
181 id = match.group(2) 128 revision = match.group(2)
182 if id: 129 url_match = svn_url_regex.search(match.group(1))
183 proc.stdout.close() # Cut pipe for fast exit. 130 if url_match:
184 return id 131 url = url_match.group(2)
185 return None 132 else:
133 url = ''
134 return url, revision
135 return None, None
186 136
187 137
188 def IsGitSVNDirty(directory): 138 def IsGitSVNDirty(directory):
189 """ 139 """
190 Checks whether our git-svn tree contains clean trunk or some branch. 140 Checks whether our git-svn tree contains clean trunk or any local changes.
191 141
192 Errors are swallowed. 142 Errors are swallowed.
193 """ 143 """
194 # For git branches the last commit message is either 144 proc = RunGitCommand(directory, ['log', '-1'])
195 # some local commit or a merge. 145 if proc:
196 return LookupGitSVNRevision(directory, 1) is None 146 output = proc.communicate()[0].strip()
147 if proc.returncode == 0 and output:
148 # Extract the latest SVN revision and the SVN URL.
149 # The target line is the last "git-svn-id: ..." line like this:
150 # git-svn-id: svn://svn.chromium.org/chrome/trunk/src@85528 0039d316....
151 match = _GIT_SVN_ID_REGEX.search(output)
152 if match:
153 # Check if there are any local uncommitted changes.
154 proc = RunGitCommand(directory, ['checkout'])
155 if proc:
156 output = proc.communicate()[0].strip()
157 if proc.returncode == 0 and not output:
158 return False
159 return True
197 160
198 161
199 def FetchGitSVNRevision(directory): 162 def FetchGitSVNRevision(directory, svn_url_regex):
200 """ 163 """
201 Fetch the Git-SVN identifier for the local tree. 164 Fetch the Git-SVN identifier for the local tree.
202 165
203 Errors are swallowed. 166 Errors are swallowed.
204 """ 167 """
205 # We assume that at least first 999 commit messages contain svn evidence. 168 url, revision = FetchGitSVNURLAndRevision(directory, svn_url_regex)
206 revision = LookupGitSVNRevision(directory, 999) 169 if url and revision:
207 if not revision: 170 if IsGitSVNDirty(directory):
208 return None 171 revision = revision + '-dirty'
209 if IsGitSVNDirty(directory): 172 return VersionInfo(url, revision)
210 revision = revision + '-dirty' 173 return None
211 url = FetchGitSVNURL(directory)
212 root = FetchGitSVNRoot(directory)
213 return VersionInfo(url, root, revision)
214 174
215 175
216 def FetchVersionInfo(default_lastchange, directory=None): 176 def FetchVersionInfo(default_lastchange, directory=None,
177 directory_regex_prior_to_src_url='chrome|svn'):
217 """ 178 """
218 Returns the last change (in the form of a branch, revision tuple), 179 Returns the last change (in the form of a branch, revision tuple),
219 from some appropriate revision control system. 180 from some appropriate revision control system.
220 """ 181 """
221 version_info = (FetchSVNRevision(directory) or 182 svn_url_regex = re.compile(
222 FetchGitSVNRevision(directory) or FetchGitRevision(directory)) 183 r'.*/(' + directory_regex_prior_to_src_url + r')(/.*)')
184
185 version_info = (FetchSVNRevision(directory, svn_url_regex) or
186 FetchGitSVNRevision(directory, svn_url_regex) or
187 FetchGitRevision(directory))
223 if not version_info: 188 if not version_info:
224 if default_lastchange and os.path.exists(default_lastchange): 189 if default_lastchange and os.path.exists(default_lastchange):
225 revision = open(default_lastchange, 'r').read().strip() 190 revision = open(default_lastchange, 'r').read().strip()
226 version_info = VersionInfo(None, None, revision) 191 version_info = VersionInfo(None, revision)
227 else: 192 else:
228 version_info = VersionInfo('unknown', '', '0') 193 version_info = VersionInfo(None, None)
229 return version_info 194 return version_info
230 195
231 196
232 def WriteIfChanged(file_name, contents): 197 def WriteIfChanged(file_name, contents):
233 """ 198 """
234 Writes the specified contents to the specified file_name 199 Writes the specified contents to the specified file_name
235 iff the contents are different than the current contents. 200 iff the contents are different than the current contents.
236 """ 201 """
237 try: 202 try:
238 old_contents = open(file_name, 'r').read() 203 old_contents = open(file_name, 'r').read()
(...skipping 24 matching lines...) Expand all
263 while len(args) and out_file is None: 228 while len(args) and out_file is None:
264 if out_file is None: 229 if out_file is None:
265 out_file = args.pop(0) 230 out_file = args.pop(0)
266 if args: 231 if args:
267 sys.stderr.write('Unexpected arguments: %r\n\n' % args) 232 sys.stderr.write('Unexpected arguments: %r\n\n' % args)
268 parser.print_help() 233 parser.print_help()
269 sys.exit(2) 234 sys.exit(2)
270 235
271 version_info = FetchVersionInfo(opts.default_lastchange) 236 version_info = FetchVersionInfo(opts.default_lastchange)
272 237
238 if version_info.revision == None:
239 version_info.revision = '0'
240
273 if opts.revision_only: 241 if opts.revision_only:
274 print version_info.revision 242 print version_info.revision
275 else: 243 else:
276 contents = "LASTCHANGE=%s\n" % version_info.revision 244 contents = "LASTCHANGE=%s\n" % version_info.revision
277 if out_file: 245 if out_file:
278 WriteIfChanged(out_file, contents) 246 WriteIfChanged(out_file, contents)
279 else: 247 else:
280 sys.stdout.write(contents) 248 sys.stdout.write(contents)
281 249
282 return 0 250 return 0
283 251
284 252
285 if __name__ == '__main__': 253 if __name__ == '__main__':
286 sys.exit(main()) 254 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | webkit/build/webkit_version.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698