Skip to content

Commit 732eab5

Browse files
committed
Unify search path handling
- Validate the path types of the entry-points - Update the entry-points values with the settings value - Flatten the entry-points to a list or add them as a dict - Print more debug messages - Fix the settings type to string Signed-off-by: Cristian Le <[email protected]>
1 parent 0cf744c commit 732eab5

File tree

4 files changed

+91
-45
lines changed

4 files changed

+91
-45
lines changed

src/scikit_build_core/_compat/importlib/resources.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,17 @@
77
else:
88
from importlib.resources import files
99

10-
__all__ = ["files"]
10+
if sys.version_info < (3, 10):
11+
# Readers were introduced in 3.10
12+
from importlib_resources import readers
13+
elif sys.version_info < (3, 11):
14+
# In 3.10 readers were implemented in importlib.readers
15+
from importlib import readers
16+
else:
17+
# From 3.11 it is accessed as importlib.resources.readers
18+
from importlib.resources import readers
19+
20+
__all__ = ["files", "readers"]
1121

1222

1323
def __dir__() -> list[str]:

src/scikit_build_core/builder/builder.py

+72-36
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
if TYPE_CHECKING:
2525
from collections.abc import Generator, Iterable, Mapping, Sequence
26+
from typing import Any
2627

2728
from packaging.version import Version
2829

@@ -84,6 +85,68 @@ def _filter_env_cmake_args(env_cmake_args: list[str]) -> Generator[str, None, No
8485
yield arg
8586

8687

88+
# Type-hinting for Traversable is rather hard because they were introduced in python 3.11.
89+
# This avoids introducing importlib_resources dependency that may not be used in the actual package loader
90+
def _sanitize_path(path: Any) -> list[Path] | None:
91+
if isinstance(path, resources.readers.MultiplexedPath):
92+
# pylint: disable-next=protected-access
93+
return path._paths
94+
if isinstance(path, Path):
95+
return [path]
96+
logger.warning("Unknown path type: [{}] {}", type(path), path)
97+
return None
98+
99+
100+
def _handle_search_paths(
101+
entry_point: str,
102+
settings_val: list[str] | dict[str, str] | None,
103+
output: list[Path] | dict[str, list[Path]],
104+
) -> None:
105+
# Sanity checks
106+
if isinstance(output, dict):
107+
assert isinstance(settings_val, dict)
108+
109+
# Get the search paths from the entry points
110+
search_paths_dict = {}
111+
eps = metadata.entry_points(group=entry_point)
112+
if eps:
113+
logger.debug(
114+
"Loading search paths {} from entry-points: {}", entry_point, len(eps)
115+
)
116+
for ep in eps:
117+
ep_value = _sanitize_path(resources.files(ep.load()))
118+
logger.debug("{}: {} -> {}", ep.name, ep.value, ep_value)
119+
if ep_value:
120+
search_paths_dict[ep.name] = ep_value
121+
122+
# Update the search paths from the settings options
123+
if isinstance(settings_val, dict) and settings_val:
124+
logger.debug(
125+
"Overriding search paths {} from config: {}", entry_point, settings_val
126+
)
127+
for key, val in settings_val.items():
128+
if val:
129+
# TODO: Allow settings_val to be dict[str, list[str]]?
130+
search_paths_dict[key] = [Path(val)]
131+
else:
132+
search_paths_dict.pop(key)
133+
134+
# Write to the output
135+
if isinstance(output, list):
136+
search_paths_list = [
137+
path for ep_values in search_paths_dict.values() for path in ep_values
138+
]
139+
# If the settings options was a list the values are appended as-is
140+
if isinstance(settings_val, list) and settings_val:
141+
logger.debug(
142+
"Appending search paths {} with config: {}", entry_point, settings_val
143+
)
144+
search_paths_list += map(Path, settings_val)
145+
output.extend(search_paths_list)
146+
return
147+
output.update(search_paths_dict)
148+
149+
87150
@dataclasses.dataclass
88151
class Builder:
89152
settings: ScikitBuildSettings
@@ -123,47 +186,20 @@ def configure(
123186
}
124187

125188
# Add any extra CMake modules
126-
module_dirs_dict = {
127-
ep.name: resources.files(ep.load())
128-
for ep in metadata.entry_points(group="cmake.module")
129-
}
130-
if isinstance(self.settings.search.modules, dict):
131-
# Allow to override any entry-point definition
132-
module_dirs_dict.update(self.settings.search.modules)
133-
module_dirs = list(module_dirs_dict.values())
134-
if isinstance(self.settings.search.modules, list):
135-
# If it was a list, append to the entry-point definitions
136-
module_dirs += self.settings.search.modules
137-
# Remove any empty paths
138-
module_dirs = [path for path in module_dirs if path]
139-
self.config.module_dirs.extend(module_dirs)
189+
_handle_search_paths(
190+
"cmake.module", self.settings.search.modules, self.config.module_dirs
191+
)
140192

141193
# Add any extra CMake prefixes
142-
prefix_dirs_dict = {
143-
ep.name: resources.files(ep.load())
144-
for ep in metadata.entry_points(group="cmake.prefix")
145-
}
146-
if isinstance(self.settings.search.prefixes, dict):
147-
# Allow to override any entry-point definition
148-
prefix_dirs_dict.update(self.settings.search.prefixes)
149-
prefix_dirs = list(prefix_dirs_dict.values())
150-
if isinstance(self.settings.search.prefixes, list):
151-
# If it was a list, append to the entry-point definitions
152-
prefix_dirs += self.settings.search.prefixes
153-
# Remove any empty paths
154-
prefix_dirs = [path for path in prefix_dirs if path]
155-
self.config.prefix_dirs.extend(prefix_dirs)
194+
_handle_search_paths(
195+
"cmake.prefix", self.settings.search.prefixes, self.config.prefix_dirs
196+
)
156197

157198
# Add all CMake roots
158-
prefix_roots = {
159-
ep.name: resources.files(ep.load())
160-
for ep in metadata.entry_points(group="cmake.root")
161-
}
162199
# TODO: Check for unique uppercase names
163-
prefix_roots.update(self.settings.search.roots)
164-
# Remove any empty paths
165-
prefix_roots = {pkg: path for pkg, path in prefix_roots.items() if path}
166-
self.config.prefix_roots.update(prefix_roots)
200+
_handle_search_paths(
201+
"cmake.root", self.settings.search.roots, self.config.prefix_roots
202+
)
167203

168204
# Add site-packages to the prefix path for CMake
169205
site_packages = Path(sysconfig.get_path("purelib"))

src/scikit_build_core/cmake.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class CMaker:
7979
build_type: str
8080
module_dirs: list[Path] = dataclasses.field(default_factory=list)
8181
prefix_dirs: list[Path] = dataclasses.field(default_factory=list)
82-
prefix_roots: dict[str, Path] = dataclasses.field(default_factory=dict)
82+
prefix_roots: dict[str, list[Path]] = dataclasses.field(default_factory=dict)
8383
init_cache_file: Path = dataclasses.field(init=False, default=Path())
8484
env: dict[str, str] = dataclasses.field(init=False, default_factory=os.environ.copy)
8585
single_config: bool = not sysconfig.get_platform().startswith("win")
@@ -185,14 +185,14 @@ def init_cache(
185185
f.write('set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE "BOTH" CACHE PATH "")\n')
186186

187187
if self.prefix_roots:
188-
for pkg, path in self.prefix_roots.items():
189-
path_str = str(path).replace("\\", "/")
188+
for pkg, path_list in self.prefix_roots.items():
189+
paths_str = ";".join(map(str, path_list)).replace("\\", "/")
190190
f.write(
191-
f'set({pkg}_ROOT [===[{path_str}]===] CACHE PATH "" FORCE)\n'
191+
f'set({pkg}_ROOT [===[{paths_str}]===] CACHE PATH "" FORCE)\n'
192192
)
193193
# Available since CMake 3.27 with CMP0144
194194
f.write(
195-
f'set({pkg.upper()}_ROOT [===[{path_str}]===] CACHE PATH "" FORCE)\n'
195+
f'set({pkg.upper()}_ROOT [===[{paths_str}]===] CACHE PATH "" FORCE)\n'
196196
)
197197

198198
contents = self.init_cache_file.read_text(encoding="utf-8").strip()

src/scikit_build_core/settings/skbuild_model.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,19 @@ class SearchSettings:
9393
Add the wheel build path to the CMake prefix paths.
9494
"""
9595

96-
modules: Optional[Union[List[Path], Dict[str, Path]]] = None
96+
modules: Optional[Union[List[str], Dict[str, str]]] = None
9797
"""
9898
List or dict of CMake module search paths. Dict from is used to override
9999
another package's entry-point definition. Populates `CMAKE_MODULE_PATH`.
100100
"""
101101

102-
prefixes: Optional[Union[List[Path], Dict[str, Path]]] = None
102+
prefixes: Optional[Union[List[str], Dict[str, str]]] = None
103103
"""
104104
List or dict of CMake prefix search paths. Dict from is used to override
105105
another package's entry-point definition. Populates `CMAKE_PREFIX_PATH`.
106106
"""
107107

108-
roots: Dict[str, Path] = dataclasses.field(default_factory=dict)
108+
roots: Dict[str, str] = dataclasses.field(default_factory=dict)
109109
"""
110110
Dict of package names and prefix paths. Populates `<Pkg>_ROOT`.
111111
"""

0 commit comments

Comments
 (0)