author Gregory Szorc <>
Thu, 01 Sep 2016 15:38:30 -0700
changeset 313084 d31f5e1edb53ced0cfd71d6692354713e40615be
parent 305782 4a4bbb7e9e798c40a3b25dbb2ddbfafb793acbe3
child 419039 81442e3861e6d8dbbbd58462416ced1369484c89
permissions -rw-r--r--
Bug 1298947 - Pin fingerprint; r=dustin We just upgraded our run-time environment to Mercurial 3.9. 3.9 features a new [hostsecurity] config section and allows certificate fingerprints to be defined using SHA-256 hashes (not just SHA-1). A TaskCluster secret with the Mercurial 3.9 fingerprint format has been added. This commit takes advantage of it. MozReview-Commit-ID: 5NwJl9zOse2

#!/usr/bin/env python
# Any copyright is dedicated to the Public Domain.
# Generate SOURCES in sources.mozbuild files from ICU's
# 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 `` after the in-tree
# copy of ICU has been updated.

from __future__ import print_function

import glob
import os
import shutil
import subprocess
import sys
import tempfile

from mozpack import path as mozpath

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):
    import pymake.parser
    from pymake.parserdata import SetVariable
    srcdir = os.path.dirname(makefile)
    for statement in pymake.parser.parsefile(makefile):
        if (isinstance(statement, SetVariable) and
                statement.vnameexp.is_static_string and
                statement.vnameexp.s == 'OBJECTS'):
            return sorted((find_source_file(srcdir, s)
                           for s in statement.value.split()),
                          key=lambda x: x.lower())

def list_headers(path):
    result = []
    for name in os.listdir(path):
        f = mozpath.join(path, name)
        if os.path.isfile(f):
    return sorted(result, key=lambda x: x.lower())

def write_sources(mozbuild, sources, headers):
    with open(mozbuild, 'wb') as f:
        f.write('# THIS FILE IS GENERATED BY /intl/ ' +
                'DO NOT EDIT\n' +
                'SOURCES += [\n')
        f.write(''.join("   '/%s',\n" % s for s in sources))
        f.write('EXPORTS.unicode += [\n')
        f.write(''.join("   '/%s',\n" % s for s in headers))

def update_sources(topsrcdir):
    print('Updating ICU sources lists...')
    sys.path.append(mozpath.join(topsrcdir, 'build/pymake'))
    for d in ['common', 'i18n']:
        base_path = mozpath.join(topsrcdir, 'intl/icu/source/%s' % d)
        makefile = mozpath.join(base_path, '')
        mozbuild = mozpath.join(topsrcdir,
                                'config/external/icu/%s/sources.mozbuild' % d)
        sources = [mozpath.relpath(s, topsrcdir)
                   for s in get_sources_from_makefile(makefile)]
        headers = [mozpath.normsep(os.path.relpath(s, topsrcdir))
                   for s in list_headers(mozpath.join(base_path, 'unicode'))]
        write_sources(mozbuild, sources, headers)

def try_run(name, command, cwd=None, **kwargs):
        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,,
        return False
        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 files
                     '-DUCONFIG_NO_LEGACY_CONVERSION ' +
                     '-DUCONFIG_NO_TRANSLITERATION ' +
                     '-DUCONFIG_NO_REGULAR_EXPRESSIONS ' +
                     '-DUCONFIG_NO_BREAK_ITERATION ' +
    print('Running ICU configure...')
    if not try_run(
            ['sh', configure,
        return False
    print('Running ICU make...')
    if not try_run('icu-make', ['make'], cwd=objdir):
        return False
    print('Copying ICU data file...')
    tree_data_path = mozpath.join(topsrcdir,
    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,
        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.
    shutil.copy(new_data_file, tree_data_path)
        print('Warning: failed to remove %s' % objdir, file=sys.stderr)
    return True

def main():
    if len(sys.argv) != 2:
        print('Usage: <mozilla topsrcdir>',

    topsrcdir = mozpath.abspath(sys.argv[1])
    if not update_data_file(topsrcdir):
        print('Error updating ICU data file', file=sys.stderr)

if __name__ == '__main__':