Also move heuristics into find_headers directly

This commit is contained in:
Harmen Stoppels 2024-11-07 17:12:47 +01:00
parent 1a8d4e46e4
commit 161b67fd09
2 changed files with 53 additions and 22 deletions

View File

@ -1691,6 +1691,7 @@ def find(
root: Union[Path, Sequence[Path]],
files: Union[str, Sequence[str]],
recursive: bool = True,
*,
max_depth: Optional[int] = None,
) -> List[str]:
"""Finds all files matching the patterns from ``files`` starting from ``root``. This function
@ -2104,7 +2105,14 @@ def add_macro(self, macro):
self._macro_definitions.append(macro)
def find_headers(headers, root, recursive=False):
def find_headers(
headers: Union[List[str], str],
root: str,
recursive: bool = False,
*,
heuristic: bool = True,
max_depth: Optional[int] = None,
) -> HeaderList:
"""Returns an iterable object containing a list of full paths to
headers if found.
@ -2120,10 +2128,13 @@ def find_headers(headers, root, recursive=False):
======= ====================================
Parameters:
headers (str or list): Header name(s) to search for
root (str): The root directory to start searching from
recursive (bool): if False search only root folder,
if True descends top-down from the root. Defaults to False.
headers: Header name(s) to search for
root: The root directory to start searching from
recursive: if False (default) search only root folder, if True recurse from the root. Note
that recursive search does not imply exhaustive search if heuristic is True.
heuristic: if True (default), use a non-exhaustive, faster search. Has no effect
if recursive is False.
max_depth: if set, don't search below this depth. Cannot be set if recursive is False.
Returns:
HeaderList: The headers that have been found
@ -2131,10 +2142,10 @@ def find_headers(headers, root, recursive=False):
if isinstance(headers, str):
headers = [headers]
elif not isinstance(headers, collections.abc.Sequence):
message = "{0} expects a string or sequence of strings as the "
message += "first argument [got {1} instead]"
message = message.format(find_headers.__name__, type(headers))
raise TypeError(message)
raise TypeError(
f"{find_headers.__name__} expects a string or sequence of strings as the "
f"first argument [got {type(headers)} instead]"
)
# Construct the right suffix for the headers
suffixes = [
@ -2154,9 +2165,23 @@ def find_headers(headers, root, recursive=False):
]
# List of headers we are searching with suffixes
headers = ["{0}.{1}".format(header, suffix) for header in headers for suffix in suffixes]
headers = [f"{header}.{suffix}" for header in headers for suffix in suffixes]
return HeaderList(find(root, headers, recursive))
if not recursive or not heuristic:
return HeaderList(find(root, headers, recursive=recursive))
# The heuristic here is simpler than the one for libraries: restrict search to <root>/include
# (if root isn't an include directory itself) and limit search depth so that headers are found
# not deeper than <root>/include/<subdir>/*.
if max_depth is None:
max_depth = 2
if os.path.basename(root) != "include":
root = os.path.join(root, "include")
max_depth -= 1
return HeaderList(find(root, headers, recursive=True, max_depth=max_depth))
@system_path_filter
@ -2328,6 +2353,7 @@ def find_libraries(
shared: bool = True,
recursive: bool = False,
runtime: bool = True,
heuristic: bool = True,
max_depth: Optional[int] = None,
) -> LibraryList:
"""Find libraries in the specified root directory.
@ -2348,12 +2374,12 @@ def find_libraries(
root: the root directory to start searching from
shared: if True searches for shared libraries, otherwise for static. Defaults to True.
recursive: if False (default) search only root folder, if True recurse from the root. Note
that recursive search does not imply exhaustive search. The function returns early if
libraries are found in typical, low-depth library directories.
max_depth: if set, don't search below this depth. Cannot be set if recursive is False.
Defaults to 4.
that recursive search does not imply exhaustive search if heuristic is True.
runtime: Windows only option, no-op elsewhere. If True (default), search for runtime shared
libs (.DLL), otherwise, search for .Lib files. If shared is False, this has no meaning.
heuristic: if True (default), use a non-exhaustive, faster search. Has no effect if
recursive is False.
max_depth: if set, don't search below this depth. Cannot be set if recursive is False.
Returns:
LibraryList: The libraries that have been found
@ -2395,13 +2421,17 @@ def find_libraries(
if not recursive:
return LibraryList(find(root, libraries, recursive=False))
elif not heuristic:
return LibraryList(find(root, libraries, recursive=True, max_depth=max_depth))
# Heuristic search: a form of non-exhaustive iterative deepening, in order to return early if
# libraries are found in their usual locations. This is the default behavior for recursive
# searches.
if max_depth is None:
# this default covers search in <root>/lib/pythonX.Y/site-packages/<package>/*.
max_depth = 4
# Even if recursive is True, we will do some form of targeted, iterative deepening, in order
# to return early if libraries are found in common, low-depth library directories.
if sys.platform == "win32":
common_lib_dirs = ("lib", "lib64", "bin", "Lib")
else:

View File

@ -1097,20 +1097,19 @@ def _command_default_handler(spec: "Spec"):
def _headers_default_handler(spec: "Spec"):
"""Default handler when looking for the 'headers' attribute.
Tries to search for ``*.h`` files recursively starting from
``spec.package.home.include``.
Tries to search heuristically for header files in ``spec.package.home``.
Parameters:
spec: spec that is being queried
Returns:
HeaderList: The headers in ``prefix.include``
HeaderList: The headers in ``prefix``
Raises:
NoHeadersError: If no headers are found
"""
home = getattr(spec.package, "home")
headers = fs.find_headers("*", root=home.include, recursive=True)
headers = fs.find_headers("*", root=home, recursive=True, heuristic=True)
if headers:
return headers
@ -1160,7 +1159,9 @@ def _libs_default_handler(spec: "Spec"):
for shared in search_shared:
# Since we are searching for link libraries, on Windows search only for
# ".Lib" extensions by default as those represent import libraries for implicit links.
libs = fs.find_libraries(name, home, shared=shared, recursive=True, runtime=False)
libs = fs.find_libraries(
name, home, shared=shared, recursive=True, runtime=False, heuristic=True
)
if libs:
return libs