|
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 | | -from distutils.errors import DistutilsExecError |
61 | 62 | from distutils.sysconfig import get_python_lib |
62 | 63 | from pathlib import Path |
63 | 64 | from typing import List, Optional |
|
67 | 68 | from setuptools.command.build_ext import build_ext |
68 | 69 | from setuptools.command.build_py import build_py |
69 | 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 | + |
70 | 77 | # For information on setuptools Command subclassing see |
71 | 78 | # https://setuptools.pypa.io/en/latest/userguide/extension.html |
72 | 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", |
| 94 | + "cpuinfo", |
| 95 | + "eigen", |
| 96 | + "flatbuffers", |
| 97 | + "FP16", |
| 98 | + "FXdiv", |
| 99 | + "gflags", |
| 100 | + "prelude", |
| 101 | + "pthreadpool", |
| 102 | + "pybind11", |
| 103 | + "XNNPACK", |
| 104 | +] |
| 105 | + |
| 106 | + |
| 107 | +def get_required_submodule_paths(): |
| 108 | + gitsubmodule_path = os.path.join(os.getcwd(), ".gitsubmodule") |
| 109 | + |
| 110 | + if not os.path.isfile(gitsubmodule_path): |
| 111 | + print("Error: .gitsubmodule file not found.") |
| 112 | + exit(1) |
| 113 | + |
| 114 | + with open(gitsubmodule_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 | + if any(submodule in path for submodule in REQUIRED_SUBMODULES): |
| 123 | + required_paths.append(path) |
| 124 | + return required_paths |
| 125 | + |
| 126 | + |
| 127 | +def check_and_update_submodules(): |
| 128 | + def check_folder(folder: str) -> bool: |
| 129 | + return os.path.isdir(folder) and os.path.isfile( |
| 130 | + os.path.join(folder, "CMakeLists.txt") |
| 131 | + ) |
| 132 | + |
| 133 | + # Check if the directories exist for each required submodule |
| 134 | + missing_submodules = [] |
| 135 | + for path in get_required_submodule_paths(): |
| 136 | + if not check_folder(path): |
| 137 | + missing_submodules.append(path) |
| 138 | + |
| 139 | + # If any required submodule directories are missing, update them |
| 140 | + if missing_submodules: |
| 141 | + logger.warn("Some required submodules are missing. Updating submodules...") |
| 142 | + try: |
| 143 | + subprocess.check_call( |
| 144 | + ["git", "submodule", "update", "--init", "--recursive"] |
| 145 | + ) |
| 146 | + except subprocess.CalledProcessError as e: |
| 147 | + logger.error(f"Error updating submodules: {e}") |
| 148 | + exit(1) |
| 149 | + |
| 150 | + # After updating submodules, check again |
| 151 | + for path in missing_submodules: |
| 152 | + if not check_folder(path): |
| 153 | + logger.error(f"Error: CMakeLists.txt not found in {path}.") |
| 154 | + logger.error("Please run `git submodule update --init --recursive`.") |
| 155 | + exit(1) |
| 156 | + logger.info("All required submodules are present and contain CMakeLists.txt.") |
| 157 | + |
73 | 158 |
|
74 | 159 | class ShouldBuild: |
75 | 160 | """Indicates whether to build various components.""" |
@@ -639,22 +724,7 @@ def run(self): |
639 | 724 | # lists. |
640 | 725 |
|
641 | 726 | # Generate the build system files. |
642 | | - try: |
643 | | - self.spawn( |
644 | | - ["cmake", "-S", repo_root, "-B", cmake_cache_dir, *cmake_args] |
645 | | - ) |
646 | | - except DistutilsExecError as e: |
647 | | - error = str(e) |
648 | | - # Our educated guesses from parsing the error message. |
649 | | - additional_log = "" |
650 | | - # 1. Prelude related errors, could be related to third-party/prelude pin out of sync |
651 | | - # 2. Missing source file, could be related to git submodules not synced or cmake cache is outdated |
652 | | - if "third-party/prelude" or "Cannot find source file" in error: |
653 | | - additional_log += "ExecuTorch: \033[31;1mEither CMake cache is outdated or git submodules are not synced.\033[0m\n" |
654 | | - additional_log += "ExecuTorch: \033[31;1mPlease run the following before retry:\033[0m\n" |
655 | | - additional_log += "ExecuTorch: \033[32;1m./install_executorch.sh --clean\033[0m\n" |
656 | | - additional_log += "ExecuTorch: \033[32;1mgit submodule update --init --recursive\033[0m\n" |
657 | | - raise Exception(error + "\n" + additional_log) from e |
| 727 | + self.spawn(["cmake", "-S", repo_root, "-B", cmake_cache_dir, *cmake_args]) |
658 | 728 |
|
659 | 729 | # Build the system. |
660 | 730 | self.spawn(["cmake", "--build", cmake_cache_dir, *build_args]) |
@@ -736,31 +806,39 @@ def get_ext_modules() -> List[Extension]: |
736 | 806 | return ext_modules |
737 | 807 |
|
738 | 808 |
|
739 | | -setup( |
740 | | - version=Version.string(), |
741 | | - # TODO(dbort): Could use py_modules to restrict the set of modules we |
742 | | - # package, and package_data to restrict the set up non-python files we |
743 | | - # include. See also setuptools/discovery.py for custom finders. |
744 | | - package_dir={ |
745 | | - "executorch/backends": "backends", |
746 | | - "executorch/codegen": "codegen", |
747 | | - # TODO(mnachin T180504136): Do not put examples/models |
748 | | - # into core pip packages. Refactor out the necessary utils |
749 | | - # or core models files into a separate package. |
750 | | - "executorch/examples/models": "examples/models", |
751 | | - "executorch/exir": "exir", |
752 | | - "executorch/extension": "extension", |
753 | | - "executorch/kernels/quantized": "kernels/quantized", |
754 | | - "executorch/schema": "schema", |
755 | | - "executorch/devtools": "devtools", |
756 | | - "executorch/devtools/bundled_program": "devtools/bundled_program", |
757 | | - "executorch/runtime": "runtime", |
758 | | - "executorch/util": "util", |
759 | | - }, |
760 | | - cmdclass={ |
761 | | - "build": CustomBuild, |
762 | | - "build_ext": InstallerBuildExt, |
763 | | - "build_py": CustomBuildPy, |
764 | | - }, |
765 | | - ext_modules=get_ext_modules(), |
766 | | -) |
| 809 | +def main(): |
| 810 | + # Check submodules |
| 811 | + check_and_update_submodules() |
| 812 | + |
| 813 | + setup( |
| 814 | + version=Version.string(), |
| 815 | + # TODO(dbort): Could use py_modules to restrict the set of modules we |
| 816 | + # package, and package_data to restrict the set up non-python files we |
| 817 | + # include. See also setuptools/discovery.py for custom finders. |
| 818 | + package_dir={ |
| 819 | + "executorch/backends": "backends", |
| 820 | + "executorch/codegen": "codegen", |
| 821 | + # TODO(mnachin T180504136): Do not put examples/models |
| 822 | + # into core pip packages. Refactor out the necessary utils |
| 823 | + # or core models files into a separate package. |
| 824 | + "executorch/examples/models": "examples/models", |
| 825 | + "executorch/exir": "exir", |
| 826 | + "executorch/extension": "extension", |
| 827 | + "executorch/kernels/quantized": "kernels/quantized", |
| 828 | + "executorch/schema": "schema", |
| 829 | + "executorch/devtools": "devtools", |
| 830 | + "executorch/devtools/bundled_program": "devtools/bundled_program", |
| 831 | + "executorch/runtime": "runtime", |
| 832 | + "executorch/util": "util", |
| 833 | + }, |
| 834 | + cmdclass={ |
| 835 | + "build": CustomBuild, |
| 836 | + "build_ext": InstallerBuildExt, |
| 837 | + "build_py": CustomBuildPy, |
| 838 | + }, |
| 839 | + ext_modules=get_ext_modules(), |
| 840 | + ) |
| 841 | + |
| 842 | + |
| 843 | +if __name__ == "__main__": |
| 844 | + main() |
0 commit comments