Skip to content

Commit 0befc09

Browse files
committed
End loop if header is found & Add max attempts limit
1 parent 4244747 commit 0befc09

File tree

1 file changed

+29
-8
lines changed

1 file changed

+29
-8
lines changed

refresh.template.py

+29-8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import time
3838
import types
3939
import typing # MIN_PY=3.9: Switch e.g. typing.List[str] -> list[str]
40+
import threading
41+
import itertools
4042

4143

4244
@enum.unique
@@ -182,6 +184,7 @@ def _get_cached_adjusted_modified_time(path: str):
182184
# Roughly 1 year into the future. This is safely below bazel's 10 year margin, but large enough that no sane normal file should be past this.
183185
BAZEL_INTERNAL_SOURCE_CUTOFF = time.time() + 60*60*24*365
184186

187+
BAZEL_INTERNAL_MAX_HEADER_SEARCH_COUNT = 500
185188

186189
def _get_headers_gcc(compile_args: typing.List[str], source_path: str, action_key: str):
187190
"""Gets the headers used by a particular compile command that uses gcc arguments formatting (including clang.)
@@ -766,22 +769,28 @@ def _all_platform_patch(compile_args: typing.List[str]):
766769
return compile_args
767770

768771

769-
def _get_cpp_command_for_files(compile_action):
772+
def _get_cpp_command_for_files(args):
770773
"""Reformat compile_action into a compile command clangd can understand.
771774
772775
Undo Bazel-isms and figures out which files clangd should apply the command to.
773776
"""
777+
(compile_action, event, should_stop_lambda) = args
778+
if event.is_set():
779+
return set(), set(), []
780+
774781
# Patch command by platform
775782
compile_action.arguments = _all_platform_patch(compile_action.arguments)
776783
compile_action.arguments = _apple_platform_patch(compile_action.arguments)
777784
# Android and Linux and grailbio LLVM toolchains: Fine as is; no special patching needed.
778785

779786
source_files, header_files = _get_files(compile_action)
780787

788+
if not event.is_set() and should_stop_lambda(source_files, header_files):
789+
event.set()
781790
return source_files, header_files, compile_action.arguments
782791

783792

784-
def _convert_compile_commands(aquery_output):
793+
def _convert_compile_commands(aquery_output, should_stop_lambda):
785794
"""Converts from Bazel's aquery format to de-Bazeled compile_commands.json entries.
786795
787796
Input: jsonproto output from aquery, pre-filtered to (Objective-)C(++) compile actions for a given build.
@@ -805,8 +814,8 @@ def _convert_compile_commands(aquery_output):
805814
with concurrent.futures.ThreadPoolExecutor(
806815
max_workers=min(32, (os.cpu_count() or 1) + 4) # Backport. Default in MIN_PY=3.8. See "using very large resources implicitly on many-core machines" in https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor
807816
) as threadpool:
808-
outputs = threadpool.map(_get_cpp_command_for_files, aquery_output.actions)
809-
817+
event = threading.Event()
818+
outputs = threadpool.map(_get_cpp_command_for_files, map(lambda action: (action, event, should_stop_lambda), aquery_output.actions))
810819
# Yield as compile_commands.json entries
811820
header_files_already_written = set()
812821
for source_files, header_files, compile_command_args in outputs:
@@ -833,7 +842,19 @@ def _convert_compile_commands(aquery_output):
833842

834843
def _get_commands(target: str, flags: str):
835844
"""Return compile_commands.json entries for a given target and flags, gracefully tolerating errors."""
836-
def _get_commands(target_statment):
845+
lock = threading.RLock()
846+
counter = itertools.count()
847+
def _should_stop(headers, file_path):
848+
if file_path:
849+
with lock:
850+
tried_count = next(counter)
851+
if tried_count >= BAZEL_INTERNAL_MAX_HEADER_SEARCH_COUNT:
852+
log_warning(f""">>> Bazel lists no applicable compile commands for {file_path} in {target} under {tried_count} Attempt.""")
853+
return True
854+
return any(header.endswith(file_path) for header in headers)
855+
return False
856+
857+
def _get_commands(target_statment, file_path):
837858
aquery_args = [
838859
'bazel',
839860
'aquery',
@@ -899,7 +920,7 @@ def _get_commands(target_statment):
899920
Continuing gracefully...""")
900921
return []
901922

902-
return _convert_compile_commands(parsed_aquery_output)
923+
return _convert_compile_commands(parsed_aquery_output, lambda _, headers: _should_stop(headers, file_path))
903924

904925
# Log clear completion messages
905926
log_info(f">>> Analyzing commands used in {target}")
@@ -949,7 +970,7 @@ def _get_commands(target_statment):
949970
])
950971

951972
for target_statment in target_statment_canidates:
952-
compile_commands.extend( _get_commands(target_statment))
973+
compile_commands.extend( _get_commands(target_statment, file_path))
953974
if any(command['file'].endswith(file_path) for command in reversed(compile_commands)):
954975
found = True
955976
break
@@ -960,7 +981,7 @@ def _get_commands(target_statment):
960981
if {exclude_external_sources}:
961982
# For efficiency, have bazel filter out external targets (and therefore actions) before they even get turned into actions or serialized and sent to us. Note: this is a different mechanism than is used for excluding just external headers.
962983
target_statment = f"filter('^(//|@//)',{target_statment})"
963-
compile_commands.extend(_get_commands(target_statment))
984+
compile_commands.extend(_get_commands(target_statment, None))
964985
if len(compile_commands) == 0:
965986
log_warning(f""">>> Bazel lists no applicable compile commands for {target}
966987
If this is a header-only library, please instead specify a test or binary target that compiles it (search "header-only" in README.md).

0 commit comments

Comments
 (0)