| 
50 | 50 | import os  | 
51 | 51 | import platform  | 
52 | 52 | import re  | 
 | 53 | +import site  | 
53 | 54 | import sys  | 
54 | 55 | 
 
  | 
55 | 56 | # Import this before distutils so that setuptools can intercept the distuils  | 
@@ -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 | 
  | 
@@ -372,6 +373,63 @@ def dst_path(self, installer: "InstallerBuildExt") -> Path:  | 
372 | 373 | class InstallerBuildExt(build_ext):  | 
373 | 374 |     """Installs files that were built by cmake."""  | 
374 | 375 | 
 
  | 
 | 376 | +    def __init__(self, *args, **kwargs):  | 
 | 377 | +        self._ran_build = False  | 
 | 378 | +        super().__init__(*args, **kwargs)  | 
 | 379 | + | 
 | 380 | +    def run(self):  | 
 | 381 | +        # Run the build command first in editable mode. Since `build` command  | 
 | 382 | +        # will also trigger `build_ext` command, only run this once.  | 
 | 383 | +        if self._ran_build:  | 
 | 384 | +            return  | 
 | 385 | + | 
 | 386 | +        if self.editable_mode:  | 
 | 387 | +            self._ran_build = True  | 
 | 388 | +            self.run_command("build")  | 
 | 389 | +        super().run()  | 
 | 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(  | 
 | 418 | +                package_dir, os.path.basename(ext.src_path(self))  | 
 | 419 | +            )  | 
 | 420 | + | 
 | 421 | +            # Always copy, even if source is older than destination, to ensure  | 
 | 422 | +            # that the right extensions for the current Python/platform are  | 
 | 423 | +            # used.  | 
 | 424 | +            if os.path.exists(regular_file) or not ext.optional:  | 
 | 425 | +                self.copy_file(regular_file, inplace_file, level=self.verbose)  | 
 | 426 | + | 
 | 427 | +            if ext._needs_stub:  | 
 | 428 | +                inplace_stub = self._get_equivalent_stub(ext, inplace_file)  | 
 | 429 | +                self._write_stub_file(inplace_stub, ext, compile=True)  | 
 | 430 | +                # Always compile stub and remove the original (leave the cache behind)  | 
 | 431 | +                # (this behaviour was observed in previous iterations of the code)  | 
 | 432 | + | 
375 | 433 |     # TODO(dbort): Depend on the "build" command to ensure it runs first  | 
376 | 434 | 
 
  | 
377 | 435 |     def build_extension(self, ext: _BaseExtension) -> None:  | 
@@ -630,6 +688,10 @@ def run(self):  | 
630 | 688 |         if not self.dry_run:  | 
631 | 689 |             # Dry run should log the command but not actually run it.  | 
632 | 690 |             (Path(cmake_cache_dir) / "CMakeCache.txt").unlink(missing_ok=True)  | 
 | 691 | +        # Set PYTHONPATH to the location of the pip package.  | 
 | 692 | +        os.environ["PYTHONPATH"] = (  | 
 | 693 | +            site.getsitepackages()[0] + ";" + os.environ.get("PYTHONPATH", "")  | 
 | 694 | +        )  | 
633 | 695 |         with Buck2EnvironmentFixer():  | 
634 | 696 |             # The context manager may patch the environment while running this  | 
635 | 697 |             # cmake command, which happens to run buck2 to get some source  | 
@@ -741,25 +803,6 @@ def get_ext_modules() -> List[Extension]:  | 
741 | 803 | 
 
  | 
742 | 804 | setup(  | 
743 | 805 |     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 | 806 |     cmdclass={  | 
764 | 807 |         "build": CustomBuild,  | 
765 | 808 |         "build_ext": InstallerBuildExt,  | 
 | 
0 commit comments