Skip to content

Commit 8df2238

Browse files
committed
End loop if header is found & Add max attempts limit
1 parent 251bbb2 commit 8df2238

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
@@ -35,6 +35,8 @@
3535
import time
3636
import types
3737
import typing # MIN_PY=3.9: Switch e.g. typing.List[str] -> list[str]
38+
import threading
39+
import itertools
3840

3941

4042
@enum.unique
@@ -178,6 +180,7 @@ def _get_cached_adjusted_modified_time(path: str):
178180
# 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.
179181
BAZEL_INTERNAL_SOURCE_CUTOFF = time.time() + 60*60*24*365
180182

183+
BAZEL_INTERNAL_MAX_HEADER_SEARCH_COUNT = 500
181184

182185
def _get_headers_gcc(compile_args: typing.List[str], source_path: str, action_key: str):
183186
"""Gets the headers used by a particular compile command that uses gcc arguments formatting (including clang.)
@@ -759,22 +762,28 @@ def _all_platform_patch(compile_args: typing.List[str]):
759762
return compile_args
760763

761764

762-
def _get_cpp_command_for_files(compile_action):
765+
def _get_cpp_command_for_files(args):
763766
"""Reformat compile_action into a compile command clangd can understand.
764767
765768
Undo Bazel-isms and figures out which files clangd should apply the command to.
766769
"""
770+
(compile_action, event, should_stop_lambda) = args
771+
if event.is_set():
772+
return set(), set(), []
773+
767774
# Patch command by platform
768775
compile_action.arguments = _all_platform_patch(compile_action.arguments)
769776
compile_action.arguments = _apple_platform_patch(compile_action.arguments)
770777
# Android and Linux and grailbio LLVM toolchains: Fine as is; no special patching needed.
771778

772779
source_files, header_files = _get_files(compile_action)
773780

781+
if not event.is_set() and should_stop_lambda(source_files, header_files):
782+
event.set()
774783
return source_files, header_files, compile_action.arguments
775784

776785

777-
def _convert_compile_commands(aquery_output):
786+
def _convert_compile_commands(aquery_output, should_stop_lambda):
778787
"""Converts from Bazel's aquery format to de-Bazeled compile_commands.json entries.
779788
780789
Input: jsonproto output from aquery, pre-filtered to (Objective-)C(++) compile actions for a given build.
@@ -798,8 +807,8 @@ def _convert_compile_commands(aquery_output):
798807
with concurrent.futures.ThreadPoolExecutor(
799808
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
800809
) as threadpool:
801-
outputs = threadpool.map(_get_cpp_command_for_files, aquery_output.actions)
802-
810+
event = threading.Event()
811+
outputs = threadpool.map(_get_cpp_command_for_files, map(lambda action: (action, event, should_stop_lambda), aquery_output.actions))
803812
# Yield as compile_commands.json entries
804813
header_files_already_written = set()
805814
for source_files, header_files, compile_command_args in outputs:
@@ -826,7 +835,19 @@ def _convert_compile_commands(aquery_output):
826835

827836
def _get_commands(target: str, flags: str):
828837
"""Return compile_commands.json entries for a given target and flags, gracefully tolerating errors."""
829-
def _get_commands(target_statment):
838+
lock = threading.RLock()
839+
counter = itertools.count()
840+
def _should_stop(headers, file_path):
841+
if file_path:
842+
with lock:
843+
tried_count = next(counter)
844+
if tried_count >= BAZEL_INTERNAL_MAX_HEADER_SEARCH_COUNT:
845+
log_warning(f""">>> Bazel lists no applicable compile commands for {file_path} in {target} under {tried_count} Attempt.""")
846+
return True
847+
return any(header.endswith(file_path) for header in headers)
848+
return False
849+
850+
def _get_commands(target_statment, file_path):
830851
aquery_args = [
831852
'bazel',
832853
'aquery',
@@ -887,7 +908,7 @@ def _get_commands(target_statment):
887908
if not getattr(parsed_aquery_output, 'actions', None): # Unifies cases: No actions (or actions list is empty)
888909
return []
889910

890-
return _convert_compile_commands(parsed_aquery_output)
911+
return _convert_compile_commands(parsed_aquery_output, lambda _, headers: _should_stop(headers, file_path))
891912

892913

893914
# Log clear completion messages
@@ -938,7 +959,7 @@ def _get_commands(target_statment):
938959
])
939960

940961
for target_statment in target_statment_canidates:
941-
compile_commands.extend( _get_commands(target_statment))
962+
compile_commands.extend( _get_commands(target_statment, file_path))
942963
if any(command['file'].endswith(file_path) for command in reversed(compile_commands)):
943964
found = True
944965
break
@@ -950,7 +971,7 @@ def _get_commands(target_statment):
950971
if {exclude_external_sources}:
951972
# 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.
952973
target_statment = f"filter('^(//|@//)',{target_statment})"
953-
compile_commands.extend(_get_commands(target_statment))
974+
compile_commands.extend(_get_commands(target_statment, None))
954975
if len(compile_commands) == 0:
955976
log_warning(f""">>> Bazel lists no applicable compile commands for {target}
956977
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)