build/subconfigure.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/build/subconfigure.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,137 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +# This script is used to capture the content of config.status-generated
     1.9 +# files and subsequently restore their timestamp if they haven't changed.
    1.10 +
    1.11 +import os
    1.12 +import re
    1.13 +import subprocess
    1.14 +import sys
    1.15 +import pickle
    1.16 +
    1.17 +class File(object):
    1.18 +    def __init__(self, path):
    1.19 +        self._path = path
    1.20 +        self._content = open(path, 'rb').read()
    1.21 +        stat = os.stat(path)
    1.22 +        self._times = (stat.st_atime, stat.st_mtime)
    1.23 +
    1.24 +    @property
    1.25 +    def path(self):
    1.26 +        return self._path
    1.27 +
    1.28 +    @property
    1.29 +    def mtime(self):
    1.30 +        return self._times[1]
    1.31 +
    1.32 +    def update_time(self):
    1.33 +        '''If the file hasn't changed since the instance was created,
    1.34 +           restore its old modification time.'''
    1.35 +        if not os.path.exists(self._path):
    1.36 +            return
    1.37 +        if open(self._path, 'rb').read() == self._content:
    1.38 +            os.utime(self._path, self._times)
    1.39 +
    1.40 +
    1.41 +# As defined in the various sub-configures in the tree
    1.42 +PRECIOUS_VARS = set([
    1.43 +    'build_alias',
    1.44 +    'host_alias',
    1.45 +    'target_alias',
    1.46 +    'CC',
    1.47 +    'CFLAGS',
    1.48 +    'LDFLAGS',
    1.49 +    'LIBS',
    1.50 +    'CPPFLAGS',
    1.51 +    'CPP',
    1.52 +    'CCC',
    1.53 +    'CXXFLAGS',
    1.54 +    'CXX',
    1.55 +    'CCASFLAGS',
    1.56 +    'CCAS',
    1.57 +])
    1.58 +
    1.59 +
    1.60 +# Autoconf, in some of the sub-configures used in the tree, likes to error
    1.61 +# out when "precious" variables change in value. The solution it gives to
    1.62 +# straighten things is to either run make distclean or remove config.cache.
    1.63 +# There's no reason not to do the latter automatically instead of failing,
    1.64 +# doing the cleanup (which, on buildbots means a full clobber), and
    1.65 +# restarting from scratch.
    1.66 +def maybe_clear_cache():
    1.67 +    comment = re.compile(r'^\s+#')
    1.68 +    cache = {}
    1.69 +    with open('config.cache') as f:
    1.70 +        for line in f.readlines():
    1.71 +            if not comment.match(line) and '=' in line:
    1.72 +                key, value = line.split('=', 1)
    1.73 +                cache[key] = value
    1.74 +    for precious in PRECIOUS_VARS:
    1.75 +        entry = 'ac_cv_env_%s_value' % precious
    1.76 +        if entry in cache and (not precious in os.environ or os.environ[precious] != cache[entry]):
    1.77 +            os.remove('config.cache')
    1.78 +            return
    1.79 +
    1.80 +
    1.81 +def dump(dump_file, shell):
    1.82 +    if os.path.exists('config.cache'):
    1.83 +        maybe_clear_cache()
    1.84 +    if not os.path.exists('config.status'):
    1.85 +        if os.path.exists(dump_file):
    1.86 +            os.remove(dump_file)
    1.87 +        return
    1.88 +
    1.89 +    config_files = [File('config.status')]
    1.90 +
    1.91 +    # Scan the config.status output for information about configuration files
    1.92 +    # it generates.
    1.93 +    config_status_output = subprocess.check_output(
    1.94 +        [shell, '-c', './config.status --help'],
    1.95 +        stderr=subprocess.STDOUT).splitlines()
    1.96 +    state = None
    1.97 +    for line in config_status_output:
    1.98 +        if line.startswith('Configuration') and line.endswith(':'):
    1.99 +            state = 'config'
   1.100 +        elif not line.startswith(' '):
   1.101 +            state = None
   1.102 +        elif state == 'config':
   1.103 +            for f in (couple.split(':')[0] for couple in line.split()):
   1.104 +                if os.path.isfile(f):
   1.105 +                    config_files.append(File(f))
   1.106 +
   1.107 +    with open(dump_file, 'wb') as f:
   1.108 +        pickle.dump(config_files, f)
   1.109 +
   1.110 +
   1.111 +def adjust(dump_file, configure):
   1.112 +    if not os.path.exists(dump_file):
   1.113 +        return
   1.114 +
   1.115 +    config_files = []
   1.116 +
   1.117 +    try:
   1.118 +        with open(dump_file, 'rb') as f:
   1.119 +            config_files = pickle.load(f)
   1.120 +    except Exception:
   1.121 +        pass
   1.122 +
   1.123 +    for f in config_files:
   1.124 +        # Still touch config.status if configure is newer than its original
   1.125 +        # mtime.
   1.126 +        if configure and os.path.basename(f.path) == 'config.status' and \
   1.127 +                os.path.getmtime(configure) > f.mtime:
   1.128 +            continue
   1.129 +        f.update_time()
   1.130 +
   1.131 +    os.remove(dump_file)
   1.132 +
   1.133 +
   1.134 +CONFIG_DUMP = 'config_files.pkl'
   1.135 +
   1.136 +if __name__ == '__main__':
   1.137 +    if sys.argv[1] == 'dump':
   1.138 +        dump(CONFIG_DUMP, sys.argv[2])
   1.139 +    elif sys.argv[1] == 'adjust':
   1.140 +        adjust(CONFIG_DUMP, sys.argv[2] if len(sys.argv) > 2 else None)

mercurial