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

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

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

Powered by Google App Engine
This is Rietveld 408576698