Repository: scikit-build/scikit-build-core
Pull request #916 (2024-09-27 16:55:59)

fix: ignore build dir automatically
See #914.



Patch:
diff --git a/src/scikit_build_core/_shutil.py b/src/scikit_build_core/_shutil.py
index 46fce89..e898587 100644
--- a/src/scikit_build_core/_shutil.py
+++ b/src/scikit_build_core/_shutil.py
@@ -24,6 +24,7 @@ def __dir__() -> list[str]:
 class Run:
     env: dict[str, str] | None = None
     cwd: os.PathLike[str] | None = None
+    timeout: None | float = None
 
     # Stores last printout, for cleaner debug logging
     _prev_env: ClassVar[dict[str, str]] = {}
@@ -75,6 +76,7 @@ class Run:
             capture_output=capture,
             env=self.env,
             cwd=self.cwd,
+            timeout=self.timeout,
         )
 
     def _key_diff(self, k: str) -> str:
diff --git a/src/scikit_build_core/build/_file_processor.py b/src/scikit_build_core/build/_file_processor.py
index ed48fa7..4825b28 100644
--- a/src/scikit_build_core/build/_file_processor.py
+++ b/src/scikit_build_core/build/_file_processor.py
@@ -32,6 +32,7 @@ def each_unignored_file(
     starting_path: Path,
     include: Sequence[str] = (),
     exclude: Sequence[str] = (),
+    build_dir: str = "",
 ) -> Generator[Path, None, None]:
     """
     Runs through all non-ignored files. Must be run from the root directory.
@@ -50,9 +51,20 @@ def each_unignored_file(
         if p != Path(".gitignore")
     }
 
+    exclude_build_dir = build_dir.format(
+        cache_tag="*",
+        wheel_tag="*",
+        build_type="*",
+        state="*",
+    )
+
+    exclude_lines = (
+        [*EXCLUDE_LINES, exclude_build_dir] if exclude_build_dir else EXCLUDE_LINES
+    )
+
     user_exclude_spec = pathspec.GitIgnoreSpec.from_lines(list(exclude))
     global_exclude_spec = pathspec.GitIgnoreSpec.from_lines(global_exclude_lines)
-    builtin_exclude_spec = pathspec.GitIgnoreSpec.from_lines(EXCLUDE_LINES)
+    builtin_exclude_spec = pathspec.GitIgnoreSpec.from_lines(exclude_lines)
 
     include_spec = pathspec.GitIgnoreSpec.from_lines(include)
 
diff --git a/src/scikit_build_core/build/_pathutil.py b/src/scikit_build_core/build/_pathutil.py
index cc1ff5e..9242290 100644
--- a/src/scikit_build_core/build/_pathutil.py
+++ b/src/scikit_build_core/build/_pathutil.py
@@ -41,6 +41,7 @@ def packages_to_file_mapping(
     include: Sequence[str],
     src_exclude: Sequence[str],
     target_exclude: Sequence[str],
+    build_dir: str,
 ) -> dict[str, str]:
     """
     This will output a mapping of source files to target files.
@@ -55,6 +56,7 @@ def packages_to_file_mapping(
             source_dir,
             include=include,
             exclude=src_exclude,
+            build_dir=build_dir,
         ):
             rel_path = filepath.relative_to(source_dir)
             target_path = platlib_dir / package_dir / rel_path
diff --git a/src/scikit_build_core/build/sdist.py b/src/scikit_build_core/build/sdist.py
index 554e807..9a06274 100644
--- a/src/scikit_build_core/build/sdist.py
+++ b/src/scikit_build_core/build/sdist.py
@@ -154,6 +154,7 @@ def build_sdist(
                 Path(),
                 include=settings.sdist.include,
                 exclude=settings.sdist.exclude,
+                build_dir=settings.build_dir,
             )
         )
         for filepath in paths:
diff --git a/src/scikit_build_core/build/wheel.py b/src/scikit_build_core/build/wheel.py
index 27236e0..d46999c 100644
--- a/src/scikit_build_core/build/wheel.py
+++ b/src/scikit_build_core/build/wheel.py
@@ -467,6 +467,7 @@ def _build_wheel_impl_impl(
             include=settings.sdist.include,
             src_exclude=settings.sdist.exclude,
             target_exclude=settings.wheel.exclude,
+            build_dir=settings.build_dir,
         )
 
         if not editable:
diff --git a/src/scikit_build_core/program_search.py b/src/scikit_build_core/program_search.py
index 29d0834..28e2324 100644
--- a/src/scikit_build_core/program_search.py
+++ b/src/scikit_build_core/program_search.py
@@ -80,7 +80,7 @@ def get_cmake_program(cmake_path: Path) -> Program:
     None if it cannot be determined.
     """
     try:
-        result = Run().capture(cmake_path, "-E", "capabilities")
+        result = Run(timeout=2).capture(cmake_path, "-E", "capabilities")
         try:
             version = Version(
                 json.loads(result.stdout)["version"]["string"].split("-")[0]
@@ -91,7 +91,7 @@ def get_cmake_program(cmake_path: Path) -> Program:
             logger.warning("Could not determine CMake version, got {!r}", result.stdout)
     except subprocess.CalledProcessError:
         try:
-            result = Run().capture(cmake_path, "--version")
+            result = Run(timeout=2).capture(cmake_path, "--version")
             try:
                 version = Version(
                     result.stdout.splitlines()[0].split()[-1].split("-")[0]
@@ -111,6 +111,8 @@ def get_cmake_program(cmake_path: Path) -> Program:
             )
     except PermissionError:
         logger.warning("Permissions Error getting CMake's version")
+    except subprocess.TimeoutExpired:
+        logger.warning("Accessing CMake timed out, ignoring")
 
     return Program(cmake_path, None)
 
@@ -133,8 +135,12 @@ def get_ninja_programs(*, module: bool = True) -> Generator[Program, None, None]
     """
     for ninja_path in _get_ninja_path(module=module):
         try:
-            result = Run().capture(ninja_path, "--version")
-        except (subprocess.CalledProcessError, PermissionError):
+            result = Run(timeout=2).capture(ninja_path, "--version")
+        except (
+            subprocess.CalledProcessError,
+            PermissionError,
+            subprocess.TimeoutExpired,
+        ):
             yield Program(ninja_path, None)
             continue
 


Test patch:
diff --git a/tests/test_editable_unit.py b/tests/test_editable_unit.py
index 4858436..ba15f9d 100644
--- a/tests/test_editable_unit.py
+++ b/tests/test_editable_unit.py
@@ -141,6 +141,7 @@ def test_navigate_editable_pkg(editable_package: EditablePackage, virtualenv: VE
         include=[],
         src_exclude=[],
         target_exclude=[],
+        build_dir="",
     )
     assert mapping == {
         str(Path("pkg/__init__.py")): str(pkg_dir / "__init__.py"),