You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(1) Fix incorrectly assumed fresh cache when (generated) files are missing. See #134
(2) Fall back on stale caches if we fail to get any headers and have some in cache.
A dependency file can be generated with the `-M`/`--dependencies` flag or its friends.
132
132
See https://clang.llvm.org/docs/ClangCommandLineReference.html#dependency-file-generation for more details.
133
133
"""
134
+
ifnotd_file_content: # There was an error generating.
135
+
returnset()
136
+
134
137
# When reading file content as text with universal newlines mode enabled (the default), Python converts OS-specific line endings to '\n' (see https://docs.python.org/3/library/functions.html#open-newline-parameter for the thrilling details).
135
138
# This function takes an arbitrary string, so we also ensure no `\r` characters have snuck through, because that's almost certainly an upstream error.
136
139
assert'\r'notind_file_content, "Something went wrong in makefile parsing to get headers. Dependency file content should not contain literal '\r' characters. Output:\n"+repr(d_file_content)
"""Get (and cache!) the modified time of a file, slightly adjusted for easy comparison.
160
-
161
-
This is primarily intended to check whether header include caches are fresh.
162
-
163
-
If the file doesn't exist or is inaccessible (either because it was deleted or wasn't generated), return 0.
164
-
For bazel's internal sources, which have timestamps 10 years in the future, also return 0.
161
+
def_get_cached_modified_time(path: str):
162
+
"""Returns 0 if the file doesn't exist.
165
163
166
164
Without the cache, most of our runtime in the cached case is `stat`'ing the same headers repeatedly.
167
165
"""
168
166
try:
169
-
mtime=os.path.getmtime(path)
167
+
returnos.path.getmtime(path)
170
168
exceptOSError: # The file doesn't exist or is inaccessible.
171
169
# For our purposes, this means we don't have a newer version, so we'll return a very old time that'll always qualify the cache as fresh in a comparison. There are two cases here:
172
170
# (1) Somehow it wasn't generated in the build that created the depfile. We therefore won't get any fresher by building, so we'll treat that as good enough; or
173
171
# (2) It has been deleted since we last cached, in which case we'd rather use the cached version if it's otherwise fresh.
174
172
return0
175
173
174
+
175
+
def_get_cached_file_exists(path: str):
176
+
return_get_cached_modified_time(path) !=0
177
+
178
+
179
+
def_get_cached_adjusted_modified_time(path: str):
180
+
"""Get the modified time of a file, slightly adjusted for easy comparison.
181
+
182
+
This is primarily intended to check whether header include caches are fresh.
183
+
184
+
If the file doesn't exist or is inaccessible (either because it was deleted or wasn't generated), return 0.
185
+
For bazel's internal sources, which have timestamps 10 years in the future, also return 0.
186
+
187
+
Without the cache, most of our runtime in the cached case is `stat`'ing the same headers repeatedly.
188
+
Cache shared with _get_cached_modified_time
189
+
"""
190
+
mtime=_get_cached_modified_time(path)
191
+
176
192
# Bazel internal sources have timestamps 10 years in the future as part of a mechanism to detect and prevent modification, so we'll similarly ignore those, since they shouldn't be changing.
header_cmd+= ['--dependencies', '--print-missing-file-dependencies'] # Allows us to continue on past missing (generated) files--whose paths may be wrong (listed as written in the include)!
239
256
240
257
header_search_process=_subprocess_run_spilling_over_to_param_file_if_needed( # Note: gcc/clang can be run from Windows, too.
# Why? We'd wrongly get a subset of the headers and we might wrongly think the cache is still fresh because we wouldn't know that the formerly missing header had been generated.
275
+
ifis_nvcc:
276
+
should_cache=bool(header_search_process.stdout) # Without --print-missing-file-dependencies, nothing is written if a file isn't found. (Something is still written if no headers are included.) This avoids hardcoding the error message. Note that some errors, like that for #bad_directive are okay to ignore! We'll know the cache isn't fresh when the user changes the file.
headers= {headerforheaderinheadersif_get_cached_file_exists(header)} # We can't filter on headers starting with bazel-out because missing headers don't have complete paths; they just listed as #included
should_cache=all('fatal error C1083:'notinerror_lineforerror_lineinerror_lines) # Error code for file not found (when trying to include). Without this, we'd wrongly get a subset of the headers and we might wrongly think the cache is still fresh because we wouldn't know that the formerly missing header had been generated.
cache_file_path=output_file+".hedron.compile-commands.headers"# Embed our cache in bazel's
515
541
ifos.path.isfile(cache_file_path):
516
542
cache_last_modified=os.path.getmtime(cache_file_path) # Do before opening just as a basic hedge against concurrent write, even though we won't handle the concurrent delete case perfectly.
517
543
try:
518
544
withopen(cache_file_path) ascache_file:
519
-
action_key, headers=json.load(cache_file)
545
+
action_key, cached_headers=json.load(cache_file)
520
546
exceptjson.JSONDecodeError:
521
547
# Corrupted cache, which can happen if, for example, the user kills the program, since writes aren't atomic.
522
548
# But if it is the result of a bug, we want to print it before it's overwritten, so it can be reported
# Emscripten is tricky. There isn't an easy way to make it emcc run without lots of environment variables.
544
570
# So...rather than doing our usual script unwrapping, we just swap in clang/gcc and use that to get headers, knowing that they'll accept the same argument format.
0 commit comments