build/subconfigure.py
author Mike Hommey <mh+mozilla@glandium.org>
Fri, 24 Jan 2014 07:57:54 +0900
changeset 181995 15899a6820f4ad558e00d08c1e9ac22f7aa7aeb7
parent 157039 e616268dba3cef144faa33f3f1ee27674309482f
child 215494 a0ab7fa5dd58ee0a92aae12474c8b11b026e90f2
permissions -rw-r--r--
Bug 962758 - Avoid build failures when js/src/configure.in changes in a way that doesn't change config.status. r=gps

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# This script is used to capture the content of config.status-generated
# files and subsequently restore their timestamp if they haven't changed.

import os
import re
import subprocess
import sys
import pickle

class File(object):
    def __init__(self, path):
        self._path = path
        self._content = open(path, 'rb').read()
        stat = os.stat(path)
        self._times = (stat.st_atime, stat.st_mtime)

    @property
    def path(self):
        return self._path

    @property
    def mtime(self):
        return self._times[1]

    def update_time(self):
        '''If the file hasn't changed since the instance was created,
           restore its old modification time.'''
        if not os.path.exists(self._path):
            return
        if open(self._path, 'rb').read() == self._content:
            os.utime(self._path, self._times)


# As defined in the various sub-configures in the tree
PRECIOUS_VARS = set([
    'build_alias',
    'host_alias',
    'target_alias',
    'CC',
    'CFLAGS',
    'LDFLAGS',
    'LIBS',
    'CPPFLAGS',
    'CPP',
    'CCC',
    'CXXFLAGS',
    'CXX',
    'CCASFLAGS',
    'CCAS',
])


# Autoconf, in some of the sub-configures used in the tree, likes to error
# out when "precious" variables change in value. The solution it gives to
# straighten things is to either run make distclean or remove config.cache.
# There's no reason not to do the latter automatically instead of failing,
# doing the cleanup (which, on buildbots means a full clobber), and
# restarting from scratch.
def maybe_clear_cache():
    comment = re.compile(r'^\s+#')
    cache = {}
    with open('config.cache') as f:
        for line in f.readlines():
            if not comment.match(line) and '=' in line:
                key, value = line.split('=', 1)
                cache[key] = value
    for precious in PRECIOUS_VARS:
        entry = 'ac_cv_env_%s_value' % precious
        if entry in cache and (not precious in os.environ or os.environ[precious] != cache[entry]):
            os.remove('config.cache')
            return


def dump(dump_file, shell):
    if os.path.exists('config.cache'):
        maybe_clear_cache()
    if not os.path.exists('config.status'):
        if os.path.exists(dump_file):
            os.remove(dump_file)
        return

    config_files = [File('config.status')]

    # Scan the config.status output for information about configuration files
    # it generates.
    config_status_output = subprocess.check_output(
        [shell, '-c', './config.status --help'],
        stderr=subprocess.STDOUT).splitlines()
    state = None
    for line in config_status_output:
        if line.startswith('Configuration') and line.endswith(':'):
            state = 'config'
        elif not line.startswith(' '):
            state = None
        elif state == 'config':
            for f in (couple.split(':')[0] for couple in line.split()):
                if os.path.isfile(f):
                    config_files.append(File(f))

    with open(dump_file, 'wb') as f:
        pickle.dump(config_files, f)


def adjust(dump_file, configure):
    if not os.path.exists(dump_file):
        return

    config_files = []

    try:
        with open(dump_file, 'rb') as f:
            config_files = pickle.load(f)
    except Exception:
        pass

    for f in config_files:
        # Still touch config.status if configure is newer than its original
        # mtime.
        if configure and os.path.basename(f.path) == 'config.status' and \
                os.path.getmtime(configure) > f.mtime:
            continue
        f.update_time()

    os.remove(dump_file)


CONFIG_DUMP = 'config_files.pkl'

if __name__ == '__main__':
    if sys.argv[1] == 'dump':
        dump(CONFIG_DUMP, sys.argv[2])
    elif sys.argv[1] == 'adjust':
        adjust(CONFIG_DUMP, sys.argv[2] if len(sys.argv) > 2 else None)