Skip to content

Commit fa242fb

Browse files
committed
Update on "[BE] Remind users to update submodule"
Summary: Fixing #7243 As titled. This adds messages when we suspect the user forgot to update git submodule or cleanup the CMake cache. Test Plan: ``` rm -rf third-party/prelude ./install_executorch.sh ``` See the following error messages ``` Command failed: From load at implicit location Caused by: File not found: `prelude//prelude.bzl` CMake Error at build/Utils.cmake:230 (message): executorch: source list generation failed Call Stack (most recent call first): CMakeLists.txt:386 (extract_sources) -- Configuring incomplete, errors occurred! Traceback (most recent call last): File "<string>", line 643, in run File "/home/larryliu/miniconda3/envs/executorch/lib/python3.11/site-packages/setuptools/_distutils/cmd.py", line 388, in spawn spawn(cmd, search_path, dry_run=self.dry_run) File "/home/larryliu/miniconda3/envs/executorch/lib/python3.11/site-packages/setuptools/_distutils/spawn.py", line 68, in spawn raise DistutilsExecError(f"command {cmd!r} failed with exit code {exitcode}") distutils.errors.DistutilsExecError: command '/home/larryliu/miniconda3/envs/executorch/bin/cmake' failed with exit code 1 ... Exception: command '/home/larryliu/miniconda3/envs/executorch/bin/cmake' failed with exit code 1 ExecuTorch: Either CMake cache is outdated or git submodules are not synced. ExecuTorch: Please run the following before retry: ExecuTorch: ./install_executorch.sh --clean ExecuTorch: git submodule update --init --recursive error: subprocess-exited-with-error ``` Reviewers: Subscribers: Tasks: Tags: Differential Revision: [D69156975](https://our.internmc.facebook.com/intern/diff/D69156975) [ghstack-poisoned]
1 parent ec456c9 commit fa242fb

File tree

2 files changed

+147
-47
lines changed

2 files changed

+147
-47
lines changed

build/extract_sources.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import argparse
99
import copy
10+
import logging
1011
import os
1112
import re
1213

@@ -18,7 +19,7 @@
1819
try:
1920
import tomllib # Standard in 3.11 and later
2021
except ModuleNotFoundError:
21-
import tomli as tomllib
22+
import tomli as tomllib # type: ignore[no-redef]
2223

2324
"""Extracts source lists from the buck2 build system and writes them to a file.
2425
@@ -66,6 +67,12 @@
6667
]
6768
"""
6869

70+
# Set up logging
71+
logging.basicConfig(
72+
level=logging.INFO, format="%(asctime)s [ExecuTorch] %(levelname)s: %(message)s"
73+
)
74+
logger = logging.getLogger()
75+
6976

7077
class Target:
7178
"""Parsed [targets.*] entry from the TOML file.
@@ -118,7 +125,22 @@ def get_sources(
118125
)
119126

120127
# Get the complete list of source files that this target depends on.
121-
sources: set[str] = set(runner.run(["cquery", query] + buck_args))
128+
# If user doesn't setup their git submodules correctly, this will fail.
129+
# If we hit here, setup.py:check_submodule() should have already run
130+
# but it could be that the submodules are not synced or there's local changes.
131+
try:
132+
sources: set[str] = set(runner.run(["cquery", query] + buck_args))
133+
except RuntimeError as e:
134+
logger.error(
135+
f"\033[31;1mFailed to query buck for sources. Failed command:\n\n"
136+
f" buck2 cquery {query} {' '.join(buck_args)}\n\n"
137+
"This is likely due "
138+
"to missing git submodules or outdated CMake cache. "
139+
"Please run the following before retry:\033[0m\n\n"
140+
" \033[32;1m./install_executorch.sh --clean\033[0m\n"
141+
" \033[32;1mgit submodule update --init --recursive\033[0m\n"
142+
)
143+
raise e
122144

123145
# Keep entries that match all of the filters.
124146
filters = [re.compile(p) for p in self._config.get("filters", [])]

setup.py

Lines changed: 123 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@
5555
# Import this before distutils so that setuptools can intercept the distuils
5656
# imports.
5757
import setuptools # noqa: F401 # usort: skip
58+
import logging
59+
import subprocess
5860

5961
from distutils import log
60-
from distutils.errors import DistutilsExecError
6162
from distutils.sysconfig import get_python_lib
6263
from pathlib import Path
6364
from typing import List, Optional
@@ -67,9 +68,93 @@
6768
from setuptools.command.build_ext import build_ext
6869
from setuptools.command.build_py import build_py
6970

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+
7077
# For information on setuptools Command subclassing see
7178
# https://setuptools.pypa.io/en/latest/userguide/extension.html
7279

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+
73158

74159
class ShouldBuild:
75160
"""Indicates whether to build various components."""
@@ -639,22 +724,7 @@ def run(self):
639724
# lists.
640725

641726
# 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])
658728

659729
# Build the system.
660730
self.spawn(["cmake", "--build", cmake_cache_dir, *build_args])
@@ -736,31 +806,39 @@ def get_ext_modules() -> List[Extension]:
736806
return ext_modules
737807

738808

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

Comments
 (0)