|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 # This script is used to capture the content of config.status-generated |
|
6 # files and subsequently restore their timestamp if they haven't changed. |
|
7 |
|
8 import os |
|
9 import re |
|
10 import subprocess |
|
11 import sys |
|
12 import pickle |
|
13 |
|
14 class File(object): |
|
15 def __init__(self, path): |
|
16 self._path = path |
|
17 self._content = open(path, 'rb').read() |
|
18 stat = os.stat(path) |
|
19 self._times = (stat.st_atime, stat.st_mtime) |
|
20 |
|
21 @property |
|
22 def path(self): |
|
23 return self._path |
|
24 |
|
25 @property |
|
26 def mtime(self): |
|
27 return self._times[1] |
|
28 |
|
29 def update_time(self): |
|
30 '''If the file hasn't changed since the instance was created, |
|
31 restore its old modification time.''' |
|
32 if not os.path.exists(self._path): |
|
33 return |
|
34 if open(self._path, 'rb').read() == self._content: |
|
35 os.utime(self._path, self._times) |
|
36 |
|
37 |
|
38 # As defined in the various sub-configures in the tree |
|
39 PRECIOUS_VARS = set([ |
|
40 'build_alias', |
|
41 'host_alias', |
|
42 'target_alias', |
|
43 'CC', |
|
44 'CFLAGS', |
|
45 'LDFLAGS', |
|
46 'LIBS', |
|
47 'CPPFLAGS', |
|
48 'CPP', |
|
49 'CCC', |
|
50 'CXXFLAGS', |
|
51 'CXX', |
|
52 'CCASFLAGS', |
|
53 'CCAS', |
|
54 ]) |
|
55 |
|
56 |
|
57 # Autoconf, in some of the sub-configures used in the tree, likes to error |
|
58 # out when "precious" variables change in value. The solution it gives to |
|
59 # straighten things is to either run make distclean or remove config.cache. |
|
60 # There's no reason not to do the latter automatically instead of failing, |
|
61 # doing the cleanup (which, on buildbots means a full clobber), and |
|
62 # restarting from scratch. |
|
63 def maybe_clear_cache(): |
|
64 comment = re.compile(r'^\s+#') |
|
65 cache = {} |
|
66 with open('config.cache') as f: |
|
67 for line in f.readlines(): |
|
68 if not comment.match(line) and '=' in line: |
|
69 key, value = line.split('=', 1) |
|
70 cache[key] = value |
|
71 for precious in PRECIOUS_VARS: |
|
72 entry = 'ac_cv_env_%s_value' % precious |
|
73 if entry in cache and (not precious in os.environ or os.environ[precious] != cache[entry]): |
|
74 os.remove('config.cache') |
|
75 return |
|
76 |
|
77 |
|
78 def dump(dump_file, shell): |
|
79 if os.path.exists('config.cache'): |
|
80 maybe_clear_cache() |
|
81 if not os.path.exists('config.status'): |
|
82 if os.path.exists(dump_file): |
|
83 os.remove(dump_file) |
|
84 return |
|
85 |
|
86 config_files = [File('config.status')] |
|
87 |
|
88 # Scan the config.status output for information about configuration files |
|
89 # it generates. |
|
90 config_status_output = subprocess.check_output( |
|
91 [shell, '-c', './config.status --help'], |
|
92 stderr=subprocess.STDOUT).splitlines() |
|
93 state = None |
|
94 for line in config_status_output: |
|
95 if line.startswith('Configuration') and line.endswith(':'): |
|
96 state = 'config' |
|
97 elif not line.startswith(' '): |
|
98 state = None |
|
99 elif state == 'config': |
|
100 for f in (couple.split(':')[0] for couple in line.split()): |
|
101 if os.path.isfile(f): |
|
102 config_files.append(File(f)) |
|
103 |
|
104 with open(dump_file, 'wb') as f: |
|
105 pickle.dump(config_files, f) |
|
106 |
|
107 |
|
108 def adjust(dump_file, configure): |
|
109 if not os.path.exists(dump_file): |
|
110 return |
|
111 |
|
112 config_files = [] |
|
113 |
|
114 try: |
|
115 with open(dump_file, 'rb') as f: |
|
116 config_files = pickle.load(f) |
|
117 except Exception: |
|
118 pass |
|
119 |
|
120 for f in config_files: |
|
121 # Still touch config.status if configure is newer than its original |
|
122 # mtime. |
|
123 if configure and os.path.basename(f.path) == 'config.status' and \ |
|
124 os.path.getmtime(configure) > f.mtime: |
|
125 continue |
|
126 f.update_time() |
|
127 |
|
128 os.remove(dump_file) |
|
129 |
|
130 |
|
131 CONFIG_DUMP = 'config_files.pkl' |
|
132 |
|
133 if __name__ == '__main__': |
|
134 if sys.argv[1] == 'dump': |
|
135 dump(CONFIG_DUMP, sys.argv[2]) |
|
136 elif sys.argv[1] == 'adjust': |
|
137 adjust(CONFIG_DUMP, sys.argv[2] if len(sys.argv) > 2 else None) |