Skip to content

Commit 8183b70

Browse files
committed
Read cmake.root entry_points and unify search path handling
Signed-off-by: Cristian Le <[email protected]>
1 parent a09eb91 commit 8183b70

File tree

5 files changed

+155
-6
lines changed

5 files changed

+155
-6
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,20 @@ messages.after-success = ""
312312
# paths.
313313
use-site-packages = true
314314

315+
# Entry points to ignore. Any entry-point in `cmake.module`, `cmake.prefix`,
316+
# `cmake.root` with a key value matching a value in this list will be ignored
317+
# when building the search paths.
318+
ignore_entry_point = []
319+
320+
# List of additional CMake module search paths. Populates `CMAKE_MODULE_PATH`.
321+
search.modules = []
322+
323+
# List of additional CMake prefix search paths. Populates `CMAKE_PREFIX_PATH`.
324+
search.prefixes = []
325+
326+
# Dict of package names and prefix paths. Populates `<Pkg>_ROOT`.
327+
search.roots = {}
328+
315329
# List dynamic metadata fields and hook locations in this table.
316330
metadata = {}
317331

src/scikit_build_core/builder/builder.py

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,32 @@ def _sanitize_path(path: os.PathLike[str]) -> list[Path]:
9797
return [Path(os.fspath(path))]
9898

9999

100+
def _merge_search_paths(
101+
entry_point_search_path: dict[str, list[Path]],
102+
settings_val: list[str] | dict[str, str],
103+
output: list[Path] | dict[str, list[Path]],
104+
) -> None:
105+
if isinstance(settings_val, dict):
106+
# if the settings and output search paths are dicts, just override them
107+
# and update the output
108+
assert isinstance(output, dict)
109+
# No need to clone this because the search paths are not used anywhere else.
110+
# Just renaming for readability
111+
search_paths_dict = entry_point_search_path
112+
for key, val in settings_val.items():
113+
search_paths_dict[key] = [Path(val)]
114+
output.update(search_paths_dict)
115+
return
116+
# Otherwise the settings and outputs are lists.
117+
# We flatten out the dict into a list and append the settings values.
118+
assert isinstance(output, list)
119+
search_paths_list = [
120+
path for ep_values in entry_point_search_path.values() for path in ep_values
121+
]
122+
search_paths_list += map(Path, settings_val)
123+
output.extend(search_paths_list)
124+
125+
100126
@dataclasses.dataclass
101127
class Builder:
102128
settings: ScikitBuildSettings
@@ -120,6 +146,23 @@ def get_cmake_args(self) -> list[str]:
120146
def get_generator(self, *args: str) -> str | None:
121147
return self.config.get_generator(*self.get_cmake_args(), *args)
122148

149+
def _get_entry_point_search_path(self, entry_point: str) -> dict[str, list[Path]]:
150+
"""Get the search path dict from the entry points"""
151+
search_paths = {}
152+
eps = metadata.entry_points(group=entry_point)
153+
if eps:
154+
logger.debug(
155+
"Loading search paths {} from entry-points: {}", entry_point, len(eps)
156+
)
157+
for ep in eps:
158+
if ep.name in self.settings.search.ignore_entry_point:
159+
logger.debug("Ignoring entry-point: {}", ep.name)
160+
ep_value = _sanitize_path(resources.files(ep.load()))
161+
logger.debug("{}: {} -> {}", ep.name, ep.value, ep_value)
162+
if ep_value:
163+
search_paths[ep.name] = ep_value
164+
return search_paths
165+
123166
def configure(
124167
self,
125168
*,
@@ -136,16 +179,29 @@ def configure(
136179
}
137180

138181
# Add any extra CMake modules
139-
eps = metadata.entry_points(group="cmake.module")
140-
self.config.module_dirs.extend(
141-
p for ep in eps for p in _sanitize_path(resources.files(ep.load()))
182+
_merge_search_paths(
183+
self._get_entry_point_search_path("cmake.module"),
184+
self.settings.search.modules,
185+
self.config.module_dirs,
142186
)
187+
logger.debug("cmake.modules: {}", self.config.module_dirs)
143188

144189
# Add any extra CMake prefixes
145-
eps = metadata.entry_points(group="cmake.prefix")
146-
self.config.prefix_dirs.extend(
147-
p for ep in eps for p in _sanitize_path(resources.files(ep.load()))
190+
_merge_search_paths(
191+
self._get_entry_point_search_path("cmake.prefix"),
192+
self.settings.search.prefixes,
193+
self.config.prefix_dirs,
194+
)
195+
logger.debug("cmake.prefix: {}", self.config.prefix_dirs)
196+
197+
# Add all CMake roots
198+
# TODO: Check for unique uppercase names
199+
_merge_search_paths(
200+
self._get_entry_point_search_path("cmake.root"),
201+
self.settings.search.roots,
202+
self.config.prefix_roots,
148203
)
204+
logger.debug("cmake.root: {}", self.config.prefix_roots)
149205

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

src/scikit_build_core/cmake.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +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, list[Path]] = dataclasses.field(default_factory=dict)
8283
init_cache_file: Path = dataclasses.field(init=False, default=Path())
8384
env: dict[str, str] = dataclasses.field(init=False, default_factory=os.environ.copy)
8485
single_config: bool = not sysconfig.get_platform().startswith("win")
@@ -183,6 +184,17 @@ def init_cache(
183184
)
184185
f.write('set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE "BOTH" CACHE PATH "")\n')
185186

187+
if self.prefix_roots:
188+
for pkg, path_list in self.prefix_roots.items():
189+
paths_str = ";".join(map(str, path_list)).replace("\\", "/")
190+
f.write(
191+
f'set({pkg}_ROOT [===[{paths_str}]===] CACHE PATH "" FORCE)\n'
192+
)
193+
# Available since CMake 3.27 with CMP0144
194+
f.write(
195+
f'set({pkg.upper()}_ROOT [===[{paths_str}]===] CACHE PATH "" FORCE)\n'
196+
)
197+
186198
contents = self.init_cache_file.read_text(encoding="utf-8").strip()
187199
logger.debug(
188200
"{}:\n{}",

src/scikit_build_core/resources/scikit-build.schema.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,36 @@
9696
"search": {
9797
"additionalProperties": false,
9898
"properties": {
99+
"modules": {
100+
"description": "List of additional CMake module search paths. Populates `CMAKE_MODULE_PATH`.",
101+
"type": "array",
102+
"items": {
103+
"type": "string"
104+
}
105+
},
106+
"prefixes": {
107+
"description": "List of additional CMake prefix search paths. Populates `CMAKE_PREFIX_PATH`.",
108+
"type": "array",
109+
"items": {
110+
"type": "string"
111+
}
112+
},
113+
"roots": {
114+
"description": "Dict of package names and prefix paths. Populates `<Pkg>_ROOT`.",
115+
"type": "object",
116+
"patternProperties": {
117+
".+": {
118+
"type": "string"
119+
}
120+
}
121+
},
122+
"ignore_entry_point": {
123+
"description": "Entry points to ignore. Any entry-point in `cmake.module`, `cmake.prefix`, `cmake.root` with a key value matching a value in this list will be ignored when building the search paths.",
124+
"type": "array",
125+
"items": {
126+
"type": "string"
127+
}
128+
},
99129
"use-site-packages": {
100130
"default": true,
101131
"description": "Add the install (or build isolation) site_packages folder to the CMake prefix paths.",
@@ -535,6 +565,21 @@
535565
}
536566
}
537567
},
568+
"search": {
569+
"type": "object",
570+
"additionalProperties": false,
571+
"properties": {
572+
"modules": {
573+
"$ref": "#/$defs/inherit"
574+
},
575+
"prefixes": {
576+
"$ref": "#/$defs/inherit"
577+
},
578+
"roots": {
579+
"$ref": "#/$defs/inherit"
580+
}
581+
}
582+
},
538583
"wheel": {
539584
"type": "object",
540585
"additionalProperties": false,

src/scikit_build_core/settings/skbuild_model.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,28 @@ class SearchSettings:
8989
paths.
9090
"""
9191

92+
ignore_entry_point: List[str] = dataclasses.field(default_factory=list)
93+
"""
94+
Entry points to ignore. Any entry-point in `cmake.module`, `cmake.prefix`,
95+
`cmake.root` with a key value matching a value in this list will be ignored
96+
when building the search paths.
97+
"""
98+
99+
modules: List[str] = dataclasses.field(default_factory=list)
100+
"""
101+
List of additional CMake module search paths. Populates `CMAKE_MODULE_PATH`.
102+
"""
103+
104+
prefixes: List[str] = dataclasses.field(default_factory=list)
105+
"""
106+
List of additional CMake prefix search paths. Populates `CMAKE_PREFIX_PATH`.
107+
"""
108+
109+
roots: Dict[str, str] = dataclasses.field(default_factory=dict)
110+
"""
111+
Dict of package names and prefix paths. Populates `<Pkg>_ROOT`.
112+
"""
113+
92114

93115
@dataclasses.dataclass
94116
class NinjaSettings:

0 commit comments

Comments
 (0)