michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: # This script is used to capture the content of config.status-generated michael@0: # files and subsequently restore their timestamp if they haven't changed. michael@0: michael@0: import os michael@0: import re michael@0: import subprocess michael@0: import sys michael@0: import pickle michael@0: michael@0: class File(object): michael@0: def __init__(self, path): michael@0: self._path = path michael@0: self._content = open(path, 'rb').read() michael@0: stat = os.stat(path) michael@0: self._times = (stat.st_atime, stat.st_mtime) michael@0: michael@0: @property michael@0: def path(self): michael@0: return self._path michael@0: michael@0: @property michael@0: def mtime(self): michael@0: return self._times[1] michael@0: michael@0: def update_time(self): michael@0: '''If the file hasn't changed since the instance was created, michael@0: restore its old modification time.''' michael@0: if not os.path.exists(self._path): michael@0: return michael@0: if open(self._path, 'rb').read() == self._content: michael@0: os.utime(self._path, self._times) michael@0: michael@0: michael@0: # As defined in the various sub-configures in the tree michael@0: PRECIOUS_VARS = set([ michael@0: 'build_alias', michael@0: 'host_alias', michael@0: 'target_alias', michael@0: 'CC', michael@0: 'CFLAGS', michael@0: 'LDFLAGS', michael@0: 'LIBS', michael@0: 'CPPFLAGS', michael@0: 'CPP', michael@0: 'CCC', michael@0: 'CXXFLAGS', michael@0: 'CXX', michael@0: 'CCASFLAGS', michael@0: 'CCAS', michael@0: ]) michael@0: michael@0: michael@0: # Autoconf, in some of the sub-configures used in the tree, likes to error michael@0: # out when "precious" variables change in value. The solution it gives to michael@0: # straighten things is to either run make distclean or remove config.cache. michael@0: # There's no reason not to do the latter automatically instead of failing, michael@0: # doing the cleanup (which, on buildbots means a full clobber), and michael@0: # restarting from scratch. michael@0: def maybe_clear_cache(): michael@0: comment = re.compile(r'^\s+#') michael@0: cache = {} michael@0: with open('config.cache') as f: michael@0: for line in f.readlines(): michael@0: if not comment.match(line) and '=' in line: michael@0: key, value = line.split('=', 1) michael@0: cache[key] = value michael@0: for precious in PRECIOUS_VARS: michael@0: entry = 'ac_cv_env_%s_value' % precious michael@0: if entry in cache and (not precious in os.environ or os.environ[precious] != cache[entry]): michael@0: os.remove('config.cache') michael@0: return michael@0: michael@0: michael@0: def dump(dump_file, shell): michael@0: if os.path.exists('config.cache'): michael@0: maybe_clear_cache() michael@0: if not os.path.exists('config.status'): michael@0: if os.path.exists(dump_file): michael@0: os.remove(dump_file) michael@0: return michael@0: michael@0: config_files = [File('config.status')] michael@0: michael@0: # Scan the config.status output for information about configuration files michael@0: # it generates. michael@0: config_status_output = subprocess.check_output( michael@0: [shell, '-c', './config.status --help'], michael@0: stderr=subprocess.STDOUT).splitlines() michael@0: state = None michael@0: for line in config_status_output: michael@0: if line.startswith('Configuration') and line.endswith(':'): michael@0: state = 'config' michael@0: elif not line.startswith(' '): michael@0: state = None michael@0: elif state == 'config': michael@0: for f in (couple.split(':')[0] for couple in line.split()): michael@0: if os.path.isfile(f): michael@0: config_files.append(File(f)) michael@0: michael@0: with open(dump_file, 'wb') as f: michael@0: pickle.dump(config_files, f) michael@0: michael@0: michael@0: def adjust(dump_file, configure): michael@0: if not os.path.exists(dump_file): michael@0: return michael@0: michael@0: config_files = [] michael@0: michael@0: try: michael@0: with open(dump_file, 'rb') as f: michael@0: config_files = pickle.load(f) michael@0: except Exception: michael@0: pass michael@0: michael@0: for f in config_files: michael@0: # Still touch config.status if configure is newer than its original michael@0: # mtime. michael@0: if configure and os.path.basename(f.path) == 'config.status' and \ michael@0: os.path.getmtime(configure) > f.mtime: michael@0: continue michael@0: f.update_time() michael@0: michael@0: os.remove(dump_file) michael@0: michael@0: michael@0: CONFIG_DUMP = 'config_files.pkl' michael@0: michael@0: if __name__ == '__main__': michael@0: if sys.argv[1] == 'dump': michael@0: dump(CONFIG_DUMP, sys.argv[2]) michael@0: elif sys.argv[1] == 'adjust': michael@0: adjust(CONFIG_DUMP, sys.argv[2] if len(sys.argv) > 2 else None)