OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 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.""" |
| 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 |