|
55 | 55 | # Import this before distutils so that setuptools can intercept the distuils
|
56 | 56 | # imports.
|
57 | 57 | import setuptools # noqa: F401 # usort: skip
|
| 58 | +import logging |
| 59 | +import subprocess |
58 | 60 |
|
59 | 61 | from distutils import log
|
60 | 62 | from distutils.sysconfig import get_python_lib
|
|
66 | 68 | from setuptools.command.build_ext import build_ext
|
67 | 69 | from setuptools.command.build_py import build_py
|
68 | 70 |
|
| 71 | +# Set up logging |
| 72 | +logging.basicConfig( |
| 73 | + level=logging.INFO, format="%(asctime)s [ExecuTorch] %(levelname)s: %(message)s" |
| 74 | +) |
| 75 | +logger = logging.getLogger() |
| 76 | + |
69 | 77 | # For information on setuptools Command subclassing see
|
70 | 78 | # https://setuptools.pypa.io/en/latest/userguide/extension.html
|
71 | 79 |
|
| 80 | +################################################################################ |
| 81 | +# Git submodules |
| 82 | +################################################################################ |
| 83 | +# The following submodules are required to be able to build ExecuTorch. If any of |
| 84 | +# these folders are missing or missing CMakeLists.txt, we will run |
| 85 | +# `git submodule update` to try to fix it. If the command fails, we will raise an |
| 86 | +# error. |
| 87 | +# An alternative to this would be to run `git submodule status` and run |
| 88 | +# `git submodule update` if there's any local changes. However this is a bit |
| 89 | +# too restrictive for users who modifies and tests the dependencies locally. |
| 90 | + |
| 91 | +# keep sorted |
| 92 | +REQUIRED_SUBMODULES = { |
| 93 | + "ao": "LICENSE", # No CMakeLists.txt, choose a sort of stable file to check. |
| 94 | + "cpuinfo": "CMakeLists.txt", |
| 95 | + "eigen": "CMakeLists.txt", |
| 96 | + "flatbuffers": "CMakeLists.txt", |
| 97 | + "FP16": "CMakeLists.txt", |
| 98 | + "FXdiv": "CMakeLists.txt", |
| 99 | + "gflags": "CMakeLists.txt", |
| 100 | + "prelude": "BUCK", |
| 101 | + "pthreadpool": "CMakeLists.txt", |
| 102 | + "pybind11": "CMakeLists.txt", |
| 103 | + "XNNPACK": "CMakeLists.txt", |
| 104 | +} |
| 105 | + |
| 106 | + |
| 107 | +def get_required_submodule_paths(): |
| 108 | + gitmodules_path = os.path.join(os.getcwd(), ".gitmodules") |
| 109 | + |
| 110 | + if not os.path.isfile(gitmodules_path): |
| 111 | + logger.error(".gitmodules file not found.") |
| 112 | + exit(1) |
| 113 | + |
| 114 | + with open(gitmodules_path, "r") as file: |
| 115 | + lines = file.readlines() |
| 116 | + |
| 117 | + # Extract paths of required submodules |
| 118 | + required_paths = {} |
| 119 | + for line in lines: |
| 120 | + if line.strip().startswith("path ="): |
| 121 | + path = line.split("=")[1].strip() |
| 122 | + for submodule, file_name in REQUIRED_SUBMODULES.items(): |
| 123 | + if submodule in path: |
| 124 | + required_paths[path] = file_name |
| 125 | + return required_paths |
| 126 | + |
| 127 | + |
| 128 | +def check_and_update_submodules(): |
| 129 | + def check_folder(folder: str, file: str) -> bool: |
| 130 | + return os.path.isdir(folder) and os.path.isfile(os.path.join(folder, file)) |
| 131 | + |
| 132 | + # Check if the directories exist for each required submodule |
| 133 | + missing_submodules = {} |
| 134 | + for path, file in get_required_submodule_paths().items(): |
| 135 | + if not check_folder(path, file): |
| 136 | + missing_submodules[path] = file |
| 137 | + |
| 138 | + # If any required submodule directories are missing, update them |
| 139 | + if missing_submodules: |
| 140 | + logger.warning("Some required submodules are missing. Updating submodules...") |
| 141 | + try: |
| 142 | + subprocess.check_call(["git", "submodule", "sync"]) |
| 143 | + subprocess.check_call(["git", "submodule", "update", "--init"]) |
| 144 | + except subprocess.CalledProcessError as e: |
| 145 | + logger.error(f"Error updating submodules: {e}") |
| 146 | + exit(1) |
| 147 | + |
| 148 | + # After updating submodules, check again |
| 149 | + for path, file in missing_submodules.items(): |
| 150 | + if not check_folder(path, file): |
| 151 | + logger.error(f"{file} not found in {path}.") |
| 152 | + logger.error("Please run `git submodule update --init`.") |
| 153 | + exit(1) |
| 154 | + logger.info("All required submodules are present.") |
| 155 | + |
72 | 156 |
|
73 | 157 | class ShouldBuild:
|
74 | 158 | """Indicates whether to build various components."""
|
@@ -638,7 +722,28 @@ def run(self):
|
638 | 722 | # lists.
|
639 | 723 |
|
640 | 724 | # Generate the build system files.
|
641 |
| - self.spawn(["cmake", "-S", repo_root, "-B", cmake_cache_dir, *cmake_args]) |
| 725 | + try: |
| 726 | + subprocess.run( |
| 727 | + ["cmake", "-S", repo_root, "-B", cmake_cache_dir, *cmake_args], |
| 728 | + stdout=subprocess.PIPE, |
| 729 | + stderr=subprocess.PIPE, |
| 730 | + check=True, |
| 731 | + text=True, |
| 732 | + ) |
| 733 | + except subprocess.CalledProcessError as e: |
| 734 | + error = str(e.stderr) |
| 735 | + # Our educated guesses from parsing the error message. |
| 736 | + # Missing source file, could be related to git submodules not synced or cmake cache is outdated |
| 737 | + additional_log = "" |
| 738 | + if "Cannot find source file" in error: |
| 739 | + additional_log = ( |
| 740 | + "\033[31;1mEither CMake cache is outdated or git submodules are not synced.\n" |
| 741 | + "Please run the following before retry:\033[0m\n" |
| 742 | + " \033[32;1m./install_executorch.sh --clean\033[0m\n" |
| 743 | + " \033[32;1mgit submodule sync\033[0m\n" |
| 744 | + " \033[32;1mgit submodule update --init\033[0m\n" |
| 745 | + ) |
| 746 | + raise Exception(error + "\n" + additional_log) from e |
642 | 747 |
|
643 | 748 | # Build the system.
|
644 | 749 | self.spawn(["cmake", "--build", cmake_cache_dir, *build_args])
|
@@ -720,31 +825,39 @@ def get_ext_modules() -> List[Extension]:
|
720 | 825 | return ext_modules
|
721 | 826 |
|
722 | 827 |
|
723 |
| -setup( |
724 |
| - version=Version.string(), |
725 |
| - # TODO(dbort): Could use py_modules to restrict the set of modules we |
726 |
| - # package, and package_data to restrict the set up non-python files we |
727 |
| - # include. See also setuptools/discovery.py for custom finders. |
728 |
| - package_dir={ |
729 |
| - "executorch/backends": "backends", |
730 |
| - "executorch/codegen": "codegen", |
731 |
| - # TODO(mnachin T180504136): Do not put examples/models |
732 |
| - # into core pip packages. Refactor out the necessary utils |
733 |
| - # or core models files into a separate package. |
734 |
| - "executorch/examples/models": "examples/models", |
735 |
| - "executorch/exir": "exir", |
736 |
| - "executorch/extension": "extension", |
737 |
| - "executorch/kernels/quantized": "kernels/quantized", |
738 |
| - "executorch/schema": "schema", |
739 |
| - "executorch/devtools": "devtools", |
740 |
| - "executorch/devtools/bundled_program": "devtools/bundled_program", |
741 |
| - "executorch/runtime": "runtime", |
742 |
| - "executorch/util": "util", |
743 |
| - }, |
744 |
| - cmdclass={ |
745 |
| - "build": CustomBuild, |
746 |
| - "build_ext": InstallerBuildExt, |
747 |
| - "build_py": CustomBuildPy, |
748 |
| - }, |
749 |
| - ext_modules=get_ext_modules(), |
750 |
| -) |
| 828 | +def main(): |
| 829 | + # Check submodules |
| 830 | + check_and_update_submodules() |
| 831 | + |
| 832 | + setup( |
| 833 | + version=Version.string(), |
| 834 | + # TODO(dbort): Could use py_modules to restrict the set of modules we |
| 835 | + # package, and package_data to restrict the set up non-python files we |
| 836 | + # include. See also setuptools/discovery.py for custom finders. |
| 837 | + package_dir={ |
| 838 | + "executorch/backends": "backends", |
| 839 | + "executorch/codegen": "codegen", |
| 840 | + # TODO(mnachin T180504136): Do not put examples/models |
| 841 | + # into core pip packages. Refactor out the necessary utils |
| 842 | + # or core models files into a separate package. |
| 843 | + "executorch/examples/models": "examples/models", |
| 844 | + "executorch/exir": "exir", |
| 845 | + "executorch/extension": "extension", |
| 846 | + "executorch/kernels/quantized": "kernels/quantized", |
| 847 | + "executorch/schema": "schema", |
| 848 | + "executorch/devtools": "devtools", |
| 849 | + "executorch/devtools/bundled_program": "devtools/bundled_program", |
| 850 | + "executorch/runtime": "runtime", |
| 851 | + "executorch/util": "util", |
| 852 | + }, |
| 853 | + cmdclass={ |
| 854 | + "build": CustomBuild, |
| 855 | + "build_ext": InstallerBuildExt, |
| 856 | + "build_py": CustomBuildPy, |
| 857 | + }, |
| 858 | + ext_modules=get_ext_modules(), |
| 859 | + ) |
| 860 | + |
| 861 | + |
| 862 | +if __name__ == "__main__": |
| 863 | + main() |
0 commit comments