Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ editable.verbose = true
# Rebuild the project when the package is imported.
editable.rebuild = false

# Build directory to use when :confval:`editable.mode` is ``inplace``.
editable.build-dir = ""

# Extra args to pass directly to the builder in the build step.
build.tool-args = []

Expand Down Expand Up @@ -281,7 +284,7 @@ strict-config = true
experimental = false

# If set, this will provide a method for backward compatibility.
minimum-version = "0.11" # current version
minimum-version = "0.1" # current version

# The CMake build directory. Defaults to a unique temporary directory.
build-dir = ""
Expand Down
9 changes: 9 additions & 0 deletions docs/reference/configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@ print(mk_skbuild_docs())

## editable

```{eval-rst}
.. confval:: editable.build-dir
:type: ``str``

Build directory to use when :confval:`editable.mode` is ``inplace``.

If empty, the project source directory is used (the historical behaviour).
```

```{eval-rst}
.. confval:: editable.mode
:type: ``"redirect" | "inplace"``
Expand Down
29 changes: 19 additions & 10 deletions src/scikit_build_core/build/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,24 +279,27 @@ def _build_wheel_impl_impl(
root_is_purelib=targetlib == "purelib",
build_tag=settings.wheel.build_tag,
)
format_data = pyproject_format(
settings=settings,
tags=tags,
state=state,
)

# A build dir can be specified, otherwise use a temporary directory
log_build_dir = True
if cmake is not None and editable and settings.editable.mode == "inplace":
build_dir = settings.cmake.source_dir
if settings.editable.build_dir:
build_dir = Path(settings.editable.build_dir.format(**format_data))
else:
build_dir = settings.cmake.source_dir
log_build_dir = False
else:
build_dir = (
Path(
settings.build_dir.format(
**pyproject_format(
settings=settings,
tags=tags,
state=state,
)
)
)
Path(settings.build_dir.format(**format_data))
if settings.build_dir
else build_tmp_folder / "build"
)
if log_build_dir:
logger.info("Build directory: {}", build_dir.resolve())

wheel_dirs = {
Expand Down Expand Up @@ -426,6 +429,12 @@ def _build_wheel_impl_impl(
f"SKBUILD_{k.upper()}_DIR": v for k, v in wheel_dirs.items()
}
cache_entries["SKBUILD_STATE"] = state
if editable:
cache_entries["SKBUILD_EDITABLE_MODE"] = settings.editable.mode
if settings.editable.build_dir:
cache_entries["SKBUILD_EDITABLE_BUILD_DIR"] = os.fspath(
build_dir.resolve()
)
builder.configure(
defines=defines,
cache_entries=cache_entries,
Expand Down
5 changes: 5 additions & 0 deletions src/scikit_build_core/resources/scikit-build.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,11 @@
"type": "boolean",
"default": false,
"description": "Rebuild the project when the package is imported."
},
"build-dir": {
"type": "string",
"default": "",
"description": "Build directory to use when :confval:`editable.mode` is ``inplace``."
}
}
},
Expand Down
7 changes: 7 additions & 0 deletions src/scikit_build_core/settings/skbuild_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,13 @@ class EditableSettings:
:confval:`build-dir` must be set.
"""

build_dir: str = ""
"""
Build directory to use when :confval:`editable.mode` is ``inplace``.

If empty, the project source directory is used (the historical behaviour).
"""
Comment on lines +347 to +352
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can read from your description is you want something like editable.mode = redirect but to fully reuse the build directory right? In that case what we need to do is create a different editable mode, because:

  • inplace mode is explicitly to have the source-dir be exactly the same as build-dir
  • redirect mode reuses the build-dir, but also rebuilds and re-installs each time
  • new mode (name TBD) reuses the build-dir completely and just points the redirect files to the build-dir values

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, but with setuptools, build_ext --inplace still drops the artifacts inside the build directory, and I honestly can’t think of a scenario where someone would expect inplace to dump products directly into the source tree.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each build backend would have their own approach. Here in scikit-build-core the focus is on the CMake workflows and trying to make it close to a vanilla CMake workflow that you would have for debugging a non-python project.

The editable modes are a way to bridge those gaps, and unfortunately this depends more on the source code than the good designs. inplace is very much a last-resort option that we must have in order to help some projects that do not use modern designs using importlib.resources, otherwise the redirect (and a future replacement) is the preferred workflow.



@dataclasses.dataclass
class BuildSettings:
Expand Down
15 changes: 10 additions & 5 deletions tests/packages/simplest_c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ install(
DESTINATION ${SKBUILD_PROJECT_NAME}
COMPONENT PythonModule)

if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}"
AND DEFINED SKBUILD)
# Editable in-place builds. THe empty generator expression ensures
# multi-config enerators keeps us from having to set
# LIBRARY_OUTPUT_DIRECTORY_<CONFIG> too.
if(DEFINED SKBUILD_EDITABLE_MODE AND SKBUILD_EDITABLE_MODE STREQUAL "inplace")
# Editable in-place builds using a dedicated build directory.
set_target_properties(
_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY
"${CMAKE_SOURCE_DIR}/src/${SKBUILD_PROJECT_NAME}$<0:>")
elseif("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}"
AND DEFINED SKBUILD)
# Editable in-place builds with an in-source build directory. The empty
# generator expression ensures multi-config generators keep us from having to
# set LIBRARY_OUTPUT_DIRECTORY_<CONFIG> too.
set_target_properties(
_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/src/${SKBUILD_PROJECT_NAME}$<0:>")
Expand Down
16 changes: 10 additions & 6 deletions tests/test_editable.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ def test_navigate_editable(isolated, isolate, package):
def test_cython_pxd(monkeypatch, tmp_path, editable, editable_mode, isolated):
editable_flag = ["-e"] if editable else []

config_mode_flags = []
config_mode_flags = ["--config-settings=build-dir=build/{wheel_tag}"]
if editable:
config_mode_flags.append(f"--config-settings=editable.mode={editable_mode}")
if editable_mode != "inplace":
config_mode_flags.append("--config-settings=build-dir=build/{wheel_tag}")
if editable_mode == "inplace":
config_mode_flags.append(
"--config-settings=editable.build-dir=build/{wheel_tag}/editable"
)

package1 = PackageInfo(
"cython_pxd_editable/pkg1",
Expand Down Expand Up @@ -170,11 +172,13 @@ def _setup_package_for_editable_layout_tests(
) -> None:
editable_flag = ["-e"] if editable else []

config_mode_flags = []
config_mode_flags = ["--config-settings=build-dir=build/{wheel_tag}"]
if editable:
config_mode_flags.append(f"--config-settings=editable.mode={editable_mode}")
if editable_mode != "inplace":
config_mode_flags.append("--config-settings=build-dir=build/{wheel_tag}")
if editable_mode == "inplace":
config_mode_flags.append(
"--config-settings=editable.build-dir=build/{wheel_tag}/editable"
)

# Use a context so that we only change into the directory up until the point where
# we run the editable install. We do not want to be in that directory when importing
Expand Down
21 changes: 16 additions & 5 deletions tests/test_pyproject_pep660.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ def editable_mode(request: pytest.FixtureRequest) -> str:
@pytest.mark.usefixtures("package_simplest_c")
def test_pep660_wheel(editable_mode: str, tmp_path: Path):
dist = tmp_path / "dist"
out = build_editable(str(dist), {"editable.mode": editable_mode})
config_settings = {
"build-dir": "build/{wheel_tag}",
"editable.mode": editable_mode,
}
if editable_mode == "inplace":
config_settings["editable.build-dir"] = "build/{wheel_tag}/editable"
out = build_editable(str(dist), config_settings)
(wheel,) = dist.glob("simplest-0.0.1-*.whl")
assert wheel == dist / out

Expand Down Expand Up @@ -61,12 +67,17 @@ def test_pep660_pip_isolated(isolated, isolate, editable_mode: str):
if not isolate:
isolated.install("scikit-build-core")

build_dir = "" if editable_mode == "inplace" else "build/{wheel_tag}"

config_flags = [
"--config-settings=build-dir=build/{wheel_tag}",
f"--config-settings=editable.mode={editable_mode}",
]
if editable_mode == "inplace":
config_flags.append(
"--config-settings=editable.build-dir=build/{wheel_tag}/editable"
)
isolated.install(
"-v",
f"--config-settings=build-dir={build_dir}",
f"--config-settings=editable.mode={editable_mode}",
*config_flags,
*isolate_args,
"-e",
".",
Expand Down