OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
hayato
2017/05/09 06:31:45
Is this a new file written from scratch?
If we hav
| |
2 | |
3 # Copyright 2017 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 import argparse | |
8 import os | |
9 import shutil | |
10 import stat | |
11 import subprocess | |
12 import sys | |
13 import tempfile | |
14 | |
15 # How to patch libxslt in Chromium: | |
16 # | |
17 # 1. Write a .patch file and add it to third_party/libxslt/chromium. | |
18 # 2. Apply the patch in src: patch -p1 <../chromium/foo.patch | |
19 # 3. Add the patch to the list of patches in this file. | |
20 # 4. Update README.chromium with the provenance of the patch. | |
21 # 5. Upload a change with the modified documentation, roll script, | |
22 # patch, applied patch and any other relevant changes like | |
23 # regression tests. Go through the usual review and commit process. | |
24 # | |
25 # How to roll libxslt in Chromium: | |
26 # | |
27 # Prerequisites: | |
28 # | |
29 # 1. Check out Chromium somewhere on Linux, Mac and Windows. | |
30 # 2. On each machine, add the experimental remote named 'wip': | |
31 # git remote add -f wip \ | |
32 # https://chromium.googlesource.com/experimental/chromium/src | |
33 # 3. On Linux: | |
34 # a. sudo apt-get install libicu-dev | |
35 # b. git clone git://git.gnome.org/libxslt somewhere | |
36 # 4. On Mac, install these MacPorts: | |
37 # autoconf automake libtool pkgconfig icu | |
38 # | |
39 # Procedure: | |
40 # | |
41 # Warning: This process is destructive. Run it on a clean branch. | |
42 # | |
43 # 1. On Linux, in the libxslt repo directory: | |
44 # a. git remote update origin | |
45 # b. git checkout origin/master | |
46 # | |
47 # This will be the upstream version of libxslt you are rolling to. | |
48 # | |
49 # 2. On Linux, in the Chromium src director: | |
50 # a. third_party/libxslt/chromium/roll.py --linux /path/to/libxslt | |
51 # | |
52 # If this fails, it may be a patch no longer applies. Reset to | |
53 # head; modify the patch files, this script, and | |
54 # README.chromium; then commit the result and run it again. | |
55 # | |
56 # b. git push -f wip HEAD:refs/wip/$USER/roll_libxslt | |
57 # | |
58 # 2. On Windows, in the Chromium src directory: | |
59 # a. git fetch wip refs/wip/$USER/roll_libxslt | |
60 # b. git checkout FETCH_HEAD | |
61 # c. third_party\libxslt\chromium\roll.py --win32 | |
62 # d. git push -f wip HEAD:refs/wip/$USER/roll_libxslt | |
63 # | |
64 # 3. On Mac, in the Chromium src directory: | |
65 # a. git fetch wip refs/wip/$USER/roll_libxslt | |
66 # b. git checkout -b roll_libxslt_nnnn FETCH_HEAD | |
67 # c. git branch --set-upstream-to origin/master | |
68 # d. third_party/libxslt/chromium/roll.py --mac | |
69 # e. Make and commit any final changes to README.chromium, BUILD.gn, etc. | |
70 # f. Complete the code review process as usual: git cl upload -d; | |
71 # git cl try-results; etc. | |
72 | |
73 PATCHES = [ | |
74 'get-file-attributes-a.patch', | |
75 ] | |
76 | |
77 | |
78 # See libxslt configure.ac and win32/configure.js to learn what | |
79 # options are available. | |
80 | |
81 # These two sets of options should be in sync. You can check the | |
82 # generated #defines in (win32|mac|linux)/config.h to confirm | |
83 # this. | |
84 SHARED_XSLT_CONFIGURE_OPTIONS = [ | |
85 # These options are turned OFF | |
86 ('--without-debug', 'xslt_debug=no'), | |
87 ('--without-debugger', 'debugger=no'), | |
88 ('--without-mem-debug', 'mem_debug=no'), | |
89 ('--without-plugins', 'modules=no'), | |
90 ] | |
91 | |
92 # These options are only available in configure.ac for Linux and Mac. | |
93 EXTRA_NIX_XSLT_CONFIGURE_OPTIONS = [ | |
94 ] | |
95 | |
96 | |
97 # These options are only available in win32/configure.js for Windows. | |
98 EXTRA_WIN32_XSLT_CONFIGURE_OPTIONS = [ | |
99 'compiler=msvc', | |
100 'iconv=no', | |
101 ] | |
102 | |
103 | |
104 XSLT_CONFIGURE_OPTIONS = ( | |
105 [option[0] for option in SHARED_XSLT_CONFIGURE_OPTIONS] + | |
106 EXTRA_NIX_XSLT_CONFIGURE_OPTIONS) | |
107 | |
108 | |
109 XSLT_WIN32_CONFIGURE_OPTIONS = ( | |
110 [option[1] for option in SHARED_XSLT_CONFIGURE_OPTIONS] + | |
111 EXTRA_WIN32_XSLT_CONFIGURE_OPTIONS) | |
112 | |
113 | |
114 FILES_TO_REMOVE = [ | |
115 # TODO: Excluding ChangeLog and NEWS because encoding problems mean | |
116 # bots can't patch these. Reinclude them when there is a consistent | |
117 # encoding. | |
118 'src/NEWS', | |
119 'src/ChangeLog', | |
120 # These have shebang but not executable bit; presubmit will barf on them. | |
121 'src/autogen.sh', | |
122 'src/ltmain.sh', | |
123 'src/xslt-config.in', | |
124 # These are not needed. | |
125 'src/doc', | |
126 'src/python', | |
127 'src/tests', | |
128 'src/xsltproc', | |
129 'src/examples', | |
130 'src/vms', | |
131 ] | |
132 | |
133 | |
134 THIRD_PARTY_LIBXML_LINUX = 'third_party/libxml/linux' | |
135 THIRD_PARTY_LIBXSLT = 'third_party/libxslt' | |
136 THIRD_PARTY_LIBXSLT_SRC = os.path.join(THIRD_PARTY_LIBXSLT, 'src') | |
137 | |
138 | |
139 def libxml_path_option(src_path): | |
140 '''Gets the path to libxml/linux in Chromium. | |
141 | |
142 libxslt needs to be configured with libxml source. | |
143 | |
144 Args: | |
145 src_path: The Chromium src path. | |
146 | |
147 Returns: | |
148 The path to the libxml2 third_party/libxml/linux configure | |
149 output. | |
150 ''' | |
151 libxml_linux_path = os.path.join(src_path, THIRD_PARTY_LIBXML_LINUX) | |
152 return ['--with-libxml-src=%s' % libxml_linux_path] | |
153 | |
154 | |
155 class WorkingDir(object): | |
156 """"Changes the working directory and resets it on exit.""" | |
hayato
2017/05/09 06:52:46
Nit: """, instead of """".
| |
157 def __init__(self, path): | |
158 self.prev_path = os.getcwd() | |
159 self.path = path | |
160 | |
161 def __enter__(self): | |
162 os.chdir(self.path) | |
163 | |
164 def __exit__(self, exc_type, exc_value, traceback): | |
165 if exc_value: | |
166 print('was in %s; %s before that' % (self.path, self.prev_path)) | |
167 os.chdir(self.prev_path) | |
168 | |
169 | |
170 def git(*args): | |
171 """Runs a git subcommand. | |
172 | |
173 On Windows this uses the shell because there's a git wrapper | |
174 batch file in depot_tools. | |
175 | |
176 Arguments: | |
177 args: The arguments to pass to git. | |
178 """ | |
179 command = ['git'] + list(args) | |
180 subprocess.check_call(command, shell=(os.name == 'nt')) | |
181 | |
182 | |
183 def remove_tracked_and_local_dir(path): | |
184 """Removes the contents of a directory from git, and the filesystem. | |
185 | |
186 Arguments: | |
187 path: The path to remove. | |
188 """ | |
189 remove_tracked_files([path]) | |
190 shutil.rmtree(path, ignore_errors=True) | |
191 os.mkdir(path) | |
192 | |
193 | |
194 def remove_tracked_files(files_to_remove): | |
195 """Removes tracked files from git. | |
196 | |
197 Arguments: | |
198 files_to_remove: The files to remove. | |
199 """ | |
200 files_to_remove = [f for f in files_to_remove if os.path.exists(f)] | |
201 git('rm', '-rf', *files_to_remove) | |
202 | |
203 | |
204 def sed_in_place(input_filename, program): | |
205 """Replaces text in a file. | |
206 | |
207 Arguments: | |
208 input_filename: The file to edit. | |
209 program: The sed program to perform edits on the file. | |
210 """ | |
211 # OS X's sed requires -e | |
212 subprocess.check_call(['sed', '-i', '-e', program, input_filename]) | |
213 | |
214 | |
215 def check_copying(path='.'): | |
216 path = os.path.join(path, 'COPYING') | |
217 if not os.path.exists(path): | |
218 return | |
219 with open(path) as f: | |
220 s = f.read() | |
221 if 'GNU' in s: | |
222 raise Exception('check COPYING') | |
223 | |
224 | |
225 def patch_config(): | |
226 '''Changes autoconf results which can not be changed with options.''' | |
227 sed_in_place('config.h', 's/#define HAVE_CLOCK_GETTIME 1//') | |
228 | |
229 # https://crbug.com/670720 | |
230 sed_in_place('config.h', 's/#define HAVE_ASCTIME 1//') | |
231 sed_in_place('config.h', 's/#define HAVE_LOCALTIME 1//') | |
232 sed_in_place('config.h', 's/#define HAVE_MKTIME 1//') | |
233 | |
234 sed_in_place('config.log', | |
235 's/[a-z.0-9]\+\.corp\.google\.com/REDACTED/') | |
236 | |
237 | |
238 def prepare_libxslt_distribution(src_path, libxslt_repo_path, temp_dir): | |
239 """Makes a libxslt distribution. | |
240 | |
241 Args: | |
242 src_path: The Chromium repository src path, for finding libxslt. | |
243 libxslt_repo_path: The path to the local clone of the libxslt repo. | |
244 temp_dir: A temporary directory to stage the distribution to. | |
245 | |
246 Returns: A tuple of commit hash and full path to the archive. | |
247 """ | |
248 # If it was necessary to push from a distribution prepared upstream, | |
249 # this is the point to inject it: Return the version string and the | |
250 # distribution tar file. | |
251 | |
252 # The libxslt repo we're pulling changes from should not have | |
253 # local changes. This *should* be a commit that's publicly visible | |
254 # in the upstream repo; reviewers should check this. | |
255 check_clean(libxslt_repo_path) | |
256 | |
257 temp_config_path = os.path.join(temp_dir, 'config') | |
258 os.mkdir(temp_config_path) | |
259 temp_src_path = os.path.join(temp_dir, 'src') | |
260 os.mkdir(temp_src_path) | |
261 | |
262 with WorkingDir(libxslt_repo_path): | |
263 commit = subprocess.check_output( | |
264 ['git', 'log', '-n', '1', '--pretty=format:%H', 'HEAD']) | |
265 subprocess.check_call( | |
266 'git archive HEAD | tar -x -C "%s"' % temp_src_path, | |
267 shell=True) | |
268 with WorkingDir(temp_src_path): | |
269 os.remove('.gitignore') | |
270 with WorkingDir(temp_config_path): | |
271 subprocess.check_call(['../src/autogen.sh'] + XSLT_CONFIGURE_OPTIONS + | |
272 libxml_path_option(src_path)) | |
273 subprocess.check_call(['make', 'dist-all']) | |
274 | |
275 # Work out what it is called | |
276 tar_file = subprocess.check_output( | |
277 '''awk '/PACKAGE =/ {p=$3} /VERSION =/ {v=$3} ''' | |
278 '''END {printf("%s-%s.tar.gz", p, v)}' Makefile''', | |
279 shell=True) | |
280 return commit, os.path.abspath(tar_file) | |
281 | |
282 | |
283 def roll_libxslt_linux(src_path, repo_path): | |
284 check_clean(src_path) | |
285 with WorkingDir(src_path): | |
286 try: | |
287 temp_dir = tempfile.mkdtemp() | |
288 print('temporary directory is: %s' % temp_dir) | |
289 commit, tar_file = prepare_libxslt_distribution( | |
290 src_path, repo_path, temp_dir) | |
291 | |
292 # Remove all of the old libxslt to ensure only desired | |
293 # cruft accumulates | |
294 remove_tracked_and_local_dir(THIRD_PARTY_LIBXSLT_SRC) | |
295 | |
296 # Export the libxslt distribution to the Chromium tree | |
297 with WorkingDir(THIRD_PARTY_LIBXSLT_SRC): | |
298 subprocess.check_call( | |
299 'tar xzf %s --strip-components=1' % tar_file, | |
300 shell=True) | |
301 finally: | |
302 shutil.rmtree(temp_dir) | |
303 | |
304 with WorkingDir(THIRD_PARTY_LIBXSLT_SRC): | |
305 # Write the commit ID into the README.chromium file | |
306 sed_in_place('../README.chromium', | |
307 's/Version: .*$/Version: %s/' % commit) | |
308 check_copying() | |
309 | |
310 for patch in PATCHES: | |
311 subprocess.check_call( | |
312 'cat ../chromium/%s | patch -p1 --fuzz=0' % patch, | |
313 shell=True) | |
314 | |
315 with WorkingDir('../linux'): | |
316 subprocess.check_call(['../src/configure'] + | |
317 XSLT_CONFIGURE_OPTIONS + | |
318 libxml_path_option(src_path)) | |
319 check_copying() | |
320 patch_config() | |
321 # Other platforms share this, even though it is | |
322 # generated on Linux. Android and Windows do not have | |
323 # xlocale. | |
324 sed_in_place('libxslt/xsltconfig.h', | |
325 '/Locale support/,/#if 1/s/#if 1/#if 0/') | |
326 shutil.move('libxslt/xsltconfig.h', '../src/libxslt') | |
327 | |
328 git('add', '*') | |
329 git('commit', '-am', '%s libxslt, linux' % commit) | |
330 | |
331 print('Now push to Windows and runs steps there.') | |
332 | |
333 | |
334 def roll_libxslt_win32(src_path): | |
335 full_path_to_libxslt = os.path.join(src_path, THIRD_PARTY_LIBXSLT) | |
336 with WorkingDir(full_path_to_libxslt): | |
337 with WorkingDir('src/win32'): | |
338 # Run the configure script. | |
339 subprocess.check_call(['cscript', '//E:jscript', 'configure.js'] + | |
340 XSLT_WIN32_CONFIGURE_OPTIONS) | |
341 shutil.copy('src/config.h', 'win32/config.h') | |
342 git('add', 'win32/config.h') | |
343 git('commit', '--allow-empty', '-m', 'Windows') | |
344 print('Now push to Mac and run steps there.') | |
345 | |
346 | |
347 def roll_libxslt_mac(src_path): | |
348 full_path_to_libxslt = os.path.join(src_path, THIRD_PARTY_LIBXSLT) | |
349 with WorkingDir(full_path_to_libxslt): | |
350 with WorkingDir('mac'): | |
351 subprocess.check_call(['autoreconf', '-i', '../src']) | |
352 os.chmod('../src/configure', | |
353 os.stat('../src/configure').st_mode | stat.S_IXUSR) | |
354 # /linux in the configure options is not a typo; configure | |
355 # looks here to find xml2-config | |
356 subprocess.check_call(['../src/configure'] + | |
357 XSLT_CONFIGURE_OPTIONS) | |
358 check_copying() | |
359 patch_config() | |
360 # Commit and upload the result | |
361 git('add', 'config.h') | |
362 remove_tracked_files(FILES_TO_REMOVE) | |
363 git('commit', '-m', 'Mac') | |
364 print('Now upload for review, etc.') | |
365 | |
366 | |
367 def check_clean(path): | |
368 with WorkingDir(path): | |
369 status = subprocess.check_output(['git', 'status', '-s']) | |
370 if len(status) > 0: | |
371 raise Exception('repository at %s is not clean' % path) | |
372 | |
373 | |
374 def main(): | |
375 src_dir = os.getcwd() | |
376 if not os.path.exists(os.path.join(src_dir, 'third_party')): | |
377 print('error: run this script from the Chromium src directory') | |
378 sys.exit(1) | |
379 | |
380 parser = argparse.ArgumentParser( | |
381 description='Roll the libxslt dependency in Chromium') | |
382 platform = parser.add_mutually_exclusive_group(required=True) | |
383 platform.add_argument('--linux', action='store_true') | |
384 platform.add_argument('--win32', action='store_true') | |
385 platform.add_argument('--mac', action='store_true') | |
386 parser.add_argument( | |
387 'libxslt_repo_path', | |
388 type=str, | |
389 nargs='?', | |
390 help='The path to the local clone of the libxslt git repo.') | |
391 args = parser.parse_args() | |
392 | |
393 if args.linux: | |
394 libxslt_repo_path = args.libxslt_repo_path | |
395 if not libxslt_repo_path: | |
396 print('Specify the path to the local libxslt repo clone.') | |
397 sys.exit(1) | |
398 libxslt_repo_path = os.path.abspath(libxslt_repo_path) | |
399 roll_libxslt_linux(src_dir, libxslt_repo_path) | |
400 elif args.win32: | |
401 roll_libxslt_win32(src_dir) | |
402 elif args.mac: | |
403 roll_libxslt_mac(src_dir) | |
404 | |
405 | |
406 if __name__ == '__main__': | |
407 main() | |
OLD | NEW |