PageRenderTime 25ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/third_party/libxslt/chromium/roll.py

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