icu_sources_data.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #!/usr/bin/env python
  2. #
  3. # Any copyright is dedicated to the Public Domain.
  4. # http://creativecommons.org/publicdomain/zero/1.0/
  5. #
  6. # Generate SOURCES in sources.mozbuild files from ICU's Makefile.in
  7. # files, and also build a standalone copy of ICU using its build
  8. # system to generate a new copy of the in-tree ICU data file.
  9. #
  10. # This script expects to be run from `update-icu.sh` after the in-tree
  11. # copy of ICU has been updated.
  12. from __future__ import print_function
  13. import glob
  14. import os
  15. import shutil
  16. import subprocess
  17. import sys
  18. import tempfile
  19. from mozpack import path as mozpath
  20. def find_source_file(dir, filename):
  21. base = os.path.splitext(filename)[0]
  22. for ext in ('.cpp', '.c'):
  23. f = mozpath.join(dir, base + ext)
  24. if os.path.isfile(f):
  25. return f
  26. raise Exception("Couldn't find source file for: %s" % filename)
  27. def get_sources_from_makefile(makefile):
  28. import pymake.parser
  29. from pymake.parserdata import SetVariable
  30. srcdir = os.path.dirname(makefile)
  31. for statement in pymake.parser.parsefile(makefile):
  32. if (isinstance(statement, SetVariable) and
  33. statement.vnameexp.is_static_string and
  34. statement.vnameexp.s == 'OBJECTS'):
  35. return sorted((find_source_file(srcdir, s)
  36. for s in statement.value.split()),
  37. key=lambda x: x.lower())
  38. def list_headers(path):
  39. result = []
  40. for name in os.listdir(path):
  41. f = mozpath.join(path, name)
  42. if os.path.isfile(f):
  43. result.append(f)
  44. return sorted(result, key=lambda x: x.lower())
  45. def write_sources(mozbuild, sources, headers):
  46. with open(mozbuild, 'wb') as f:
  47. f.write('# THIS FILE IS GENERATED BY /intl/icu_sources_data.py ' +
  48. 'DO NOT EDIT\n' +
  49. 'SOURCES += [\n')
  50. f.write(''.join(" '/%s',\n" % s for s in sources))
  51. f.write(']\n\n')
  52. f.write('EXPORTS.unicode += [\n')
  53. f.write(''.join(" '/%s',\n" % s for s in headers))
  54. f.write(']\n')
  55. def update_sources(topsrcdir):
  56. print('Updating ICU sources lists...')
  57. sys.path.append(mozpath.join(topsrcdir, 'build/pymake'))
  58. for d in ['common', 'i18n']:
  59. base_path = mozpath.join(topsrcdir, 'intl/icu/source/%s' % d)
  60. makefile = mozpath.join(base_path, 'Makefile.in')
  61. mozbuild = mozpath.join(topsrcdir,
  62. 'config/external/icu/%s/sources.mozbuild' % d)
  63. sources = [mozpath.relpath(s, topsrcdir)
  64. for s in get_sources_from_makefile(makefile)]
  65. headers = [mozpath.normsep(os.path.relpath(s, topsrcdir))
  66. for s in list_headers(mozpath.join(base_path, 'unicode'))]
  67. write_sources(mozbuild, sources, headers)
  68. def try_run(name, command, cwd=None, **kwargs):
  69. try:
  70. with tempfile.NamedTemporaryFile(prefix=name, delete=False) as f:
  71. subprocess.check_call(command, cwd=cwd, stdout=f,
  72. stderr=subprocess.STDOUT, **kwargs)
  73. except subprocess.CalledProcessError:
  74. print('''Error running "{}" in directory {}
  75. See output in {}'''.format(' '.join(command), cwd, f.name),
  76. file=sys.stderr)
  77. return False
  78. else:
  79. os.unlink(f.name)
  80. return True
  81. def get_data_file(data_dir):
  82. files = glob.glob(mozpath.join(data_dir, 'icudt*.dat'))
  83. return files[0] if files else None
  84. def update_data_file(topsrcdir):
  85. objdir = tempfile.mkdtemp(prefix='icu-obj-')
  86. configure = mozpath.join(topsrcdir, 'intl/icu/source/configure')
  87. env = dict(os.environ)
  88. # bug 1262101 - these should be shared with the moz.build files
  89. env.update({
  90. 'CPPFLAGS': ('-DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 ' +
  91. '-DUCONFIG_NO_LEGACY_CONVERSION ' +
  92. '-DUCONFIG_NO_TRANSLITERATION ' +
  93. '-DUCONFIG_NO_REGULAR_EXPRESSIONS ' +
  94. '-DUCONFIG_NO_BREAK_ITERATION ' +
  95. '-DU_CHARSET_IS_UTF8')
  96. })
  97. print('Running ICU configure...')
  98. if not try_run(
  99. 'icu-configure',
  100. ['sh', configure,
  101. '--with-data-packaging=archive',
  102. '--enable-static',
  103. '--disable-shared',
  104. '--disable-extras',
  105. '--disable-icuio',
  106. '--disable-layout',
  107. '--disable-tests',
  108. '--disable-samples',
  109. '--disable-strict'],
  110. cwd=objdir,
  111. env=env):
  112. return False
  113. print('Running ICU make...')
  114. if not try_run('icu-make', ['make'], cwd=objdir):
  115. return False
  116. print('Copying ICU data file...')
  117. tree_data_path = mozpath.join(topsrcdir,
  118. 'config/external/icu/data/')
  119. old_data_file = get_data_file(tree_data_path)
  120. if not old_data_file:
  121. print('Error: no ICU data file in %s' % tree_data_path,
  122. file=sys.stderr)
  123. return False
  124. new_data_file = get_data_file(mozpath.join(objdir, 'data/out'))
  125. if not new_data_file:
  126. print('Error: no ICU data in ICU objdir', file=sys.stderr)
  127. return False
  128. if os.path.basename(old_data_file) != os.path.basename(new_data_file):
  129. # Data file name has the major version number embedded.
  130. os.unlink(old_data_file)
  131. shutil.copy(new_data_file, tree_data_path)
  132. try:
  133. shutil.rmtree(objdir)
  134. except:
  135. print('Warning: failed to remove %s' % objdir, file=sys.stderr)
  136. return True
  137. def main():
  138. if len(sys.argv) != 2:
  139. print('Usage: icu_sources_data.py <mozilla topsrcdir>',
  140. file=sys.stderr)
  141. sys.exit(1)
  142. topsrcdir = mozpath.abspath(sys.argv[1])
  143. update_sources(topsrcdir)
  144. if not update_data_file(topsrcdir):
  145. print('Error updating ICU data file', file=sys.stderr)
  146. sys.exit(1)
  147. if __name__ == '__main__':
  148. main()