|
51 | 51 | import platform |
52 | 52 | import re |
53 | 53 | import sys |
| 54 | +import site |
54 | 55 |
|
55 | 56 | # Import this before distutils so that setuptools can intercept the distuils |
56 | 57 | # imports. |
@@ -239,7 +240,7 @@ def src_path(self, installer: "InstallerBuildExt") -> Path: |
239 | 240 | srcs = tuple(cmake_cache_dir.glob(self.src)) |
240 | 241 | if len(srcs) != 1: |
241 | 242 | raise ValueError( |
242 | | - f"""Expected exactly one file matching '{self.src}'; found {repr(srcs)}. |
| 243 | + f"""Expected exactly one file matching '{self.src}' in {cmake_cache_dir}; found {repr(srcs)}. |
243 | 244 |
|
244 | 245 | If that file is a CMake-built extension module file, and we are installing in editable mode, please disable the corresponding build option since it's not supported yet. |
245 | 246 |
|
@@ -371,7 +372,63 @@ def dst_path(self, installer: "InstallerBuildExt") -> Path: |
371 | 372 |
|
372 | 373 | class InstallerBuildExt(build_ext): |
373 | 374 | """Installs files that were built by cmake.""" |
| 375 | + def __init__(self, *args, **kwargs): |
| 376 | + self._ran_build = False |
| 377 | + super().__init__(*args, **kwargs) |
374 | 378 |
|
| 379 | + def run(self): |
| 380 | + # Run the build command first in editable mode. Since `build` command |
| 381 | + # will also trigger `build_ext` command, only run this once. |
| 382 | + if self._ran_build: |
| 383 | + return |
| 384 | + |
| 385 | + if self.editable_mode: |
| 386 | + self._ran_build = True |
| 387 | + self.run_command("build") |
| 388 | + super().run() |
| 389 | + |
| 390 | + |
| 391 | + def copy_extensions_to_source(self) -> None: |
| 392 | + """For each extension in `ext_modules`, we need to copy the extension |
| 393 | + file from the build directory to the correct location in the local |
| 394 | + directory. |
| 395 | +
|
| 396 | + This should only be triggered when inplace mode (editable mode) is enabled. |
| 397 | +
|
| 398 | + Args: |
| 399 | +
|
| 400 | + Returns: |
| 401 | + """ |
| 402 | + build_py = self.get_finalized_command('build_py') |
| 403 | + for ext in self.extensions: |
| 404 | + if isinstance(ext, BuiltExtension): |
| 405 | + modpath = ext.name.split('.') |
| 406 | + package = '.'.join(modpath[:-1]) |
| 407 | + package_dir = os.path.abspath(build_py.get_package_dir(package)) |
| 408 | + else: |
| 409 | + # HACK: get rid of the leading "executorch" in ext.dst. |
| 410 | + # This is because we don't have a root level "executorch" module. |
| 411 | + package_dir = ext.dst.removeprefix("executorch/") |
| 412 | + |
| 413 | + # Ensure that the destination directory exists. |
| 414 | + self.mkpath(os.fspath(package_dir)) |
| 415 | + |
| 416 | + regular_file = ext.src_path(self) |
| 417 | + inplace_file = os.path.join(package_dir, os.path.basename(ext.src_path(self))) |
| 418 | + |
| 419 | + # Always copy, even if source is older than destination, to ensure |
| 420 | + # that the right extensions for the current Python/platform are |
| 421 | + # used. |
| 422 | + if os.path.exists(regular_file) or not ext.optional: |
| 423 | + self.copy_file(regular_file, inplace_file, level=self.verbose) |
| 424 | + |
| 425 | + if ext._needs_stub: |
| 426 | + inplace_stub = self._get_equivalent_stub(ext, inplace_file) |
| 427 | + self._write_stub_file(inplace_stub, ext, compile=True) |
| 428 | + # Always compile stub and remove the original (leave the cache behind) |
| 429 | + # (this behaviour was observed in previous iterations of the code) |
| 430 | + |
| 431 | + |
375 | 432 | # TODO(dbort): Depend on the "build" command to ensure it runs first |
376 | 433 |
|
377 | 434 | def build_extension(self, ext: _BaseExtension) -> None: |
@@ -630,6 +687,8 @@ def run(self): |
630 | 687 | if not self.dry_run: |
631 | 688 | # Dry run should log the command but not actually run it. |
632 | 689 | (Path(cmake_cache_dir) / "CMakeCache.txt").unlink(missing_ok=True) |
| 690 | + # Set PYTHONPATH to the location of the pip package. |
| 691 | + os.environ["PYTHONPATH"] = site.getsitepackages()[0] + ";" + os.environ["PYTHONPATH"] |
633 | 692 | with Buck2EnvironmentFixer(): |
634 | 693 | # The context manager may patch the environment while running this |
635 | 694 | # cmake command, which happens to run buck2 to get some source |
@@ -741,25 +800,6 @@ def get_ext_modules() -> List[Extension]: |
741 | 800 |
|
742 | 801 | setup( |
743 | 802 | version=Version.string(), |
744 | | - # TODO(dbort): Could use py_modules to restrict the set of modules we |
745 | | - # package, and package_data to restrict the set up non-python files we |
746 | | - # include. See also setuptools/discovery.py for custom finders. |
747 | | - package_dir={ |
748 | | - "executorch/backends": "backends", |
749 | | - "executorch/codegen": "codegen", |
750 | | - # TODO(mnachin T180504136): Do not put examples/models |
751 | | - # into core pip packages. Refactor out the necessary utils |
752 | | - # or core models files into a separate package. |
753 | | - "executorch/examples/models": "examples/models", |
754 | | - "executorch/exir": "exir", |
755 | | - "executorch/extension": "extension", |
756 | | - "executorch/kernels/quantized": "kernels/quantized", |
757 | | - "executorch/schema": "schema", |
758 | | - "executorch/devtools": "devtools", |
759 | | - "executorch/devtools/bundled_program": "devtools/bundled_program", |
760 | | - "executorch/runtime": "runtime", |
761 | | - "executorch/util": "util", |
762 | | - }, |
763 | 803 | cmdclass={ |
764 | 804 | "build": CustomBuild, |
765 | 805 | "build_ext": InstallerBuildExt, |
|
0 commit comments