Skip to content

Commit

Permalink
Include Extension.depends in manifests and sdists by default (#4000)
Browse files Browse the repository at this point in the history
  • Loading branch information
abravalheri committed Aug 15, 2023
2 parents 463b4df + 5c453aa commit 3c25cdd
Show file tree
Hide file tree
Showing 3 changed files with 329 additions and 17 deletions.
2 changes: 2 additions & 0 deletions newsfragments/4000.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Automatically add files listed in ``Extension.depends`` to sdists,
as long as they are contained in the project directory -- by :user:`RuRo`
42 changes: 42 additions & 0 deletions setuptools/command/build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from importlib.machinery import EXTENSION_SUFFIXES
from importlib.util import cache_from_source as _compiled_file_name
from typing import Dict, Iterator, List, Tuple
from pathlib import Path

from distutils.command.build_ext import build_ext as _du_build_ext
from distutils.ccompiler import new_compiler
Expand Down Expand Up @@ -261,6 +262,47 @@ def links_to_dynamic(self, ext):
pkg = '.'.join(ext._full_name.split('.')[:-1] + [''])
return any(pkg + libname in libnames for libname in ext.libraries)

def get_source_files(self) -> List[str]:
return [*_build_ext.get_source_files(self), *self._get_internal_depends()]

def _get_internal_depends(self) -> Iterator[str]:
"""Yield ``ext.depends`` that are contained by the project directory"""
project_root = Path(self.distribution.src_root or os.curdir).resolve()
depends = (dep for ext in self.extensions for dep in ext.depends)

def skip(orig_path: str, reason: str) -> None:
log.info(
"dependency %s won't be automatically "
"included in the manifest: the path %s",
orig_path,
reason,
)

for dep in depends:
path = Path(dep)

if path.is_absolute():
skip(dep, "must be relative")
continue

if ".." in path.parts:
skip(dep, "can't have `..` segments")
continue

try:
resolved = (project_root / path).resolve(strict=True)
except OSError:
skip(dep, "doesn't exist")
continue

try:
resolved.relative_to(project_root)
except ValueError:
skip(dep, "must be inside the project root")
continue

yield path.as_posix()

def get_outputs(self) -> List[str]:
if self.inplace:
return list(self.get_output_mapping().keys())
Expand Down
Loading

0 comments on commit 3c25cdd

Please sign in to comment.