#!/usr/bin/env python # # Any copyright is dedicated to the Public Domain. # http://creativecommons.org/publicdomain/zero/1.0/ # # Generate SOURCES in sources.mozbuild files from ICU's Makefile.in # files, and also build a standalone copy of ICU using its build # system to generate a new copy of the in-tree ICU data file. # # This script expects to be run from `update-icu.sh` after the in-tree # copy of ICU has been updated. from __future__ import absolute_import from __future__ import print_function import glob import multiprocessing import os import shutil import subprocess import sys import tempfile from mozpack import path as mozpath # The following files have been determined to be dead/unused by a # semi-automated analysis. You can just remove any of the files below # if you need them. However, files marked with a "Cluster" comment # can only be removed together, as they have (directional) dependencies. # If you want to rerun this analysis, contact :decoder. UNUSED_SOURCES = set([ 'intl/icu/source/common/bytestrieiterator.cpp', 'intl/icu/source/common/cstr.cpp', 'intl/icu/source/common/cwchar.cpp', 'intl/icu/source/common/icudataver.cpp', 'intl/icu/source/common/icuplug.cpp', 'intl/icu/source/common/pluralmap.cpp', 'intl/icu/source/common/ucat.cpp', 'intl/icu/source/common/ucnv2022.cpp', 'intl/icu/source/common/ucnv_ct.cpp', 'intl/icu/source/common/ucnvdisp.cpp', 'intl/icu/source/common/ucnv_ext.cpp', 'intl/icu/source/common/ucnvhz.cpp', 'intl/icu/source/common/ucnvisci.cpp', 'intl/icu/source/common/ucnv_lmb.cpp', 'intl/icu/source/common/ucnvmbcs.cpp', 'intl/icu/source/common/uidna.cpp', 'intl/icu/source/common/unorm.cpp', 'intl/icu/source/common/usc_impl.cpp', 'intl/icu/source/common/ustr_wcs.cpp', 'intl/icu/source/common/util_props.cpp', 'intl/icu/source/i18n/anytrans.cpp', 'intl/icu/source/i18n/brktrans.cpp', 'intl/icu/source/i18n/casetrn.cpp', 'intl/icu/source/i18n/cpdtrans.cpp', 'intl/icu/source/i18n/esctrn.cpp', 'intl/icu/source/i18n/fmtable_cnv.cpp', 'intl/icu/source/i18n/funcrepl.cpp', 'intl/icu/source/i18n/gender.cpp', 'intl/icu/source/i18n/name2uni.cpp', 'intl/icu/source/i18n/nortrans.cpp', 'intl/icu/source/i18n/nultrans.cpp', 'intl/icu/source/i18n/quant.cpp', 'intl/icu/source/i18n/rbt.cpp', 'intl/icu/source/i18n/rbt_data.cpp', 'intl/icu/source/i18n/rbt_pars.cpp', 'intl/icu/source/i18n/rbt_rule.cpp', 'intl/icu/source/i18n/rbt_set.cpp', 'intl/icu/source/i18n/regexcmp.cpp', 'intl/icu/source/i18n/regeximp.cpp', 'intl/icu/source/i18n/regexst.cpp', 'intl/icu/source/i18n/regextxt.cpp', 'intl/icu/source/i18n/rematch.cpp', 'intl/icu/source/i18n/remtrans.cpp', 'intl/icu/source/i18n/repattrn.cpp', 'intl/icu/source/i18n/scientificnumberformatter.cpp', 'intl/icu/source/i18n/strmatch.cpp', 'intl/icu/source/i18n/strrepl.cpp', 'intl/icu/source/i18n/titletrn.cpp', 'intl/icu/source/i18n/tolowtrn.cpp', 'intl/icu/source/i18n/toupptrn.cpp', 'intl/icu/source/i18n/translit.cpp', 'intl/icu/source/i18n/transreg.cpp', 'intl/icu/source/i18n/tridpars.cpp', 'intl/icu/source/i18n/unesctrn.cpp', 'intl/icu/source/i18n/uni2name.cpp', 'intl/icu/source/i18n/uregexc.cpp', 'intl/icu/source/i18n/uregex.cpp', 'intl/icu/source/i18n/uregion.cpp', 'intl/icu/source/i18n/uspoof_build.cpp', 'intl/icu/source/i18n/uspoof_conf.cpp', 'intl/icu/source/i18n/utrans.cpp', 'intl/icu/source/i18n/vzone.cpp', 'intl/icu/source/i18n/zrule.cpp', 'intl/icu/source/i18n/ztrans.cpp', # Cluster 'intl/icu/source/common/resbund_cnv.cpp', 'intl/icu/source/common/ures_cnv.cpp', # Cluster 'intl/icu/source/common/propsvec.cpp', 'intl/icu/source/common/ucnvsel.cpp', 'intl/icu/source/common/ucnv_set.cpp', # Cluster 'intl/icu/source/common/ubiditransform.cpp', 'intl/icu/source/common/ushape.cpp', # Cluster 'intl/icu/source/i18n/csdetect.cpp', 'intl/icu/source/i18n/csmatch.cpp', 'intl/icu/source/i18n/csr2022.cpp', 'intl/icu/source/i18n/csrecog.cpp', 'intl/icu/source/i18n/csrmbcs.cpp', 'intl/icu/source/i18n/csrsbcs.cpp', 'intl/icu/source/i18n/csrucode.cpp', 'intl/icu/source/i18n/csrutf8.cpp', 'intl/icu/source/i18n/inputext.cpp', 'intl/icu/source/i18n/ucsdet.cpp', # Cluster 'intl/icu/source/i18n/alphaindex.cpp', 'intl/icu/source/i18n/ulocdata.cpp', ]) def find_source_file(dir, filename): base = os.path.splitext(filename)[0] for ext in ('.cpp', '.c'): f = mozpath.join(dir, base + ext) if os.path.isfile(f): return f raise Exception("Couldn't find source file for: %s" % filename) def get_sources_from_makefile(makefile): srcdir = os.path.dirname(makefile) with open(makefile) as f: contents = f.read().replace('\\\n', '').split('\n') for line in contents: if line.startswith('OBJECTS ='): return sorted((find_source_file(srcdir, s) for s in line[len('OBJECTS ='):].strip().split()), key=lambda x: x.lower()) raise AssertionError('OBJECTS definition not found in file %s' % makefile) def list_headers(path): result = [] for name in os.listdir(path): f = mozpath.join(path, name) if os.path.isfile(f): result.append(f) return sorted(result, key=lambda x: x.lower()) def write_sources(mozbuild, sources, headers): with open(mozbuild, 'w', newline='\n', encoding='utf-8') as f: f.write('# THIS FILE IS GENERATED BY /intl/icu_sources_data.py ' + 'DO NOT EDIT\n') def write_list(name, content): if content: f.write('%s %s [\n' % (name, '=' if name.islower() else '+=')) f.write(''.join(" '/%s',\n" % s for s in content)) f.write(']\n') write_list('sources', [s for s in sources if s not in UNUSED_SOURCES]) write_list('other_sources', [s for s in sources if s in UNUSED_SOURCES]) write_list('EXPORTS.unicode', headers) def update_sources(topsrcdir): print('Updating ICU sources lists...') for d in ['common', 'i18n', 'tools/toolutil', 'tools/icupkg']: base_path = mozpath.join(topsrcdir, 'intl/icu/source/%s' % d) makefile = mozpath.join(base_path, 'Makefile.in') mozbuild = mozpath.join(topsrcdir, 'config/external/icu/%s/sources.mozbuild' % mozpath.basename(d)) sources = [mozpath.relpath(s, topsrcdir) for s in get_sources_from_makefile(makefile)] unicode_dir = mozpath.join(base_path, 'unicode') if os.path.exists(unicode_dir): headers = [mozpath.normsep(os.path.relpath(s, topsrcdir)) for s in list_headers(unicode_dir)] else: headers = None write_sources(mozbuild, sources, headers) def try_run(name, command, cwd=None, **kwargs): try: with tempfile.NamedTemporaryFile(prefix=name, delete=False) as f: subprocess.check_call(command, cwd=cwd, stdout=f, stderr=subprocess.STDOUT, **kwargs) except subprocess.CalledProcessError: print('''Error running "{}" in directory {} See output in {}'''.format(' '.join(command), cwd, f.name), file=sys.stderr) return False else: os.unlink(f.name) return True def get_data_file(data_dir): files = glob.glob(mozpath.join(data_dir, 'icudt*.dat')) return files[0] if files else None def update_data_file(topsrcdir): objdir = tempfile.mkdtemp(prefix='icu-obj-') configure = mozpath.join(topsrcdir, 'intl/icu/source/configure') env = dict(os.environ) # bug 1262101 - these should be shared with the moz.build files env.update({ 'CPPFLAGS': ('-DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 ' + '-DU_HIDE_OBSOLETE_UTF_OLD_H=1' + '-DUCONFIG_NO_LEGACY_CONVERSION ' + '-DUCONFIG_NO_TRANSLITERATION ' + '-DUCONFIG_NO_REGULAR_EXPRESSIONS ' + '-DUCONFIG_NO_BREAK_ITERATION ' + '-DU_CHARSET_IS_UTF8') }) # Exclude data that we currently don't need. # # The file format for ICU's data build tool is described at # . env["ICU_DATA_FILTER_FILE"] = mozpath.join(topsrcdir, 'intl/icu/data_filter.json') print('Running ICU configure...') if not try_run( 'icu-configure', ['sh', configure, '--with-data-packaging=archive', '--enable-static', '--disable-shared', '--disable-extras', '--disable-icuio', '--disable-layout', '--disable-layoutex', '--disable-tests', '--disable-samples', '--disable-strict'], cwd=objdir, env=env): return False print('Running ICU make...') if not try_run( 'icu-make', ['make', '--jobs=%d' % multiprocessing.cpu_count(), '--output-sync'], cwd=objdir): return False print('Copying ICU data file...') tree_data_path = mozpath.join(topsrcdir, 'config/external/icu/data/') old_data_file = get_data_file(tree_data_path) if not old_data_file: print('Error: no ICU data file in %s' % tree_data_path, file=sys.stderr) return False new_data_file = get_data_file(mozpath.join(objdir, 'data/out')) if not new_data_file: print('Error: no ICU data in ICU objdir', file=sys.stderr) return False if os.path.basename(old_data_file) != os.path.basename(new_data_file): # Data file name has the major version number embedded. os.unlink(old_data_file) shutil.copy(new_data_file, tree_data_path) try: shutil.rmtree(objdir) except Exception: print('Warning: failed to remove %s' % objdir, file=sys.stderr) return True def main(): if len(sys.argv) != 2: print('Usage: icu_sources_data.py ', file=sys.stderr) sys.exit(1) topsrcdir = mozpath.abspath(sys.argv[1]) update_sources(topsrcdir) if not update_data_file(topsrcdir): print('Error updating ICU data file', file=sys.stderr) sys.exit(1) if __name__ == '__main__': main()